memhtml 0.2.2 → 0.2.3

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.
@@ -1 +1 @@
1
- {"version":3,"file":"memhtml-mcp.mjs","names":["isTagged","text","nowSecond","summarize"],"sources":["../../apps/cli/dist/serve.js","../../apps/cli/dist/config.js","../../apps/cli/dist/extraction.js","../../apps/cli/dist/api-layer.js","../../apps/cli/dist/errors.js","../../apps/cli/dist/operations.js","../../apps/cli/dist/commands.js","../../apps/cli/dist/prose.js","../../apps/cli/dist/apply.js","../../apps/cli/dist/doctor.js","../../apps/cli/dist/run.js","../../apps/mcp/src/failure.ts","../../apps/mcp/src/tools.ts","../../apps/mcp/src/handlers.ts","../../apps/mcp/src/resources.ts","../../apps/mcp/src/server.ts","../../apps/mcp/src/bin.ts"],"sourcesContent":["import { spawn } from \"node:child_process\";\nimport { access } from \"node:fs/promises\";\nimport { fileURLToPath } from \"node:url\";\nimport { StorageFailure } from \"@memhtml/contracts/errors\";\nimport { Effect } from \"effect\";\n/** An explicit path to the server, for a deployment that does not keep the two apps side by side. */\nexport const MCP_BIN_VAR = \"MEMHTML_MCP_BIN\";\n/**\n * Where the `memhtml-mcp` entry point sits relative to this module, in each layout that ships.\n *\n * Resolved by PATH rather than by `require.resolve`, which the dependency direction forces.\n * `@memhtml/mcp` depends on `@memhtml/cli` for the composition root, so `@memhtml/cli` cannot depend on\n * `@memhtml/mcp` without a cycle, and node resolution can only find a package that is a dependency.\n * (`require.resolve(\"@memhtml/mcp/bin\")` from here raises `MODULE_NOT_FOUND`, which is how this was\n * found.)\n *\n * Two candidates, tried in order, because the two apps are one build and where that build puts them\n * differs:\n *\n * - `./memhtml-mcp.mjs` — the PUBLISHED package, where both bins are entry points of one bundle and\n * land beside each other in `dist/`.\n * - `../../mcp/dist/bin.js` — the workspace, where `apps/cli/dist/serve.js` reaches\n * `apps/mcp/dist/bin.js` two directories up and across.\n *\n * Both are real directories rather than pnpm symlinks into the store, so each walk is stable where it\n * applies. {@link MCP_BIN_VAR} still overrides for a deployment that separates them.\n */\nconst MCP_CANDIDATES = [\"./memhtml-mcp.mjs\", \"../../mcp/dist/bin.js\"];\nexport const mcpEntryPoint = () => Effect.gen(function* () {\n const override = process.env[MCP_BIN_VAR];\n if (override !== undefined && override.trim() !== \"\")\n return override.trim();\n for (const candidate of MCP_CANDIDATES) {\n const path = fileURLToPath(new URL(candidate, import.meta.url));\n const present = yield* Effect.tryPromise({\n try: () => access(path),\n catch: () => \"absent\"\n }).pipe(Effect.as(true), Effect.orElseSucceed(() => false));\n if (present)\n return path;\n }\n // A build that produced the CLI but not the server is the one thing that lands here, so the\n // message names the fix rather than the missing path.\n return yield* Effect.fail(StorageFailure.make({\n operation: `serve.resolveMcp: run \\`pnpm build\\`, or set ${MCP_BIN_VAR}`\n }));\n});\nexport const serveMcp = (memhtmlRoot) => Effect.gen(function* () {\n const entry = yield* mcpEntryPoint();\n return yield* Effect.callback((resume) => {\n const child = spawn(process.execPath, [entry], {\n stdio: \"inherit\",\n env: { ...process.env, MEMHTML_ROOT: memhtmlRoot }\n });\n child.on(\"error\", () => resume(Effect.fail(StorageFailure.make({ operation: \"serve.spawn\" }))));\n child.on(\"exit\", (code, signal) => resume(Effect.succeed({\n server: entry,\n exitCode: code ?? 0,\n signal: signal ?? null\n })));\n // Killing the child on interruption is what keeps a `memhtml serve mcp` that the operator\n // Ctrl-C'd from leaving an orphaned server holding the repo's database open.\n return Effect.sync(() => {\n child.kill();\n });\n });\n});\n//# sourceMappingURL=serve.js.map","import { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { expandRoot } from \"@memhtml/store\";\nimport { Config } from \"effect\";\nimport { MCP_BIN_VAR } from \"./serve.js\";\nexport const CONFIG_VARS = [\n {\n name: \"MEMHTML_ROOT\",\n description: \"The memory repo's root: a git repository holding the corpus and `.memhtml/`.\",\n fallback: join(\"~\", \"memhtml\")\n },\n {\n name: \"MEMHTML_TRACE_ROOT\",\n description: \"Where `memhtml trace index` reads Claude Code transcripts from. Read-only; never written.\",\n fallback: join(\"~\", \".claude\")\n },\n {\n name: \"MEMHTML_AWS_REGION\",\n description: \"The Bedrock region for embeddings and the sleep cycle's four LLM phases.\",\n fallback: \"us-east-1\"\n },\n {\n name: \"AWS_BEARER_TOKEN_BEDROCK\",\n description: \"Bedrock bearer token, read by the AWS SDK itself. Absent means the default credential chain; retrieval then degrades to the lexical floor rather than failing.\",\n fallback: null\n },\n {\n name: \"MEMHTML_EMBED\",\n description: \"`off` disables the embedder entirely. An explicit opt-out, distinct from a missing credential: a missing credential degrades one search at call time, `off` degrades every search, and an operator reading this manifest needs those to be different states.\",\n fallback: \"on\"\n },\n {\n name: \"MEMHTML_LLM\",\n description: \"`off` makes the four LLM sleep phases report `no model bound` and stay `ok`, so a credential-free run is honest rather than red.\",\n fallback: \"on\"\n },\n {\n name: \"MEMHTML_EXTRACT_ENTITIES\",\n description: \"`on` adds one GPT-5.6 Luna call per write batch that extracts `memhtml-entity` metas the ops did not declare. Opt-in, unlike MEMHTML_EMBED, because it changes what a write STORES: extracted entities land in the files as if authored, and the write itself never waits on or fails with the model. A failed extraction is a logged warning and an unextracted batch.\",\n fallback: \"off\"\n },\n {\n /**\n * The name is imported rather than retyped: this row and the `process.env` read at serve.ts:50\n * must name the same string, and a literal here would let a rename disclose a variable nothing\n * reads.\n */\n name: MCP_BIN_VAR,\n description: \"An explicit path to the `memhtml-mcp` entry point, read only by the `memhtml serve mcp` supervisor. Absent means the sibling-path default. The two apps ship as one build, so `apps/cli/dist/serve.js` finds `apps/mcp/dist/bin.js` two directories over. An operator sets it for a split deployment that does not keep the apps side by side; it locates the server rather than configuring the store, so it changes no retrieval behaviour.\",\n fallback: null\n }\n];\n/**\n * `MEMHTML_ROOT`. Re-exported from `@memhtml/store`'s own config rather than redeclared, because the\n * store's config expands a leading `~`. This value arrives from a shell profile, an MCP client\n * config, and a cron line, and only the shell expands tildes on its own.\n */\nexport const MemhtmlRoot = Config.string(\"MEMHTML_ROOT\").pipe(Config.withDefault(join(\"~\", \"memhtml\")), Config.map(expandRoot));\n/**\n * `MEMHTML_TRACE_ROOT`, defaulting to `~/.claude`.\n *\n * A parameter rather than a constant so the trace indexer is drivable against a fixture tree and\n * against an archived copy, which is also what keeps real transcripts out of the test suite.\n */\nexport const TraceRoot = Config.string(\"MEMHTML_TRACE_ROOT\").pipe(Config.withDefault(join(homedir(), \".claude\")), Config.map(expandRoot));\n//# sourceMappingURL=config.js.map","import { ModelUnavailable } from \"@memhtml/contracts/errors\";\nimport { wrapAsData } from \"@memhtml/llm\";\nimport { Effect } from \"effect\";\n/**\n * GPT-5.6 Luna, the fast high-volume model on the mantle endpoint. A constant rather than config\n * because the schema below is tested against this model's strict-mode behaviour. Changing the model\n * is a code change with a test run, not an env var.\n */\nexport const EXTRACTION_MODEL_ID = \"openai.gpt-5.6-luna\";\n/** Entity types the prompt offers. Downstream the vocabulary is open: `unknown:` is a valid store type. */\nconst ENTITY_TYPES = [\"person\", \"org\", \"service\", \"place\", \"work\", \"concept\", \"event\"];\n/**\n * The strict output schema. `additionalProperties: false` and `required` on every level because\n * the Responses API's `strict: true` demands both, and a lax schema invites the model to answer\n * with prose keys the parser would then be guessing at.\n */\nconst RESPONSE_SCHEMA = {\n type: \"object\",\n properties: {\n items: {\n type: \"array\",\n items: {\n type: \"object\",\n properties: {\n index: { type: \"integer\" },\n entities: {\n type: \"array\",\n items: {\n type: \"object\",\n properties: {\n type: { type: \"string\", enum: [...ENTITY_TYPES] },\n name: { type: \"string\" }\n },\n required: [\"type\", \"name\"],\n additionalProperties: false\n }\n }\n },\n required: [\"index\", \"entities\"],\n additionalProperties: false\n }\n }\n },\n required: [\"items\"],\n additionalProperties: false\n};\nconst INSTRUCTIONS = \"Extract the named entities each memory mentions. \" +\n \"An entity is a specific nameable thing a later search would look up: a person, an \" +\n \"organization, a service or system, a place, a titled work, a defined concept, or a named \" +\n \"event. Skip generic nouns, dates, and quantities. Use the memory's own spelling for the \" +\n \"name. Return one result per input index, with an empty entities array when a memory names \" +\n \"nothing.\";\n/** The request body for one batch. Exported for the wire test, where the schema is the contract. */\nexport const requestBodyOf = (modelId, items) => JSON.stringify({\n model: modelId,\n instructions: INSTRUCTIONS,\n input: wrapAsData(\"memories\", JSON.stringify(items.map((item, index) => ({ index, title: item.title, text: item.text })))),\n text: {\n format: {\n type: \"json_schema\",\n name: \"entities\",\n strict: true,\n schema: RESPONSE_SCHEMA\n }\n }\n});\n/**\n * Decode one Responses-API payload into index-aligned `type:name` arrays.\n *\n * Total over unknown input: every malformed shape returns `undefined` and the caller maps that to\n * `ModelUnavailable`. A payload this code cannot read carries no answer, and treating it as\n * \"no entities\" would record a model failure as a fact about the corpus.\n */\nexport const entitiesOf = (payload, expected) => {\n const text = outputTextOf(payload);\n if (text === undefined)\n return undefined;\n let parsed;\n try {\n parsed = JSON.parse(text);\n }\n catch {\n return undefined;\n }\n const items = parsed.items;\n if (!Array.isArray(items))\n return undefined;\n const results = Array.from({ length: expected }, () => []);\n for (const item of items) {\n const index = item.index;\n const entities = item.entities;\n if (typeof index !== \"number\" || !Number.isInteger(index) || index < 0 || index >= expected) {\n continue;\n }\n if (!Array.isArray(entities))\n continue;\n results[index] = entities.flatMap((entity) => {\n const type = entity.type;\n const name = entity.name;\n if (typeof type !== \"string\" || typeof name !== \"string\")\n return [];\n const trimmedName = name.trim();\n return trimmedName === \"\" ? [] : [`${type}:${trimmedName}`];\n });\n }\n return results;\n};\n/** The assistant message text out of a Responses payload, or `undefined` off-shape. */\nconst outputTextOf = (payload) => {\n const output = payload.output;\n if (!Array.isArray(output))\n return undefined;\n for (const entry of output) {\n if (entry.type !== \"message\")\n continue;\n const content = entry.content;\n if (!Array.isArray(content))\n continue;\n for (const part of content) {\n const text = part.text;\n if (part.type === \"output_text\" && typeof text === \"string\") {\n return text;\n }\n }\n }\n return undefined;\n};\n/**\n * Per-call ceiling. Generous against the probed ~1s because a batch of 256 ops is a bigger\n * prompt than the probe's one sentence, and a late abort costs only this batch's entities. The\n * write itself is unaffected.\n */\nconst EXTRACT_TIMEOUT_MS = 60_000;\n/** The extractor over a transport. The transport owns the endpoint; this owns prompt and parse. */\nexport const makeEntityExtractor = (transport, modelId) => ({\n extract: (items) => items.length === 0\n ? Effect.succeed([])\n : Effect.gen(function* () {\n const payload = yield* Effect.tryPromise({\n try: (signal) => {\n const timeout = AbortSignal.timeout(EXTRACT_TIMEOUT_MS);\n return transport.post(requestBodyOf(modelId, items), AbortSignal.any([signal, timeout]));\n },\n catch: (cause) => ModelUnavailable.make({\n modelId,\n reason: cause instanceof Error ? `${cause.name}: ${cause.message}` : String(cause)\n })\n });\n const entities = entitiesOf(payload, items.length);\n if (entities === undefined) {\n return yield* Effect.fail(ModelUnavailable.make({ modelId, reason: \"unreadable extraction payload\" }));\n }\n return entities;\n })\n});\n/**\n * The production transport: bearer-token fetch against the mantle endpoint.\n *\n * A non-2xx status is a rejection carrying the status and the body's first line, because mantle\n * reports quota and auth failures as structured JSON the operator needs verbatim. Folding it into\n * a generic message was the mistake the embeddings lane made first.\n */\nexport const fetchMantleTransport = (region, token) => ({\n post: async (body, signal) => {\n const response = await fetch(`https://bedrock-mantle.${region}.api.aws/openai/v1/responses`, {\n method: \"POST\",\n headers: { Authorization: `Bearer ${token}`, \"Content-Type\": \"application/json\" },\n body,\n signal\n });\n const text = await response.text();\n if (!response.ok) {\n throw new Error(`mantle ${response.status}: ${text.slice(0, 200)}`);\n }\n return JSON.parse(text);\n }\n});\n//# sourceMappingURL=extraction.js.map","import { readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { hasConsolidatorCredentials, makeConsolidator } from \"@memhtml/consolidator\";\nimport { StorageFailure } from \"@memhtml/contracts/errors\";\nimport { DatabaseService, Indexer, IndexGit, IndexRecorder, MIGRATIONS_DIR, makeDatabase, makeGitPort, makeIndexer, makeIndexRecorder, makeRetrieval, Retrieval, STATE_MIGRATIONS_DIR } from \"@memhtml/index\";\nimport { EMBED_DIM, EMBED_WATERMARK, Embeddings, EmbeddingsLive, ModelClient, ModelClientLive } from \"@memhtml/llm\";\nimport { makeSleep, Sleep } from \"@memhtml/sleep\";\nimport { Git, INDEX_DB_PATH, makeGit, makeStore, STATE_DB_PATH, Store } from \"@memhtml/store\";\nimport { Config, Context, Effect, Layer } from \"effect\";\nimport { MemhtmlRoot, TraceRoot } from \"./config.js\";\nimport { EXTRACTION_MODEL_ID, fetchMantleTransport, makeEntityExtractor } from \"./extraction.js\";\n/**\n * The service tags, re-exported from the composition root.\n *\n * A handler imports its services from here rather than from six packages, so \"which tag does this\n * come from\" is answered once. `IndexGit` is the case that needs care. `@memhtml/store` publishes\n * `memhtml/Git` for its `GitShape` and `@memhtml/index` publishes `memhtml/IndexGit` for a different\n * shape, and the two appearing side by side in this list keeps them from being confused.\n */\nexport { DatabaseService, Indexer, IndexGit, IndexRecorder, Retrieval } from \"@memhtml/index\";\nexport { Embeddings, ModelClient } from \"@memhtml/llm\";\nexport { Sleep } from \"@memhtml/sleep\";\nexport { Git, Store } from \"@memhtml/store\";\nexport const Roots = Context.Service(\"memhtml/Roots\");\n/**\n * The roots layer. `repoOverride` is `--repo`, and it wins over `MEMHTML_ROOT` so an operator running\n * against a second repo does not have to mutate their environment to do it.\n */\nexport const layerRoots = (repoOverride) => Layer.effect(Roots)(Effect.gen(function* () {\n const fromConfig = yield* MemhtmlRoot;\n const traceRoot = yield* TraceRoot;\n const memhtmlRoot = repoOverride !== undefined && repoOverride.trim() !== \"\" ? repoOverride.trim() : fromConfig;\n return { memhtmlRoot, traceRoot };\n})).pipe(Layer.orDie);\n/**\n * The database, rooted in the repo's `.memhtml/`.\n *\n * Both planes on one connection, always. The salience retrieval arm `LEFT JOIN`s `state.access`\n * in the same statement as `main.files`, so a connection without the attachment silently drops\n * that arm. `DatabaseShape.hasState` is what the arm registry consults, and it is `false` only\n * for a caller that deliberately asked for the index alone.\n */\nexport const layerDatabase = Layer.effect(DatabaseService)(Effect.gen(function* () {\n const roots = yield* Roots;\n return yield* makeDatabase(join(roots.memhtmlRoot, INDEX_DB_PATH), MIGRATIONS_DIR, {\n path: join(roots.memhtmlRoot, STATE_DB_PATH),\n migrationsDir: STATE_MIGRATIONS_DIR\n });\n})).pipe(Layer.orDie);\n/** Git over the repo root. The store's shape, under the store's own tag. */\nexport const layerGit = Layer.effect(Git)(Effect.gen(function* () {\n const roots = yield* Roots;\n return makeGit(roots.memhtmlRoot);\n}));\n/**\n * The indexer's git port, over the store's git service.\n *\n * `readFile` is `Effect.tryPromise` rather than `Effect.promise`. `Effect.promise` turns an ENOENT\n * into a defect, and a defect travels past the `Effect.catch` the indexer wraps each projection in.\n * An absent path would then kill the fiber mid-update instead of becoming the counted skip the indexer\n * already handles. An agent listing a path it just archived is the normal case, which makes this\n * the difference between a working `index update` and a crash on an ordinary day.\n */\nexport const layerIndexGit = Layer.effect(IndexGit)(Effect.gen(function* () {\n const roots = yield* Roots;\n const git = yield* Git;\n return makeGitPort({\n git,\n readFile: (path) => Effect.tryPromise({\n try: () => readFile(join(roots.memhtmlRoot, path), \"utf8\"),\n catch: (cause) => cause\n }),\n fail: (operation) => Effect.fail(StorageFailure.make({ operation: `git.${operation}` }))\n });\n}));\n/** The recorder: the dedupe lookup the store gates writes on, and the session-link writer. */\nexport const layerRecorder = Layer.effect(IndexRecorder)(Effect.gen(function* () {\n const db = yield* DatabaseService;\n return makeIndexRecorder(db);\n}));\n/**\n * The store, with the recorder's dedupe hook attached.\n *\n * `onMove` mirrors `state.access.path` across an archive. Cross-database foreign keys do not\n * exist, so the mirror is an explicit call at the one place a path can change; without it every\n * eviction leaves an orphan access row and the salience arm stops finding the memory it describes.\n */\nexport const layerStore = Layer.effect(Store)(Effect.gen(function* () {\n const git = yield* Git;\n const recorder = yield* IndexRecorder;\n const db = yield* DatabaseService;\n return makeStore(git, {\n dedupeLookup: recorder.activePathForHash,\n onMove: (from, to) => db.run(\"UPDATE state.access SET path = ? WHERE path = ?\", [to, from]).pipe(\n // A move whose mirror fails must not fail the move. The archive commit has already\n // landed, and the orphan row is what `memhtml doctor` reports. Losing the commit to a\n // bookkeeping error would be worse than an orphan.\n Effect.catch((error) => Effect.logWarning(`state.access mirror missed ${from} -> ${to}: ${error.operation}`)))\n });\n}));\nexport const Embedder = Context.Service(\"memhtml/Embedder\");\n/**\n * Bedrock embeddings when the region resolves, absent when `MEMHTML_EMBED` is `off`.\n *\n * The switch is an explicit opt-out rather than credential sniffing. A missing credential is\n * discovered at call time and degrades one search. A deliberate `off` degrades every search, and\n * an operator reading `memhtml manifest` needs those to be different states.\n */\nexport const layerEmbedder = Layer.effect(Embedder)(Effect.gen(function* () {\n const enabled = yield* Config.string(\"MEMHTML_EMBED\").pipe(Config.withDefault(\"on\"), Config.map((value) => value.trim().toLowerCase() !== \"off\"));\n if (!enabled)\n return { document: undefined, query: undefined };\n const embeddings = yield* Embeddings;\n return { document: embeddings, query: embeddings };\n})).pipe(Layer.orDie);\n/** A layer supplying the embedder ports directly, for a test that wants a deterministic vector. */\nexport const layerEmbedderFrom = (embedder) => Layer.succeed(Embedder)(embedder);\n/** The indexer, over the database and the git port. */\nexport const layerIndexer = Layer.effect(Indexer)(Effect.gen(function* () {\n const db = yield* DatabaseService;\n const git = yield* IndexGit;\n const embedder = yield* Embedder;\n return makeIndexer({\n db,\n git,\n embedWatermark: EMBED_WATERMARK,\n embedDim: EMBED_DIM,\n embeddings: embedder.document,\n // Wall-clock through the Effect clock would need an Effect here; the indexer wants a plain\n // thunk for `indexed_at`. A test that must pin the instant builds the indexer directly.\n now: () => new Date().toISOString()\n });\n}));\n/** Retrieval, over the database and the query embedder. */\nexport const layerRetrieval = Layer.effect(Retrieval)(Effect.gen(function* () {\n const db = yield* DatabaseService;\n const embedder = yield* Embedder;\n return makeRetrieval({ db, embeddings: embedder.query });\n}));\nexport const ModelPort = Context.Service(\"memhtml/ModelPort\");\nexport const layerModelPort = Layer.effect(ModelPort)(Effect.gen(function* () {\n const enabled = yield* Config.string(\"MEMHTML_LLM\").pipe(Config.withDefault(\"on\"), Config.map((value) => value.trim().toLowerCase() !== \"off\"));\n if (!enabled)\n return { model: undefined };\n return { model: yield* ModelClient };\n})).pipe(Layer.orDie);\n/** A layer supplying the model port directly, for a test that scripts the model's answers. */\nexport const layerModelFrom = (model) => Layer.succeed(ModelPort)({ model });\nexport const ExtractorPort = Context.Service(\"memhtml/ExtractorPort\");\nexport const layerExtractorPort = Layer.effect(ExtractorPort)(Effect.gen(function* () {\n const enabled = yield* Config.string(\"MEMHTML_EXTRACT_ENTITIES\").pipe(Config.withDefault(\"off\"), Config.map((value) => value.trim().toLowerCase() === \"on\"));\n if (!enabled)\n return { extractor: undefined };\n const region = yield* Config.string(\"MEMHTML_AWS_REGION\").pipe(Config.withDefault(\"us-east-1\"));\n const token = yield* Config.string(\"AWS_BEARER_TOKEN_BEDROCK\").pipe(Config.withDefault(\"\"));\n if (token === \"\") {\n yield* Effect.logWarning(\"MEMHTML_EXTRACT_ENTITIES=on but AWS_BEARER_TOKEN_BEDROCK is absent; writes proceed unextracted\");\n return { extractor: undefined };\n }\n return {\n extractor: makeEntityExtractor(fetchMantleTransport(region, token), EXTRACTION_MODEL_ID)\n };\n})).pipe(Layer.orDie);\n/** A layer supplying the extractor directly, for a test that scripts the extraction answers. */\nexport const layerExtractorFrom = (extractor) => Layer.succeed(ExtractorPort)({ extractor });\nexport const ConsolidatorPortService = Context.Service(\"memhtml/ConsolidatorPort\");\n/**\n * Two gates, both cheap, both before anything is spawned.\n *\n * `MEMHTML_LLM=off` is the same explicit opt-out `layerModelPort` reads, and it covers the consolidator\n * too, because an operator who turned the models off did not mean \"except the expensive agent\".\n *\n * `hasConsolidatorCredentials` is the credential preflight, read here as well as inside the client.\n * The redundancy is deliberate and the two reads do different jobs. This one decides whether the phase\n * sees a consolidator at all, so a credential-free environment gets `detail: \"no consolidator bound\"`,\n * the same shape the other three LLM phases report with no model, rather than a bound port that\n * fails on every call and reports a degradation. CI has no credentials and must read as skipped rather\n * than degraded.\n *\n * The check cannot be skipped in favour of the client's own, because the provider is lazy.\n * `createAmazonBedrock` and `provider(modelId)` both succeed with zero credentials and nothing fails\n * until the first request (verified in T-EVE-1's probe, recorded at\n * `apps/consolidator/src/contract.ts:301-319`).\n *\n * **`env` is a parameter, and it has to be.** `Config` reads its values through a `ConfigProvider`,\n * which a test substitutes, while `hasConsolidatorCredentials` reads `process.env` directly, and\n * effect's default provider snapshots `process.env` at module load (probed 2026-08-08: mutating\n * `process.env.MEMHTML_LLM` after importing `effect` changes nothing `Config.string` returns). A test\n * that set both by mutation would read a stale snapshot for one gate and a live object for the other,\n * and the two gates would disagree about which environment they are in. Threading the credential\n * environment through as an argument makes both injectable from one call. See\n * `apps/cli/tests/consolidator-wiring.test.ts`, where that disagreement produced a false defect\n * before this parameter existed.\n *\n * **It now requires `RootsShape`, for `traceRoot`.** That is how transcripts reach the agent. The\n * consolidator mounts the trace root read-only rather than sending transcripts as a model message\n * (`apps/consolidator/src/client.ts`, `manifestFor`, records what the superseded path actually did).\n * The root is `MEMHTML_TRACE_ROOT` and this file is where config becomes services, so it is read from the\n * same `Roots` service `memhtml trace index` scans with. One resolution of one variable is what\n * keeps the mounted tree and the indexed `traces` rows describing the same directory. A second\n * `Config.string(\"MEMHTML_TRACE_ROOT\")` here would be a second place the `~/.claude` default lives.\n */\nexport const layerConsolidatorPort = (env = process.env) => Layer.effect(ConsolidatorPortService)(Effect.gen(function* () {\n const roots = yield* Roots;\n const enabled = yield* Config.string(\"MEMHTML_LLM\").pipe(Config.withDefault(\"on\"), Config.map((value) => value.trim().toLowerCase() !== \"off\"));\n if (!enabled)\n return { consolidator: undefined };\n if (!hasConsolidatorCredentials(env)) {\n yield* Effect.logDebug(\"trace consolidation unbound: no Bedrock credentials in the environment\");\n return { consolidator: undefined };\n }\n /**\n * The client is built over the same environment the gate just read. A client over ambient\n * `process.env` while the gate read an injected one would pass the gate and fail at the call,\n * which is the degradation-instead-of-skip outcome this gate exists to prevent.\n */\n return { consolidator: makeConsolidator({ env, traceRoot: roots.traceRoot }) };\n})).pipe(Layer.orDie);\n/** A layer supplying the consolidator directly, for a test that scripts its candidates. */\nexport const layerConsolidatorFrom = (consolidator) => Layer.succeed(ConsolidatorPortService)({ consolidator });\n/**\n * The sleep runner over the same services every other command uses.\n *\n * `@memhtml/sleep` deliberately ships no `SleepLive` that resolves its own git, database, and model.\n * A layer that built its own would open a second connection to one database file and a second git\n * wrapper on one root, and the run would then curate a corpus the indexer is not describing.\n */\nexport const layerSleep = Layer.effect(Sleep)(Effect.gen(function* () {\n const git = yield* Git;\n const store = yield* Store;\n const db = yield* DatabaseService;\n const indexer = yield* Indexer;\n const modelPort = yield* ModelPort;\n const consolidatorPort = yield* ConsolidatorPortService;\n return makeSleep({\n git,\n store,\n db,\n indexer,\n model: modelPort.model,\n consolidator: consolidatorPort.consolidator\n });\n}));\n/**\n * Everything above the embedder and the model, as one layer requiring only the roots and those two.\n *\n * Written top-down because that is what `Layer.provideMerge(that)` means. It feeds `that`'s output\n * into `self`'s requirements, so the consumer is `self` and each `.pipe` step below adds the level\n * beneath it. Chaining in dependency order instead, with the database first, reads naturally and is\n * wrong. It would provide git to the database and leave `GitShape` in the final requirement set, which\n * typechecks as an unsatisfied layer rather than failing where the mistake is.\n *\n * Split out from `layerApp` so a test provides a deterministic embedder and a real temp repo with\n * no Bedrock anywhere in the graph. The composition under test is then the same composition\n * production runs, which a hand-assembled test wiring would not be.\n */\nexport const layerCore = Layer.mergeAll(layerSleep, layerRetrieval).pipe(Layer.provideMerge(Layer.mergeAll(layerIndexer, layerStore)), Layer.provideMerge(Layer.mergeAll(layerIndexGit, layerRecorder)), Layer.provideMerge(Layer.mergeAll(layerDatabase, layerGit)));\n/**\n * The production graph: roots from config, Bedrock behind both model ports, everything else over\n * them. `repoOverride` is `--repo`.\n *\n * This is the one composition production runs. `memhtml serve mcp` runs the same one in a child\n * process, so an MCP tool and its CLI twin cannot be looking at different databases.\n */\nexport const layerApp = (repoOverride) => layerCore.pipe(Layer.provideMerge(Layer.mergeAll(layerRoots(repoOverride), layerEmbedder.pipe(Layer.provide(EmbeddingsLive), Layer.orDie), layerModelPort.pipe(Layer.provide(ModelClientLive), Layer.orDie), layerExtractorPort, \n/**\n * `layerRoots` is provided to the consolidator port explicitly rather than merged beside it.\n * The consolidator needs `traceRoot` to mount, and a sibling in one `mergeAll` is not a\n * dependency. The roots layer is built once with `repoOverride` and fed in, so a `--repo`\n * run and the mounted trace root cannot come from two different resolutions.\n */\nlayerConsolidatorPort().pipe(Layer.provide(layerRoots(repoOverride))))));\n/**\n * The graph a test provides: the real composition, with the embedder and the model injected.\n *\n * Same `layerCore`, so a test exercises the wiring production uses rather than a parallel one. The\n * only substituted edges are the two that reach the network.\n */\nexport const layerAppWith = (options) => layerCore.pipe(Layer.provideMerge(Layer.mergeAll(layerRoots(options.repo), layerEmbedderFrom(options.embedder), layerModelFrom(options.model), layerConsolidatorFrom(options.consolidator), layerExtractorFrom(options.extractor))));\n//# sourceMappingURL=api-layer.js.map","import { fail } from \"./envelope.js\";\nconst isTagged = (value) => typeof value === \"object\" &&\n value !== null &&\n typeof value._tag === \"string\";\nconst text = (value) => (typeof value === \"string\" ? value : undefined);\nconst paths = (value) => Array.isArray(value) ? value.filter((entry) => typeof entry === \"string\") : [];\n/**\n * The code for a tag.\n *\n * `GitFailure` lives in `@memhtml/store` rather than `@memhtml/contracts`. It is the one error class\n * outside the shared contracts package, because it carries a git subcommand name and only the store\n * speaks git. It maps to `ERR_GIT` here at the CLI edge, the only place the two vocabularies meet.\n *\n * `EmbedModelMismatch` is a plain class rather than a schema error (it predates the contracts\n * package), so it arrives with the same `_tag` shape and needs no special case.\n */\nexport const codeFor = (error) => {\n if (!isTagged(error))\n return \"ERR_UNKNOWN\";\n switch (error._tag) {\n case \"GitFailure\":\n return \"ERR_GIT\";\n case \"StorageFailure\":\n return \"ERR_STORAGE\";\n case \"InvalidMemory\":\n return \"ERR_INVALID_MEMORY\";\n case \"PathNotFound\":\n return \"ERR_PATH_NOT_FOUND\";\n case \"WriteConflict\":\n return \"ERR_WRITE_CONFLICT\";\n case \"DirtyTree\":\n return \"ERR_DIRTY_TREE\";\n case \"DuplicateContent\":\n return \"ERR_DUPLICATE_CONTENT\";\n case \"ModelUnavailable\":\n return \"ERR_MODEL_UNAVAILABLE\";\n case \"EmbedModelMismatch\":\n return \"ERR_EMBED_MODEL_MISMATCH\";\n case \"DiscriminationFailed\":\n return \"ERR_DISCRIMINATION_FAILED\";\n default:\n return \"ERR_UNKNOWN\";\n }\n};\n/**\n * The human message for a failure.\n *\n * Deliberately narrow. Every payload field named here is one a caller can act on: a path to\n * re-read, two shas to reconcile, a model to check. The message omits the driver's own text, the\n * SQL, the git argv, and any memory body. Each typed error class already dropped those at its\n * adapter edge so a tool response could not carry corpus content, and reconstructing them here\n * would undo that.\n */\nexport const messageFor = (error) => {\n if (!isTagged(error))\n return String(error);\n switch (error._tag) {\n case \"GitFailure\":\n return `git ${text(error.command) ?? \"command\"} failed (exit ${String(error.exitCode)})`;\n case \"StorageFailure\":\n return `storage operation failed: ${text(error.operation) ?? \"unknown\"}`;\n case \"InvalidMemory\":\n return `invalid memory: ${text(error.reason) ?? \"unstated reason\"}`;\n case \"PathNotFound\":\n return `no memory at ${text(error.path) ?? \"the given path\"}`;\n case \"WriteConflict\":\n return `write conflict on ${text(error.path) ?? \"a path\"}: ours ${text(error.ourSha) ?? \"?\"}, theirs ${text(error.theirSha) ?? \"?\"}`;\n case \"DirtyTree\":\n return `the working tree has uncommitted changes: ${paths(error.paths).join(\", \")}`;\n case \"DuplicateContent\":\n return `this content already lives at ${text(error.existingPath) ?? \"another path\"}`;\n case \"ModelUnavailable\":\n return `bedrock refused ${text(error.modelId) ?? \"the model\"}: ${text(error.reason) ?? \"no reason given\"}`;\n case \"EmbedModelMismatch\":\n return `the index was built in vector space ${text(error.stored) ?? \"?\"}, configured is ${text(error.configured) ?? \"?\"}`;\n case \"LlmContractViolation\":\n return `the model broke its structured-output contract: ${text(error.reason) ?? \"no reason given\"}`;\n case \"DiscriminationFailed\":\n return text(error.reason) ?? \"the discrimination gate refused\";\n default:\n return `unexpected failure: ${error._tag}`;\n }\n};\n/**\n * What to do about a failure, as commands the caller can run.\n *\n * A suggestion is part of the contract. An agent that receives `ERR_INDEX_STALE` and a\n * `memhtml index update` suggestion can recover in one step without a round trip to a human. Absent\n * suggestions are an empty array rather than a null, so a parser never branches on presence.\n *\n * A record rather than a `switch`, which is what closes the drift class. Every `memhtml …` string\n * below names a command from the table in `commands.ts`, and a rename there used to leave a stale\n * suggestion here that nothing failed on. A record's keys and arms are both walkable, so the suite\n * can enumerate every tag, run every suggestion through the real `parseArgv`, and fail on a name the\n * table does not hold. A `switch` cannot expose any of that to a test.\n *\n * Validated in the test rather than here on purpose: `errors.ts` importing `commands.ts` closes the\n * cycle `commands.ts` → `operations.ts` → `errors.ts` (commands.ts:8, operations.ts:35) and leaves\n * `AUTHORABLE_RELS` undefined in `commands.ts`'s module body under an operations-first import order.\n */\nexport const SUGGESTIONS = {\n PathNotFound: () => [\"memhtml search <what you were looking for>\", \"memhtml list\"],\n WriteConflict: (error) => [\n `memhtml read ${text(error.path) ?? \"<path>\"}`,\n \"re-apply the change to current content\"\n ],\n DirtyTree: () => [\"git -C $MEMHTML_ROOT status\", \"commit or stash the changes, then retry\"],\n DuplicateContent: (error) => [`memhtml read ${text(error.existingPath) ?? \"<path>\"}`],\n EmbedModelMismatch: () => [\"memhtml index rebuild --embed\"],\n ModelUnavailable: () => [\"retry: search still works on the lexical floor\", \"memhtml status\"],\n InvalidMemory: () => [\"memhtml manifest\"],\n // No `--json`: it is a global flag defaulting to true (commands.ts:36-42), so naming it here only\n // gave the suggestion a second way to go stale.\n DiscriminationFailed: () => [\n \"memhtml eval discriminate\",\n \"memhtml sleep review\",\n \"git branch -D <run-id>\"\n ]\n};\nexport const suggestionsFor = (error) => {\n if (!isTagged(error))\n return [];\n return SUGGESTIONS[error._tag]?.(error) ?? [];\n};\n/** A typed failure as an envelope. The one call every command's error path makes. */\nexport const failureFor = (error) => fail(codeFor(error), messageFor(error), suggestionsFor(error));\n//# sourceMappingURL=errors.js.map","import { isEdgeRel, MEMORY_RELS, relClassFor, TASK_RELS } from \"@memhtml/contracts/edges\";\nimport { InvalidMemory } from \"@memhtml/contracts/errors\";\nimport { normalizePath } from \"@memhtml/contracts/paths\";\nimport { isTaskStatus, isWritableMemoryType, TASK_STATUSES, WRITABLE_MEMORY_TYPES } from \"@memhtml/contracts/types\";\nimport { frameKeyOf, REINFORCE_SIGNALS } from \"@memhtml/domain\";\nimport { isValidDatetime, setMeta } from \"@memhtml/html\";\nimport { DatabaseService, Indexer, IndexRecorder, persistScanned, Retrieval, readIndexState, readWatermark, reinforce, sanitizeFtsQuery } from \"@memhtml/index\";\nimport { EMBED_WATERMARK } from \"@memhtml/llm\";\nimport { attemptIo, commitSubject, Store } from \"@memhtml/store\";\nimport { mergeTailExtract, scanTraceRoot } from \"@memhtml/traces\";\nimport { Effect } from \"effect\";\nimport { ExtractorPort, Roots } from \"./api-layer.js\";\nimport { codeFor, messageFor } from \"./errors.js\";\n/**\n * The use cases, one per tool. Every CLI command and every MCP tool is a thin adapter over exactly\n * one of these, which makes `memhtml search` and `memory_search` provably the same query\n * rather than two implementations that agree today.\n *\n * Nothing here parses argv or builds an envelope. A function takes decoded parameters, returns a\n * typed result, and fails with a typed error. The adapters own the shape of the wire.\n */\n/** Wall-clock as an ISO-8601 UTC second, through the Effect clock so a test can pin it. */\nconst nowSecond = Effect.clockWith((clock) => Effect.map(clock.currentTimeMillis, (millis) => `${new Date(millis).toISOString().slice(0, 19)}Z`));\n/** Drop `undefined`-valued keys, so `exactOptionalPropertyTypes` sees an absent key. */\nconst defined = (input) => {\n const out = {};\n for (const [key, value] of Object.entries(input))\n if (value !== undefined)\n out[key] = value;\n return out;\n};\n/**\n * Narrow an untrusted memory-type string.\n *\n * `arc` is refused even though it is a valid storage type. An arc is synthesized by the sleep\n * cycle from many memories, so an agent naming one directly would be asserting a conclusion the\n * corpus has not earned. The vocabulary the tool exposes is therefore narrower than the CHECK\n * constraint by exactly that one value.\n */\nexport const decodeWritableType = (value) => WRITABLE_MEMORY_TYPES.includes(value)\n ? Effect.succeed(value)\n : Effect.fail(InvalidMemory.make({\n reason: `unknown memory type: ${value}. One of: ${WRITABLE_MEMORY_TYPES.join(\", \")}`\n }));\n/**\n * The rels a CALLER may author: the nine memory rels plus the two task rels.\n *\n * The two classes the vocabulary withholds are the ones the system mints itself. A `person` edge is\n * written by sleep's person-links phase against `resources/people/*`, and `from_session` is written\n * by the write path from the provenance a caller already supplied. Authoring either by hand would put\n * a hand-guessed row where a derivation belongs.\n */\nexport const AUTHORABLE_RELS = [...MEMORY_RELS, ...TASK_RELS];\n/**\n * Narrow an untrusted rel to one a caller may author.\n *\n * A `blocks` edge between two tasks is a legitimate authored assertion, so the task class is in and\n * refusing it would leave the task graph writable by nothing. Whether the rel agrees with its\n * endpoints is not this function's business. `@memhtml/store`'s `linkMemories` reads both files'\n * `memhtml-type` and refuses a mismatch, and it is the only layer that can see the endpoints at all.\n *\n * `memory_link`'s MCP schema stays memory-rels-only (`MemoryRelSchema`, `apps/mcp/src/tools.ts`) and\n * refuses a task rel at decode. That is one narrow surface for agents and one wider one for the\n * operator, with the store's endpoint guard governing both.\n */\nexport const decodeAuthorableRel = (value) => isEdgeRel(value) && AUTHORABLE_RELS.includes(value)\n ? Effect.succeed(value)\n : Effect.fail(InvalidMemory.make({\n reason: `unknown rel: ${value}. One of: ${AUTHORABLE_RELS.join(\", \")}`\n }));\n/** Narrow an untrusted task status. */\nexport const decodeTaskStatus = (value) => isTaskStatus(value)\n ? Effect.succeed(value)\n : Effect.fail(InvalidMemory.make({\n reason: `unknown task status: ${value}. One of: ${TASK_STATUSES.join(\", \")}`\n }));\n/**\n * Narrow an untrusted due date, using the FORMAT's own validator.\n *\n * `isValidDatetime` rather than a local regex or `Date.parse`, because `files.due_at` is compared and\n * ordered as a string. `2026-8-9` and `Aug 9 2026` both parse as instants and neither sorts alongside\n * `2026-08-09`, so the overdue query would silently miss them. Reusing the parser's own validator is\n * also what keeps this refusal and the parser's violation from drifting apart.\n */\nexport const decodeDueAt = (value) => isValidDatetime(value)\n ? Effect.succeed(value)\n : Effect.fail(InvalidMemory.make({\n reason: `due date is not an ISO date or datetime: ${value}. Expected YYYY-MM-DD or YYYY-MM-DDThh:mm:ssZ`\n }));\n/** Narrow an untrusted reinforcement signal. */\nexport const decodeSignal = (value) => REINFORCE_SIGNALS.includes(value)\n ? Effect.succeed(value)\n : Effect.fail(InvalidMemory.make({\n reason: `unknown signal: ${value}. One of: ${REINFORCE_SIGNALS.join(\", \")}`\n }));\n/**\n * Record the session link for an operation that touched a path.\n *\n * Fire-and-log rather than fail-the-call. The link is a note about what happened, and losing the\n * memory over a failed note about it would invert the priority. The file's own head already\n * carries `memhtml-session`/`memhtml-prompt`/`memhtml-turn`, so the durable half of the link survives even\n * when this row does not.\n */\nconst recordLink = (path, linkKind, provenance, at) => Effect.gen(function* () {\n if (provenance.sessionId === undefined || provenance.sessionId === \"\")\n return;\n const recorder = yield* IndexRecorder;\n yield* recorder\n .recordLink({\n path,\n sessionId: provenance.sessionId,\n linkKind,\n at,\n ...defined({ promptId: provenance.promptId, turnUuid: provenance.turnUuid })\n })\n .pipe(Effect.catch((error) => Effect.logWarning(`session link not recorded for ${path}: ${error.operation}`)));\n});\n/**\n * Bring the index up to the commit a write just made.\n *\n * `indexer.update()` rather than `indexPaths([…])`, and the difference changes behaviour twice:\n *\n * 1. **`indexPaths` cannot express a rename.** Every correction and every archive is a `git mv`, and\n * an index that handled one as \"index the destination\" leaves the source row live. The archived\n * memory stays in `memhtml list`, `files` gains a row the tree does not have, and the chunk rows the\n * move exists to preserve are duplicated under two paths. `update()` reads `diff --name-status -M`,\n * sees the `R`, and re-points the row, which keeps the embedding and drops nothing.\n * 2. **`indexPaths` never records the watermark.** `index_state.head_sha` is what makes\n * \"the index describes the current commit\" answerable at all, so a write path that skipped it\n * would leave `memhtml status` reporting `index_fresh: false` forever and `index update` re-deriving\n * from a stale base.\n *\n * The cost is one `git diff` over one commit, which is what the watermark exists to bound. On the\n * very first write the watermark is absent and `update()` falls through to a full rebuild. That is\n * correct, and cheap on a corpus that has one file in it.\n */\nconst reindex = () => Effect.gen(function* () {\n const indexer = yield* Indexer;\n return yield* indexer.update({ embed: true });\n});\n/**\n * Decode untrusted write parameters into the store's `WriteInput`.\n *\n * Shared by {@link writeMemory} and {@link batchWrite}, and the sharing matters. A batch that\n * re-derived this would be a second decode of the same vocabulary, and the two would agree today\n * and drift the first time a field is added. This is `symspec`'s lesson stated as code: the batch\n * folds the singular's own decode rather than a parallel one.\n *\n * The two task metas are decoded here, before any file is rendered, and only for a task.\n * `@memhtml/html`'s parser refuses `memhtml-task-status` on a non-task and refuses a `memhtml-due` that is not\n * an ISO datetime, so a bad value passed through would render a file the indexer then declines to\n * project. That file is present in the tree, absent from every search, and visible only as a log\n * line. Deciding here turns it into a typed `InvalidMemory` before the commit.\n */\nconst toWriteInput = (params, at) => Effect.gen(function* () {\n const memoryType = yield* decodeWritableType(params.memoryType);\n const taskStatus = memoryType === \"task\" && params.taskStatus !== undefined && params.taskStatus !== \"\"\n ? yield* decodeTaskStatus(params.taskStatus)\n : undefined;\n const dueAt = memoryType === \"task\" && params.dueAt !== undefined && params.dueAt !== \"\"\n ? yield* decodeDueAt(params.dueAt)\n : undefined;\n return {\n title: params.title,\n claim: params.claim,\n memoryType,\n at,\n ...defined({\n body: params.body,\n articleHtml: params.articleHtml,\n path: params.path,\n workspace: params.workspace,\n tags: params.tags,\n entities: params.entities,\n importance: params.importance,\n confidence: params.confidence,\n sessionId: params.sessionId,\n promptId: params.promptId,\n turnUuid: params.turnUuid,\n taskStatus,\n dueAt\n })\n };\n});\n/**\n * Write one memory: render, dedupe, commit, index.\n *\n * On a dedupe nothing is indexed, because nothing changed. A dedupe writes no file, stages nothing,\n * and commits nothing, so the tree is byte-identical and the index already describes it.\n */\nexport const writeMemory = (params) => Effect.gen(function* () {\n const store = yield* Store;\n const at = yield* nowSecond;\n const result = yield* store.writeMemory(yield* toWriteInput(params, at));\n if (result.created)\n yield* reindex();\n yield* recordLink(result.path, \"wrote\", params, at);\n return result;\n});\n/**\n * A typed failure as a per-op report, through the same `codeFor`/`messageFor` every envelope error\n * takes.\n *\n * Mapped here rather than in each door, deliberately. A per-op code is part of the batch's payload\n * rather than of the envelope, so two doors shaping it independently is two mappings that agree today.\n * `memhtml apply` and `memory_write_batch` reporting different codes for the same refused op is\n * the drift the shared-use-case rule exists to prevent.\n */\nconst reportFailure = (index, error) => ({\n index,\n ok: false,\n code: codeFor(error),\n error: messageFor(error)\n});\n/**\n * The `detect_conflicts` assist: which claim, if any, each op's own claim contradicts.\n *\n * **Propose-only, and that is the design rather than a v1 limitation.** The function returns a report\n * per op index and writes nothing, stages nothing, and refuses nothing, because sometimes the\n * contradiction is the answer. A memory recording that a runbook step changed necessarily contradicts\n * the memory stating the old step, and an assist that auto-archived, applied last-wins, or blocked the\n * write would destroy the pair a later reader needs to see the change in. The caller decides: write\n * anyway, `memory_correct` the match, or skip.\n *\n * **One query for the whole batch.** Every op's frame key is collected first and `activeFramesFor` is\n * called once with all of them. The signature takes an array so a caller cannot loop, and a\n * per-op lookup would be the quadratic-write-cost pattern this codebase has already paid for once.\n *\n * **Two match sources, checked in that order.** The store answers for active non-task memories. Its\n * predicate, and 0009's index, exclude archived rows and tasks, because an archived claim is not a\n * competing assertion and an open to-do phrased as a claim is working state rather than knowledge. Then\n * come the batch's own earlier ops, folded as this loop walks them in order. Two ops in one call can\n * occupy the same slot, and neither is in the store yet, so nothing but this fold can see that pair. A\n * store match wins when an op has both, because the store's memory is a fact already in the corpus\n * while the earlier op is one this same call is about to create.\n *\n * **A later op reports on an earlier one, never the reverse.** The fold is asymmetric on purpose. Op 3\n * matching op 1 tells a caller \"you are about to restate something you just said\", which is actionable\n * with op 3 still in hand. Reporting it on op 1 as well would name a conflict with something that did\n * not exist when op 1 was written, and would double one finding into two.\n *\n * **A lookup failure degrades to no conflicts.** The assist is a note about the writes, so losing the\n * memories over a failed note about them would invert the priority as it would for\n * {@link recordLink} and {@link bumpAccess}, with the same `Effect.catch` → `logWarning` → neutral value.\n * The write path never sees this function's failure, which makes \"the assist cannot block a\n * write\" true structurally rather than by review.\n */\nconst detectFrameConflicts = (ops) => Effect.gen(function* () {\n /**\n * `frameKeyOf(op.claim)` per op, computed once and kept alongside the index.\n *\n * On the `article_html` path `claim` is `\"\"` by construction, because both doors leave it empty and\n * the `<mark>` inside the markup is the claim. `frameKeyOf` therefore returns null and a markup op\n * gets no assist. Deriving one here would mean parsing every op's article at the ops layer, a second\n * render of bytes the store is about to render anyway. The boundary is stated in the tool\n * description instead of hidden behind a duplicate parse.\n */\n const keyed = [];\n for (const [index, op] of ops.entries()) {\n const key = frameKeyOf(op.claim);\n if (key !== null)\n keyed.push({ index, key, claim: op.claim });\n }\n if (keyed.length === 0)\n return new Map();\n const recorder = yield* IndexRecorder;\n const live = yield* recorder\n .activeFramesFor(keyed.map((entry) => entry.key))\n .pipe(Effect.catch((error) => Effect.logWarning(`conflict assist skipped: ${error.operation}`).pipe(Effect.as(new Map()))));\n const conflicts = new Map();\n /** frame key → the first op in this batch to occupy it. Built as the loop walks in order. */\n const seen = new Map();\n for (const entry of keyed) {\n const [stored] = live.get(entry.key) ?? [];\n const earlier = seen.get(entry.key);\n if (stored !== undefined) {\n conflicts.set(entry.index, {\n path: stored.path,\n batchIndex: null,\n claim: stored.gist\n });\n }\n else if (earlier !== undefined) {\n conflicts.set(entry.index, {\n path: null,\n batchIndex: earlier.index,\n claim: earlier.claim\n });\n }\n if (earlier === undefined)\n seen.set(entry.key, { index: entry.index, claim: entry.claim });\n }\n return conflicts;\n});\n/**\n * Fold last-wins over the caller's op array, before the decode fold, so a batch-internal loser\n * never reaches disk. The surviving value simply occupies the earliest slot with that key.\n *\n * Not derived from {@link detectFrameConflicts}' output, although the walk mirrors it. A store\n * match wins there, masking the batch-internal pair the plan needs, and the plan needs both: the\n * batch collision decides which value writes, and the store match decides what that write supersedes.\n *\n * The slot rule: the first occupant of a key keeps its position and later ops with the same key\n * replace its content (`plannedOps[slot] = laterOp`, provenance and all, since the surviving value\n * is the later op's own statement). The occupant-tracking never moves, so a third restatement\n * replaces the slot again, last wins, at a stable position a caller can index by.\n *\n * Fail-closed on both of the rule's own guards: a null frame key is never consolidated, and a\n * failed store lookup degrades to batch-internal consolidation only, through the same\n * `Effect.catch` → `logWarning` → neutral-shape path the conflict assist takes, because an opt-in\n * consolidation must not become a new way to lose writes.\n */\nconst planLastWins = (ops) => Effect.gen(function* () {\n /** frame key → the slot (earliest occupant's index) that carries this key's surviving value. */\n const slotOf = new Map();\n /** slot index → the op whose value currently occupies it. */\n const content = new Map();\n const losers = new Map();\n /** Slot indices in caller order, keyed and keyless alike. */\n const order = [];\n for (const [index, op] of ops.entries()) {\n const key = frameKeyOf(op.claim);\n if (key === null) {\n // No frame shape, no slot. The rule's guards fail closed, so this op is never touched.\n order.push(index);\n content.set(index, op);\n continue;\n }\n const slot = slotOf.get(key);\n if (slot === undefined) {\n slotOf.set(key, index);\n order.push(index);\n content.set(index, op);\n continue;\n }\n content.set(slot, op);\n losers.set(index, slot);\n }\n const pendingSupersede = new Map();\n if (slotOf.size > 0) {\n const recorder = yield* IndexRecorder;\n // One query for every surviving key, for detectFrameConflicts' reason: a per-slot lookup is\n // the quadratic-write-cost shape this codebase has already paid for once.\n const live = yield* recorder\n .activeFramesFor([...slotOf.keys()])\n .pipe(Effect.catch((error) => Effect.logWarning(`consolidation store lookup skipped: ${error.operation}`).pipe(Effect.as(new Map()))));\n for (const [key, slot] of slotOf) {\n const [stored] = live.get(key) ?? [];\n if (stored !== undefined)\n pendingSupersede.set(slot, stored.path);\n }\n }\n return {\n ops: order.flatMap((index) => {\n const op = content.get(index);\n return op === undefined ? [] : [{ index, op }];\n }),\n losers,\n pendingSupersede\n };\n});\n/**\n * Loser reports for a last-wins plan, derived from the winner slots' own final reports.\n *\n * A loser reports `ok` with `consolidatedInto` only when its slot's write landed, which means the\n * surviving value is on disk and the pointer names where. A slot that was skipped or refused took the\n * loser's value down with it, so the loser reports `skipped`, which is the retryable outcome and\n * the one an atomic abort already means: nothing of this op reached disk.\n */\nconst withConsolidation = (results, plan) => {\n if (plan === null || plan.losers.size === 0)\n return results;\n return results.map((report, index) => {\n const slot = plan.losers.get(index);\n if (slot === undefined)\n return report;\n const winner = results[slot];\n return winner?.ok === true && winner.skipped !== true\n ? { index, ok: true, consolidatedInto: slot }\n : { index, ok: false, skipped: true };\n });\n};\n/**\n * Write N memories: one commit, one reindex, per-op results in input order.\n *\n * **Two folds, not one.** Decode is the operations layer's job and the store never sees it, so a\n * malformed `memory_type` on op 4 has to be caught here. This function therefore folds decode\n * over the ops and hands the store only what decoded. The store then folds the render gate, dedup,\n * and path claim over that, and this function splices the two result sets back into one array in\n * the caller's index space. Anything less and a decode failure would either be invisible per-op or\n * would shift every later op's index by one.\n *\n * **One reindex, gated on a file having been written** (G4). The indexer's `update()` reads\n * `git diff` over one commit, so a batch that committed once costs one diff. A dedupe-only\n * batch, which commits nothing, skips it entirely, because moving the watermark for a commit that\n * never happened is what `writeMemory`'s own `if (result.created)` guard exists to avoid.\n *\n * **The conflict assist is a third pass and it is read-only** (AC-1-2). It runs before the store's\n * fold, over the ops as the caller sent them, and its findings are merged into the reports at the\n * end, so it observes the batch and never participates in it. Nothing downstream of\n * {@link detectFrameConflicts} branches on its result: the same files are written, the same commit is\n * made, and the same ops are refused whether the flag is on or off. That is what propose-only means,\n * and it is checkable by reading this function rather than by trusting a description.\n */\nexport const batchWrite = (params) => Effect.gen(function* () {\n const continueOnError = params.continueOnError === true;\n const store = yield* Store;\n const at = yield* nowSecond;\n /**\n * The assist, over the caller's own op array and before anything is written.\n *\n * Over `params.ops` rather than the decoded `inputs` below, so a conflict is reported in the\n * caller's index space directly and needs no `originOf` translation. An op the store then\n * refuses still gets its finding, which is the more useful order, because a caller told both \"this\n * op is malformed\" and \"it also contradicts X\" fixes one thing.\n *\n * Not gated on the ops being valid, and deliberately so. `frameKeyOf` is a pure lexical function\n * over a string, so it has nothing to refuse and cannot fail on an op the decode is about to reject.\n */\n const conflicts = params.detectConflicts === true\n ? yield* detectFrameConflicts(params.ops)\n : new Map();\n /**\n * The consolidation plan, before the decode fold and in the caller's index space. A\n * batch-internal loser is excluded from everything downstream, so its value never earns a file.\n * The surviving value sits at the earliest slot with its key, so every later report and\n * conflict finding stays at the index the caller sent.\n */\n const plan = params.consolidate === \"last-wins\" ? yield* planLastWins(params.ops) : null;\n const planned = plan === null ? [...params.ops.entries()].map(([index, op]) => ({ index, op })) : plan.ops;\n /**\n * Fold 1, decode. `Effect.result` rather than letting the failure escape, because a decode\n * refusal is this op's result and not the batch's.\n */\n const reports = params.ops.map(() => undefined);\n const inputs = [];\n /** Store-result position → caller's op index, since the store never sees a skipped op. */\n const originOf = [];\n let decodeAborted = false;\n for (const { index, op } of planned) {\n const decoded = yield* Effect.result(toWriteInput({ ...op, ...provenanceOf(params, op) }, at));\n if (decoded._tag === \"Failure\") {\n reports[index] = reportFailure(index, decoded.failure);\n if (!continueOnError) {\n decodeAborted = true;\n break;\n }\n continue;\n }\n originOf.push(index);\n inputs.push(decoded.success);\n }\n /**\n * An atomic decode abort touches the store at all. Nothing was written, so every other op,\n * including the ones that decoded, reports `skipped`, matching the store's own abort semantics\n * exactly rather than inventing a second one.\n */\n if (decodeAborted) {\n const results = withConsolidation(merged(reports, conflicts), plan);\n return { results, summary: summarize(results), commitSha: null };\n }\n /**\n * The extraction assist: one model call over the decoded ops, extracted entities unioned into\n * each op's own `entities` before anything is written, so they land as ordinary `memhtml-entity`\n * metas and the git tree, rather than the index, is what remembers them.\n *\n * After the decode fold because a refused op must not reach the prompt, and before the store\n * because the render is what serializes the metas. Failure costs exactly this batch's\n * extracted entities. The port being absent, the model being down, and an unreadable payload\n * all take the same logged-warning path, and the write itself never waits on a retry.\n * `entities: []` is what every write produced before this assist existed.\n */\n const extractor = (yield* ExtractorPort).extractor;\n if (extractor !== undefined && inputs.length > 0) {\n const items = inputs.map((input) => ({\n title: input.title,\n text: input.articleHtml !== undefined\n ? input.articleHtml\n : [input.claim, ...(input.body ?? [])].join(\"\\n\")\n }));\n const outcome = yield* Effect.result(extractor.extract(items));\n if (outcome._tag === \"Failure\") {\n yield* Effect.logWarning(`entity extraction skipped for this batch: ${outcome.failure.reason}`);\n }\n else {\n for (const [index, extracted] of outcome.success.entries()) {\n const input = inputs[index];\n if (input === undefined || extracted.length === 0)\n continue;\n const declared = input.entities ?? [];\n const union = [...declared, ...extracted.filter((entity) => !declared.includes(entity))];\n inputs[index] = { ...input, entities: union };\n }\n }\n }\n // Fold 2, the store: render gate, dedup against the folded state, one commit.\n const batch = yield* store.writeMemories(inputs, { continueOnError });\n for (const entry of batch.results) {\n const index = originOf[entry.index];\n if (index === undefined)\n continue;\n reports[index] =\n entry.ok || entry.skipped === true\n ? {\n index,\n ok: entry.ok,\n ...defined({\n path: entry.path,\n deduped: entry.deduped,\n existingPath: entry.existingPath,\n skipped: entry.skipped\n })\n }\n : reportFailure(index, entry.error);\n }\n // One reindex, after the commit, only when a file was actually written.\n if (batch.writtenPaths.length > 0)\n yield* reindex();\n for (const path of batch.writtenPaths)\n yield* recordLink(path, \"wrote\", params, at);\n /**\n * The store-supersede pass, after a successful batch commit: every surviving slot whose frame\n * key a live memory occupied archives that memory, in one `supersedeMemories` call.\n *\n * A slot qualifies when its report is `ok` with a path, including a dedupe, where the path is\n * the pre-existing file that already carries this slot's value. The stored occupant still\n * states the losing value, so superseding it is still correct. A slot that failed or was\n * skipped wrote nothing, so there is nothing for its occupant to lose to.\n *\n * `Effect.result` rather than a bare yield, because a failed supersede must not fail a batch whose\n * memories already landed. The degradation is annotate-only: `supersededPath` is omitted, the\n * warning says why, and the corpus is merely unconsolidated, which is what every batch produced\n * before this flag existed. On success there is one extra reindex, because archive paths moved.\n */\n if (plan !== null && plan.pendingSupersede.size > 0) {\n const pairs = [];\n const winnerOf = new Map();\n for (const [slot, storedPath] of plan.pendingSupersede) {\n const report = reports[slot];\n if (report === undefined || !report.ok || report.skipped === true)\n continue;\n if (report.path === undefined)\n continue;\n // A slot whose content deduped onto the occupant itself is a restatement rather than a\n // supersession. Winner and loser are one file, and archiving it would lose the value.\n if (report.path === storedPath)\n continue;\n pairs.push({ winnerPath: report.path, loserPath: storedPath });\n winnerOf.set(storedPath, slot);\n }\n if (pairs.length > 0) {\n const outcome = yield* Effect.result(store.supersedeMemories(pairs));\n if (outcome._tag === \"Failure\") {\n yield* Effect.logWarning(`consolidation supersede skipped: ${messageFor(outcome.failure)}`);\n }\n else {\n for (const entry of outcome.success.archived) {\n const slot = winnerOf.get(entry.loserPath);\n const report = slot === undefined ? undefined : reports[slot];\n if (slot === undefined || report === undefined)\n continue;\n reports[slot] = { ...report, supersededPath: entry.archivePath };\n }\n if (outcome.success.archived.length > 0)\n yield* reindex();\n }\n }\n }\n /**\n * An op the store aborted before reaching has no result of its own, and neither does one whose\n * decode succeeded in a batch the store then aborted. Both are `skipped`. Losers pick up their\n * `consolidatedInto` pointer last, from their winner slot's own final report.\n */\n const results = withConsolidation(merged(reports, conflicts), plan);\n return {\n results,\n summary: summarize(results),\n commitSha: batch.commitSha\n };\n});\n/**\n * Per-op provenance falls back to the batch's own.\n *\n * The batch call carries the session the agent is in, and an op may name its own (a `memhtml apply`\n * file replaying a previous session's writes). Per-op wins, because it is the more specific statement\n * about where that one memory came from.\n */\nconst provenanceOf = (params, op) => defined({\n sessionId: op.sessionId ?? params.sessionId,\n promptId: op.promptId ?? params.promptId,\n turnUuid: op.turnUuid ?? params.turnUuid\n});\n/**\n * The reports as their final array: an unreported op becomes `skipped`, and every op picks up the\n * assist's finding for its index.\n *\n * One function for both exit paths, the atomic decode abort and the normal return, because they had\n * already grown two copies of the same `?? skipped` fill and a third responsibility spliced into only\n * one of them is how a batch that aborted would silently lose its conflict findings. The abort path\n * needs them because nothing was written. A caller told \"op 2 is malformed\" and also \"op 0\n * contradicts areas/x.html\" can fix both before retrying, rather than discovering the second on the\n * next round trip.\n *\n * Merging here rather than at each report's construction site also keeps the assist out of the\n * write path. The reports are already final when the conflicts are attached, so there is no point at\n * which a conflict could be read by anything that decides an outcome.\n */\nconst merged = (reports, conflicts) => reports.map((report, index) => {\n const base = report ?? { index, ok: false, skipped: true };\n const conflict = conflicts.get(index);\n return conflict === undefined ? base : { ...base, conflict };\n});\n/** The counts, derived from the reports in one pass so they cannot disagree with them. */\nconst summarize = (results) => {\n let written = 0;\n let deduped = 0;\n let failed = 0;\n let skipped = 0;\n let consolidated = 0;\n for (const result of results) {\n // A batch-internal loser is neither written nor failed. Its value survived at another slot,\n // and no file of its own was ever attempted, so it partitions into its own count.\n if (result.consolidatedInto !== undefined)\n consolidated += 1;\n else if (result.skipped === true)\n skipped += 1;\n else if (!result.ok)\n failed += 1;\n else if (result.deduped === true)\n deduped += 1;\n else\n written += 1;\n }\n return { total: results.length, written, deduped, failed, skipped, consolidated };\n};\n/**\n * Read one memory, optionally recording that the session read it.\n *\n * The access bump lives here and nowhere else on the retrieval side, because salience accumulates\n * evidence that someone chose a memory and a ranker's guess is not a choice. An explicit open names\n * one path, through this call and the `memhtml://file/{path}` resource that funnels through it, which is\n * the strongest signal short of a write. A path merely returned by search or recall was the ranker's own\n * suggestion, and bumping it builds a rich-get-richer loop: today's top five rank higher\n * tomorrow while the memory that should displace them never breaks in to earn a first bump.\n *\n * `bumpAccess` sits beside `recordLink` deliberately. Both are notes about the read, both swallow their\n * own failures, and neither may cost the caller the memory it asked for.\n */\nexport const readMemory = (path, provenance = {}) => Effect.gen(function* () {\n const store = yield* Store;\n const result = yield* store.readMemory(path);\n yield* recordLink(result.path, \"read\", provenance, yield* nowSecond);\n yield* bumpAccess([result.path]);\n return result;\n});\n/**\n * Ranked search. The retrieval service sanitizes the query text itself in `fts-query.ts`, so this\n * function never MATCHes user prose and neither does any caller of it.\n *\n * **No access bump, and the omission is the rule rather than an oversight.** A hit is the ranker's\n * guess about what the caller wanted, so counting it as salience would let the ranking teach itself.\n * A memory in today's top five would rank higher tomorrow purely for having been listed, and the\n * memory that should displace it never appears and so never earns a first bump. The cooldown does not\n * help, because it bounds one query replayed within 900 seconds, while the drift it would have to\n * bound operates across days. Salience moves when a caller opens a path ({@link readMemory}) or names\n * an outcome ({@link reinforceMemories}).\n */\nexport const searchMemories = (params) => Effect.gen(function* () {\n const retrieval = yield* Retrieval;\n return yield* retrieval.search(params);\n});\n/**\n * A context pack under a character budget.\n *\n * No access bump either, for {@link searchMemories}' reason. A disclosed body is still the ranker's\n * choice of what to spend the budget on rather than the caller's choice of what to read.\n */\nexport const recallMemories = (params) => Effect.gen(function* () {\n const retrieval = yield* Retrieval;\n return yield* retrieval.recall(params);\n});\n/**\n * Bump access bookkeeping for paths a caller chose to open. A missing state plane makes this a no-op.\n *\n * `reinforce` is the one SQL writer for `state.access` and this helper does not become a second one.\n * It moves callers to that writer rather than moving the write here.\n */\nconst bumpAccess = (paths) => Effect.gen(function* () {\n if (paths.length === 0)\n return;\n const db = yield* DatabaseService;\n if (!db.hasState)\n return;\n yield* reinforce(db, paths, \"neutral\", yield* nowSecond).pipe(Effect.catch((error) => Effect.logWarning(`access bookkeeping missed: ${error.operation}`).pipe(Effect.as({ bumped: [], cooledDown: [] }))));\n});\n/**\n * Supersede a memory: the new file and the archived target land in one commit.\n *\n * The type defaults to the target's own. A correction that silently changed the type would move\n * the memory to a different retention profile and a different PARA directory, and that is a second\n * decision the caller did not make.\n */\nexport const correctMemory = (params) => Effect.gen(function* () {\n const store = yield* Store;\n const target = yield* store.readMemory(params.targetPath);\n const requested = params.memoryType ?? target.doc.metas.memoryType;\n const memoryType = yield* decodeWritableType(requested);\n const at = yield* nowSecond;\n const result = yield* store.correctMemory(params.targetPath, {\n title: params.title,\n claim: params.claim,\n memoryType,\n at,\n ...defined({\n body: params.body,\n articleHtml: params.articleHtml,\n reason: params.reason,\n sessionId: params.sessionId,\n promptId: params.promptId,\n turnUuid: params.turnUuid\n })\n });\n // A correction is an add and a rename in one commit, so it needs the diff-driven path. The\n // archived target's row has to move rather than be re-added under a new name beside its old one.\n yield* reindex();\n yield* recordLink(result.path, \"corrected\", params, at);\n return result;\n});\n/**\n * Add an authored edge. Idempotent on `(rel, href)`, so a re-run commits nothing.\n *\n * The rel is decoded against {@link AUTHORABLE_RELS}, the memory class plus the task class, so\n * `memhtml link a.html blocks b.html` reaches the task graph while a person or provenance rel, both of\n * which the system mints itself, stays unauthorable.\n */\nexport const linkMemories = (srcPath, rel, dstPath) => Effect.gen(function* () {\n const edgeRel = yield* decodeAuthorableRel(rel);\n const store = yield* Store;\n const src = normalizePath(srcPath);\n const result = yield* store.linkMemories(src, edgeRel, dstPath);\n // `addLink` is idempotent on the pair, so a re-link commits nothing and there is nothing to\n // index. Re-deriving a diff for a no-op would move the watermark for a commit that never was.\n if (result.commitSha !== null)\n yield* reindex();\n return { ...result, srcPath: src, dstPath: normalizePath(dstPath), rel: edgeRel };\n});\n/** Soft-evict: `git mv` into `archive/<YYYY>/` with the archive stamps. Never a delete. */\nexport const archiveMemory = (path, reason) => Effect.gen(function* () {\n const store = yield* Store;\n const result = yield* store.archiveMemory(path, reason);\n // An archive is a pure rename. Handled as two independent paths it would leave the source row\n // live and duplicate the chunks. The diff path re-points the row and keeps the vector.\n yield* reindex();\n return result;\n});\n/** Bump access bookkeeping deliberately, with a caller-chosen signal. */\nexport const reinforceMemories = (paths, signal) => Effect.gen(function* () {\n const decoded = yield* decodeSignal(signal);\n const db = yield* DatabaseService;\n const at = yield* nowSecond;\n if (!db.hasState) {\n return { bumped: [], cooledDown: paths, signal: decoded };\n }\n const result = yield* reinforce(db, paths, decoded, at);\n return { ...result, signal: decoded };\n});\n/**\n * The memory graph around one path, to a fixed depth of at most two hops.\n *\n * **Two fixed-depth joins in a `UNION ALL`, deliberately not a recursive CTE.** The depth is\n * bounded at 2 by the tool's contract, so recursion buys nothing and costs the one thing a graph\n * query must not have here: an unbounded worst case on a corpus whose `relates_to` edges are\n * mined by the sleep cycle and can be dense. A fixed join is also index-covered by `edges_src`\n * and `edges_dst`, which a recursive walk is not.\n *\n * **Both directions, and `derived = 0 ∪ derived = 1`.** An edge is an assertion about a pair, and\n * which file happens to hold the `<link>` is authorship rather than direction of meaning. A\n * neighbourhood that read only outbound edges would show a superseding memory its target and hide\n * from the target that it had been superseded. Derived edges are included because lateral retrieval\n * is what they are for. `derived` is still reported per node so a caller can tell a\n * sleep-mined suspicion from an authored assertion.\n *\n * `edge_class = 'memory'` on every join. A person edge entering here would put\n * `resources/people/*` into a memory neighbourhood, and the class column exists to make that\n * structurally impossible.\n */\nexport const neighborsOf = (params) => Effect.gen(function* () {\n const db = yield* DatabaseService;\n const centre = normalizePath(params.path);\n const depth = Math.min(2, Math.max(1, Math.trunc(params.depth ?? 1)));\n const rels = (params.rels ?? []).filter((rel) => isEdgeRel(rel) && relClassFor(rel) === \"memory\");\n const relFilter = rels.length > 0 ? ` AND e.rel IN (${rels.map(() => \"?\").join(\", \")})` : \"\";\n const relFilter2 = rels.length > 0 ? ` AND e2.rel IN (${rels.map(() => \"?\").join(\", \")})` : \"\";\n /**\n * Hop 1 is the centre's own edges, either direction. Hop 2 walks one further from each hop-1\n * node and excludes the centre, so a two-cycle does not report the centre as its own neighbour\n * at distance 2.\n */\n const hopOne = `\n SELECT e.dst_path AS path, e.rel AS rel, e.derived AS derived, 1 AS hop\n FROM edges e\n WHERE e.src_path = ?1 AND e.edge_class = 'memory'${relFilter}\n UNION ALL\n SELECT e.src_path AS path, e.rel AS rel, e.derived AS derived, 1 AS hop\n FROM edges e\n WHERE e.dst_path = ?1 AND e.edge_class = 'memory'${relFilter}`;\n const hopTwo = `\n SELECT e2.dst_path AS path, e2.rel AS rel, e2.derived AS derived, 2 AS hop\n FROM edges e\n JOIN edges e2 ON e2.src_path = e.dst_path\n WHERE e.src_path = ?1 AND e.edge_class = 'memory' AND e2.edge_class = 'memory'\n AND e2.dst_path <> ?1${relFilter}${relFilter2}\n UNION ALL\n SELECT e2.src_path AS path, e2.rel AS rel, e2.derived AS derived, 2 AS hop\n FROM edges e\n JOIN edges e2 ON e2.dst_path = e.src_path\n WHERE e.dst_path = ?1 AND e.edge_class = 'memory' AND e2.edge_class = 'memory'\n AND e2.src_path <> ?1${relFilter}${relFilter2}`;\n const walk = depth === 1 ? hopOne : `${hopOne}\\n UNION ALL${hopTwo}`;\n /**\n * `min(hop)` per path: a node reachable both directly and via a detour is a 1-hop neighbour,\n * and reporting it twice would let one memory occupy two slots in a bounded answer.\n *\n * The join onto `files` is an inner join, so an edge pointing at a path the tree does not hold\n * contributes nothing. A dangling href is `memhtml doctor`'s finding rather than a titleless node.\n */\n const rows = yield* db.all(`SELECT w.path AS path, f.title AS title, min(w.hop) AS hop,\n min(w.rel) AS rel, max(w.derived) AS derived\n FROM (${walk}) w\n JOIN files f ON f.path = w.path\n GROUP BY w.path\n ORDER BY hop ASC, w.path ASC`, \n // The rel list binds once per occurrence of the filter, in textual order: hop 1 uses it\n // twice, hop 2 uses it four times. Getting this count wrong is a bind mismatch rather than a\n // wrong answer, so it fails loudly.\n [\n centre,\n ...(depth === 1\n ? [...rels, ...rels]\n : [...rels, ...rels, ...rels, ...rels, ...rels, ...rels])\n ]);\n const nodes = rows.map((row) => ({\n path: row.path,\n title: row.title,\n hop: row.hop,\n rel: row.rel\n }));\n return { centre, depth, nodes, edges: nodes.length };\n});\n/**\n * Page the corpus by facet.\n *\n * Keyset pagination on `path` rather than `LIMIT/OFFSET`. `files.path` is the primary key and it also\n * moves, because eviction is a `git mv`, so an offset page taken while a sleep cycle archives a file\n * would skip a row or repeat one. A cursor on the path itself is stable against that.\n */\nexport const listMemories = (params) => Effect.gen(function* () {\n const db = yield* DatabaseService;\n const limit = Math.min(500, Math.max(1, Math.trunc(params.limit ?? 50)));\n const conditions = [];\n const values = [];\n if (params.includeArchived !== true)\n conditions.push(\"f.archived = 0\");\n if (params.memoryType !== undefined && params.memoryType !== \"\") {\n const memoryType = yield* decodeWritableType(params.memoryType);\n conditions.push(\"f.memory_type = ?\");\n values.push(memoryType);\n }\n if (params.workspace !== undefined && params.workspace !== \"\") {\n conditions.push(\"f.workspace = ?\");\n values.push(params.workspace);\n }\n if (params.para !== undefined && params.para !== \"\") {\n conditions.push(\"f.para = ?\");\n values.push(params.para);\n }\n if (params.tag !== undefined && params.tag !== \"\") {\n conditions.push(\"EXISTS (SELECT 1 FROM file_tags t WHERE t.path = f.path AND t.tag = ?)\");\n values.push(params.tag);\n }\n if (params.entity !== undefined && params.entity !== \"\") {\n // The entity arrives as `type:name` and the table splits it at the first colon, so the\n // comparison rebuilds the reference rather than making the caller know the split.\n conditions.push(\"EXISTS (SELECT 1 FROM file_entities e WHERE e.path = f.path AND e.entity_type || ':' || e.entity_name = ?)\");\n values.push(params.entity);\n }\n if (params.cursor !== undefined && params.cursor !== \"\") {\n conditions.push(\"f.path > ?\");\n values.push(normalizePath(params.cursor));\n }\n const where = conditions.length === 0 ? \"\" : `WHERE ${conditions.join(\" AND \")}`;\n const rows = yield* db.all(`SELECT f.path, f.title, f.memory_type, f.gist, f.workspace, f.para,\n f.confidence, f.importance, f.archived, f.updated_at\n FROM files f ${where} ORDER BY f.path ASC LIMIT ?`, [...values, limit + 1]);\n // One row over the limit is fetched to decide whether a next page exists, then dropped. A\n // separate COUNT would be a second statement over the same predicate and could disagree with\n // this one under a concurrent write.\n const page = rows.slice(0, limit);\n const nextCursor = rows.length > limit ? (page.at(-1)?.path ?? null) : null;\n return {\n files: page.map((row) => ({\n path: row.path,\n title: row.title,\n memoryType: row.memory_type,\n gist: row.gist,\n workspace: row.workspace,\n para: row.para,\n confidence: row.confidence,\n importance: row.importance,\n archived: row.archived === 1,\n updatedAt: row.updated_at\n })),\n nextCursor\n };\n});\n/**\n * Move a task to a new status.\n *\n * **`setMeta`, never parse→serialize.** The editors splice by source offset, so the article's bytes\n * cannot move on a status change, and neither can `memhtml-content-hash`, the dedupe key, or any chunk\n * id hanging off it. A round trip through the serializer drops a `<pre>` newline per write, which\n * would re-embed a task for a one-word edit and break the hash the file claims for itself.\n *\n * **`done` routes through `store.archiveMemory`**, which is a `git mv`. That is the design decision\n * that keeps `done` off the `memhtml-status` axis. Finishing a task stamps the status and moves the file\n * under `archive/<YYYY>/`, so \"what did I finish\" is the archive tree plus `git log` rather than a\n * fifth value every archive, correction, and publish path would have to learn. The stamp is written\n * before the move so both land in one commit and `git log --follow` reads through it.\n *\n * `indexer.update()` afterwards rather than `indexPaths`, because the `done` transition is a rename and\n * `indexPaths` cannot express one. It would leave the pre-archive row live, duplicate the chunks\n * under two paths, and skip the watermark (finding from T9, stated at {@link reindex}).\n */\nexport const setTaskStatus = (params) => Effect.gen(function* () {\n const status = yield* decodeTaskStatus(params.status);\n const store = yield* Store;\n const path = normalizePath(params.path);\n const at = yield* nowSecond;\n // Read through the parser. A `memhtml task status` on a memory file would otherwise stamp a meta the\n // format refuses on that type, producing a file the indexer then declines to project.\n const existing = yield* store.readMemory(path);\n if (existing.doc.metas.memoryType !== \"task\") {\n return yield* Effect.fail(InvalidMemory.make({\n reason: `${path} is a ${existing.doc.metas.memoryType} memory, not a task: only a task carries memhtml-task-status`\n }));\n }\n /**\n * A no-op status change writes nothing and commits nothing, so a re-run is free and the tree\n * stays byte-identical. The `memhtml-updated` stamp is skipped along with it, because a fresh\n * timestamp with no status change would claim the task moved when it did not.\n */\n if (existing.doc.metas.taskStatus === status) {\n return {\n path,\n taskStatus: status,\n archived: false,\n commitSha: null,\n unchanged: true\n };\n }\n const stamped = setMeta(setMeta(existing.html, \"memhtml-task-status\", status), \"memhtml-updated\", at);\n yield* attemptIo(`task.write:${path}`, async () => {\n const { writeFile } = await import(\"node:fs/promises\");\n const { join } = await import(\"node:path\");\n await writeFile(join(store.root, path), stamped, \"utf8\");\n });\n if (status !== \"done\") {\n yield* store.git.add([path]);\n const commit = yield* store.git.commit(commitSubject(\"task\", `${status} ${path}`));\n yield* reindex();\n return {\n path,\n taskStatus: status,\n archived: false,\n commitSha: commit.sha,\n unchanged: false\n };\n }\n /**\n * `archiveMemory` stages the `git mv` and commits, and it reads the file from disk, so the\n * `memhtml-task-status: done` stamp written just above travels with the move rather than needing a\n * second commit. `git mv` carries a working-tree modification with it (probed live 2026-08-02:\n * the staged blob is the pre-edit content and the worktree keeps the edit), and `archiveMemory`\n * re-writes the stamped bytes at the destination before staging, so the committed file holds\n * both the archive stamps and the done status.\n */\n const archived = yield* store.archiveMemory(path, params.reason ?? `task ${status}`);\n yield* reindex();\n return {\n path,\n taskStatus: status,\n archived: true,\n archivePath: archived.archivePath,\n commitSha: archived.commitSha,\n unchanged: false\n };\n});\n/**\n * The task working set: a direct indexed scan, deliberately not retrieval.\n *\n * No RRF, no MMR, no embedding. A to-do list is not a ranking problem. An agent asking \"what is\n * open\" wants every row in a stable order, and a relevance score over working state would make the\n * answer depend on a query the caller does not have. The partial index `files_task_status`\n * (`WHERE memory_type='task' AND archived=0`) is what makes the default scan cheap.\n *\n * `blockedBy` is one correlated subquery over `edges`, filtered to `edge_class='task'` and\n * `rel='blocks'`. **The class filter is redundant with the rel filter today and is kept anyway.**\n * Probed live 2026-08-02: `0008_tasks.sql`'s per-class CHECKs refuse `blocks` under `memory`,\n * `person`, and `provenance`, so `rel='blocks'` already implies the class, and a mutation removing the\n * class predicate leaves every test green. It stays because the class column is what every\n * memory-graph query filters on, and a reader who saw this one query trust the rel alone would learn\n * the wrong rule about how the firewall is enforced.\n *\n * `group_concat` over an ordered subselect, probed 2026-08-12 on node 24.19.0. The inner `ORDER BY`\n * is preserved, and `char(10)` is the separator because a path cannot contain a newline while it can\n * contain a comma.\n *\n * The join is deliberately not an inner join onto `files`. A blocker whose file left the tree still\n * blocks, and hiding it here would make a permanently-blocked task look ready. `memhtml doctor` reports\n * that as a finding, and this function reports the edge as the corpus states it.\n */\nexport const listTasks = (params) => Effect.gen(function* () {\n const db = yield* DatabaseService;\n const limit = Math.min(500, Math.max(1, Math.trunc(params.limit ?? 50)));\n const conditions = [\"f.memory_type = 'task'\"];\n const values = [];\n if (params.includeArchived !== true)\n conditions.push(\"f.archived = 0\");\n if (params.status !== undefined && params.status !== \"\") {\n const status = yield* decodeTaskStatus(params.status);\n conditions.push(\"f.task_status = ?\");\n values.push(status);\n }\n if (params.workspace !== undefined && params.workspace !== \"\") {\n conditions.push(\"f.workspace = ?\");\n values.push(params.workspace);\n }\n if (params.dueBefore !== undefined && params.dueBefore !== \"\") {\n const dueBefore = yield* decodeDueAt(params.dueBefore);\n /**\n * `substr(…, 1, 10)` on both sides, so the comparison is one of calendar days.\n *\n * The case it fixes, established by enumeration 2026-08-02: a due date stored as a bare day\n * against a bound carrying a time on that same day. Whole-string,\n * `\"2026-08-25\" < \"2026-08-25T09:00:00Z\"` is true, because the shorter string is a prefix and\n * sorts first, so a task due sometime on the 25th would be reported late at 09:00 on the 25th.\n * A day-granularity deadline is not late until the day is over, and truncating both sides is\n * what says so. Every other combination of the two forms agrees either way.\n */\n conditions.push(\"f.due_at IS NOT NULL AND substr(f.due_at, 1, 10) < substr(?, 1, 10)\");\n values.push(dueBefore);\n }\n if (params.cursor !== undefined && params.cursor !== \"\") {\n conditions.push(\"f.path > ?\");\n values.push(normalizePath(params.cursor));\n }\n const rows = yield* db.all(`SELECT f.path, f.title, f.task_status, f.due_at, f.workspace, f.archived, f.updated_at,\n (SELECT group_concat(b.src_path, char(10)) FROM (\n SELECT e.src_path AS src_path FROM edges e\n WHERE e.dst_path = f.path AND e.edge_class = 'task' AND e.rel = 'blocks'\n ORDER BY e.src_path ASC) b) AS blocked_by\n FROM files f\n WHERE ${conditions.join(\" AND \")}\n ORDER BY f.path ASC LIMIT ?`, [...values, limit + 1]);\n const page = rows.slice(0, limit);\n const nextCursor = rows.length > limit ? (page.at(-1)?.path ?? null) : null;\n return {\n tasks: page.map((row) => ({\n path: row.path,\n title: row.title,\n taskStatus: row.task_status,\n dueAt: row.due_at,\n workspace: row.workspace,\n archived: row.archived === 1,\n updatedAt: row.updated_at,\n blockedBy: row.blocked_by === null ? [] : row.blocked_by.split(\"\\n\")\n })),\n nextCursor\n };\n});\n/**\n * `mergeTailExtract` as the merger `persistScanned` requires.\n *\n * The two shapes differ in exactly one field, and the difference is real rather than cosmetic.\n * `SessionExtract` carries `counters`, the parse bookkeeping of one scan, and the persisted\n * `traces` row does not, because those are facts about a read rather than about the session.\n * `readStoredExtract` therefore cannot reconstruct them, and the merge is handed zeros for the\n * stored side. The merged counters then describe this scan alone, which is the only reading\n * available. Inventing a stored value would produce a number that claims to count lines nobody\n * read.\n *\n * Every other field the merge reads is present on both shapes, so this adapter is total.\n */\nconst ZERO_COUNTERS = {\n parsedLines: 0,\n droppedLines: 0,\n droppedNoSession: 0,\n skippedTypeLines: 0,\n unknownTypeLines: 0\n};\nconst tailMerger = (stored, tail) => mergeTailExtract({ ...stored, counters: ZERO_COUNTERS }, { ...tail, counters: ZERO_COUNTERS });\n/**\n * Scan the trace root and persist what changed.\n *\n * {@link tailMerger} is passed as the tail merger, and this is the only correct way to call\n * `persistScanned` on a `tail` action. A tail's extract describes the appended slice, so its\n * `first_prompt` is a mid-conversation prompt, its `started_at` is later than the session's, and\n * its prompt ordinals restart at 0. `persistScanned` takes the merger as a parameter precisely so\n * that \"never upsert a tail extract directly\" is a type-level obligation rather than a convention.\n */\nexport const indexTraces = () => Effect.gen(function* () {\n const roots = yield* Roots;\n const db = yield* DatabaseService;\n const at = yield* nowSecond;\n const report = yield* scanTraceRoot(roots.traceRoot, readWatermark(db));\n let sessionsWritten = 0;\n let promptsWritten = 0;\n let merged = 0;\n for (const scanned of report.files) {\n const outcome = yield* persistScanned(db, scanned, tailMerger, at);\n if (outcome.action !== \"skip\")\n sessionsWritten += 1;\n if (outcome.merged)\n merged += 1;\n promptsWritten += outcome.promptsWritten;\n }\n return {\n traceRoot: roots.traceRoot,\n filesSeen: report.files.length,\n skipped: report.skipped,\n tailed: report.tailed,\n rescanned: report.rescanned,\n bytesRead: report.bytesRead,\n sessionsWritten,\n promptsWritten,\n tailsMerged: merged\n };\n});\n/**\n * FTS over session first-prompts and AI titles.\n *\n * The query goes through the same sanitizer the memory arms use, and it has to. An apostrophe is a\n * hard driver error rather than an empty result, and \"what did I ask about don't-repeat-yourself\"\n * is an ordinary trace query. An empty sanitized query returns the most recent sessions rather\n * than nothing, because a caller with no terms wants a listing and an empty MATCH is not a listing.\n *\n * This is the trace plane and it stops here. No memory table is named, and nothing in the\n * retrieval assembler names `traces`. The firewall is by table name, in both directions.\n */\nexport const searchTraces = (params) => Effect.gen(function* () {\n const db = yield* DatabaseService;\n const match = sanitizeFtsQuery(params.query);\n const limit = Math.min(200, Math.max(1, Math.trunc(params.limit ?? 20)));\n const conditions = [];\n const values = [];\n /**\n * The MATCH names `traces_fts` rather than a column of `traces`. The index is an external-content\n * FTS5 table, so it is joined in by rowid and only reached when there is something to match. Without\n * a query the statement never mentions it, which is what keeps a bare listing a plain table scan.\n */\n const matched = match !== \"\";\n const from = matched\n ? \"FROM traces_fts JOIN traces t ON t.rowid = traces_fts.rowid\"\n : \"FROM traces t\";\n if (matched) {\n conditions.push(\"traces_fts MATCH ?\");\n values.push(match);\n }\n if (params.cwd !== undefined && params.cwd !== \"\") {\n conditions.push(\"t.cwd = ?\");\n values.push(params.cwd);\n }\n if (params.since !== undefined && params.since !== \"\") {\n conditions.push(\"t.started_at >= ?\");\n values.push(params.since);\n }\n const where = conditions.length === 0 ? \"\" : `WHERE ${conditions.join(\" AND \")}`;\n // A matched query orders by relevance, ascending because FTS5's bm25 is negative-is-better.\n // Without a match there is no relevance to order by and recency is the useful order.\n const order = matched ? \"ORDER BY bm25(traces_fts)\" : \"ORDER BY t.started_at DESC\";\n const rows = yield* db.all(`SELECT t.session_id, t.slug, t.cwd, t.started_at, t.prompt_count, t.first_prompt, t.ai_title\n ${from} ${where} ${order} LIMIT ?`, [...values, limit]);\n return {\n sessions: rows.map((row) => ({\n sessionId: row.session_id,\n slug: row.slug,\n cwd: row.cwd,\n startedAt: row.started_at,\n promptCount: row.prompt_count,\n firstPrompt: row.first_prompt,\n aiTitle: row.ai_title\n })),\n degraded: match === \"\"\n };\n});\n/**\n * The memory-session links, from either side.\n *\n * Both parameters absent is a refusal rather than an unbounded scan of every link ever recorded. A\n * tool whose no-argument form returns the whole table is a tool an agent calls by accident.\n */\nexport const traceLinks = (params) => Effect.gen(function* () {\n const hasSession = params.sessionId !== undefined && params.sessionId !== \"\";\n const hasPath = params.path !== undefined && params.path !== \"\";\n if (!hasSession && !hasPath) {\n return yield* Effect.fail(InvalidMemory.make({ reason: \"trace links needs a session_id or a path\" }));\n }\n const db = yield* DatabaseService;\n const conditions = [];\n const values = [];\n if (hasSession) {\n conditions.push(\"l.session_id = ?\");\n values.push(params.sessionId);\n }\n if (hasPath) {\n conditions.push(\"l.path = ?\");\n values.push(normalizePath(params.path));\n }\n const rows = yield* db.all(`SELECT l.path, l.session_id, l.prompt_id, l.turn_uuid, l.link_kind, l.at\n FROM memory_session_links l\n WHERE ${conditions.join(\" AND \")}\n ORDER BY l.at DESC, l.path ASC`, values);\n return {\n links: rows.map((row) => ({\n path: row.path,\n sessionId: row.session_id,\n promptId: row.prompt_id,\n turnUuid: row.turn_uuid,\n linkKind: row.link_kind,\n at: row.at\n }))\n };\n});\n/**\n * Corpus health, in one call.\n *\n * `indexFresh` compares the recorded watermark to `HEAD`, which is the only answer that means\n * anything. The index is a projection of a commit, so \"fresh\" means \"the commit it describes is the\n * commit we are on\". A count of rows would say the index exists rather than that it is current.\n *\n * `embedderUp` is read off the stored watermark rather than by probing Bedrock. A status call that\n * made a network request would fail for a reason unrelated to the corpus, and what a caller\n * needs to know is whether the vectors in this index are usable.\n */\nexport const statusReport = () => Effect.gen(function* () {\n const store = yield* Store;\n const db = yield* DatabaseService;\n const headSha = yield* store.git.revParseHead();\n const dirty = yield* store.dirtyPaths();\n const state = yield* readIndexState(db).pipe(Effect.orElseSucceed(() => undefined));\n const byType = yield* countRows(db, \"SELECT memory_type AS k, count(*) AS n FROM files WHERE archived = 0 GROUP BY memory_type\");\n const archivedCount = yield* countOne(db, \"SELECT count(*) AS n FROM files WHERE archived = 1\");\n const edges = yield* countOne(db, \"SELECT count(*) AS n FROM edges\");\n const derivedEdges = yield* countOne(db, \"SELECT count(*) AS n FROM edges WHERE derived = 1\");\n const embeddings = yield* countOne(db, \"SELECT count(*) AS n FROM embeddings\");\n const chunks = yield* countOne(db, \"SELECT count(*) AS n FROM chunks\");\n const traces = yield* countOne(db, \"SELECT count(*) AS n FROM traces\");\n const lastSleep = yield* db\n .get(\"SELECT run_id, status, started_at FROM sleep_runs ORDER BY started_at DESC LIMIT 1\")\n .pipe(Effect.orElseSucceed(() => undefined));\n return {\n root: store.root,\n headSha,\n dirty: dirty.length > 0,\n dirtyPaths: dirty,\n countsByType: byType,\n archivedCount,\n edges,\n derivedEdges,\n chunks,\n embeddings,\n traces,\n indexFresh: state?.head_sha !== null && state?.head_sha === headSha,\n indexHeadSha: state?.head_sha ?? null,\n embedModel: state?.embed_model ?? null,\n // A stored watermark that disagrees with the configured one means every cosine in this index\n // is against a different vector space. Reporting it as \"up\" would be the silent half-migration\n // the indexer refuses at write time.\n embedderUp: state !== undefined && state.embed_model === EMBED_WATERMARK && embeddings > 0,\n hasState: db.hasState,\n lastSleep: lastSleep === undefined\n ? null\n : { runId: lastSleep.run_id, status: lastSleep.status, startedAt: lastSleep.started_at }\n };\n});\n/** One scalar count, `0` when the table is unreachable. */\nconst countOne = (db, sql) => db.get(sql).pipe(Effect.map((row) => row?.n ?? 0));\n/** A `GROUP BY` into a record. An absent key means zero, so the caller never reads a null. */\nconst countRows = (db, sql) => db\n .all(sql)\n .pipe(Effect.map((rows) => Object.fromEntries(rows.map((row) => [row.k, row.n]))));\n/** Re-exported so the write path's type guard is usable by a caller building tool schemas. */\nexport { isWritableMemoryType };\n//# sourceMappingURL=operations.js.map","import { MEMORY_RELS } from \"@memhtml/contracts/edges\";\nimport { TASK_STATUSES, WRITABLE_MEMORY_TYPES } from \"@memhtml/contracts/types\";\nimport { REINFORCE_SIGNALS } from \"@memhtml/domain\";\nimport { SLEEP_PHASES } from \"@memhtml/sleep\";\nimport { CONFIG_VARS } from \"./config.js\";\nimport { ERROR_CODES } from \"./envelope.js\";\nimport { AUTHORABLE_RELS } from \"./operations.js\";\n/** Flags every command accepts. Listed once so the manifest cannot drift from behavior. */\nexport const GLOBAL_FLAGS = [\n {\n name: \"json\",\n type: \"boolean\",\n description: \"Emit the typed JSON envelope on stdout (default; logs go to stderr).\",\n default: true\n },\n {\n name: \"dense\",\n type: \"boolean\",\n description: \"Minify JSON and drop null fields, for pasting into a context window.\",\n default: false\n },\n {\n name: \"repo\",\n type: \"string\",\n description: \"Path to the memory repo. Defaults to $MEMHTML_ROOT.\",\n default: \"\"\n }\n];\n/** Flags every retrieval command shares, so `search` and `recall` cannot scope differently. */\nconst SCOPE_FLAGS = [\n {\n name: \"type\",\n type: \"string\",\n description: \"Restrict to one memory type. Repeatable; each occurrence broadens (ANY-of).\",\n values: WRITABLE_MEMORY_TYPES,\n repeatable: true\n },\n {\n name: \"workspace\",\n type: \"string\",\n description: \"Restrict to one workspace. STRICT: a scoped query never returns a memory with no workspace.\"\n },\n {\n name: \"tag\",\n type: \"string\",\n description: \"Restrict to memories carrying any of these tags. Repeatable; each broadens.\",\n repeatable: true\n },\n {\n name: \"entity\",\n type: \"string\",\n // Singular, unlike --tag, because the scope exists to chain one hop off a hit's own entity list,\n // which is one reference at a time. Same spelling `memhtml list --entity` takes, so the two are\n // one vocabulary rather than two facets that happen to share a word.\n description: \"Restrict to memories carrying one `type:name` entity reference, e.g. service:checkout-api, the form a hit's `entities` publishes, so a hop is a copy. A scope matching nothing returns no hits and says so; it never widens.\"\n },\n {\n name: \"include-archived\",\n type: \"boolean\",\n description: \"Include archived memories. Eviction is a `git mv`, so they still exist.\",\n default: false\n },\n {\n name: \"as-of\",\n type: \"string\",\n description: \"Point-in-time view: returns what was believed valid at this ISO instant, including since-superseded memories (marked superseded_by). The validity window is coalesce(valid_from, event_at, created_at) <= as-of < valid_until.\"\n }\n];\n/**\n * The single source of parsing, validation, and the manifest. A command lands here\n * before it lands anywhere else, so `memhtml manifest` and `memhtml agents-doc` describe\n * what the binary actually accepts rather than what someone remembered to document.\n *\n * A subcommand is one entry with a space in its name (`index rebuild`), not a nested tree.\n * Flattening keeps `nearest()` able to suggest across the whole surface, a typo in the noun\n * (`memhtml indx rebuild`) and a typo in the verb (`memhtml index rebiuld`) both get a candidate, and\n * keeps one table driving parsing, the manifest, and the generated doc.\n */\nexport const COMMANDS = [\n {\n name: \"manifest\",\n summary: \"Emit this CLI's full machine-readable contract.\",\n args: [],\n flags: [],\n responseTypes: [\"cli.manifest\"]\n },\n {\n name: \"init\",\n summary: \"Scaffold a memory repo at --repo/$MEMHTML_ROOT: git init, PARA dirs, merge driver.\",\n args: [],\n flags: [],\n responseTypes: [\"repo.init\"]\n },\n {\n name: \"write\",\n summary: \"Write one memory. Content-hash duplicates return the existing path, uncommitted.\",\n args: [],\n flags: [\n {\n name: \"title\",\n type: \"string\",\n description: \"The memory's title. Becomes the <title> and the filename slug.\",\n required: true\n },\n {\n name: \"claim\",\n type: \"string\",\n description: \"The one load-bearing sentence. Becomes the <mark> span and files.gist. Exactly one of --claim or --article-html.\"\n },\n {\n name: \"body\",\n type: \"string\",\n description: \"A prose paragraph after the claim. Repeatable, one <p> each.\",\n repeatable: true\n },\n {\n name: \"article-html\",\n type: \"string\",\n description: \"Raw <article> markup used verbatim in place of --claim/--body. Must contain exactly one <mark> in the first <p> or <li>; the first <time datetime> becomes the memory's event time. The store refuses format violations before any commit. Exactly one of --claim or --article-html.\"\n },\n {\n name: \"type\",\n type: \"string\",\n description: \"The memory type. `arc` is absent: an arc is synthesized by sleep.\",\n values: WRITABLE_MEMORY_TYPES,\n required: true\n },\n {\n name: \"path\",\n type: \"string\",\n description: \"An explicit path override. Ignored when it is not a valid memory path.\"\n },\n { name: \"workspace\", type: \"string\", description: \"Routes the memory to projects/<slug>/.\" },\n {\n name: \"tag\",\n type: \"string\",\n description: \"A tag. Repeatable; the first one routes an unplaced resource memory.\"\n },\n {\n name: \"entity\",\n type: \"string\",\n description: \"A `type:name` entity reference, e.g. service:checkout-api. Repeatable.\",\n repeatable: true\n },\n {\n name: \"importance\",\n type: \"int\",\n description: \"1-10, a display ordinal. The retention scorer divides by 10.\"\n },\n { name: \"confidence\", type: \"string\", description: \"0-1. 1.0 is an unqualified assertion.\" },\n {\n name: \"session-id\",\n type: \"string\",\n description: \"The Claude Code session. Stamped into the head AND indexed as a link.\"\n },\n { name: \"prompt-id\", type: \"string\", description: \"The prompt within that session.\" },\n { name: \"turn-uuid\", type: \"string\", description: \"The turn within that session.\" }\n ],\n responseTypes: [\"memory.written\"]\n },\n {\n name: \"apply\",\n summary: \"Write many memories from a JSONL op stream: ONE commit, ONE index update, per-op results.\",\n args: [],\n flags: [\n {\n name: \"file\",\n type: \"string\",\n description: \"The JSONL file to read. One complete JSON object per line. Omit it (or pass `-`) to read the stream from stdin.\"\n },\n {\n name: \"continue-on-error\",\n type: \"boolean\",\n description: \"Best-effort: a refused op is reported and skipped while every surviving op lands in the one commit. Atomic by default. The first refused op aborts the batch and nothing is written.\",\n default: false\n },\n {\n name: \"detect-conflicts\",\n type: \"boolean\",\n description: \"Report each op's frame-matches as a per-op `conflict`: the ACTIVE memory (or the earlier op) whose claim occupies the same subject-and-relation slot. PROPOSE-ONLY: every op still writes exactly as it would have, because sometimes the contradiction is the answer. You decide: write anyway, `memhtml correct` the match, or drop the line.\",\n default: false\n },\n {\n name: \"consolidate\",\n type: \"string\",\n values: [\"last-wins\"],\n description: \"Resolve frame-key matches instead of only reporting them: `--consolidate last-wins` makes the LATER op's value win a shared claim slot (one file, written at the FIRST index that claimed the slot, with each later restatement reporting `consolidated_into` naming that slot) and archives a stored ACTIVE memory a surviving slot displaces, reported as `superseded_path`. Off by default; claims with no frame shape are never consolidated.\"\n },\n {\n name: \"session-id\",\n type: \"string\",\n description: \"The Claude Code session for every op that names none. A line's own `session_id` wins over this.\"\n },\n { name: \"prompt-id\", type: \"string\", description: \"The prompt within that session.\" },\n { name: \"turn-uuid\", type: \"string\", description: \"The turn within that session.\" }\n ],\n responseTypes: [\"batch.applied\"]\n },\n {\n name: \"read\",\n summary: \"Read one memory: its metas, links, article, and format warnings.\",\n args: [{ name: \"path\", description: \"Repo-root-relative path to the memory.\", required: true }],\n flags: [\n {\n name: \"session-id\",\n type: \"string\",\n description: \"Records a `read` session link, so provenance is queryable both ways.\"\n }\n ],\n responseTypes: [\"memory.detail\"]\n },\n {\n name: \"search\",\n summary: \"Ranked search: four RRF arms plus MMR. Degrades to the lexical floor.\",\n args: [{ name: \"query\", description: \"Prose. Never a query language.\", required: true }],\n flags: [\n ...SCOPE_FLAGS,\n { name: \"limit\", type: \"int\", description: \"Hits to return.\", default: 10 }\n ],\n responseTypes: [\"memory.hits\"]\n },\n {\n name: \"recall\",\n summary: \"A disclosure pack under a character budget: arcs and memories folded separately.\",\n args: [{ name: \"query\", description: \"Prose.\", required: true }],\n flags: [\n ...SCOPE_FLAGS,\n {\n name: \"budget\",\n type: \"int\",\n description: \"Characters of quoted body. Arcs get their own envelope on top.\",\n default: 16_000\n }\n ],\n responseTypes: [\"recall.pack\"]\n },\n {\n name: \"correct\",\n summary: \"Supersede a memory: write the new file and archive the target in ONE commit.\",\n args: [{ name: \"target\", description: \"The memory being corrected.\", required: true }],\n flags: [\n { name: \"title\", type: \"string\", description: \"The new memory's title.\", required: true },\n {\n name: \"claim\",\n type: \"string\",\n description: \"The corrected claim. Exactly one of --claim or --article-html.\"\n },\n {\n name: \"body\",\n type: \"string\",\n description: \"A prose paragraph. Repeatable.\",\n repeatable: true\n },\n {\n name: \"article-html\",\n type: \"string\",\n description: \"Raw <article> markup for the superseding memory, used verbatim in place of --claim/--body. Must contain exactly one <mark> in the first <p> or <li>; the first <time datetime> becomes the memory's event time. The store refuses format violations before any commit. Exactly one of --claim or --article-html.\"\n },\n {\n name: \"type\",\n type: \"string\",\n description: \"The new memory's type. Defaults to the target's.\",\n values: WRITABLE_MEMORY_TYPES\n },\n { name: \"reason\", type: \"string\", description: \"Why the correction was made.\" },\n { name: \"session-id\", type: \"string\", description: \"Records a `corrected` session link.\" }\n ],\n responseTypes: [\"memory.corrected\"]\n },\n {\n name: \"link\",\n summary: \"Add an authored edge to the source file and commit it. Idempotent.\",\n args: [\n { name: \"src\", description: \"The asserting memory or task.\", required: true },\n {\n name: \"rel\",\n // The task rels are authorable here rather than in `memory_link`, because a `blocks` edge\n // between two tasks is a real authored assertion, while a person or provenance rel is minted.\n description: `One of: ${AUTHORABLE_RELS.join(\", \")}. A task rel needs two tasks; a memory rel refuses a task endpoint.`,\n required: true\n },\n { name: \"dst\", description: \"The memory or task being pointed at.\", required: true }\n ],\n flags: [],\n responseTypes: [\"memory.linked\"]\n },\n {\n name: \"neighbors\",\n summary: \"The memory graph around one path, to a fixed depth of at most two hops.\",\n args: [{ name: \"path\", description: \"The centre of the neighbourhood.\", required: true }],\n flags: [\n { name: \"depth\", type: \"int\", description: \"1 or 2. Never more.\", default: 1 },\n {\n name: \"rel\",\n type: \"string\",\n description: \"Restrict to these rels. Repeatable.\",\n values: MEMORY_RELS,\n repeatable: true\n }\n ],\n responseTypes: [\"memory.neighbors\"]\n },\n {\n name: \"archive\",\n summary: \"Soft-evict: `git mv` into archive/<YYYY>/ with the archive stamps. Never a delete.\",\n args: [{ name: \"path\", description: \"The memory to archive.\", required: true }],\n flags: [{ name: \"reason\", type: \"string\", description: \"Why it was evicted.\", required: true }],\n responseTypes: [\"memory.archived\"]\n },\n {\n name: \"reinforce\",\n summary: \"Bump access bookkeeping, gated by a 900-second per-path cooldown.\",\n args: [\n { name: \"path\", description: \"A memory path. Repeat the argument for more.\", required: true }\n ],\n flags: [\n {\n name: \"signal\",\n type: \"string\",\n description: \"`neutral` bumps access without claiming the memory was right.\",\n values: REINFORCE_SIGNALS,\n default: \"neutral\"\n }\n ],\n responseTypes: [\"memory.reinforced\"]\n },\n {\n name: \"list\",\n summary: \"Page through the corpus by type, workspace, tag, entity, or PARA bucket.\",\n args: [],\n flags: [\n {\n name: \"type\",\n type: \"string\",\n description: \"One memory type.\",\n values: WRITABLE_MEMORY_TYPES\n },\n { name: \"workspace\", type: \"string\", description: \"One workspace.\" },\n { name: \"tag\", type: \"string\", description: \"One tag.\" },\n { name: \"entity\", type: \"string\", description: \"One `type:name` entity reference.\" },\n {\n name: \"para\",\n type: \"string\",\n description: \"One PARA bucket.\",\n values: [\"projects\", \"areas\", \"resources\", \"archive\"]\n },\n { name: \"limit\", type: \"int\", description: \"Rows per page.\", default: 50 },\n {\n name: \"cursor\",\n type: \"string\",\n description: \"The `next_cursor` from the previous page: the last path returned.\"\n },\n {\n name: \"include-archived\",\n type: \"boolean\",\n description: \"Include archived memories.\",\n default: false\n }\n ],\n responseTypes: [\"memory.list\"]\n },\n /**\n * The task family: CRUDL over the 10th memory type, without retrieval.\n *\n * Sugar over the same use cases everything else uses. `task add` is `writeMemory` with\n * `--type task`, and `task status` is one head meta plus (for `done`) the archive machinery. The\n * design intent is that an agent works tasks with `Read`, `Edit`, and `ls` as readily as with these.\n * A task is a file in a directory, and this family exists so the common moves are one call rather\n * than three.\n */\n {\n name: \"task add\",\n summary: \"Open a task: a `task` memory in projects/<ws>/tasks/ or areas/inbox/tasks/.\",\n args: [],\n flags: [\n {\n name: \"title\",\n type: \"string\",\n description: \"What the task is. Becomes the <title> and the filename slug.\",\n required: true\n },\n {\n name: \"claim\",\n type: \"string\",\n description: \"The task statement, as the <mark> span. Defaults to --title.\"\n },\n {\n name: \"body\",\n type: \"string\",\n description: \"A prose paragraph of working notes. Repeatable, one <p> each.\",\n repeatable: true\n },\n {\n name: \"status\",\n type: \"string\",\n description: \"The opening status. `todo` unless you are recording work already underway.\",\n values: TASK_STATUSES,\n default: \"todo\"\n },\n {\n name: \"due\",\n type: \"string\",\n description: \"An ISO date or datetime deadline. Compared as a string, so the form matters.\"\n },\n {\n name: \"workspace\",\n type: \"string\",\n description: \"Routes the task to projects/<slug>/tasks/.\"\n },\n {\n name: \"tag\",\n type: \"string\",\n description: \"A tag. Repeatable; tags scope search but never route a task.\",\n repeatable: true\n },\n {\n name: \"entity\",\n type: \"string\",\n description: \"A `type:name` entity reference. Repeatable.\",\n repeatable: true\n },\n {\n name: \"session-id\",\n type: \"string\",\n description: \"The Claude Code session that opened the task.\"\n },\n { name: \"prompt-id\", type: \"string\", description: \"The prompt within that session.\" },\n { name: \"turn-uuid\", type: \"string\", description: \"The turn within that session.\" }\n ],\n responseTypes: [\"task.written\"]\n },\n {\n name: \"task status\",\n summary: \"Move a task's status. `done` stamps AND archives it, in one commit.\",\n args: [\n { name: \"path\", description: \"The task file.\", required: true },\n { name: \"status\", description: `One of: ${TASK_STATUSES.join(\", \")}.`, required: true }\n ],\n flags: [\n {\n name: \"reason\",\n type: \"string\",\n description: \"Why it closed. Recorded on the archive commit when the status is `done`.\"\n }\n ],\n responseTypes: [\"task.updated\"]\n },\n {\n name: \"task list\",\n summary: \"The task working set: a direct indexed scan with blockers, never ranked retrieval.\",\n args: [],\n flags: [\n {\n name: \"status\",\n type: \"string\",\n description: \"One task status.\",\n values: TASK_STATUSES\n },\n { name: \"workspace\", type: \"string\", description: \"One workspace.\" },\n {\n name: \"due-before\",\n type: \"string\",\n description: \"An ISO date. Returns tasks due strictly before it, by calendar day.\"\n },\n { name: \"limit\", type: \"int\", description: \"Rows per page.\", default: 50 },\n {\n name: \"cursor\",\n type: \"string\",\n description: \"The `next_cursor` from the previous page: the last path returned.\"\n },\n {\n name: \"include-archived\",\n type: \"boolean\",\n description: \"Include finished tasks. `done` archives, so they are otherwise absent.\",\n default: false\n }\n ],\n responseTypes: [\"task.list\"]\n },\n {\n name: \"index rebuild\",\n summary: \"Rebuild index.db from the git tree at HEAD. Destroys nothing outside .memhtml/.\",\n args: [],\n flags: [\n {\n name: \"embed\",\n type: \"boolean\",\n description: \"Fill missing vectors from Bedrock. --no-embed makes the rebuild instant.\",\n default: true\n }\n ],\n responseTypes: [\"index.report\"]\n },\n {\n name: \"index update\",\n summary: \"Index only what moved since the recorded watermark, plus the dirty working tree.\",\n args: [],\n flags: [\n { name: \"embed\", type: \"boolean\", description: \"Fill missing vectors.\", default: true }\n ],\n responseTypes: [\"index.report\"]\n },\n {\n name: \"index status\",\n summary: \"The index watermark, the vector space it was built in, and its row counts.\",\n args: [],\n flags: [],\n responseTypes: [\"index.report\"]\n },\n {\n name: \"trace index\",\n summary: \"Scan $MEMHTML_TRACE_ROOT for Claude Code transcripts, reading only what changed.\",\n args: [],\n flags: [],\n responseTypes: [\"trace.report\"]\n },\n {\n name: \"trace search\",\n summary: \"FTS over session first-prompts and AI titles. Never enters memory retrieval.\",\n args: [{ name: \"query\", description: \"Prose.\", required: true }],\n flags: [\n { name: \"cwd\", type: \"string\", description: \"Restrict to sessions from this directory.\" },\n { name: \"since\", type: \"string\", description: \"ISO-8601 lower bound on started_at.\" },\n { name: \"limit\", type: \"int\", description: \"Sessions to return.\", default: 20 }\n ],\n responseTypes: [\"trace.sessions\"]\n },\n {\n name: \"trace links\",\n summary: \"The memory-session links, from either side.\",\n args: [],\n flags: [\n { name: \"session-id\", type: \"string\", description: \"Every memory this session touched.\" },\n { name: \"path\", type: \"string\", description: \"Every session that touched this memory.\" }\n ],\n responseTypes: [\"trace.links\"]\n },\n {\n name: \"sleep run\",\n summary: \"The nightly curation cycle: 15 phases, each an isolated commit on a review branch.\",\n args: [],\n flags: [\n {\n name: \"date\",\n type: \"string\",\n description: \"The run date, `YYYY-MM-DD`. Defaults to today. Names the branch.\"\n },\n {\n name: \"phases\",\n type: \"string\",\n description: `Comma-separated subset. All 15 by default: ${SLEEP_PHASES.join(\", \")}.`\n },\n {\n name: \"dry-run\",\n type: \"boolean\",\n description: \"Report per-phase counts and commit nothing.\",\n default: false\n }\n ],\n responseTypes: [\"sleep.report\"]\n },\n {\n name: \"sleep resume\",\n summary: \"Re-run only the phases with no Memhtml-Phase trailer on the branch.\",\n args: [{ name: \"run-id\", description: \"The run id, e.g. sleep/2026-08-02.\", required: true }],\n flags: [],\n responseTypes: [\"sleep.report\"]\n },\n {\n name: \"sleep review\",\n summary: \"Per-phase counts, the commit list, diff --stat, and a per-file classification.\",\n args: [{ name: \"run-id\", description: \"The run id.\", required: true }],\n flags: [\n { name: \"diff\", type: \"boolean\", description: \"Include the raw diff.\", default: false }\n ],\n responseTypes: [\"sleep.review\"]\n },\n {\n name: \"sleep merge\",\n summary: \"Fast-forward main to the run's branch, after the discrimination gate passes.\",\n args: [{ name: \"run-id\", description: \"The run id.\", required: true }],\n flags: [\n {\n name: \"skip-gate\",\n type: \"boolean\",\n description: \"Merge without re-running discrimination. A deliberate, logged override, never a default.\",\n default: false\n }\n ],\n responseTypes: [\"sleep.merge\"]\n },\n {\n name: \"sleep status\",\n summary: \"The latest sleep run and its per-phase outcomes.\",\n args: [],\n flags: [],\n responseTypes: [\"sleep.report\"]\n },\n {\n name: \"status\",\n summary: \"Corpus health: HEAD, dirty state, counts by type, edges, index freshness.\",\n args: [],\n flags: [],\n responseTypes: [\"status.health\"]\n },\n {\n name: \"publish\",\n summary: \"Regenerate the per-directory index.html listings and sitemap.xml, and commit them.\",\n args: [],\n flags: [],\n responseTypes: [\"publish.report\"]\n },\n {\n name: \"doctor\",\n summary: \"Corpus health: dangling hrefs, orphan state rows, inbox depth, vocabulary, staleness.\",\n args: [],\n flags: [\n {\n name: \"fix\",\n type: \"boolean\",\n description: \"Repair dangling hrefs and prune orphan access rows. The other findings need a decision.\",\n default: false\n }\n ],\n responseTypes: [\"doctor.report\"]\n },\n {\n name: \"eval discriminate\",\n summary: \"The refusable retrieval gate: every probe must outrank its own wrong-fact twins.\",\n args: [],\n flags: [\n {\n name: \"mode\",\n type: \"string\",\n description: \"`fake` is the deterministic embedder CI measures; `live` needs AWS_BEARER_TOKEN_BEDROCK and refuses loudly without it.\",\n values: [\"fake\", \"live\"],\n default: \"fake\"\n },\n {\n name: \"seed\",\n type: \"int\",\n description: \"The fixture corpus seed. A failing run is reproducible from this number.\"\n },\n { name: \"size\", type: \"int\", description: \"Base memories to generate.\", default: 200 },\n {\n name: \"probes\",\n type: \"int\",\n description: \"Probes to run. Design §5 wants ≥30.\",\n default: 36\n },\n {\n name: \"mrr-floor\",\n type: \"string\",\n description: \"Mean-reciprocal-rank floor. Lowering it is a deliberate, visible choice.\",\n default: \"0.85\"\n }\n ],\n responseTypes: [\"eval.discrimination\"]\n },\n /**\n * Code-mode (ROADMAP item 7b): one script, one execution, one envelope.\n *\n * The flag surface answers three questions a script cannot answer for itself, and nothing else.\n *\n * **How does the script arrive?** Three doors, exactly one per call, enforced in `validate` so a\n * wrong combination is exit 2. `--file` for a script under version control, `--script` for the\n * inline one-liner an agent composes, and a bare `memhtml exec` (or `-`) for stdin, which is the same\n * three-door shape and the same `-` spelling `memhtml apply` already uses for its op stream, so an\n * agent that learned one learned both. `--script` rather than a positional argument, because the\n * positional slot on a two-word command is where a run-id or a path goes on every other command\n * here, and a multi-line program in that slot would read as one.\n *\n * **How long may it run?** `--timeout-ms`, bounded and defaulted, because the guest is a QuickJS\n * worker with no reaper of its own and an unbounded script holds the CLI process open. The\n * millisecond unit is in the flag name rather than left to a note, since `--timeout 30` is\n * ambiguous by a factor of a thousand.\n *\n * **Which tree does it see?** `--sha`, defaulting to `HEAD`. Never the live working tree, which is\n * a containment decision rather than a convenience. A mounted `$MEMHTML_ROOT` exposes `.memhtml/index.db`\n * to the guest, whose `sqlite3` reads it happily (probed 2026-08-09: a read-only mount is no barrier\n * to a reader). A gitignored file is absent from a detached worktree, so pinning a commit is what\n * keeps the ranked planes out of reach, and the read-only mount is the second layer rather than\n * the only one. A pin also makes the answer reproducible. `sha` rides back in the envelope, so a\n * rerun is exact.\n *\n * There is deliberately no flag for the guest's own opt-ins. `javascript` is on because `js-exec`\n * is the feature. `python` and `network` are off and unofferable, so no invocation can turn either\n * on. `apps/cli/src/exec.ts` carries the mechanism and the egress probe.\n */\n {\n name: \"exec\",\n summary: \"Run a read-only traversal script over the corpus in a sandbox: multi-hop in ONE execution.\",\n args: [],\n flags: [\n {\n name: \"file\",\n type: \"string\",\n description: \"The script to run, as a path on the HOST. Omit it (or pass `-`) to read the script from stdin. Mutually exclusive with `--script`.\"\n },\n {\n name: \"script\",\n type: \"string\",\n description: \"The script source, inline. Mutually exclusive with `--file` and with reading stdin.\"\n },\n {\n name: \"timeout-ms\",\n type: \"int\",\n description: \"Wall-clock bound on the script. Exceeding it is `exitCode` 124 with `timedOut: true`, not an error envelope. Capped at 600000.\",\n default: 30000\n },\n {\n name: \"sha\",\n type: \"string\",\n description: \"The commit to mount, materialized as a detached worktree. Defaults to HEAD. Never the live working tree, whose gitignored .memhtml/index.db a worktree omits.\"\n }\n ],\n responseTypes: [\"exec.report\"]\n },\n {\n name: \"state export\",\n summary: \"Write .memhtml/state/access.jsonl, the only durable copy of the state plane, and commit.\",\n args: [],\n flags: [],\n responseTypes: [\"state.export\"]\n },\n {\n name: \"state import\",\n summary: \"Replay the committed sidecar into state.db. Counters merge by max, never last-wins.\",\n args: [],\n flags: [],\n responseTypes: [\"state.import\"]\n },\n {\n name: \"agents-doc\",\n summary: \"Regenerate AGENTS.md from this command table. --check fails on drift.\",\n args: [],\n flags: [\n {\n name: \"check\",\n type: \"boolean\",\n description: \"Compare the committed doc to the regenerated one and fail on a difference.\",\n default: false\n },\n { name: \"out\", type: \"string\", description: \"Where to write. Defaults to ./AGENTS.md.\" }\n ],\n responseTypes: [\"agents.doc\"]\n },\n {\n name: \"serve mcp\",\n summary: \"Run the `memhtml-mcp` stdio server: 14 tools and 2 resources over this same repo.\",\n args: [],\n flags: [],\n responseTypes: [\"serve.exit\"]\n }\n];\nexport const COMMAND_NAMES = COMMANDS.map((command) => command.name);\n/**\n * The example op line, quoted verbatim into the `when-to-batch` block.\n *\n * A constant rather than a literal inside the prose, because a test parses it. An example an agent\n * copies has to be valid JSONL, and it stays valid because the doc and the parser read the\n * same bytes. A prose-only example drifts silently the first time a field is renamed.\n */\nexport const GUIDE_OP_EXAMPLE = '{\"op\":\"write\",\"title\":\"One writer and many readers share the index\",\"type\":\"semantic\",\"body\":\"WAL admits a single writer at a time and any number of concurrent readers, so a CLI command and a running `memhtml serve mcp` can work against one store.\",\"tag\":\"infra\"}';\n/**\n * The guide: what an agent reads on its first call, before it has written anything.\n *\n * Prose, in a structured field, authored here beside `COMMANDS`, which is the design (spec\n * D8/G6). The manifest carries it on a bare `memhtml`, `memhtml help`, `memhtml --help`, and `memhtml manifest`, and\n * `memhtml agents-doc` renders these same strings into `AGENTS.md`, so the doc and the live answer cannot\n * disagree. Prose kept in a separate Markdown file would be a second copy that drifts, and prose kept\n * only in `AGENTS.md` would be invisible to an agent that never opens the repo.\n *\n * Written for an LLM agent mid-task rather than for an operator browsing: complete sentences, action\n * first, and every claim true of this build rather than of the design. A guide that describes an\n * intention is worse than no guide, because an agent acts on it.\n */\nexport const GUIDE = [\n {\n topic: \"first-call\",\n body: \"You are reading this CLI's manifest: every command, argument, flag, response type, error code, \" +\n \"and environment variable the binary accepts. A bare `memhtml`, `memhtml help`, `memhtml --help`, and \" +\n \"`memhtml manifest` all return it, and all four answer on a machine with no repo, no database, and \" +\n \"no credentials, so this is also the liveness check when something else has failed. \" +\n \"Every command writes exactly ONE JSON envelope to stdout and nothing else; logs go to stderr. \" +\n \"A success is `{apiVersion, type, data}` and a failure is `{apiVersion, error, code, suggestions}`. \" +\n \"Branch on `code`, never on the `error` prose: the codes and response types are append-only and a \" +\n \"shipped one never changes meaning, while the prose changes freely as wording improves. \" +\n \"Exit 0 is success, exit 2 is a usage error you fix by changing the call, exit 1 is a runtime \" +\n \"failure you fix by changing the repo or the environment. Add `--dense` to any command to get \" +\n \"minified JSON with null fields dropped, which is what you want when the output goes into a prompt.\"\n },\n {\n topic: \"write-surfaces\",\n body: \"There are three ways to put a memory into the corpus, and they are all legitimate. \" +\n \"First, this CLI: `memhtml write` for one memory, `memhtml apply` for many. \" +\n \"Second, the MCP server: `memhtml serve mcp` speaks stdio with 14 tools and 2 resources over this \" +\n \"same repo, and it is the door to use when you are already an MCP client. \" +\n \"Third, editing files under $MEMHTML_ROOT directly with your normal file tools: the git tree IS the \" +\n \"system of record and `.memhtml/index.db` is only a projection of it, so a hand-written or hand-edited \" +\n \"memory file is as real as one this CLI wrote. `memhtml index update` projects uncommitted working-tree \" +\n \"changes as well as committed ones, so a dirty edit is searchable before you commit it. \" +\n \"What you take on by editing directly is everything the write path would have done for you: the \" +\n \"file must satisfy the format (run `memhtml doctor`, and `memhtml read <path>` reports per-file format \" +\n \"warnings), you own choosing a path that does not collide, you own noticing that the content \" +\n \"already exists somewhere else, and you own the commit. The nightly `memhtml sleep run` refuses to \" +\n \"start on a dirty tree, so an uncommitted edit blocks curation until it is committed or stashed. \" +\n \"A CLI command and a running `memhtml serve mcp` may share one store: the index is WAL SQLite, \" +\n \"which admits one writer at a time and any number of concurrent readers, so a second writer \" +\n \"waits its turn rather than failing. The one thing to keep clear of is `memhtml sleep run`, and \" +\n \"for a git reason rather than a database one: a run holds a checked-out `sleep/<date>` branch, \" +\n \"so a write landing during it commits onto that branch and is merged as if it were curation or \" +\n \"lost when the branch is dropped.\"\n },\n {\n topic: \"when-to-batch\",\n body: \"Writing more than about three memories in one task? Call `memhtml apply` once with a JSONL op stream \" +\n \"instead of running `memhtml write` N times. A batch stages every file, makes ONE commit, and \" +\n \"reindexes ONCE, where N separate writes make N commits and pay N index passes over N diffs. \" +\n \"Pass the stream as `memhtml apply --file ops.jsonl`, or pipe it: `memhtml apply -` and a bare `memhtml apply` \" +\n \"both read stdin. One complete JSON object per line, no wrapping array, no pretty-printing. A \" +\n \"line looks like this:\\n\" +\n `${GUIDE_OP_EXAMPLE}\\n` +\n \"`op` is `write` (the only verb in the vocabulary today), `title` and `type` are required, and each \" +\n \"op carries the same optional fields `memhtml write` takes, in snake_case: `path`, `workspace`, `tag`, \" +\n \"`entity`, `importance`, `confidence`, `session_id`, `prompt_id`, `turn_uuid`. \" +\n \"The whole file is validated for shape before ANY op executes, so a malformed line 7 is exit 2 \" +\n \"naming line 7 with nothing written. A failed apply costs you nothing but the call. \" +\n \"You get one result per op in INPUT ORDER, each naming its own `index`, so you can match results \" +\n \"back to the lines you sent. \" +\n \"A batch is ATOMIC by default: the first refused op aborts the whole batch, no file is written, no \" +\n \"commit is made, and the surviving ops report `skipped: true`. Pass `--continue-on-error` for \" +\n \"best-effort instead, and a refused op comes back as one failed result carrying its own `code` and \" +\n \"`error` while every op that succeeded lands in the one commit. \" +\n \"A duplicate is never an error: an op whose exact content is already stored comes back `ok: true` \" +\n \"with `deduped: true` and the existing path, so re-applying a file you already applied is safe and \" +\n \"writes nothing. `commit_sha` is null exactly when nothing was committed: a batch that only \" +\n \"deduped, or one that aborted.\"\n },\n {\n topic: \"conflicts\",\n body: \"Pass `--detect-conflicts` to `memhtml apply` and each result gains a `conflict` field naming what \" +\n \"that op's claim contradicts. Dedupe catches an op whose content is IDENTICAL to something stored; \" +\n \"this catches an op that says something DIFFERENT about the same thing, the case dedupe is blind \" +\n \"to and the one that actually rots a corpus. \" +\n \"The match is grammatical, not semantic: a claim is split into a frame (the subject and relation, \" +\n \"up to its last `of`/`is`/`in`/`to`/`by`/`as`) and a value, and two claims conflict when they share \" +\n \"a frame. `The pool ceiling is 64` and `The pool ceiling is 128` share `the pool ceiling is`. \" +\n \"`conflict.path` names an ACTIVE memory already holding that slot; `conflict.batch_index` names an \" +\n \"EARLIER op in this same call, which is the case nothing else can see because neither op is stored \" +\n \"yet; `conflict.claim` is the other claim's own text, so you can decide without a second read. \" +\n \"It is null when nothing matched, and also when the claim has no frame shape. The rule refuses \" +\n \"frames under three tokens and values over six, so short claims and claims trailed by a clause are \" +\n \"deliberately unmatched rather than loosely matched. On a line using `article_html` instead of \" +\n \"`body` it is always null, because the claim lives inside your markup and is not read until the \" +\n \"store renders it. \" +\n \"THE ASSIST NEVER CHANGES WHAT IS WRITTEN. An op carrying a conflict is written exactly as it \" +\n \"would have been without the flag: nothing is archived, nothing is refused, and later does not win. \" +\n \"That is deliberate, because sometimes the contradiction IS the answer. A memory recording that a \" +\n \"runbook step changed necessarily contradicts the memory stating the old step, and a system that \" +\n \"resolved that for you would delete the pair a reader needs in order to see the change at all. \" +\n \"You decide per conflict: keep both (they are about different things, or both are true), \" +\n \"`memhtml correct <path>` instead (the new claim supersedes the old one, and the old one stays readable \" +\n \"under archive/), or drop the line (you were about to restate something already stored). \" +\n \"Archived memories never match, so a superseded claim stops contradicting the claim that superseded it.\\n\" +\n \"When you have already decided that later wins (a re-scrape, a settings sync, any stream where \" +\n \"each line is the newest statement of its slot), pass `--consolidate last-wins` (the batch tool's \" +\n '`consolidate: \"last-wins\"`) and the batch RESOLVES those matches instead of reporting them. ' +\n \"Ops sharing a frame key write ONE file carrying the LATER value at the FIRST index that claimed \" +\n \"the slot; each later restatement reports `consolidated_into` naming that slot and the summary \" +\n \"counts it under `consolidated`, neither written nor failed. A stored ACTIVE memory occupying a \" +\n \"surviving slot is archived with a supersedes link from the new file, its archive path reported \" +\n \"as `superseded_path`, the same chain `memhtml correct` leaves, so ancestry reads identically. \" +\n \"OFF by default, and the key is the conflict rule's own: the frame split is a rule measured in \" +\n \"the eval harness before it was believed and ported verbatim into `@memhtml/domain`'s frame.ts, \" +\n \"which detection and consolidation share, so anything the rule refuses to key (short frames, \" +\n \"clause values) is never consolidated, and what you saw reported with `--detect-conflicts` is \" +\n \"exactly what this flag would have acted on.\\n\" +\n \"Every supersede, `memhtml correct` and `--consolidate last-wins` alike, also stamps a VALIDITY \" +\n \"WINDOW, in the same one commit. The superseded memory gains `memhtml-valid-until` set to the \" +\n \"moment the new fact became true (the winner's own `memhtml-valid-from`, else its first \" +\n \"`<time datetime>`, else the operation's instant), and the winner gains `memhtml-valid-from` at \" +\n \"that same moment, so one window closes exactly where the next opens. Min-wins: a memory \" +\n \"already stating an EARLIER `memhtml-valid-until` keeps it, because a fact cannot outlive its \" +\n \"earliest stated bound. That is what `--as-of` on `memhtml search` reads: pass an ISO instant and \" +\n \"the result is what was believed valid AT THAT MOMENT. Since-superseded memories return, each \" +\n \"marked `superseded_by` naming what replaced it, and facts not yet valid then are absent. \" +\n \"History is read from the files, not replayed from git, so it survives a full index rebuild.\"\n },\n {\n topic: \"authoring\",\n body: \"Every write authors the article in exactly one of two ways, and supplying both or neither is \" +\n \"refused. Either you write prose and the template owns the markup (`--claim` is the one \" +\n \"load-bearing sentence and becomes the `<mark>` claim span and `files.gist`, and each `--body` \" +\n \"is one paragraph after it) or you supply `--article-html` and own the markup yourself. \" +\n \"On a `memhtml apply` line the prose form is the `body` field, whose first sentence becomes the claim, \" +\n \"and the markup form is `article_html`. \" +\n \"When you supply markup you own two constraints. It must contain EXACTLY ONE `<mark>`, and that \" +\n \"`<mark>` must sit in the article's first `<p>` or `<li>` and not inside an `<aside>` or \" +\n \"`<details>`. The claim leads the article and is never a caveat or behind a fold. And the first \" +\n \"`<time datetime>` in your markup becomes the memory's event time, which is what recency ranks \" +\n \"on, so a memory about something that happened last year should say so rather than being ranked \" +\n \"as today's news. \" +\n \"Markup is checked before anything is written: the store renders your article, runs the format \" +\n \"check, and refuses with the list of violations before it creates a file, stages it, or commits. \" +\n \"A refused write leaves the tree byte-identical, so a failed attempt costs nothing and you can fix \" +\n \"the markup and retry. \" +\n \"Code goes in the prose path as a fenced block: a body paragraph that is entirely a ``` fence \" +\n \"becomes <figure><pre><code>, whitespace preserved verbatim, and the fence's info string \" +\n \"(```ts) is stamped as data-lang and promoted to a `lang:ts` entity, so `memhtml list --entity \" +\n \"lang:ts` finds every memory carrying TypeScript. A blank line inside a fence does NOT split \" +\n 'paragraphs. On the markup path write the same <figure><pre><code data-lang=\"ts\"> yourself; ' +\n \"never `class` (forbidden) and never `lang=` (that attribute names human languages).\"\n },\n {\n topic: \"code-mode\",\n body: \"Answering a question that takes MORE THAN ONE HOP through the corpus? Write it as a script and \" +\n \"run `memhtml exec` once, instead of spending a tool call per hop. Supersedence ancestry, live \" +\n \"contradiction pairs, orphan census, entity co-occurrence, 'which of these 40 paths has no \" +\n \"backlink': each of those is one traversal in code and N round trips through `memhtml read` and \" +\n \"`memhtml neighbors`. Measured on a 305-file corpus: a full census in 598ms, and 410 edges resolved \" +\n \"into 201 chains, longest 8 hops, in one execution at 430ms. \" +\n \"The script runs under QuickJS in a sandbox with the corpus mounted READ-ONLY at `/mnt/memhtml`, and \" +\n \"a helper is already seeded for you at `/workspace/lib/corpus.mjs`. Import it: \" +\n '`import { corpus, backlinks, chain, edges } from \"/workspace/lib/corpus.mjs\"`. `corpus()` ' +\n \"returns a Map keyed by root-absolute path (the SAME string an edge's href holds, so \" +\n \"`memories.get(link.href)` resolves with no path juggling) and each value carries `claim`, \" +\n \"`memoryType`, `status`, `tags`, `entities`, `links`, `facets`, `citations`, `eventAt`, and a \" +\n \"`document` escape hatch for any selector the fields do not cover. \" +\n \"Print your answer as JSON on stdout with `console.log`; it comes back verbatim in `data.stdout`, \" +\n \"so keep it small and structured rather than dumping the corpus. \" +\n \"THREE THINGS IT CANNOT DO, by design. It cannot write: the corpus is read-only and a write \" +\n \"answers EROFS, so every write still goes through `memhtml write` / `memhtml apply`, which own commits, \" +\n \"dedup, and conflict detection. It cannot rank: no cosine, no RRF, no salience, and no index \" +\n \"database. For ranked retrieval shell out to `memhtml search --json` and parse its envelope, which \" +\n \"the one-envelope-per-command contract already makes a code-mode API. And it cannot reach the \" +\n \"network: there is no curl and the guest's `fetch` refuses on call. \" +\n \"The intended opening move is ranked retrieval FIRST, code-mode second: `memhtml search` or \" +\n \"`memhtml recall` to get the handful of paths the ranking stack says matter, then `memhtml exec` to walk, \" +\n \"join, count, and filter from there. Starting in code-mode means starting with a full-corpus scan \" +\n \"and no relevance signal. \" +\n \"A non-zero `exitCode` in the response is YOUR script failing, not the command failing. Read \" +\n \"`data.stderr` for the diagnostic and the exit code is still 0. A script that runs past \" +\n \"`--timeout-ms` (default 30000) comes back `exitCode: 124` with `timedOut: true`. \" +\n \"The tree you get is a pinned commit, HEAD by default, named in `data.sha`, so an answer is \" +\n \"reproducible with `--sha`, and an uncommitted edit is NOT visible to the script.\"\n }\n];\nexport const GUIDE_TOPICS = GUIDE.map((block) => block.topic);\n/**\n * Derived from `COMMANDS` and `GLOBAL_FLAGS` by walking them, so adding a flag\n * updates the manifest automatically. A hand-written manifest drifts the first\n * time someone adds a flag and forgets to edit it.\n */\nexport const buildManifest = () => ({\n name: \"memhtml\",\n version: \"0.2.2\", // x-release-please-version\n summary: \"Read, write, and curate the git-backed memory repo.\",\n apiVersion: \"1\",\n /**\n * The prose an agent needs before the command table means anything, so it is listed before it.\n * A manifest that opened with 33 command specifications makes an agent infer the workflow from a\n * surface, while `guide` states it.\n */\n guide: GUIDE,\n globalFlags: GLOBAL_FLAGS,\n errorCodes: ERROR_CODES,\n config: CONFIG_VARS,\n responseTypes: [...new Set(COMMANDS.flatMap((command) => command.responseTypes))],\n commands: COMMANDS.map((command) => ({\n name: command.name,\n summary: command.summary,\n args: command.args,\n flags: command.flags,\n responseTypes: command.responseTypes,\n supportsJson: true,\n supportsDense: true\n }))\n});\n//# sourceMappingURL=commands.js.map","/**\n * Prose → claim derivation: the single implementation both write doors use.\n *\n * The tools take `{title, body}` because that is what a model produces, and the format needs a\n * `<mark>` claim plus one `<p>` per paragraph. Turning the first into the second is a text heuristic,\n * and it lives here for two reasons. It was duplicated once, as `claimOf`/`restOf` in `apps/mcp` and\n * `claimFromProse`/`proseTail` in `apps/cli`, the same regex in two packages. A sentence-splitting\n * rule that drifts between the doors also makes `memhtml apply` and `memory_write_batch` derive different\n * claims from the same body, so the gist of a memory would depend on which door wrote it.\n *\n * It does not live in `@memhtml/html`, which owns markup and the format's own rules. \"Where does a\n * sentence end\" is a guess about natural-language prose, and the format states no such constraint. It\n * is not in `operations.ts` either, because that module holds the use cases both doors call, and this\n * is a text helper they apply before calling one.\n *\n * The derivation is defense in depth now; it was once the only guard. `@memhtml/html` constraint 1 now\n * rejects an empty `<mark>` outright, so a door that skipped this would be stopped by the store's\n * render gate instead of landing a file with an empty `files.gist`. What is left here is the\n * authoring convenience the doors exist to provide: a JSONL line and an MCP call carry no `claim`\n * field, so the door derives one instead of asking an author to restate the body's first sentence.\n */\nimport { closesFence, fenceOpeningOf } from \"@memhtml/html\";\n/**\n * Split prose into paragraphs on blank lines, dropping the empties. Inside a fenced code block a\n * blank line is content, so the split skips it there. Without that carve-out, a snippet containing a\n * blank line splits into two paragraphs, neither of which is a complete fence, and both land as\n * escaped backtick text instead of the `<figure><pre><code>` an intact fence renders as.\n *\n * The fence grammar comes from `@memhtml/html` (`fenceOpeningOf`/`closesFence`) rather than a second\n * copy here. The splitter deciding \"this is one block\" and the template deciding \"this is a fence\"\n * must be the same judgment, or the doors drift the way the claim derivation once did.\n */\nconst paragraphsOf = (prose) => {\n const parts = [[]];\n let opening;\n for (const line of prose.split(\"\\n\")) {\n const current = parts.at(-1);\n if (opening === undefined && line.trim() === \"\") {\n if (current.length > 0)\n parts.push([]);\n continue;\n }\n current.push(line);\n if (opening === undefined) {\n opening = fenceOpeningOf(line);\n }\n else if (closesFence(line, opening)) {\n opening = undefined;\n }\n }\n return parts.map((lines) => lines.join(\"\\n\").trim()).filter((part) => part !== \"\");\n};\n/**\n * The claim: the first sentence of the prose.\n *\n * The first sentence is where a model puts the assertion. Taking the title instead would make every\n * gist a restatement of the filename, which is the one thing a Tier-1 disclosure line must not be.\n * Prose with no sentence terminator is its own claim in full. A fragment is still an assertion, and\n * rejecting it would reject the shortest legitimate memory there is.\n */\nexport const claimFromProse = (prose) => {\n const trimmed = prose.trim();\n const match = /^(.*?[.!?])(\\s|$)/s.exec(trimmed);\n return (match?.[1] ?? trimmed).trim();\n};\n/**\n * The prose after the claim, as paragraphs. Empty when the claim was the whole body.\n *\n * The first element becomes the claim paragraph's own tail rather than a second `<p>`, which is\n * `articleHtmlFor`'s contract in `@memhtml/html`'s template. A one-paragraph body therefore yields\n * exactly one `<p>` with the `<mark>` inside it, which is what constraint 1 requires.\n */\nexport const proseTail = (prose) => {\n const remainder = prose.trim().slice(claimFromProse(prose).length).trim();\n return remainder === \"\" ? [] : paragraphsOf(remainder);\n};\n//# sourceMappingURL=prose.js.map","import { readFile } from \"node:fs/promises\";\nimport { fail } from \"./envelope.js\";\nimport { claimFromProse, proseTail } from \"./prose.js\";\n/**\n * `memhtml apply`'s own layer: JSONL text in, decoded ops or a usage failure out.\n *\n * Separated from `run.ts` because everything here is a decision about one untrusted text format, and\n * because AC-6-4's contract is that the whole file is judged before any op executes. That makes\n * this a pure function from text to either an op list or a refusal, testable without a repo.\n *\n * The refusals are `Failure` values rather than thrown errors for the reason `validate` returns one:\n * the exit code is the contract. A usage error is exit 2 and a runtime error is exit 1, and a\n * malformed line is a usage error, because the caller wrote a bad file and the corpus is fine.\n */\n/**\n * The op vocabulary, v1.\n *\n * Writes only, per spec D4. `op` is carried on the wire anyway so v2 can add `correct`/`link`/\n * `archive` without a format break. An unknown value is refused with this list attached rather than\n * ignored, because a file of `{\"op\":\"wrote\",…}` lines that applied nothing and exited 0 is the silent\n * failure the whole pre-validation pass exists to prevent.\n */\nexport const APPLY_OPS = [\"write\"];\n/**\n * Every field a line may carry, mapped to the `WriteParams` field it becomes.\n *\n * A table rather than a hand-written decode, so the snake_case → camelCase rename is stated once and\n * the unknown-field check below is derived from it. The MCP tool's parameters use exactly these\n * snake_case names (`apps/mcp/src/tools.ts`), so an agent that learned the field names from one door\n * can write a JSONL file for the other without translating.\n */\nconst SCALAR_FIELDS = {\n title: \"title\",\n type: \"memoryType\",\n body: \"body\",\n article_html: \"articleHtml\",\n path: \"path\",\n workspace: \"workspace\",\n importance: \"importance\",\n confidence: \"confidence\",\n session_id: \"sessionId\",\n prompt_id: \"promptId\",\n turn_uuid: \"turnUuid\",\n status: \"taskStatus\",\n due: \"dueAt\"\n};\n/** Fields that accept a string or an array of strings, and always become an array. */\nconst LIST_FIELDS = { tag: \"tags\", tags: \"tags\", entity: \"entities\", entities: \"entities\" };\n/** `op` is the discriminator rather than a `WriteParams` field, so it is legal and never mapped. */\nconst KNOWN_FIELDS = new Set([\n \"op\",\n ...Object.keys(SCALAR_FIELDS),\n ...Object.keys(LIST_FIELDS)\n]);\n/** A usage failure naming the offending line, 1-based as a text editor counts. */\nconst lineError = (code, line, reason, suggestions = []) => fail(code, `${APPLY_DOC}: line ${line}: ${reason}`, suggestions);\n/** The prefix every apply refusal carries, so a caller can tell a file error from a corpus error. */\nconst APPLY_DOC = \"memhtml apply\";\n/** One line's parsed JSON as a record, or the refusal. */\nconst objectAt = (text, line) => {\n let value;\n try {\n value = JSON.parse(text);\n }\n catch (error) {\n return lineError(\"ERR_INVALID_FLAG\", line, `not valid JSON (${error instanceof Error ? error.message : String(error)}). Every line is one complete JSON object; a pretty-printed object spanning several lines is not JSONL`, [\n 'memhtml apply --file ops.jsonl, one object per line: {\"op\":\"write\",\"title\":\"…\",\"type\":\"semantic\",\"body\":\"…\"}'\n ]);\n }\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n return lineError(\"ERR_INVALID_FLAG\", line, `parsed as ${Array.isArray(value) ? \"an array\" : typeof value}, not a JSON object`);\n }\n return value;\n};\nconst isFailure = (value) => typeof value === \"object\" && value !== null && \"code\" in value && \"error\" in value;\n/** A field that must be a non-empty string, or the refusal naming it. */\nconst requiredString = (record, field, line) => {\n const value = record[field];\n if (value === undefined) {\n return lineError(\"ERR_MISSING_ARGUMENT\", line, `missing required field \\`${field}\\``);\n }\n if (typeof value !== \"string\" || value.trim() === \"\") {\n return lineError(\"ERR_INVALID_FLAG\", line, `\\`${field}\\` must be a non-empty string, got ${value === null ? \"null\" : typeof value}`);\n }\n return value;\n};\n/** A list field as an array of strings: a bare string is a one-element list, as `--tag` is. */\nconst strings = (value, field, line) => {\n if (typeof value === \"string\")\n return value === \"\" ? [] : [value];\n if (!Array.isArray(value)) {\n return lineError(\"ERR_INVALID_FLAG\", line, `\\`${field}\\` must be a string or an array of strings, got ${typeof value}`);\n }\n const out = [];\n for (const entry of value) {\n if (typeof entry !== \"string\") {\n return lineError(\"ERR_INVALID_FLAG\", line, `\\`${field}\\` holds a ${typeof entry} where every element must be a string`);\n }\n if (entry !== \"\")\n out.push(entry);\n }\n return out;\n};\n/** A numeric field, accepting the JSON number or a numeric string. */\nconst numeric = (value, field, line) => {\n const parsed = typeof value === \"number\" ? value : Number(value);\n if (typeof value !== \"number\" && typeof value !== \"string\") {\n return lineError(\"ERR_INVALID_FLAG\", line, `\\`${field}\\` must be a number, got ${typeof value}`);\n }\n if (!Number.isFinite(parsed)) {\n return lineError(\"ERR_INVALID_FLAG\", line, `\\`${field}\\` is not a finite number: ${String(value)}`);\n }\n return parsed;\n};\n/**\n * One line as a `WriteParams`, or the refusal naming the line.\n *\n * The shape rules AC-6-4 puts at this door and nowhere else: the line parses, it declares an op in\n * the vocabulary, and it carries `title` and `type` as non-empty strings. Everything past that is\n * the operations layer's decode (is `type` in the vocabulary) or the store's render gate (is the\n * markup valid). Those are checked per op and reported per op, and they are not duplicated here,\n * because a second copy of the type vocabulary is a second thing to update when it moves.\n */\nconst opAt = (record, line) => {\n for (const field of Object.keys(record)) {\n if (!KNOWN_FIELDS.has(field)) {\n return lineError(\"ERR_INVALID_FLAG\", line, `unknown field \\`${field}\\`. Fields: ${[...KNOWN_FIELDS].sort().join(\", \")}`);\n }\n }\n const op = record.op;\n if (op === undefined) {\n return lineError(\"ERR_MISSING_ARGUMENT\", line, `missing required field \\`op\\`. One of: ${APPLY_OPS.join(\", \")}`);\n }\n if (typeof op !== \"string\" || !APPLY_OPS.includes(op)) {\n return lineError(\"ERR_INVALID_FLAG\", line, `\\`op\\` must be one of: ${APPLY_OPS.join(\", \")}, got ${JSON.stringify(op)}`);\n }\n const title = requiredString(record, \"title\", line);\n if (isFailure(title))\n return title;\n const memoryType = requiredString(record, \"type\", line);\n if (isFailure(memoryType))\n return memoryType;\n const params = { title, memoryType, claim: \"\" };\n for (const [field, target] of Object.entries(SCALAR_FIELDS)) {\n const value = record[field];\n if (value === undefined || value === null)\n continue;\n if (field === \"title\" || field === \"type\")\n continue;\n if (target === \"importance\" || target === \"confidence\") {\n const parsed = numeric(value, field, line);\n if (isFailure(parsed))\n return parsed;\n params[target] = parsed;\n continue;\n }\n if (typeof value !== \"string\") {\n return lineError(\"ERR_INVALID_FLAG\", line, `\\`${field}\\` must be a string, got ${typeof value}`);\n }\n params[target] = value;\n }\n for (const [field, target] of Object.entries(LIST_FIELDS)) {\n const value = record[field];\n if (value === undefined || value === null)\n continue;\n const parsed = strings(value, field, line);\n if (isFailure(parsed))\n return parsed;\n params[target] = [...(params[target] ?? []), ...parsed];\n }\n /**\n * `body` prose becomes claim + tail; `article_html` is used verbatim and leaves `claim` empty.\n *\n * The XOR itself is not enforced here. The store's render gate owns it per op, so a batch with one\n * bad op reports that op and not the whole file. This branch owns the claim instead: the JSONL wire\n * has no `claim` field, so a prose line's claim is derived rather than restated by its author (see\n * {@link claimFromProse}, the one copy both doors share).\n *\n * Skipping this no longer lands a bad file. `@memhtml/html` constraint 1 rejects an empty `<mark>`, so\n * the render gate would stop the op instead of committing a file with an empty `files.gist`. The\n * derivation is what makes a prose line valid in the first place. It is no longer the only thing\n * standing between a missing claim and a silent write.\n */\n const prose = typeof params.body === \"string\" ? params.body : undefined;\n if (prose !== undefined && prose.trim() !== \"\") {\n params.claim = claimFromProse(prose);\n params.body = proseTail(prose);\n }\n else if (prose !== undefined) {\n delete params.body;\n }\n return params;\n};\n/**\n * Decode a whole JSONL document, refusing on the first bad line.\n *\n * Every line is judged before any op runs (AC-6-4), and that ordering is the contract rather than an\n * implementation detail. An apply that executed lines 1-6 and then refused line 7 would leave a\n * commit behind for a call that reported failure, and the caller's only recovery would be to work out\n * which prefix landed. Refusing first costs nothing, since no service has been touched yet.\n *\n * Blank lines are skipped rather than refused, because a file written by `printf '%s\\n'` or a heredoc\n * ends in one. The line numbers still count them, so an error naming line 7 means the seventh\n * line of the file the caller can open in an editor.\n */\nexport const decodeApply = (text) => {\n const ops = [];\n const lines = text.split(\"\\n\");\n for (const [at, raw] of lines.entries()) {\n const line = at + 1;\n if (raw.trim() === \"\")\n continue;\n const record = objectAt(raw, line);\n if (isFailure(record))\n return { ok: false, failure: record };\n const op = opAt(record, line);\n if (isFailure(op))\n return { ok: false, failure: op };\n ops.push(op);\n }\n if (ops.length === 0) {\n return {\n ok: false,\n failure: fail(\"ERR_MISSING_ARGUMENT\", `${APPLY_DOC}: no ops. The input held no non-blank lines, so there is nothing to write`, [\n \"memhtml apply --file ops.jsonl\",\n 'printf \\'%s\\\\n\\' \\'{\"op\":\"write\",\"title\":\"A fact\",\"type\":\"semantic\",\"body\":\"The thing that happened.\"}\\' | memhtml apply -'\n ])\n };\n }\n return { ok: true, ops };\n};\n/**\n * Read the JSONL text for one invocation: `--file <path>`, or stdin.\n *\n * **The stdin seam.** `run()` takes this reader as an injectable parameter and defaults to\n * {@link readStdin}, so `bin.ts` needs no edit and a test supplies the text directly. Reading stdin\n * in `bin.ts` instead would make the entry point parse argv to discover whether\n * the command it is about to dispatch even wants stdin, and would put an I/O decision in the one file\n * whose whole job is \"call run, write the envelope, exit\".\n */\nexport const applyText = async (file, stdin) => {\n if (file !== undefined && file.trim() !== \"\") {\n try {\n return await readFile(file, \"utf8\");\n }\n catch (error) {\n return fail(\"ERR_PATH_NOT_FOUND\", `${APPLY_DOC}: cannot read --file ${file}: ${error instanceof Error ? error.message : String(error)}`, [`ls ${file}`, \"memhtml apply - < ops.jsonl\"]);\n }\n }\n return await stdin();\n};\n/**\n * `process.stdin` as text, and nothing when a human is at a terminal.\n *\n * The TTY check is what makes a bare `memhtml apply` with no `--file` and no pipe answer with the\n * empty-input usage error instead of hanging forever waiting on a keyboard. An agent invoking this\n * without a pipe gets an envelope; a hang would get a timeout and no diagnosis.\n */\nexport const readStdin = async () => {\n if (process.stdin.isTTY === true)\n return \"\";\n const chunks = [];\n for await (const chunk of process.stdin) {\n chunks.push(typeof chunk === \"string\" ? Buffer.from(chunk) : chunk);\n }\n return Buffer.concat(chunks).toString(\"utf8\");\n};\n/**\n * The `batch.applied` payload: the operation's result, renamed to the wire's snake_case.\n *\n * snake_case here and camelCase in `memory.written` is a real inconsistency, and it comes from the\n * spec (D6 names `commit_sha`). It was taken on purpose, because it makes this payload byte-comparable\n * with `memory_write_batch`'s over MCP. An agent that has parsed one has parsed the other, and a batch\n * result is the payload most likely to be handled by shared code across the two doors.\n *\n * Absent fields are `null` rather than missing. `deduped` and `skipped` are always booleans. An agent\n * branching on `deduped === true` should not have to also handle the key being absent, and `--dense`\n * strips the nulls for the context-window case anyway.\n */\nconst opPayload = (report) => ({\n index: report.index,\n ok: report.ok,\n path: report.path ?? null,\n deduped: report.deduped === true,\n existing_path: report.existingPath ?? null,\n code: report.code ?? null,\n error: report.error ?? null,\n skipped: report.skipped === true,\n /**\n * What this op's claim contradicts, when `--detect-conflicts` was passed and something matched.\n *\n * The inner field names are snake_case (`batch_index`) for the same reason the outer ones are: this\n * payload is byte-comparable with `memory_write_batch`'s, so shared code across the two doors reads\n * one shape. Null when the flag was off, when nothing matched, or when the claim has no frame\n * shape. An op carrying a conflict was still written, because the field is a report rather than a\n * refusal.\n */\n conflict: report.conflict === undefined\n ? null\n : {\n path: report.conflict.path,\n batch_index: report.conflict.batchIndex,\n claim: report.conflict.claim\n },\n /**\n * The two `--consolidate last-wins` outcomes, null everywhere else, including when the flag was\n * off. That is the same \"absent is null\" rule every field above follows, and the same shape\n * `memory_write_batch` publishes.\n */\n consolidated_into: report.consolidatedInto ?? null,\n superseded_path: report.supersededPath ?? null\n});\nexport const applyPayload = (result) => ({\n results: result.results.map(opPayload),\n summary: result.summary,\n commit_sha: result.commitSha\n});\n//# sourceMappingURL=apply.js.map","import { mkdir, writeFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport { isEdgeRel } from \"@memhtml/contracts/edges\";\nimport { INBOX_DIR, normalizePath, TASKS_SUBDIR } from \"@memhtml/contracts/paths\";\nimport { checkMemory } from \"@memhtml/html\";\nimport { DatabaseService, STATE_SCHEMA } from \"@memhtml/index\";\nimport { EMBED_WATERMARK } from \"@memhtml/llm\";\nimport { allPaths, applyHeadEdits, archivedFormOf, danglingEdges, hrefFor, link, meta, unlink } from \"@memhtml/sleep\";\nimport { attemptIo, commitSubject, readFileOrNull } from \"@memhtml/store\";\nimport { Effect } from \"effect\";\nimport { Git, Store } from \"./api-layer.js\";\n/**\n * `memhtml doctor`: the corpus's own health check, and `--fix` for the two findings a repair can settle\n * without a judgement call.\n *\n * Eight checks, and each one is a claim the design makes about the corpus rather than a lint:\n *\n * 1. **Dangling `<link>` hrefs**: an authored edge pointing at a path the tree does not hold. Design\n * §2.3 has no foreign key on `edges` deliberately (a `<link>` may name a file the indexer has not\n * reached), so a LEFT JOIN is the only thing that finds these.\n * 2. **Orphan state rows**: a `state.access` row whose path left the tree. There are no\n * cross-database foreign keys, so the store mirrors a move explicitly and an interrupted mirror\n * leaves a row describing nothing.\n * 3. **Inbox depth**: design §2.1 rule 6 routes an unplaceable memory to `areas/inbox/` and says\n * doctor reports the depth as a health signal. A deep inbox is a placement rule that stopped\n * matching what agents write, which nothing else surfaces.\n * 4. **Vocabulary warnings**: format constraint 6. An element outside the closed vocabulary still\n * indexes, so the only way it ever becomes visible is here.\n * 5. **Index staleness**: the index is a projection of a commit, so \"fresh\" means the commit it\n * describes is the commit we are on. Plus the vector-space watermark, because a stored space that\n * differs from the configured one makes every cosine in the index incomparable.\n * 6. **Overdue tasks**: a task is default-excluded from search and skipped by every sleep phase, so\n * nothing else in the system will ever mention that a deadline passed. Doctor is the only surface\n * that reads `due_at`.\n * 7. **Stale task blockers**: a `blocks` edge whose blocker is archived or absent. This is the one\n * task-graph state no single file can reveal, since each file individually is valid and the pair\n * is a task waiting on something that will never move.\n * 8. **Task inbox depth**: a task in `areas/inbox/tasks/` is work with no project, and a task inbox\n * is meant to be drained rather than accumulated.\n *\n * **`--fix` repairs exactly two of the eight, and the repair logic is imported from the sleep\n * integrity phase rather than re-ported.** `archivedFormOf` decides whether a dangling target moved\n * to the archive or is genuinely gone, and `applyHeadEdits`/`link`/`unlink`/`meta` are the byte-splice\n * editors that change one head line without touching the article. A parse→serialize round trip drops\n * a `<pre>` newline per write, so a \"repair\" through the serializer would move the content hash of\n * every file it touched. A second implementation of either would be the consumer-side reimplementation\n * of producer semantics the fleet has paid for repeatedly.\n *\n * The other six report and do not repair. An inbox memory or task needs a human or an agent to decide\n * where it belongs, a vocabulary warning needs the author's intent, and a stale index needs\n * `memhtml index update`, which doctor names in its own suggestions rather than running behind the\n * operator's back. An overdue task needs the work done or the deadline moved, and a stale blocker\n * needs someone to decide whether the blocked task is actually ready.\n */\n/** How deep the inbox may get before doctor calls it a finding. */\nexport const INBOX_WARN_DEPTH = 20;\n/**\n * How many unplaced tasks may sit in `areas/inbox/tasks/` before doctor calls it a finding.\n *\n * Lower than {@link INBOX_WARN_DEPTH} because the two crowds mean different things. An unplaced\n * memory is a placement rule that stopped matching. An unplaced task is work with no project, which\n * is the state a to-do list rots in, and a task inbox is meant to be drained rather than accumulated.\n */\nexport const INBOX_TASK_WARN_DEPTH = 10;\n/** Every `state.access` path the index has no `files` row for. */\nconst orphanAccess = (db) => db.hasState\n ? db\n .all(`SELECT a.path AS path FROM ${STATE_SCHEMA}.access a\n LEFT JOIN files f ON f.path = a.path\n WHERE f.path IS NULL ORDER BY a.path ASC`)\n .pipe(Effect.map((rows) => rows.map((row) => row.path)), Effect.orElseSucceed(() => []))\n : Effect.succeed([]);\n/** How many ACTIVE memories sit in the inbox. An archived one is no longer awaiting placement. */\nconst inboxDepth = (db) => db\n .get(\"SELECT count(*) AS n FROM files WHERE archived = 0 AND path LIKE ? || '/%'\", [INBOX_DIR])\n .pipe(Effect.map((row) => row?.n ?? 0), Effect.orElseSucceed(() => 0));\n/**\n * How many ACTIVE tasks sit in the task inbox.\n *\n * `memory_type = 'task'` as well as the path prefix, because `areas/inbox/tasks/` is a directory and\n * a directory is not a type: a hand-authored memory filed there would inflate the task count and make\n * the finding say something it does not mean.\n */\nconst inboxTaskDepth = (db) => db\n .get(`SELECT count(*) AS n FROM files\n WHERE archived = 0 AND memory_type = 'task' AND path LIKE ? || '/%'`, [`${INBOX_DIR}/${TASKS_SUBDIR}`])\n .pipe(Effect.map((row) => row?.n ?? 0), Effect.orElseSucceed(() => 0));\n/**\n * Open tasks past their deadline.\n *\n * `substr(due_at, 1, 10)` states that the comparison is one of calendar days. The bound is always a\n * bare `YYYY-MM-DD` (today, from the clock), and enumerated against that bound the truncation changes\n * no answer, because a time-bearing due date on the bound's own day sorts after it either way. It\n * stays as the statement of intent. `listTasks`' `--due-before` takes a caller-supplied bound that may\n * carry a time, where the truncation does change answers, and one form across both queries keeps\n * \"overdue\" meaning the same thing in the two places an operator reads it.\n *\n * **`archived = 0` and `task_status <> 'done'` both change the result** (mutation-verified\n * 2026-08-02). A finished task's deadline is history, and reporting it would make the finding grow\n * forever and never reach zero.\n */\nconst overdueTasks = (db, today) => db\n .all(`SELECT path, task_status, due_at FROM files\n WHERE memory_type = 'task' AND archived = 0 AND due_at IS NOT NULL\n AND substr(due_at, 1, 10) < ? AND task_status <> 'done'\n ORDER BY due_at ASC, path ASC`, [today])\n .pipe(Effect.map((rows) => rows.map((row) => ({ path: row.path, taskStatus: row.task_status, dueAt: row.due_at }))), Effect.orElseSucceed(() => []));\n/**\n * Open tasks whose blocker can never close them.\n *\n * A `blocks` edge points blocker → blocked, so the blocked task is the edge's `dst_path`. A LEFT JOIN\n * rather than an inner one, because the two failure modes differ. An archived blocker is finished work\n * whose edge nobody cleared, and a missing one is an edge whose source has no `files` row. Either way\n * the blocked task waits on something that will never move. This is the one task-graph state no single\n * file can reveal, since each file is individually valid and only the pair is wrong.\n *\n * **The `archived` arm is the reachable one, and `missing` is defence in depth.** Probed and\n * mutation-confirmed 2026-08-02: deleting a blocker's file makes `indexer.update` clear\n * `edges WHERE src_path = ?` in the same batch, so an edge cannot outlive its source file and the\n * `missing` branch has nothing to find. Removing that `DELETE` turns the branch on, and it stays for\n * that reason. `edges` carries no foreign key deliberately, so a future writer of edge rows would not\n * inherit the indexer's discipline.\n *\n * **`edge_class = 'task'` is redundant with `rel = 'blocks'` today.** The migration's per-class CHECKs\n * refuse `blocks` under every other class, so a mutation dropping it leaves the suite green. It is kept\n * because every memory-graph query filters on the class column, and a reader who saw this one trust\n * the rel alone would learn the wrong rule about where the firewall lives.\n *\n * Report-only. Clearing the edge is an authoring decision, since the blocked task may be genuinely\n * ready or the blocker may have been archived prematurely. `--fix` guessing between those would rewrite\n * a plan.\n */\nconst staleBlockers = (db) => db\n .all(`SELECT t.path AS path, e.src_path AS blocker_path,\n CASE WHEN b.path IS NULL THEN 'missing' ELSE 'archived' END AS blocker_state\n FROM files t\n JOIN edges e ON e.dst_path = t.path AND e.edge_class = 'task' AND e.rel = 'blocks'\n LEFT JOIN files b ON b.path = e.src_path\n WHERE t.memory_type = 'task' AND t.archived = 0 AND t.task_status <> 'done'\n AND (b.path IS NULL OR b.archived = 1)\n ORDER BY t.path ASC, e.src_path ASC`)\n .pipe(Effect.map((rows) => rows.map((row) => ({\n path: row.path,\n blockerPath: row.blocker_path,\n blockerState: row.blocker_state === \"missing\" ? \"missing\" : \"archived\"\n}))), Effect.orElseSucceed(() => []));\n/**\n * Re-read every active file and collect its format warnings.\n *\n * Re-read rather than taken from the index, because a warning is not a stored column. The indexer\n * counts a parse failure and projects what it can, and constraint 6 is deliberately non-fatal. Doctor\n * is the one caller that wants the list, so it is the one caller that pays for the read.\n */\nconst collectWarnings = (root, paths) => Effect.gen(function* () {\n const warnings = [];\n const unparseable = [];\n for (const path of paths) {\n const html = yield* readFileOrNull(join(root, path)).pipe(Effect.orElseSucceed(() => null));\n if (html === null) {\n unparseable.push(path);\n continue;\n }\n const checked = checkMemory(html);\n if (checked.violations.length > 0)\n unparseable.push(path);\n if (checked.warnings.length > 0)\n warnings.push({ path, warnings: checked.warnings });\n }\n return { warnings, unparseable };\n});\n/** The year a run's repairs partition archive lookups under: the current calendar year. */\nconst currentYear = Effect.clockWith((clock) => Effect.map(clock.currentTimeMillis, (millis) => new Date(millis).getUTCFullYear()));\n/** Today as `YYYY-MM-DD`, through the Effect clock so a test can pin what \"overdue\" means. */\nconst todayDate = Effect.clockWith((clock) => Effect.map(clock.currentTimeMillis, (millis) => new Date(millis).toISOString().slice(0, 10)));\n/** An ISO-8601 UTC second, for the `memhtml-updated` stamp a repair writes. */\nconst nowSecond = Effect.clockWith((clock) => Effect.map(clock.currentTimeMillis, (millis) => `${new Date(millis).toISOString().slice(0, 19)}Z`));\n/**\n * Repair the dangling hrefs and prune the orphan access rows.\n *\n * The href repair mirrors the integrity phase exactly, using its own `archivedFormOf` and its own\n * head editors. A dangling target that moved under `archive/<YYYY>/` gets its href rewritten, so the\n * edge still says something true. A target with no file anywhere gets the link dropped with a\n * warning, because the edge asserts a relationship to nothing and leaving it would produce the same\n * finding on every rebuild forever.\n *\n * Remove-then-add on the same file in one pass, so a repair replaces one line rather than dropping a\n * line and appending another elsewhere in the head. A re-run is then a no-op: once the href points at\n * the archive path the removal matches nothing and the addition is already present.\n */\nconst repair = (root, findings, orphans) => Effect.gen(function* () {\n const git = yield* Git;\n const db = yield* DatabaseService;\n const at = yield* nowSecond;\n let rewritten = 0;\n let dropped = 0;\n const touched = [];\n for (const finding of findings) {\n if (!isEdgeRel(finding.rel))\n continue;\n const rel = finding.rel;\n const absolute = join(root, finding.srcPath);\n const html = yield* readFileOrNull(absolute).pipe(Effect.orElseSucceed(() => null));\n if (html === null)\n continue;\n const edits = finding.rewriteTo === null\n ? [unlink(rel, hrefFor(finding.dstPath)), meta(\"memhtml-updated\", at)]\n : [\n unlink(rel, hrefFor(finding.dstPath)),\n link(rel, hrefFor(finding.rewriteTo)),\n meta(\"memhtml-updated\", at)\n ];\n const edited = applyHeadEdits(html, edits);\n if (edited === html)\n continue;\n if (finding.rewriteTo === null) {\n yield* Effect.logWarning(`doctor dropped a dangling ${rel} from ${finding.srcPath}: target has no file`);\n }\n yield* attemptIo(`doctor.write:${finding.srcPath}`, async () => {\n await mkdir(dirname(absolute), { recursive: true });\n await writeFile(absolute, edited, \"utf8\");\n }).pipe(Effect.orElseSucceed(() => undefined));\n touched.push(finding.srcPath);\n if (finding.rewriteTo === null)\n dropped += 1;\n else\n rewritten += 1;\n }\n let prunedAccessRows = 0;\n if (orphans.length > 0 && db.hasState) {\n /**\n * One statement per path rather than an `IN` list, because the list is unbounded. A driver\n * parameter limit reached mid-prune would fail the whole batch and leave every row in place.\n */\n for (const path of orphans) {\n const done = yield* db\n .run(`DELETE FROM ${STATE_SCHEMA}.access WHERE path = ?`, [path])\n .pipe(Effect.as(true), Effect.orElseSucceed(() => false));\n if (done)\n prunedAccessRows += 1;\n }\n }\n let commitSha = null;\n if (touched.length > 0) {\n yield* git.add(touched);\n const commit = yield* git.commit(commitSubject(\"link\", `repair ${rewritten + dropped} dangling links`));\n commitSha = commit.sha;\n }\n return { rewritten, dropped, prunedAccessRows, commitSha };\n});\n/**\n * Run the health check, optionally repairing.\n *\n * The findings are gathered before any repair and the report carries the pre-repair lists alongside\n * `repaired`, which is what makes a `--fix` run auditable. An operator reads what was wrong and what\n * was done about it in one envelope, rather than a clean report that says nothing happened.\n */\nexport const doctor = (options) => Effect.gen(function* () {\n const git = yield* Git;\n const store = yield* Store;\n const db = yield* DatabaseService;\n const headSha = yield* git.revParseHead().pipe(Effect.orElseSucceed(() => null));\n const dirty = yield* store.dirtyPaths().pipe(Effect.orElseSucceed(() => []));\n const state = yield* db\n .get(\"SELECT head_sha, embed_model FROM index_state WHERE id = 1\")\n .pipe(Effect.orElseSucceed(() => undefined));\n const known = new Set((yield* allPaths(db).pipe(Effect.orElseSucceed(() => []))).map((row) => row.path));\n const year = yield* currentYear;\n const edges = yield* danglingEdges(db).pipe(Effect.orElseSucceed(() => []));\n const dangling = edges.map((edge) => {\n const dstPath = normalizePath(edge.dst_path);\n return {\n srcPath: edge.src_path,\n rel: edge.rel,\n dstPath,\n rewriteTo: archivedFormOf(dstPath, known, year) ?? null\n };\n });\n const orphanAccessRows = yield* orphanAccess(db);\n const depth = yield* inboxDepth(db);\n const taskDepth = yield* inboxTaskDepth(db);\n const overdue = yield* overdueTasks(db, yield* todayDate);\n const stale = yield* staleBlockers(db);\n const active = yield* db\n .all(\"SELECT path FROM files WHERE archived = 0 ORDER BY path ASC\")\n .pipe(Effect.orElseSucceed(() => []));\n const { warnings, unparseable } = yield* collectWarnings(git.root, active.map((row) => row.path));\n const repaired = options.fix ? yield* repair(git.root, dangling, orphanAccessRows) : undefined;\n const indexFresh = state?.head_sha !== null && state?.head_sha === headSha;\n const embedModelMatches = state?.embed_model === EMBED_WATERMARK;\n return {\n root: git.root,\n /**\n * `healthy` is computed from the findings and not from the repair. A `--fix` run that repaired\n * everything still reports the corpus as it was found. A command that flipped itself green by\n * fixing what it found would make \"doctor is clean\" unfalsifiable.\n */\n healthy: dangling.length === 0 &&\n orphanAccessRows.length === 0 &&\n depth <= INBOX_WARN_DEPTH &&\n /**\n * The task inbox counts toward `healthy` for the same reason the memory inbox does: an\n * unplaced item is a routing signal. `overdueTasks` and `staleBlockers` are excluded, because\n * those two are facts about the work rather than defects in the corpus. A repo whose owner is\n * late on a to-do is structurally sound, and folding them in would make `healthy: false` the\n * normal state and stop anyone reading the flag at all. Every other finding here is a defect\n * in the corpus; those two describe work that has fallen behind.\n */\n taskDepth <= INBOX_TASK_WARN_DEPTH &&\n warnings.length === 0 &&\n unparseable.length === 0 &&\n indexFresh &&\n embedModelMatches,\n dangling,\n orphanAccessRows,\n inboxDepth: depth,\n inboxCrowded: depth > INBOX_WARN_DEPTH,\n inboxTaskDepth: taskDepth,\n inboxTasksCrowded: taskDepth > INBOX_TASK_WARN_DEPTH,\n overdueTasks: overdue,\n staleBlockers: stale,\n warnings,\n unparseable,\n indexFresh,\n indexHeadSha: state?.head_sha ?? null,\n headSha,\n embedModelMatches,\n storedEmbedModel: state?.embed_model ?? null,\n configuredEmbedModel: EMBED_WATERMARK,\n dirty,\n ...(repaired === undefined ? {} : { repaired })\n };\n});\n//# sourceMappingURL=doctor.js.map","import { discriminationGate, runDiscrimination } from \"@memhtml/eval\";\nimport { initRepo } from \"@memhtml/store\";\nimport { Effect, Logger } from \"effect\";\nimport { runAgentsDoc } from \"./agents-doc.js\";\nimport { Git, Indexer, layerApp, Sleep } from \"./api-layer.js\";\nimport { applyPayload, applyText, decodeApply, readStdin } from \"./apply.js\";\nimport { buildManifest, COMMAND_NAMES, COMMANDS, GLOBAL_FLAGS } from \"./commands.js\";\nimport { MemhtmlRoot } from \"./config.js\";\nimport { doctor } from \"./doctor.js\";\nimport { API_VERSION, EXIT_OK, EXIT_RUNTIME, EXIT_USAGE, fail, nearest, render, succeed } from \"./envelope.js\";\nimport { failureFor } from \"./errors.js\";\nimport { DEFAULT_TIMEOUT_MS, execCommand, MAX_TIMEOUT_MS, readScript } from \"./exec.js\";\nimport * as ops from \"./operations.js\";\nimport { publish } from \"./publish.js\";\nimport { serveMcp } from \"./serve.js\";\nimport { stateExport, stateImport } from \"./state.js\";\nimport { indexReport, sleepPhases, sleepRunReport } from \"./views.js\";\nconst KNOWN_FLAGS = new Set([\n ...GLOBAL_FLAGS.map((flag) => flag.name),\n ...COMMANDS.flatMap((command) => command.flags.map((flag) => flag.name))\n]);\n/**\n * The two-word command names, longest first.\n *\n * A subcommand is matched greedily so `index status` beats `index`, and the leftover tokens become\n * positionals. Matching the shorter name first would make `memhtml index status` a call to a\n * hypothetical `index` command with `status` as an argument, which is a wrong answer rather than an\n * error.\n */\nconst COMPOUND_NAMES = COMMAND_NAMES.filter((name) => name.includes(\" \")).sort((left, right) => right.length - left.length);\n/**\n * `--flag value`, `--flag=value`, `--no-flag`, and bare `--flag`.\n *\n * Every flag's value is an array, because several flags are repeatable (`--tag`, `--entity`,\n * `--body`) and a map of scalars would silently keep only the last occurrence, so a write with three\n * entities would store one. Non-repeatable flags read `.at(-1)`, so a duplicate is last-wins rather\n * than an error, which is what a shell user retyping a flag expects.\n */\nexport const parseArgv = (argv) => {\n const positional = [];\n const flags = new Map();\n const push = (name, value) => {\n const existing = flags.get(name);\n if (existing === undefined)\n flags.set(name, [value]);\n else\n existing.push(value);\n };\n let index = 0;\n while (index < argv.length) {\n const token = argv[index];\n if (token.startsWith(\"--\")) {\n const body = token.slice(2);\n const eq = body.indexOf(\"=\");\n if (eq !== -1) {\n push(body.slice(0, eq), body.slice(eq + 1));\n index += 1;\n continue;\n }\n // `--no-embed` is how a boolean defaulting to true is turned off. Without it, a flag whose\n // default is `true` would be unsettable from a shell.\n if (body.startsWith(\"no-\") && KNOWN_FLAGS.has(body.slice(3))) {\n push(body.slice(3), false);\n index += 1;\n continue;\n }\n const next = argv[index + 1];\n if (next !== undefined && !next.startsWith(\"--\")) {\n push(body, next);\n index += 2;\n continue;\n }\n push(body, true);\n index += 1;\n continue;\n }\n positional.push(token);\n index += 1;\n }\n const joined = positional.join(\" \");\n const compound = COMPOUND_NAMES.find((name) => joined === name || joined.startsWith(`${name} `));\n if (compound !== undefined) {\n const consumed = compound.split(\" \").length;\n return { command: compound, positional: positional.slice(consumed), flags };\n }\n return { command: positional[0] ?? \"\", positional: positional.slice(1), flags };\n};\n/** A flag's last value as a string, or `undefined` when it was not given. */\nconst str = (parsed, name) => {\n const value = parsed.flags.get(name)?.at(-1);\n return value === undefined || typeof value === \"boolean\" ? undefined : value;\n};\n/** Every value a repeatable flag was given, in order. Empty when absent. */\nconst list = (parsed, name) => (parsed.flags.get(name) ?? []).flatMap((value) => typeof value === \"string\" && value !== \"\" ? [value] : []);\n/** A flag as a boolean: bare `--flag` is true, `--no-flag` is false, `--flag=false` is false. */\nconst bool = (parsed, name, fallback) => {\n const value = parsed.flags.get(name)?.at(-1);\n if (value === undefined)\n return fallback;\n if (typeof value === \"boolean\")\n return value;\n return value !== \"false\" && value !== \"0\" && value !== \"no\";\n};\n/** A flag as an integer, or `undefined` when absent or unparseable. */\nconst int = (parsed, name) => {\n const raw = str(parsed, name);\n if (raw === undefined)\n return undefined;\n const value = Number.parseInt(raw, 10);\n return Number.isFinite(value) ? value : undefined;\n};\n/** A flag as a finite number in a range, or `undefined`. */\nconst num = (parsed, name) => {\n const raw = str(parsed, name);\n if (raw === undefined)\n return undefined;\n const value = Number.parseFloat(raw);\n return Number.isFinite(value) ? value : undefined;\n};\n/** The scope every retrieval command shares, so `search` and `recall` cannot diverge. */\nconst scopeOf = (parsed) => ({\n memoryTypes: list(parsed, \"type\"),\n workspace: str(parsed, \"workspace\"),\n tags: list(parsed, \"tag\"),\n entity: str(parsed, \"entity\"),\n includeArchived: bool(parsed, \"include-archived\", false),\n asOf: str(parsed, \"as-of\")\n});\n/** Session provenance, from the three flags every write-path command accepts. */\nconst provenanceOf = (parsed) => ({\n sessionId: str(parsed, \"session-id\"),\n promptId: str(parsed, \"prompt-id\"),\n turnUuid: str(parsed, \"turn-uuid\")\n});\n/**\n * Dispatch one parsed invocation against the provided services.\n *\n * Every arm is decode → call → name the response type. No arm builds an envelope, catches an error,\n * or writes to a stream. Those happen once in {@link run}, which keeps thirty-one commands\n * from having thirty-one slightly different failure shapes.\n *\n * `applyOps` is the one piece of state an arm cannot derive from `parsed`. Reading a file or draining\n * stdin is async I/O whose failures are usage errors (exit 2), and `run` has already done it and\n * refused before reaching here. It is passed in rather than read here, so the `apply` arm stays what\n * every other arm is: one call to a shared use case.\n */\nconst dispatch = (parsed, applyOps = []) => {\n switch (parsed.command) {\n case \"manifest\":\n return Effect.succeed([\"cli.manifest\", buildManifest()]);\n case \"init\":\n return Effect.gen(function* () {\n const git = yield* Git;\n const result = yield* initRepo(git);\n return [\"repo.init\", result];\n });\n case \"write\":\n return Effect.gen(function* () {\n const result = yield* ops.writeMemory({\n title: str(parsed, \"title\") ?? \"\",\n // `claim` is \"\" exactly when `--article-html` supplied the article instead, which\n // `validate` has already proven is the only way to get here without a claim. The\n // template ignores `claim` entirely on that branch (`@memhtml/html` template.ts:88-91).\n claim: str(parsed, \"claim\") ?? \"\",\n body: list(parsed, \"body\"),\n articleHtml: str(parsed, \"article-html\"),\n memoryType: str(parsed, \"type\") ?? \"\",\n path: str(parsed, \"path\"),\n workspace: str(parsed, \"workspace\"),\n tags: list(parsed, \"tag\"),\n entities: list(parsed, \"entity\"),\n importance: int(parsed, \"importance\"),\n confidence: num(parsed, \"confidence\"),\n ...provenanceOf(parsed)\n });\n return [\"memory.written\", result];\n });\n /**\n * The batch door. One call to the shared `batchWrite`, and the per-op `code`/`error` it returns\n * are not re-mapped here. The operation already ran them through the same `codeFor`/`messageFor`\n * every envelope error takes, so this door and `memory_write_batch` cannot report\n * different codes for one refused op.\n */\n case \"apply\":\n return Effect.gen(function* () {\n const result = yield* ops.batchWrite({\n ops: applyOps,\n continueOnError: bool(parsed, \"continue-on-error\", false),\n detectConflicts: bool(parsed, \"detect-conflicts\", false),\n // `validate` has already refused any value outside the flag's closed vocabulary, so the\n // narrowing here cannot silently drop a caller's ask.\n ...(str(parsed, \"consolidate\") === \"last-wins\"\n ? { consolidate: \"last-wins\" }\n : {}),\n ...provenanceOf(parsed)\n });\n return [\"batch.applied\", applyPayload(result)];\n });\n case \"read\":\n return Effect.gen(function* () {\n const result = yield* ops.readMemory(parsed.positional[0] ?? \"\", provenanceOf(parsed));\n return [\n \"memory.detail\",\n {\n path: result.path,\n title: result.doc.title,\n metas: result.doc.metas,\n entities: result.doc.entities,\n tags: result.doc.tags,\n links: result.doc.links,\n gist: result.doc.article.gist,\n body: result.doc.article.bodyText,\n html: result.doc.article.html,\n archived: result.doc.metas.status === \"archived\",\n warnings: result.doc.warnings\n }\n ];\n });\n case \"search\":\n return Effect.gen(function* () {\n const result = yield* ops.searchMemories({\n query: parsed.positional[0] ?? \"\",\n limit: int(parsed, \"limit\"),\n ...scopeOf(parsed)\n });\n return [\"memory.hits\", result];\n });\n case \"recall\":\n return Effect.gen(function* () {\n const pack = yield* ops.recallMemories({\n query: parsed.positional[0] ?? \"\",\n budgetChars: int(parsed, \"budget\"),\n ...scopeOf(parsed)\n });\n return [\"recall.pack\", pack];\n });\n case \"correct\":\n return Effect.gen(function* () {\n const result = yield* ops.correctMemory({\n targetPath: parsed.positional[0] ?? \"\",\n title: str(parsed, \"title\") ?? \"\",\n claim: str(parsed, \"claim\") ?? \"\",\n body: list(parsed, \"body\"),\n articleHtml: str(parsed, \"article-html\"),\n memoryType: str(parsed, \"type\"),\n reason: str(parsed, \"reason\"),\n ...provenanceOf(parsed)\n });\n return [\"memory.corrected\", result];\n });\n case \"link\":\n return Effect.gen(function* () {\n const result = yield* ops.linkMemories(parsed.positional[0] ?? \"\", parsed.positional[1] ?? \"\", parsed.positional[2] ?? \"\");\n return [\"memory.linked\", result];\n });\n case \"neighbors\":\n return Effect.gen(function* () {\n const result = yield* ops.neighborsOf({\n path: parsed.positional[0] ?? \"\",\n depth: int(parsed, \"depth\"),\n rels: list(parsed, \"rel\")\n });\n return [\"memory.neighbors\", result];\n });\n case \"archive\":\n return Effect.gen(function* () {\n const result = yield* ops.archiveMemory(parsed.positional[0] ?? \"\", str(parsed, \"reason\") ?? \"\");\n return [\"memory.archived\", result];\n });\n case \"reinforce\":\n return Effect.gen(function* () {\n // Every positional is a path: `memhtml reinforce a.html b.html --signal positive` is the shape\n // an agent reaching for the MCP tool's `paths` array writes on a command line.\n const result = yield* ops.reinforceMemories(parsed.positional, str(parsed, \"signal\") ?? \"neutral\");\n return [\"memory.reinforced\", result];\n });\n case \"list\":\n return Effect.gen(function* () {\n const result = yield* ops.listMemories({\n memoryType: str(parsed, \"type\"),\n workspace: str(parsed, \"workspace\"),\n tag: str(parsed, \"tag\"),\n entity: str(parsed, \"entity\"),\n para: str(parsed, \"para\"),\n limit: int(parsed, \"limit\"),\n cursor: str(parsed, \"cursor\"),\n includeArchived: bool(parsed, \"include-archived\", false)\n });\n return [\"memory.list\", result];\n });\n case \"task add\":\n return Effect.gen(function* () {\n const title = str(parsed, \"title\") ?? \"\";\n const result = yield* ops.writeMemory({\n title,\n // The claim defaults to the title, because a task's statement and its name are usually the\n // same sentence, and a required second phrasing would be restated verbatim every time.\n claim: str(parsed, \"claim\") ?? title,\n body: list(parsed, \"body\"),\n memoryType: \"task\",\n workspace: str(parsed, \"workspace\"),\n tags: list(parsed, \"tag\"),\n entities: list(parsed, \"entity\"),\n taskStatus: str(parsed, \"status\"),\n dueAt: str(parsed, \"due\"),\n ...provenanceOf(parsed)\n });\n return [\n \"task.written\",\n {\n path: result.path,\n created: result.created,\n deduped: result.deduped,\n // Two open tasks with identical bodies are two real work items, so the dedup carve-out\n // means this is normally false. It is reported anyway, because a caller cannot tell a\n // fresh file from a returned one without it.\n existingPath: result.existingPath ?? null,\n taskStatus: str(parsed, \"status\") ?? \"todo\",\n dueAt: str(parsed, \"due\") ?? null,\n commitSha: result.commitSha\n }\n ];\n });\n case \"task status\":\n return Effect.gen(function* () {\n const result = yield* ops.setTaskStatus({\n path: parsed.positional[0] ?? \"\",\n status: parsed.positional[1] ?? \"\",\n reason: str(parsed, \"reason\")\n });\n return [\"task.updated\", { ...result, archivePath: result.archivePath ?? null }];\n });\n case \"task list\":\n return Effect.gen(function* () {\n const result = yield* ops.listTasks({\n status: str(parsed, \"status\"),\n workspace: str(parsed, \"workspace\"),\n dueBefore: str(parsed, \"due-before\"),\n limit: int(parsed, \"limit\"),\n cursor: str(parsed, \"cursor\"),\n includeArchived: bool(parsed, \"include-archived\", false)\n });\n return [\"task.list\", result];\n });\n case \"index rebuild\":\n return Effect.gen(function* () {\n const indexer = yield* Indexer;\n const report = yield* indexer.rebuild({ embed: bool(parsed, \"embed\", true) });\n return [\"index.report\", { mode: \"rebuild\", ...report }];\n });\n case \"index update\":\n return Effect.gen(function* () {\n const indexer = yield* Indexer;\n const report = yield* indexer.update({ embed: bool(parsed, \"embed\", true) });\n return [\"index.report\", { mode: \"update\", ...report }];\n });\n case \"index status\":\n return Effect.gen(function* () {\n const report = yield* indexReport();\n return [\"index.report\", report];\n });\n case \"trace index\":\n return Effect.gen(function* () {\n const report = yield* ops.indexTraces();\n return [\"trace.report\", report];\n });\n case \"trace search\":\n return Effect.gen(function* () {\n const result = yield* ops.searchTraces({\n query: parsed.positional[0] ?? \"\",\n cwd: str(parsed, \"cwd\"),\n since: str(parsed, \"since\"),\n limit: int(parsed, \"limit\")\n });\n return [\"trace.sessions\", result];\n });\n case \"trace links\":\n return Effect.gen(function* () {\n const result = yield* ops.traceLinks({\n sessionId: str(parsed, \"session-id\"),\n path: str(parsed, \"path\")\n });\n return [\"trace.links\", result];\n });\n case \"sleep run\":\n return Effect.gen(function* () {\n const sleep = yield* Sleep;\n const phases = yield* sleepPhases(str(parsed, \"phases\"));\n const report = yield* sleep.run({\n date: str(parsed, \"date\") ?? (yield* today),\n ...(phases === undefined ? {} : { phases }),\n dryRun: bool(parsed, \"dry-run\", false)\n });\n return [\"sleep.report\", sleepRunReport(report)];\n });\n case \"sleep resume\":\n return Effect.gen(function* () {\n const sleep = yield* Sleep;\n const report = yield* sleep.resume(parsed.positional[0] ?? \"\");\n return [\"sleep.report\", sleepRunReport(report)];\n });\n case \"sleep review\":\n return Effect.gen(function* () {\n const sleep = yield* Sleep;\n const report = yield* sleep.review(parsed.positional[0]);\n const withDiff = bool(parsed, \"diff\", false);\n if (!withDiff)\n return [\"sleep.review\", report];\n // The raw diff is fetched here rather than inside `review`, because it is the one field whose\n // size is unbounded, and a review that always carried it would make the default response\n // unusable in a context window.\n const git = yield* Git;\n const diff = yield* git\n .run([\"diff\", `${report.baseSha}..${report.headSha}`])\n .pipe(Effect.orElseSucceed(() => \"\"));\n return [\"sleep.review\", { ...report, diff }];\n });\n case \"sleep merge\":\n return Effect.gen(function* () {\n const sleep = yield* Sleep;\n const skipGate = bool(parsed, \"skip-gate\", false);\n if (skipGate) {\n yield* Effect.logWarning(\"sleep merge --skip-gate: merging without re-running discrimination\");\n }\n /**\n * **The discrimination gate, composed here.** A sleep run that degrades retrieval quality\n * cannot land. `@memhtml/sleep` takes the gate as a parameter and supplies none, so a package\n * that cannot import the eval also cannot silently default it. The composition is visible in\n * this wiring or it does not exist.\n *\n * `discriminationGate` fails on an inversion, `merge` wraps it in `Effect.result`, and the\n * failure becomes `refusal: \"gate-failed\"` with `main` never moving.\n *\n * `fake` mode, always. The gate measures the ranking stack against its own generated fixture\n * corpus, so a live-Bedrock run would make a nightly merge conditional on a network call and\n * on credentials being present at 3am. The deterministic embedder's cosine relations are\n * a pure function of the text, which is the property a regression gate needs. A\n * cron whose merge silently skipped its gate because a token expired is the failure this\n * arrangement prevents.\n */\n const report = yield* sleep.merge(parsed.positional[0] ?? \"\", skipGate ? {} : { preMergeGate: discriminationGate().pipe(Effect.asVoid) });\n return [\"sleep.merge\", report];\n });\n case \"publish\":\n return Effect.gen(function* () {\n const report = yield* publish();\n return [\"publish.report\", report];\n });\n case \"doctor\":\n return Effect.gen(function* () {\n const report = yield* doctor({ fix: bool(parsed, \"fix\", false) });\n return [\"doctor.report\", report];\n });\n case \"state export\":\n return Effect.gen(function* () {\n const report = yield* stateExport();\n return [\"state.export\", report];\n });\n case \"state import\":\n return Effect.gen(function* () {\n const report = yield* stateImport();\n return [\"state.import\", report];\n });\n case \"sleep status\":\n return Effect.gen(function* () {\n const sleep = yield* Sleep;\n const report = yield* sleep.review();\n return [\n \"sleep.report\",\n {\n runId: report.runId,\n branch: report.branch,\n baseSha: report.baseSha,\n headSha: report.headSha,\n phases: report.phases,\n commits: report.commits.length\n }\n ];\n });\n case \"status\":\n return Effect.gen(function* () {\n const report = yield* ops.statusReport();\n return [\"status.health\", report];\n });\n default:\n // Unreachable while every COMMANDS entry has a case. A new spec with no handler surfaces\n // here as a usage error rather than an empty stdout.\n return Effect.fail({ _tag: \"UnhandledCommand\", command: parsed.command });\n }\n};\n/** Today as `YYYY-MM-DD`, through the Effect clock so a test can pin the run date. */\nconst today = Effect.clockWith((clock) => Effect.map(clock.currentTimeMillis, (millis) => new Date(millis).toISOString().slice(0, 10)));\n/**\n * An unknown command, with candidates measured against the whole typed invocation.\n *\n * `parseArgv` only matches a compound name exactly, so a typo in either word of `memhtml index rebuild`\n * leaves `command` holding the first token alone and every remaining token in `positional`.\n * Measuring `\"index\"` against the flat name list scores `init` at 2 and `index rebuild` at 8, so the\n * suggestion an operator needs loses to one they did not ask for. Re-joining the tokens makes\n * the distance a comparison of the two things: `\"index rebiuld\"` is 2 from `index rebuild` and 12\n * from `init`.\n *\n * Both are offered, the joined form first, because the typo could be in either half. A one-word\n * invocation joins to itself, so the single-command path is unchanged.\n */\nconst unknownCommand = (parsed) => {\n const typed = [parsed.command, ...parsed.positional].join(\" \").trim();\n const candidates = [\n ...nearest(typed, COMMAND_NAMES),\n ...nearest(parsed.command, COMMAND_NAMES)\n ].filter((name, at, all) => all.indexOf(name) === at);\n return fail(\"ERR_UNKNOWN_COMMAND\", `unknown command: ${typed === \"\" ? parsed.command : typed}`, candidates.slice(0, 3));\n};\n/**\n * The commands where the article body comes from either a claim or pre-authored markup, never both.\n *\n * Listed here rather than expressed in `FlagSpec`, because `FlagSpec` has one `required: boolean` and\n * no notion of a conditional. Inventing a table field for a rule that holds on two commands\n * would put a second, weaker copy of this check into the manifest for every command that does not\n * need it. Both flag descriptions state the rule, so `memhtml manifest` still carries it.\n */\nconst EITHER_CLAIM_OR_ARTICLE = new Set([\"write\", \"correct\"]);\n/**\n * `memhtml exec` takes at most one script door, and a bound inside the cap.\n *\n * Here for the reason `claimOrArticle` is: `validate`'s return becomes exit 2 and a failure raised in\n * `dispatch` becomes exit 1, so \"you passed the wrong flags\" must be decided before any service is\n * built. `.erpaval/solutions/api-patterns/xor-params-and-mcp-error-masking.md` records the rule.\n *\n * At most one rather than exactly one, because zero doors is legal and means stdin, the same shape\n * `memhtml apply` has, where a bare invocation drains the pipe. A missing script is not a usage error\n * here. An empty one is, and that check sits beside the read in {@link run} because reading is async.\n *\n * `--timeout-ms` is checked for a positive integer within the cap. Zero and negatives are refused\n * rather than clamped, because just-bash treats a non-positive `maxJsTimeoutMs` as no bound at all, so\n * `--timeout-ms 0` would read as \"be quick\" and mean \"run forever\".\n */\nconst execFlags = (parsed) => {\n if (parsed.command !== \"exec\")\n return undefined;\n const doors = [\n str(parsed, \"file\") === undefined ? undefined : \"--file\",\n str(parsed, \"script\") === undefined ? undefined : \"--script\"\n ].filter((door) => door !== undefined);\n if (doors.length > 1) {\n return fail(\"ERR_INVALID_FLAG\", \"exec takes at most one of --file or --script, not both: two scripts cannot both be the one that runs\", [\n \"memhtml exec --file traverse.mjs\",\n \"memhtml exec --script 'console.log(1)'\",\n \"cat s.mjs | memhtml exec\"\n ]);\n }\n // A `-` positional is the explicit stdin spelling, so it cannot sit beside a door either.\n if (doors.length === 1 && parsed.positional[0] === \"-\") {\n return fail(\"ERR_INVALID_FLAG\", `exec cannot read stdin and ${doors[0]} in the same call: \\`-\\` names stdin as the script source`, [\"cat s.mjs | memhtml exec\", `memhtml exec ${doors[0]} …`]);\n }\n const raw = str(parsed, \"timeout-ms\");\n if (raw !== undefined) {\n const timeout = int(parsed, \"timeout-ms\");\n if (timeout === undefined || timeout <= 0 || timeout > MAX_TIMEOUT_MS) {\n return fail(\"ERR_INVALID_FLAG\", `--timeout-ms must be a positive integer of at most ${MAX_TIMEOUT_MS}: a non-positive bound is no bound at all, which is the one thing a sandbox may not be`, [`memhtml exec --timeout-ms ${DEFAULT_TIMEOUT_MS}`]);\n }\n }\n return undefined;\n};\n/**\n * Exactly one of `--claim` / `--article-html`.\n *\n * Checked here rather than in the dispatch arm, because the exit code is the contract. `validate`'s\n * return is emitted as exit 2 ({@link EXIT_USAGE}), while a failure raised inside `dispatch` travels\n * through `failureFor` and becomes exit 1. Supplying the wrong flags is a usage error, and a shell\n * caller branching on the code must not see it as a runtime one.\n *\n * Two codes for two conditions, each following the convention already in this function. An absent\n * required flag is `ERR_MISSING_ARGUMENT` (as below), and a flag present but unusable as given is\n * `ERR_INVALID_FLAG` (as above, and in the closed-vocabulary check). Neither is newly minted.\n */\nconst claimOrArticle = (parsed) => {\n if (!EITHER_CLAIM_OR_ARTICLE.has(parsed.command))\n return undefined;\n const hasClaim = str(parsed, \"claim\") !== undefined;\n const hasArticle = str(parsed, \"article-html\") !== undefined;\n if (hasClaim && hasArticle) {\n return fail(\"ERR_INVALID_FLAG\", `${parsed.command} takes exactly one of --claim or --article-html, not both: --article-html is the whole article, so a --claim beside it would be silently discarded`, [\n `memhtml ${parsed.command} --claim <sentence>`,\n `memhtml ${parsed.command} --article-html '<p>…</p>'`\n ]);\n }\n if (!hasClaim && !hasArticle) {\n return fail(\"ERR_MISSING_ARGUMENT\", `${parsed.command} requires exactly one of --claim or --article-html`, [\n `memhtml ${parsed.command} --claim <sentence>`,\n `memhtml ${parsed.command} --article-html '<p>…</p>'`\n ]);\n }\n return undefined;\n};\n/**\n * Validate a parsed invocation against its spec. Usage errors only; nothing here touches a service.\n *\n * Returning the failure rather than throwing keeps the exit code decision in one place. A usage\n * error is exit 2 and a runtime error is exit 1, and a validator that emitted its own envelope would\n * have to know that too.\n */\nconst validate = (parsed) => {\n for (const name of parsed.flags.keys()) {\n if (!KNOWN_FLAGS.has(name)) {\n return fail(\"ERR_INVALID_FLAG\", `unknown flag: --${name}`, nearest(name, [...KNOWN_FLAGS]));\n }\n }\n const spec = COMMANDS.find((command) => command.name === parsed.command);\n if (spec === undefined)\n return unknownCommand(parsed);\n const missingArgs = spec.args.filter((arg, position) => arg.required && parsed.positional[position] === undefined);\n if (missingArgs.length > 0) {\n return fail(\"ERR_MISSING_ARGUMENT\", `${spec.name} requires: ${missingArgs.map((arg) => arg.name).join(\", \")}`, [`memhtml ${spec.name} <${missingArgs[0]?.name}>`]);\n }\n const missingFlags = spec.flags.filter((flag) => flag.required === true && parsed.flags.get(flag.name) === undefined);\n if (missingFlags.length > 0) {\n return fail(\"ERR_MISSING_ARGUMENT\", `${spec.name} requires: ${missingFlags.map((flag) => `--${flag.name}`).join(\", \")}`, missingFlags.map((flag) => `memhtml ${spec.name} --${flag.name} <value>`));\n }\n // Presence rules together: the unconditionally-required flags above, then the two conditional\n // rules the table cannot express, then the value checks below.\n const eitherOr = claimOrArticle(parsed);\n if (eitherOr !== undefined)\n return eitherOr;\n const exec = execFlags(parsed);\n if (exec !== undefined)\n return exec;\n /**\n * A closed-vocabulary flag is checked here rather than at the service, so a typo answers with the\n * whole vocabulary and never touches the database. Every value of a repeatable flag is checked, not\n * only the last one, so a `--type` list with one bad entry is a usage error rather than a silently\n * narrowed search.\n */\n for (const flag of spec.flags) {\n if (flag.values === undefined)\n continue;\n for (const value of parsed.flags.get(flag.name) ?? []) {\n if (typeof value !== \"string\")\n continue;\n if (!flag.values.includes(value)) {\n return fail(\"ERR_INVALID_FLAG\", `--${flag.name} must be one of: ${flag.values.join(\", \")}`, nearest(value, flag.values));\n }\n }\n }\n return undefined;\n};\n/**\n * Returns the rendered envelope and an exit code rather than writing to the process, so tests\n * assert on the exact bytes an agent would parse.\n *\n * `layer` is injectable for that reason. A test supplies the real composition over a temp\n * repo and a deterministic embedder, and every assertion below then describes the shipped path.\n *\n * `stdin` is injectable for the same reason one step further out. `memhtml apply` reads a JSONL stream\n * from a pipe, and a test that had to spawn a process and write to its descriptor to exercise the\n * stdin path would be an integration test of the shell rather than of this function. The default\n * reads `process.stdin`, so `bin.ts` needs no knowledge of which commands want input.\n */\nexport const run = async (argv, layer, stdin = readStdin) => {\n const parsed = parseArgv(argv);\n const dense = bool(parsed, \"dense\", false);\n const emit = (payload, exitCode) => ({\n stdout: render(payload, dense),\n exitCode\n });\n if (parsed.command === \"\" || parsed.command === \"help\") {\n return emit(succeed(\"cli.manifest\", buildManifest()), EXIT_OK);\n }\n const invalid = validate(parsed);\n if (invalid !== undefined)\n return emit(invalid, EXIT_USAGE);\n /**\n * The two self-describing commands answer without building the app layer.\n *\n * `manifest` matters most here. It is the first call an agent makes and it must answer on a\n * machine with no repo, no database, and no credentials. Building the layer first would make the\n * self-description conditional on the thing it describes being already working.\n *\n * `agents-doc` is here because building the layer has a side effect. `layerDatabase` opens\n * `$MEMHTML_ROOT/.memhtml/index.db`, creating the directory and running every migration. A doc generator\n * that scaffolded a memory repo as a side effect of rendering Markdown would create `~/memhtml`\n * on any machine that ran `memhtml agents-doc --check` in CI. It reads only the command table, so it\n * has no reason to touch the app graph at all.\n */\n if (parsed.command === \"manifest\") {\n return emit(succeed(\"cli.manifest\", buildManifest()), EXIT_OK);\n }\n if (parsed.command === \"agents-doc\") {\n return Effect.runPromise(runAgentsDoc({ check: bool(parsed, \"check\", false), out: str(parsed, \"out\") }).pipe(Effect.map((data) => emit(succeed(\"agents.doc\", data), EXIT_OK)), Effect.catch((error) => Effect.succeed(emit(failureFor(error), EXIT_RUNTIME))), Effect.provideService(Logger.LogToStderr, true)));\n }\n /**\n * `serve mcp` must not build the app layer either, and here the reason is the database.\n *\n * The supervisor's only job is to spawn the server and wait. Building `layerApp` first would open\n * `$MEMHTML_ROOT/.memhtml/index.db` and run its migrations in the parent. That is a second writer\n * against the store the child exists to serve, held open for as long as the child lives, by a process\n * that never issues a query. The parent needs the resolved repo root, which is config rather than a\n * service.\n *\n * Nothing is emitted until the child exits, because stdout belongs to the child from the moment it\n * is spawned. The `serve.exit` envelope describes how the server ended, and it is written after\n * the descriptors are the parent's again.\n */\n if (parsed.command === \"serve mcp\") {\n return Effect.runPromise(Effect.gen(function* () {\n const override = str(parsed, \"repo\");\n const configured = yield* MemhtmlRoot;\n const memhtmlRoot = override !== undefined && override.trim() !== \"\" ? override.trim() : configured;\n return yield* serveMcp(memhtmlRoot);\n }).pipe(Effect.map((data) => emit(succeed(\"serve.exit\", data), EXIT_OK)), Effect.catch((error) => Effect.succeed(emit(failureFor(error), EXIT_RUNTIME))), Effect.catchCause((cause) => Effect.succeed(emit(fail(\"ERR_UNKNOWN\", `unexpected failure: ${String(cause)}`, []), EXIT_RUNTIME))), Effect.provideService(Logger.LogToStderr, true)));\n }\n /**\n * `eval discriminate` does not build the app layer either, for the reason the command above gives.\n * The gate measures the ranking stack against its own generated fixture corpus in a temp directory\n * with an in-memory database, and reads the operator's `index.db` not at all. Building `layerApp`\n * would open and migrate a store this command never queries, and an operator checking the gate is\n * typically doing it while `memhtml-mcp` serves that store.\n *\n * **Exit 1 on a failed gate**, with `ERR_DISCRIMINATION_FAILED`. A gate that exited 0 and\n * left the verdict inside the payload would be a gate every shell caller forgets to read. The\n * exit code is what stops a pipeline.\n */\n if (parsed.command === \"eval discriminate\") {\n const requested = (str(parsed, \"mode\") ?? \"fake\");\n return Effect.runPromise(runDiscrimination({\n mode: requested,\n ...(int(parsed, \"seed\") === undefined ? {} : { seed: int(parsed, \"seed\") }),\n ...(int(parsed, \"size\") === undefined ? {} : { size: int(parsed, \"size\") }),\n ...(int(parsed, \"probes\") === undefined ? {} : { probes: int(parsed, \"probes\") }),\n ...(num(parsed, \"mrr-floor\") === undefined ? {} : { mrrFloor: num(parsed, \"mrr-floor\") })\n }).pipe(Effect.map((outcome) => outcome.passed\n ? emit(succeed(\"eval.discrimination\", outcome), EXIT_OK)\n : {\n stdout: render(succeed(\"eval.discrimination\", outcome), dense),\n exitCode: EXIT_RUNTIME\n }), Effect.catchCause((cause) => Effect.succeed(emit(fail(\"ERR_UNKNOWN\", `unexpected failure: ${String(cause)}`, []), EXIT_RUNTIME))), Effect.provideService(Logger.LogToStderr, true)));\n }\n /**\n * `memhtml exec` does not build the app layer either, for the reason two commands over.\n *\n * The command reads a git tree and nothing else. It materializes a commit as a detached worktree and\n * mounts that directory read-only. It never queries `index.db`, so building `layerApp` would open and\n * migrate a database it does not use, on the path an agent reaches for while `memhtml serve mcp` is\n * serving the repo. Nothing here can be reached through `dispatch`, because `dispatch`'s service set\n * is the app layer's.\n *\n * **Non-zero `exitCode` in the payload is still exit 0 for the process**, and that split is the\n * contract. A failing script is a report with `stderr` an agent reads and fixes. The CLI's exit 1 is\n * reserved for the runtime failing to run the script at all (no repo, unreadable sha, absent helper).\n * Collapsing the two would make an agent unable to tell a bad selector from a broken install, and\n * would bury the script's own diagnostic inside an `error` string.\n *\n * The script is read here rather than in `runExec`, so a missing or empty script is exit 2 like every\n * other input error. `runExec` takes source, never a path.\n */\n if (parsed.command === \"exec\") {\n const inline = str(parsed, \"script\");\n const file = parsed.positional[0] === \"-\" ? undefined : str(parsed, \"file\");\n const script = inline !== undefined ? inline : file === undefined ? await stdin() : await readScript(file);\n if (typeof script !== \"string\")\n return emit(script, EXIT_USAGE);\n if (script.trim() === \"\") {\n return emit(fail(\"ERR_MISSING_ARGUMENT\", \"exec needs a script: a blank one would report an empty answer rather than an error\", [\n \"memhtml exec --script 'console.log(1)'\",\n \"memhtml exec --file traverse.mjs\",\n \"cat s.mjs | memhtml exec\"\n ]), EXIT_USAGE);\n }\n const override = str(parsed, \"repo\");\n return Effect.runPromise(Effect.gen(function* () {\n const configured = yield* MemhtmlRoot;\n const memhtmlRoot = override !== undefined && override.trim() !== \"\" ? override.trim() : configured;\n return yield* execCommand({\n script,\n memhtmlRoot,\n sha: str(parsed, \"sha\"),\n timeoutMs: int(parsed, \"timeout-ms\")\n });\n }).pipe(Effect.map((report) => emit(succeed(\"exec.report\", report), EXIT_OK)), Effect.catch((error) => Effect.succeed(emit(failureFor(error), EXIT_RUNTIME))), Effect.catchCause((cause) => Effect.succeed(emit(fail(\"ERR_UNKNOWN\", `unexpected failure: ${String(cause)}`, []), EXIT_RUNTIME))), Effect.provideService(Logger.LogToStderr, true), Effect.scoped));\n }\n /**\n * `memhtml apply` checks the shape of its whole op stream before any service is built (AC-6-4).\n *\n * Here rather than in `validate` because reading a file is async and `validate` is a pure synchronous\n * function of the parsed argv. Here rather than in the dispatch arm because a refusal raised inside\n * `dispatch` travels through `failureFor` and becomes exit 1, while a malformed input file is a usage\n * error and must be exit 2. The caller wrote a bad file, and the corpus is fine.\n *\n * The ordering is the observable contract. Nothing is written for a file with a bad line, and at this\n * point nothing can have been, because the app layer has not been built, so no database is open and\n * no git command has run.\n */\n let applyOps = [];\n if (parsed.command === \"apply\") {\n // `memhtml apply -` is the explicit \"read stdin\" spelling, and the dash is not a path.\n const file = parsed.positional[0] === \"-\" ? undefined : str(parsed, \"file\");\n const text = await applyText(file, stdin);\n if (typeof text !== \"string\")\n return emit(text, EXIT_USAGE);\n const decoded = decodeApply(text);\n if (!decoded.ok)\n return emit(decoded.failure, EXIT_USAGE);\n applyOps = decoded.ops;\n }\n const program = dispatch(parsed, applyOps).pipe(Effect.map(([type, data]) => emit(succeed(type, data), EXIT_OK)), Effect.catch((error) => Effect.succeed(emit(failureFor(error), EXIT_RUNTIME))), \n // A defect is still an answer. An unexpected throw anywhere below would otherwise reach the\n // process as an unhandled rejection and print a stack trace onto stdout. Stdout is a parse\n // target, so it carries the envelope and nothing else.\n Effect.catchCause((cause) => Effect.succeed(emit(fail(\"ERR_UNKNOWN\", `unexpected failure: ${String(cause)}`, []), EXIT_RUNTIME))), Effect.provide(layer ?? layerApp(str(parsed, \"repo\"))), \n // Logs go to stderr, always. Effect's default logger writes to stdout, which would interleave\n // log lines with the envelope and break every parser.\n Effect.provideService(Logger.LogToStderr, true), Effect.scoped);\n return Effect.runPromise(program);\n};\n/** The envelope's api version, re-exported so a caller can assert on it without a second import. */\nexport { API_VERSION };\n//# sourceMappingURL=run.js.map","import { codeFor, messageFor } from \"@memhtml/cli\"\nimport { Schema } from \"effect\"\n\n/**\n * The MCP wire failure: one error class, declared on every tool, whose `.message` IS the response an\n * agent reads.\n *\n * **Why a declared class at all.** `McpServer` has three catch branches for a failed `tools/call`\n * (`McpServer.ts:831-847`, effect 4.0.0-beta.102) and only ONE of them lets prose through. An\n * `AiError`, which is what this module replaced, takes branch 1 and is rewritten to \"Tool execution\n * failed due to an internal server error\" unless its reason is a parameter-validation error. A value\n * the tool's own `failureSchema` accepts takes branch 2, where `error instanceof Error ? error.message`\n * passes the text through verbatim. The schema declaration is therefore the whole\n * difference between an agent that can recover and an agent that reads a sentence with no content in\n * it. `Effect.tapCause(Effect.logError)` runs before all three branches, so stderr logging is\n * unaffected either way.\n *\n * **Why the message is composed at construction.** `McpServer` reads `.message` and nothing else.\n * `code` and `suggestions` are not on the wire as fields, because MCP's tool-error channel is one\n * text block. So the three parts are folded into the string HERE, once, and the structured fields stay\n * for tests and for any future surface that can carry them. A consumer that wanted the code back out\n * reads the prefix, which is why the code comes first and is followed by a colon: `ERR_*` is a stable\n * vocabulary and the prose after it is not.\n *\n * **Why `Schema.TaggedError` rather than a hand-written `Error` subclass.** `Schema.is(failureSchema)`\n * is the branch-2 predicate, so the value has to be something a schema accepts, and it has to be an\n * `Error` for `.message` to be read. `Schema.TaggedError` is the one construction that is both: an\n * instance is `instanceof Error`, `Schema.is` accepts it, and `Schema.is` REJECTS a plain `Error`,\n * which is what keeps a genuine defect on branch 3 where it belongs. All three of those are asserted\n * in `tests/failure.test.ts`, so the construction cannot be swapped for one that loses any of them.\n */\nexport class ToolFailure extends Schema.TaggedError<ToolFailure>()(\"ToolFailure\", {\n /** The stable code, from the same `ERROR_CODES` vocabulary the CLI envelope publishes. */\n code: Schema.String,\n /** The composed wire text: code, reason, then suggestions. This is what the agent reads. */\n message: Schema.String,\n /** The suggestions, kept structured so a test can assert them without parsing prose. */\n suggestions: Schema.Array(Schema.String)\n}) {}\n\n/** A typed failure as it arrives here: a `_tag` plus whatever payload its class carries. */\ninterface TaggedError {\n readonly _tag: string\n readonly [field: string]: unknown\n}\n\nconst isTagged = (value: unknown): value is TaggedError =>\n typeof value === \"object\" &&\n value !== null &&\n typeof (value as { _tag?: unknown })._tag === \"string\"\n\nconst text = (value: unknown): string | undefined => (typeof value === \"string\" ? value : undefined)\n\n/**\n * What to do about a failure, phrased as calls this agent can actually make.\n *\n * The reader is an LLM mid-task holding fourteen tools and no shell. `suggestionsFor` in\n * `apps/cli/src/errors.ts:115-137` answers the same question for a human at a prompt and answers it in\n * `memhtml` commands and `git` invocations, every one of which is unreachable from here. A suggestion\n * an agent cannot execute costs more than none: it spends the model's attention on a plan that ends in\n * \"I don't have a terminal\", and the recovery that WAS available goes unmentioned. So this is a\n * deliberate parallel mapping rather than a reuse, and the rule it holds to is that every string names\n * a tool in the toolkit or an action inside the current call's own control.\n *\n * The path payloads are interpolated rather than left as `<path>` placeholders: the agent has to type\n * the argument, and a code that already knows the path and does not say it forces a `memory_list` the\n * response could have skipped.\n *\n * The FIRST suggestion is always the action, and any state the agent needs in order to trust that\n * action comes second. `toToolFailure` joins the list behind \"Try: \", so a list that opened with\n * \"nothing was written\" would put a fact where the reader is looking for a verb.\n *\n * `DirtyTree`, `GitFailure` and `StorageFailure` share the same answer, and it states the ceiling:\n * an agent cannot commit, stash, or repair a database from a tool call. `memory_status` is\n * the one read that distinguishes \"the repo is wedged\" from \"that one write raced\", and escalation is\n * the correct terminal move rather than a retry loop.\n */\nexport const mcpSuggestionsFor = (error: unknown): ReadonlyArray<string> => {\n if (!isTagged(error)) return []\n switch (error._tag) {\n case \"PathNotFound\":\n return [\n \"call memory_search with a query for what you were looking for\",\n \"call memory_list to page the corpus by type or workspace\"\n ]\n case \"WriteConflict\":\n return [\n `call memory_read on ${text(error.path) ?? \"that path\"} to get the current content`,\n \"re-apply your change to that content and retry the write\"\n ]\n case \"DuplicateContent\":\n return [\n `call memory_read on ${text(error.existingPath) ?? \"that path\"} — your content already lives there`,\n \"nothing was written and no commit was made, so there is nothing to clean up\"\n ]\n case \"InvalidMemory\":\n return [\n \"fix the violated constraint named above and call the same tool again\",\n \"nothing was written and no commit was made — the store refused at the render gate\"\n ]\n case \"ModelUnavailable\":\n return [\n \"retry — search degrades to the lexical floor without the embedder, so results are narrower but real\",\n \"call memory_status to see whether the embedder is up\"\n ]\n case \"EmbedModelMismatch\":\n return [\n \"keep working — memory_search still runs on the lexical, recency, and salience arms\",\n \"the vector arm stays unusable until an operator rebuilds the index\"\n ]\n case \"DirtyTree\":\n case \"GitFailure\":\n case \"StorageFailure\":\n return [\n \"call memory_status to see repo health: HEAD, dirty state, and index freshness\",\n \"report this to the operator if it persists — an agent cannot repair the repo from a tool call\"\n ]\n case \"DiscriminationFailed\":\n return [\"call memory_status to see when sleep last ran\", \"report this to the operator\"]\n default:\n return []\n }\n}\n\n/**\n * A reason ending in a sentence terminator, so the suggestions read as a second sentence.\n *\n * `messageFor` returns fragments without final punctuation because the CLI envelope carries the reason\n * in its own JSON field and the suggestions in another, so there is nothing to run together. Here the\n * three parts share one string, and \"no memory at areas/x.html Try: call memory_search\" is a sentence\n * an LLM has to re-parse.\n */\nconst sentence = (reason: string): string => (/[.!?]$/.test(reason) ? reason : `${reason}.`)\n\n/**\n * A typed domain failure as the wire failure.\n *\n * Total by construction, three times over: `codeFor` maps an unknown `_tag` to `ERR_UNKNOWN`,\n * `messageFor` maps it to a stated fallback, and `mcpSuggestionsFor` returns an empty array. So an\n * error class added upstream tomorrow reaches an agent as prose with a documented code rather than as\n * the internal-error string. That string is the failure mode this whole module exists to end, and it\n * would come straight back if the mapping could fall off the end.\n *\n * The reason text is `messageFor`'s and only `messageFor`'s: it excludes the driver's message, the\n * SQL, the git argv, and every memory body, because each error class dropped those at its adapter edge\n * so that a tool response could not carry corpus content. Enriching past it here would undo that at\n * the one boundary where the content leaves the process.\n */\nexport const toToolFailure = (error: unknown): ToolFailure => {\n /**\n * An already-composed failure passes through UNCHANGED, and that branch is what lets a handler\n * compose its own wire failure at all.\n *\n * `handled` in `handlers.ts` is `Effect.mapError(toToolFailure)` over every handler, so a handler that\n * fails with a `ToolFailure` it built itself arrives here too. `batchAbortFailure` is that case,\n * since it needs an op index no typed domain error carries. Without this branch it falls off the end\n * of `codeFor`'s switch (its `_tag` is `\"ToolFailure\"`, in no error vocabulary) and is rewritten to\n * `ERR_UNKNOWN: unexpected failure: ToolFailure`, the whole composed message replaced by its own\n * class name. That is the masking this module exists to end, arriving from the inside. Caught by the\n * batch abort tests; kept here rather than by exempting the batch handler from `handled`, since a\n * handler outside the one error translation is a handler that can leak an untranslated failure.\n */\n if (error instanceof ToolFailure) return error\n\n const code = codeFor(error)\n const suggestions = mcpSuggestionsFor(error)\n const reason = sentence(messageFor(error))\n return new ToolFailure({\n code,\n suggestions,\n message:\n suggestions.length === 0\n ? `${code}: ${reason}`\n : `${code}: ${reason} Try: ${suggestions.join(\"; \")}`\n })\n}\n\n/**\n * An atomic batch's abort as the wire failure, naming the op that caused it.\n *\n * **Why the error channel and not a success payload.** An atomic batch that aborted wrote nothing, made\n * no commit, and produced no path, so there is no result to return, and a success response carrying\n * `written: 0` is one an agent has to inspect to discover its call did nothing. Every other refusal on\n * this server is an error, so a batch that refused through the success channel would be the one tool\n * whose failures an agent could miss by not looking. `memory_write_batch`'s description promises exactly\n * this (\"the first refused op aborts the whole call … and the failure names the offending op as\n * ops[N]\"), and that promise is what this function makes true.\n *\n * **Why it is composed HERE rather than in the handler.** `failure.ts` is the single place the wire\n * failure is produced, and there are TWO atomic refusals that must be indistinguishable to a reader: the\n * handler's own per-op XOR check, and an op the store's render gate refused inside `batchWrite`. The\n * second arrives as a `BatchOpReport`, carrying a code and a reason STRING because `operations.ts`\n * already mapped the typed error and deliberately dropped it, so `toToolFailure` cannot be reached for\n * it. Two hand-composed messages would be two shapes for one outcome; one function is one shape.\n *\n * **Why the suggestions are the batch's own and not `mcpSuggestionsFor`'s.** The singular's advice for\n * `InvalidMemory` is \"fix the violated constraint and call the same tool again\", which is right and\n * incomplete here: the agent is holding N-1 ops that WOULD have landed, and the thing it most needs to\n * know is that `continue_on_error` exists. Both entries open with a verb, because the list is joined\n * behind \"Try: \".\n *\n * The `code` is the op's own, carried through unchanged from `codeFor` so the batch and the singular\n * report one refusal under one code. An agent branching on `ERR_INVALID_MEMORY` must not have to know\n * which door produced it.\n */\nexport const batchAbortFailure = (index: number, code: string, reason: string): ToolFailure => {\n const suggestions = [\n `fix ops[${index}] and call memory_write_batch again`,\n \"set continue_on_error to true to write the ops that would have succeeded\"\n ]\n return new ToolFailure({\n code,\n suggestions,\n message:\n `${code}: ops[${index}]: ${sentence(reason)} ` +\n `The batch is atomic, so nothing was written and no commit was made. ` +\n `Try: ${suggestions.join(\"; \")}`\n })\n}\n","import {\n DatabaseService,\n ExtractorPort,\n Indexer,\n IndexRecorder,\n Retrieval,\n Store\n} from \"@memhtml/cli\"\nimport { MEMORY_RELS } from \"@memhtml/contracts/edges\"\nimport { PARA_BUCKETS, WRITABLE_MEMORY_TYPES } from \"@memhtml/contracts/types\"\nimport { REINFORCE_SIGNALS } from \"@memhtml/domain\"\nimport { Schema } from \"effect\"\nimport { Tool, Toolkit } from \"effect/unstable/ai\"\n\nimport { ToolFailure } from \"./failure.js\"\n\n/**\n * The fourteen tools: design.md §8 verbatim, plus `memory_write_batch` (spec 004 D7).\n *\n * **`parameters` is always `Schema.Struct`, never `Schema.Class`.** A client sends a plain object\n * literal, and a class schema's decode expects an instance. The failure is a decode error on every\n * call, at runtime, for every tool. This is the one trap the whole surface is arranged around.\n *\n * **Sleep is deliberately absent.** It is a cron/operator action producing a reviewable branch, not\n * something an agent fires mid-conversation: a sleep run rewrites confidence across the corpus,\n * archives memories, and creates a branch a human is expected to read. `memhtml sleep run` is the\n * entry point, and if the fleet ever wants one here it is `sleep_status` (read-only). The write\n * side stays behind an operator.\n *\n * Every `success` schema is also a `Schema.Struct`, so `tools/list` publishes a JSON Schema the\n * client can validate a response against rather than an opaque object.\n *\n * **Every tool declares `failure: ToolFailure`, and the omission is a silent wire bug.** A tool with\n * no declared failure schema gets `Schema.Never` (`Tool.ts:1265`), so `McpServer`'s declared-failure\n * predicate rejects everything and every failure, typed domain error included, is rewritten to\n * \"Tool execution failed due to an internal server error\" before it reaches the caller\n * (`McpServer.ts:831-847`). The declaration is what puts a tool's failures on the branch that passes\n * prose through; see `failure.ts` for the mechanism. `failureMode` is left at its `\"error\"` default\n * on purpose: the error CHANNEL is what `McpServer` catches, and `\"return\"` would instead fold the\n * failure into the success union, where the server would see a successful call carrying a failure\n * payload no MCP client knows to read.\n */\n\n/** The eight types an agent may write. `arc` is system-written by the sleep cycle. */\nconst WritableType = Schema.Literals(WRITABLE_MEMORY_TYPES)\n\n/** The nine MEMORY-class rels. A person or provenance rel cannot be named here. */\nconst MemoryRelSchema = Schema.Literals(MEMORY_RELS)\n\n/**\n * A repo-root-relative path: `areas/oncall/rollback-order.html`.\n *\n * The git-tree form with no leading slash, which is `files.path`, and the ID of a memory. The\n * `<link href>` form in the HTML carries a leading slash and is converted at the store boundary, so\n * a tool never sees it.\n */\nconst MemoryPath = Schema.String\n\n/**\n * `Schema.Finite`, not `Schema.Number`, for every numeric field.\n *\n * `Number` derives a JSON Schema with an `anyOf` carrying a STRING branch, because `Infinity` and\n * `NaN` are not JSON numbers and the codec represents them as strings. Probed on this beta,\n * `Schema.Number` derives `{\"anyOf\":[{\"type\":\"number\"},{\"type\":\"string\",\"enum\":[\"Infinity\",\n * \"-Infinity\",\"NaN\"]}]}`. A client reading that sees a union where the tool wants a number. `Finite`\n * derives a clean `{\"type\":\"number\"}`.\n */\nconst Finite = Schema.Finite\n\n/** A count: a non-negative quantity. */\nconst Count = Schema.Int\n\n/**\n * An optional parameter that a client may also send explicitly as `null`.\n *\n * A bare `Schema.optional(X)` is a WIRE BUG here, and it is the kind a byte-comparison fixture\n * cannot see: the derived JSON Schema publishes `{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"null\"}]}` , telling\n * every client that `null` is acceptable, while the decoder rejects it with \"Expected string |\n * undefined, got null\" (both probed on effect 4.0.0-beta.102). So a client that read the schema and\n * did the obvious thing, sending `{\"workspace\": null}` for \"no workspace\", would get a decode error\n * on a call the published contract said was valid. Many clients serialize an absent optional exactly\n * that way.\n *\n * `optionalKey(NullOr(X))` makes the decoder accept all three forms a client can produce (absent,\n * a value, and `null`) and publishes the FLAT `{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"null\"}]}`.\n * `optional` rather than `optionalKey` would derive a nested `anyOf` wrapping that union in a second\n * one, which is the same contract spelled in a way a client has to unwrap twice to read.\n *\n * `null` and absent both mean \"not supplied\", which is what the handlers normalize to `undefined`.\n */\nconst Optional = <S extends Schema.Top>(schema: S) => Schema.optionalKey(Schema.NullOr(schema))\n\n/**\n * The services a tool handler may reach for, declared per tool.\n *\n * `Tool.make`'s `dependencies` is what moves a service from the handler's requirement set into the\n * TOOL's, so `kit.toLayer({…})` accepts a handler that yields `Store`, and the requirement then\n * surfaces on the layer where `layerApp` satisfies it. Without the declaration a handler that reads\n * a service is a type error, and the only ways out are casting the handler or building the services\n * inside it: the first loses the check that the app layer provides what the tools need, and the\n * second gives every tool call its own database connection.\n *\n * Each tool declares only what it actually uses, so a handler that grows a dependency has to say so,\n * which keeps `memory_search` provably unable to reach the store and write.\n *\n * A FUNCTION per set, not a shared constant: the option's type is a mutable array, so handing the\n * same array to fourteen tools would let one tool's construction mutate the dependency list of the\n * other thirteen.\n */\nconst READS = () => [DatabaseService]\n// ExtractorPort is in the write set because `batchWrite` reads it (the write-time entity assist);\n// the port resolves to `{ extractor: undefined }` unless MEMHTML_EXTRACT_ENTITIES=on.\nconst WRITES = () => [Store, Indexer, IndexRecorder, ExtractorPort]\nconst RETRIEVES = () => [Retrieval, DatabaseService]\n\n/**\n * The `article_html` contract, stated in the description of every tool that takes it.\n *\n * A description, not a doc comment on the parameter: `tools/list` publishes `description` and an\n * agent chooses and fills a tool from it, so a contract stated anywhere else is a contract the\n * caller never reads. And it has to be stated HERE rather than left to the store's refusal, because\n * `article_html` is the one parameter where the caller owns a format constraint. Every other\n * parameter is a value the template places itself. An agent that learns the `<mark>` rule from an\n * `InvalidMemory` on its first write has already spent a round trip on something the tool could\n * have told it.\n *\n * The four clauses are the ones a caller can actually violate: format.md constraint 1 (exactly one\n * `<mark>`, inside the first `<p>` or `<li>`), constraint 3 (no `class`, `style`, or `<script>`),\n * the closed vocabulary, and the `<time datetime>` rule. That last one is a CONSEQUENCE the caller\n * has to know about rather than a constraint: the first such element becomes `files.event_at`,\n * and the recency arm ranks episodic memories by it rather than by write time.\n */\nconst ARTICLE_HTML_CONTRACT =\n \"Supply EXACTLY ONE of `body` or `article_html`. Both or neither is refused. \" +\n \"`article_html` is raw <article> inner markup used verbatim, and the caller owns the format: exactly one <mark>, \" +\n \"inside the first <p> or the first <li>, and never inside <aside> or <details>; only elements from the closed \" +\n \"vocabulary in docs/format.md; no class attribute, no style attribute, no <script>, no event handlers. \" +\n 'The FIRST <time datetime=\"…\"> element becomes the memory\\'s event time, which is what the recency arm ranks ' +\n \"by, so an episodic memory about last week should carry last week's date, not today's. Markup that violates \" +\n \"the format is refused before any file is written or committed. \" +\n \"Code snippets: in `body` prose, a paragraph that is entirely a fenced code block (```ts … ```) becomes \" +\n '<figure><pre><code data-lang=\"ts\">, whitespace verbatim, and the language promotes to a `lang:ts` entity; ' +\n \"a blank line inside the fence does not split it. In `article_html`, author the same markup yourself: \" +\n \"data-lang, never class (forbidden) and never lang= (that names human languages).\"\n\n/**\n * When to batch and what a batch does, stated in the description of BOTH write tools.\n *\n * A shared constant for the same reason `ARTICLE_HTML_CONTRACT` is one: `memory_write` has to point at\n * `memory_write_batch` and `memory_write_batch` has to explain itself, and two hand-written versions of\n * one workflow drift the first time the semantics move. Written once, appended twice.\n *\n * And it lives in a DESCRIPTION because this server has nowhere else to put it. MCP has a server-level\n * `instructions` field for exactly this kind of cross-tool guidance, and effect 4.0.0-beta.102 never\n * emits it. See the comment in `server.ts` next to `layerStdio`. Tool descriptions are the only\n * channel, so a workflow rule that is not in one is a rule no agent reads.\n *\n * Every clause is something the caller decides or has to predict, and nothing else: the threshold that\n * makes batching worth it, the ordering guarantee it can index results by, the atomicity default it\n * would otherwise have to discover from a refusal, the flag that changes that default, and the two\n * outcomes an agent most often mistakes for errors, a dedupe and a per-op failure in continue mode.\n * The cost of leaving any of them out is a wrong assumption an agent acts on for the rest of the task.\n */\nconst BATCH_GUIDANCE =\n \"Call memory_write_batch ONCE rather than memory_write N times whenever this task will write more than about three memories: \" +\n \"a batch stages every file, makes ONE commit, and reindexes ONCE, so it costs less than N calls and leaves a history a reader can follow. \" +\n \"It returns one result per op in INPUT ORDER, each naming that op's index, its path, and whether it deduped. \" +\n \"A batch is ATOMIC by default: the first refused op aborts the whole call, no file is written and no commit is made, and the failure names the offending op as ops[N]. \" +\n \"Set continue_on_error to true for best-effort instead, and a refused op comes back as a failed result carrying its own code and reason while every surviving op lands in the one commit. \" +\n \"A duplicate is never a failure: an op whose exact content is already stored returns ok with deduped=true and the existing path. \" +\n \"Each op supplies EXACTLY ONE of body or article_html, the same rule memory_write follows.\"\n\n/**\n * The `detect_conflicts` assist, stated in `memory_write_batch`'s description.\n *\n * A constant beside `BATCH_GUIDANCE` for the same reason that one exists, and for one more: a test\n * asserts the whole string is present, so the semantics and the published prose cannot drift into two\n * versions. It is not appended to `memory_write`'s description. The singular has no such flag, and a\n * paragraph about a parameter a tool does not accept is a paragraph that makes an agent try to send it.\n *\n * Every clause is something a caller acts on. The distinction from dedupe, because an agent that\n * thought this was dedupe would stop checking. The RULE, because it is grammatical rather than semantic\n * and an agent expecting meaning-matching would trust a null it should not. The two match sources, since\n * the intra-batch one is invisible to every other tool. The nulls, all four of them, because each one is\n * an absence of information rather than an absence of conflict. And the propose-only contract WITH its\n * reason, the BEAM caveat, spelled out rather than asserted: an agent told only \"this does not block\"\n * will assume it is a v1 limitation and hand-roll the archiving the design deliberately refuses.\n */\nconst CONFLICT_GUIDANCE =\n \"Set detect_conflicts to true and each per-op result gains a `conflict` field naming what that op's claim CONTRADICTS. \" +\n \"This is not dedupe: dedupe catches an op whose content is IDENTICAL to something stored, while this catches an op that says something DIFFERENT about the same thing, the case dedupe is blind to, and the one that actually rots a corpus. \" +\n \"The match is grammatical rather than semantic. A claim splits into a frame (the subject and relation up to its LAST of/is/in/to/by/as) and a value, and two claims conflict when they share a frame: 'The pool ceiling is 64' and 'The pool ceiling is 128' both key on 'the pool ceiling is'. \" +\n \"conflict.path names an ACTIVE memory already holding that slot. conflict.batch_index names an EARLIER op in this same call, which no other tool can see because neither op is stored yet; it has no path for that reason. conflict.claim is the other claim's own text, so you can decide without a second call. \" +\n \"conflict is null when nothing matched, when detect_conflicts was absent, when the claim states no frame shape (the rule refuses frames under three tokens and values over six, so short claims and claims trailed by a clause are deliberately unmatched rather than loosely matched), and always on an op that used article_html. The claim is inside your markup there and is not read until the store renders it. \" +\n \"THE ASSIST NEVER CHANGES WHAT IS WRITTEN. An op carrying a conflict is written exactly as it would have been without the flag: nothing is archived, nothing is refused, later does not win, and the summary counts are unchanged. \" +\n \"That is deliberate, not a limitation. Sometimes the contradiction IS the answer. A memory recording that a runbook step changed necessarily contradicts the memory stating the old step, and a system that resolved that for you would destroy the pair a reader needs in order to see the change at all. \" +\n \"So YOU decide, per conflict: keep both (they are about different things, or both are true), call memory_correct on the named path instead (the new claim supersedes the old one, which stays readable under archive/), or drop the op. \" +\n \"Archived memories never match, so a superseded claim stops contradicting the claim that superseded it.\"\n\n/**\n * The `consolidate` opt-in, stated in `memory_write_batch`'s description.\n *\n * A third constant beside the two above and AFTER `CONFLICT_GUIDANCE` in the description, because it\n * is the acting counterpart of the assist: an agent has to know what a conflict IS before \"resolve it\n * last-wins\" means anything, and stating the flag first would make the propose-only contract above\n * read as contradicted two paragraphs later.\n */\nconst CONSOLIDATE_GUIDANCE =\n 'Set consolidate to \"last-wins\" and the batch RESOLVES frame-key matches instead of only reporting them: for ops sharing a claim slot (the same deterministic frame key the conflict rule uses), the LATER value wins. Exactly one file is written, at the FIRST index that claimed the slot, and every later restatement reports consolidated_into naming that slot instead of a path of its own. ' +\n \"A stored ACTIVE memory occupying a surviving slot is archived with a supersedes link from the new file, its archive path reported on the winner as superseded_path. \" +\n \"Off by default, and claims with no frame shape are never consolidated. The guards fail closed, so this only ever acts on claims the conflict rule would have matched.\"\n\n/**\n * The fields that author ONE memory, shared by `memory_write`'s parameters and `memory_write_batch`'s\n * op struct.\n *\n * D7 says the batch op is \"the same fields as memory_write\". Written twice, that is a claim two\n * literals make about each other and stop making the first time a field is added to one of them. An\n * agent that learned `tags` from `memory_write` and had it silently dropped by a batch op would get a\n * memory it could not find by the facet it filed it under. Shared, the widening is automatic and the\n * published schemas cannot disagree.\n *\n * A FUNCTION returning a fresh literal, matching `READS`/`WRITES` above: the field record is handed to\n * a schema constructor and nothing here should be able to observe another tool's construction.\n */\nconst writeFields = () => ({\n title: Schema.String,\n /**\n * Prose. The first sentence becomes the `<mark>` claim and the rest becomes one `<p>` per blank-line\n * paragraph. See `claimFromProse`/`proseTail` in `@memhtml/cli`'s `prose.ts`, the one copy this door and\n * `memhtml apply` share. Optional because `article_html` is the other way to author the same article, and\n * the handler refuses a call that names both or neither.\n */\n body: Optional(Schema.String),\n /** Pre-authored article markup, used verbatim in place of `body`. See the description's contract. */\n article_html: Optional(Schema.String),\n memory_type: WritableType,\n path: Optional(MemoryPath),\n workspace: Optional(Schema.String),\n tags: Optional(Schema.Array(Schema.String)),\n entities: Optional(Schema.Array(Schema.String)),\n importance: Optional(Count),\n confidence: Optional(Finite),\n session_id: Optional(Schema.String),\n prompt_id: Optional(Schema.String),\n turn_uuid: Optional(Schema.String)\n})\n\nconst MemoryWrite = Tool.make(\"memory_write\", {\n description:\n \"Write one memory to the corpus. Returns the existing path with deduped=true when an active memory already holds this exact content. A duplicate creates no file and no commit. \" +\n ARTICLE_HTML_CONTRACT +\n \" \" +\n BATCH_GUIDANCE,\n dependencies: WRITES(),\n parameters: Schema.Struct(writeFields()),\n failure: ToolFailure,\n success: Schema.Struct({\n path: MemoryPath,\n created: Schema.Boolean,\n deduped: Schema.Boolean,\n existing_path: Schema.NullOr(MemoryPath)\n })\n})\n\n/**\n * One op in a batch: a whole `memory_write` payload, with the tool name standing in for D4's `op`\n * discriminator.\n *\n * A nested `Schema.Struct`, which is what makes the array's `items` a published object schema with its\n * own `required`. Probed on effect 4.0.0-beta.102, `Schema.Array(Schema.Struct({…}))` derives the\n * struct INLINE under `items` rather than hoisting it into a `$defs` a client would have to resolve.\n * So `ops[].title` is as legible to a caller reading `tools/list` as `memory_write`'s own `title`, and\n * the `Optional` discipline carries in unchanged: an optional inside an op publishes the same FLAT\n * `{\"anyOf\":[{…},{\"type\":\"null\"}]}` and accepts absent, a value, or `null`.\n */\nconst BatchOp = Schema.Struct(writeFields())\n\n/** One op's outcome, mirroring `memhtml apply`'s own per-op payload field for field, in snake_case. */\nconst BatchOpResult = Schema.Struct({\n /** This op's position in the `ops` array the caller sent. Results come back in that order too. */\n index: Count,\n ok: Schema.Boolean,\n /**\n * Every field below is PRESENT and nullable rather than optional, for the reason `memory_write`'s\n * `existing_path` is: a client reading an absent key cannot tell \"this op did not dedupe\" from \"this\n * server does not report dedupes\", and an agent deciding whether to retry needs that distinction.\n */\n path: Schema.NullOr(MemoryPath),\n deduped: Schema.Boolean,\n existing_path: Schema.NullOr(MemoryPath),\n /** The stable `ERR_*` code for this op's refusal, null when it did not fail. */\n code: Schema.NullOr(Schema.String),\n error: Schema.NullOr(Schema.String),\n /**\n * True when this op was never attempted: an atomic abort reports every op other than the offending\n * one as skipped, which is how a caller tells \"refused\" from \"not reached\".\n */\n skipped: Schema.Boolean,\n /**\n * What this op's claim contradicts, when `detect_conflicts` was on and something matched. Null when\n * the flag was off, when nothing matched, or when the claim states no frame shape.\n *\n * `Schema.NullOr(Schema.Struct(…))`, present like every field above rather than optional: a client\n * reading an absent key cannot tell \"this op conflicts with nothing\" from \"this server does not\n * report conflicts\", and the two lead to opposite decisions.\n *\n * ONE struct with both source fields nullable rather than a union of two, so a client reads `claim`\n * unconditionally (that is the disagreement, and it is what the decision is made on) and then\n * whichever of `path`/`batch_index` is non-null. A `Schema.Union` would publish two near-identical\n * three-field shapes under an `anyOf` and force every consumer to discriminate before reading the\n * field it wanted, which is the same trap the `body`/`article_html` XOR avoids by not being a union.\n */\n conflict: Schema.NullOr(\n Schema.Struct({\n /** The ACTIVE memory already holding this frame key. Null for an intra-batch match. */\n path: Schema.NullOr(MemoryPath),\n /**\n * The EARLIER op in THIS call holding it. Null for a store match, and it has no path because\n * that op's file does not exist yet. The batch has not been written when the assist runs.\n */\n batch_index: Schema.NullOr(Count),\n /** The other claim's own text. */\n claim: Schema.String\n })\n ),\n /**\n * Set on a batch-internal LOSER under `consolidate: \"last-wins\"`: a later op with the same frame\n * key replaced this op's value before anything was written, and the number is the caller-space\n * index of the op whose position carries the surviving value. Null everywhere else, present like\n * every field above so a client can tell \"not consolidated\" from \"not reported\".\n */\n consolidated_into: Schema.NullOr(Count),\n /**\n * Set on a WINNER whose write superseded a live stored memory under `consolidate: \"last-wins\"`:\n * the loser's ARCHIVE path, where its bytes now live. Null when nothing stored occupied the\n * slot, and when the supersede degraded (the batch still wrote; the corpus is merely\n * unconsolidated).\n */\n superseded_path: Schema.NullOr(Schema.String)\n})\n\nconst MemoryWriteBatch = Tool.make(\"memory_write_batch\", {\n description:\n \"Write many memories in ONE commit: every op is validated first, every surviving file is staged, and the batch commits and reindexes exactly once. \" +\n \"commit_sha is null when nothing was written: an all-deduped batch, or an aborted one. \" +\n /**\n * Same order as `memory_write`'s (the article contract, then the batch workflow), so an agent that\n * has read one description finds the other's clauses where it expects them. Reversed here, the\n * guidance's closing XOR reminder would sit immediately before the full statement of that same rule,\n * which reads as a repetition rather than as two sections.\n */\n ARTICLE_HTML_CONTRACT +\n \" \" +\n BATCH_GUIDANCE +\n /**\n * LAST, after the workflow, and consolidation after the conflict rule it acts on. The guidance\n * states what a batch IS and an agent needs that before an optional assist over it means anything;\n * leading with the conflict rule would explain a field on a result shape the reader has not been\n * told about yet.\n */\n \" \" +\n CONFLICT_GUIDANCE +\n \" \" +\n CONSOLIDATE_GUIDANCE,\n dependencies: WRITES(),\n parameters: Schema.Struct({\n ops: Schema.Array(BatchOp),\n /** Best-effort mode: a refused op is reported and skipped, survivors land in the one commit. */\n continue_on_error: Optional(Schema.Boolean),\n /**\n * Report each op's frame-matches as a per-op `conflict`. Propose-only: it changes nothing about what\n * is written. `Optional` rather than defaulted-true because the assist costs one extra query per\n * batch, and a caller that did not ask for the field would be paying for an answer it does not read.\n */\n detect_conflicts: Optional(Schema.Boolean),\n /**\n * Opt-in deterministic last-wins consolidation over the conflict rule's own frame keys. A\n * `Literals` of one value rather than a boolean, so the vocabulary can widen (a `first-wins`, a\n * semantic mode) without a shipped `true` changing meaning under a caller.\n */\n consolidate: Optional(Schema.Literals([\"last-wins\"])),\n /**\n * Batch-level provenance: the session this call is being made in. An op that names its own wins,\n * because it is the more specific statement about where that one memory came from, which is what\n * lets a batch replay writes from an earlier session without relabelling them.\n */\n session_id: Optional(Schema.String),\n prompt_id: Optional(Schema.String),\n turn_uuid: Optional(Schema.String)\n }),\n failure: ToolFailure,\n success: Schema.Struct({\n results: Schema.Array(BatchOpResult),\n /** Derived from `results` in one pass, so the counts cannot disagree with the array. */\n summary: Schema.Struct({\n total: Count,\n written: Count,\n deduped: Count,\n failed: Count,\n skipped: Count,\n /** Batch-internal losers under `consolidate: \"last-wins\"`: neither written nor failed. */\n consolidated: Count\n }),\n commit_sha: Schema.NullOr(Schema.String)\n })\n})\n\nconst MemoryRead = Tool.make(\"memory_read\", {\n description:\n \"Read one memory in full: its head metadata, authored links, and complete article body. The only path to a <details> body, which recall never quotes. An explicit open of a named path COUNTS as salience. This is the read that moves the access plane, while a search or recall hit does not.\",\n /**\n * `DatabaseService` is here because an explicit open bumps the access plane: `readMemory` reaches the\n * state plane through `bumpAccess`, so the tool has to declare it or the handler is a type error. The\n * widening is the salience rule made visible in the dependency set. `memory_search` still cannot\n * reach it, which is what keeps a ranker's guess out of the plane.\n */\n dependencies: [Store, IndexRecorder, DatabaseService],\n parameters: Schema.Struct({\n path: MemoryPath,\n session_id: Optional(Schema.String)\n }),\n failure: ToolFailure,\n success: Schema.Struct({\n path: MemoryPath,\n title: Schema.String,\n body: Schema.String,\n gist: Schema.String,\n memory_type: Schema.String,\n meta: Schema.Record(Schema.String, Schema.String),\n links: Schema.Array(Schema.Struct({ rel: Schema.String, href: Schema.String })),\n archived: Schema.Boolean,\n warnings: Schema.Array(Schema.String)\n })\n})\n\nconst MemorySearch = Tool.make(\"memory_search\", {\n description:\n \"Ranked search over the corpus: lexical, vector, recency, and salience arms fused with RRF, then diversified. Each hit carries a `snippet`: the text of the file's best-matching chunk for this query (its opening chunk when the vector arm did not fire), truncated with a trailing `…` when cut. `degraded` is true when the vector arm did not fire, so the result came from fewer signals. Each hit also carries `entities` in `type:name` form; pass one of those values back as `entity` to make the next call the second hop of a chain. That is two calls, not a guess about spelling. An `entity` scope that matches nothing returns NO hits and says so through `scope_empty`: this tool never widens a scope it could not satisfy. `as_of` is a point-in-time view: pass an ISO instant and the result is what was believed valid at that moment, including since-superseded memories (marked superseded_by). Returning a path changes nothing: a hit is this ranker's guess, so it never bumps salience. Call memory_read to open the one you chose, and memory_reinforce to record whether it was right.\",\n dependencies: RETRIEVES(),\n parameters: Schema.Struct({\n query: Schema.String,\n limit: Optional(Count),\n memory_types: Optional(Schema.Array(WritableType)),\n workspace: Optional(Schema.String),\n tags: Optional(Schema.Array(Schema.String)),\n /**\n * One entity reference in `type:name` form, the same spelling `memory_list` takes and the same\n * spelling a hit's `entities` publishes, so a value read off a hit is a valid scope verbatim.\n */\n entity: Optional(Schema.String),\n include_archived: Optional(Schema.Boolean),\n /**\n * Point-in-time view: returns what was believed valid at this moment, including\n * since-superseded memories (marked superseded_by). The window is\n * `coalesce(valid_from, event_at, created_at) <= as_of < valid_until`. The supersede path\n * stamps both ends, so history is read from the files rather than replayed from git.\n */\n as_of: Optional(Schema.String)\n }),\n failure: ToolFailure,\n success: Schema.Struct({\n hits: Schema.Array(\n Schema.Struct({\n path: MemoryPath,\n title: Schema.String,\n gist: Schema.String,\n memory_type: Schema.String,\n /** The fused RRF score. Unitless and comparable only within one result set. */\n score: Finite,\n confidence: Finite,\n updated_at: Schema.String,\n /**\n * The best-matching chunk's text for THIS query (the vector arm's winning chunk, or the\n * file's opening chunk on the degraded path), truncated with a trailing `…` when cut.\n */\n snippet: Schema.String,\n /**\n * This memory's entity references in `type:name` form, sorted, possibly empty.\n *\n * The next hop's `entity` parameter, published in the form that parameter accepts: the whole\n * point is that a caller chains by COPYING a value rather than by reconstructing one.\n */\n entities: Schema.Array(Schema.String),\n /**\n * The path of the memory that superseded this one, or `null` when nothing has. Non-null\n * only for an archived hit, which reaches a result through `as_of` or\n * `include_archived`, so a point-in-time answer is legible as history. Present and\n * nullable like `consolidated_into`: a client must be able to tell \"not superseded\" from\n * \"this build does not report supersession\".\n */\n superseded_by: Schema.NullOr(Schema.String)\n })\n ),\n degraded: Schema.Boolean,\n arms: Schema.Array(Schema.String),\n /** The `entity` this search was scoped to, or `null` when it was not scoped by entity. */\n entity_scope: Schema.NullOr(Schema.String),\n /**\n * True when a scope was named, it narrowed the query, and nothing survived it.\n *\n * A boolean in every case, following `degraded`: this is the field that makes an empty scoped\n * result attributable to the scope, and it would be worth nothing if its absence had to be read\n * as `false`.\n */\n scope_empty: Schema.Boolean\n })\n})\n\nconst MemoryRecall = Tool.make(\"memory_recall\", {\n description:\n \"A context pack under a character budget: full bodies for what fits, one index line each for what does not. Arcs are folded under their own envelope so a synthesis cannot crowd out the evidence behind it.\",\n dependencies: RETRIEVES(),\n parameters: Schema.Struct({\n query: Schema.String,\n budget_chars: Optional(Count),\n workspace: Optional(Schema.String)\n }),\n failure: ToolFailure,\n success: Schema.Struct({\n sections: Schema.Struct({\n arcs: Schema.Array(\n Schema.Struct({\n path: MemoryPath,\n title: Schema.String,\n gist: Schema.String,\n body: Schema.String\n })\n ),\n memories: Schema.Array(\n Schema.Struct({\n path: MemoryPath,\n title: Schema.String,\n gist: Schema.String,\n body: Schema.String\n })\n ),\n /** What did not fit: claim plus path, for a deliberate drill-down. */\n lateral: Schema.Array(\n Schema.Struct({ path: MemoryPath, title: Schema.String, gist: Schema.String })\n )\n }),\n spent_chars: Count,\n truncated: Schema.Boolean,\n degraded: Schema.Boolean\n })\n})\n\nconst MemoryCorrect = Tool.make(\"memory_correct\", {\n description:\n \"Supersede a memory: write the corrected version and archive the target in ONE commit, linked in both directions. Never edits in place. The superseded memory stays readable under archive/. \" +\n ARTICLE_HTML_CONTRACT,\n dependencies: WRITES(),\n parameters: Schema.Struct({\n target_path: MemoryPath,\n title: Schema.String,\n /** The corrected prose; first sentence becomes the new `<mark>`. Exclusive with `article_html`. */\n body: Optional(Schema.String),\n /** Pre-authored markup for the superseding article, used verbatim. Exclusive with `body`. */\n article_html: Optional(Schema.String),\n reason: Schema.String,\n session_id: Optional(Schema.String)\n }),\n failure: ToolFailure,\n success: Schema.Struct({\n path: MemoryPath,\n superseded: Schema.Array(MemoryPath),\n archived: Schema.Array(MemoryPath)\n })\n})\n\nconst MemoryLink = Tool.make(\"memory_link\", {\n description:\n \"Assert an edge between two memories. Written into the source file's head, so it survives an index rebuild. Idempotent: re-linking the same pair commits nothing.\",\n dependencies: [Store, Indexer],\n parameters: Schema.Struct({\n src_path: MemoryPath,\n rel: MemoryRelSchema,\n dst_path: MemoryPath,\n strength: Optional(Finite)\n }),\n failure: ToolFailure,\n success: Schema.Struct({\n ok: Schema.Boolean,\n rel: Schema.String,\n src_path: MemoryPath,\n dst_path: MemoryPath\n })\n})\n\nconst MemoryNeighbors = Tool.make(\"memory_neighbors\", {\n description:\n \"The memory graph around one path, to at most two hops, in both directions. Includes sleep-mined edges: lateral retrieval is what they are for.\",\n dependencies: READS(),\n parameters: Schema.Struct({\n path: MemoryPath,\n depth: Optional(Count),\n rels: Optional(Schema.Array(MemoryRelSchema))\n }),\n failure: ToolFailure,\n success: Schema.Struct({\n nodes: Schema.Array(\n Schema.Struct({\n path: MemoryPath,\n title: Schema.String,\n /** 1-based distance from the centre: 1 or 2, never 0. */\n hop: Count,\n rel: Schema.String\n })\n ),\n edges: Count\n })\n})\n\nconst MemoryArchive = Tool.make(\"memory_archive\", {\n description:\n \"Soft-evict a memory: `git mv` into archive/<YYYY>/ with the archive stamps. Nothing is ever deleted, and `git log --follow` reads straight through.\",\n dependencies: [Store, Indexer],\n parameters: Schema.Struct({\n path: MemoryPath,\n reason: Schema.String\n }),\n failure: ToolFailure,\n success: Schema.Struct({\n path: MemoryPath,\n archive_path: MemoryPath\n })\n})\n\nconst MemoryReinforce = Tool.make(\"memory_reinforce\", {\n description:\n \"Record that a memory helped or misled. Gated by a 900-second per-path cooldown, so a replayed query cannot inflate a memory's ranking; `cooled_down` lists the paths the cooldown held back.\",\n dependencies: READS(),\n parameters: Schema.Struct({\n paths: Schema.Array(MemoryPath),\n signal: Schema.Literals(REINFORCE_SIGNALS)\n }),\n failure: ToolFailure,\n success: Schema.Struct({\n bumped: Schema.Array(MemoryPath),\n cooled_down: Schema.Array(MemoryPath)\n })\n})\n\nconst MemoryList = Tool.make(\"memory_list\", {\n description:\n \"Page through the corpus by facet. `next_cursor` is a keyset on the path, so a page stays correct even while a sleep cycle archives files.\",\n dependencies: READS(),\n parameters: Schema.Struct({\n memory_type: Optional(WritableType),\n workspace: Optional(Schema.String),\n tag: Optional(Schema.String),\n entity: Optional(Schema.String),\n para: Optional(Schema.Literals(PARA_BUCKETS)),\n limit: Optional(Count),\n cursor: Optional(Schema.String)\n }),\n failure: ToolFailure,\n success: Schema.Struct({\n files: Schema.Array(\n Schema.Struct({\n path: MemoryPath,\n title: Schema.String,\n memory_type: Schema.String,\n gist: Schema.String,\n workspace: Schema.NullOr(Schema.String),\n para: Schema.String,\n confidence: Finite,\n importance: Count,\n archived: Schema.Boolean,\n updated_at: Schema.String\n })\n ),\n next_cursor: Schema.NullOr(Schema.String)\n })\n})\n\nconst TraceSearch = Tool.make(\"trace_search\", {\n description:\n \"Find past Claude Code sessions by what was asked in them. A read-only index over transcript files: no session content is stored, only pointers and capped heads.\",\n dependencies: READS(),\n parameters: Schema.Struct({\n query: Schema.String,\n cwd: Optional(Schema.String),\n since: Optional(Schema.String),\n limit: Optional(Count)\n }),\n failure: ToolFailure,\n success: Schema.Struct({\n sessions: Schema.Array(\n Schema.Struct({\n session_id: Schema.String,\n slug: Schema.String,\n cwd: Schema.NullOr(Schema.String),\n started_at: Schema.NullOr(Schema.String),\n prompt_count: Count,\n first_prompt: Schema.String,\n ai_title: Schema.NullOr(Schema.String)\n })\n )\n })\n})\n\nconst TraceLinks = Tool.make(\"trace_links\", {\n description:\n \"Which memories a session produced, or which sessions touched a memory. Needs a session_id or a path. Both absent is refused rather than returning every link ever recorded.\",\n dependencies: READS(),\n parameters: Schema.Struct({\n session_id: Optional(Schema.String),\n path: Optional(MemoryPath)\n }),\n failure: ToolFailure,\n success: Schema.Struct({\n links: Schema.Array(\n Schema.Struct({\n path: MemoryPath,\n session_id: Schema.String,\n prompt_id: Schema.NullOr(Schema.String),\n turn_uuid: Schema.NullOr(Schema.String),\n link_kind: Schema.String,\n at: Schema.String\n })\n )\n })\n})\n\nconst MemoryStatus = Tool.make(\"memory_status\", {\n description:\n \"Corpus health in one call: HEAD, dirty state, counts by type, edge totals, whether the index describes the current commit, and when sleep last ran.\",\n dependencies: [Store, DatabaseService],\n /**\n * `Tool.EmptyParams`, not `Schema.Struct({})`.\n *\n * Probed on effect 4.0.0-beta.102: an empty `Schema.Struct` derives\n * `{\"anyOf\":[{\"type\":\"object\"},{\"type\":\"array\"}]}`, a union with an ARRAY branch, because a struct\n * with no fields constrains nothing and the codec's encoded form admits both. A client reading that\n * cannot tell it should send `{}`, and a strict one may refuse to call the tool at all.\n * `Tool.EmptyParams` derives `{\"type\":\"object\",\"additionalProperties\":false}`, which says exactly\n * \"an object, and no fields\", the intent.\n */\n parameters: Tool.EmptyParams,\n failure: ToolFailure,\n success: Schema.Struct({\n head_sha: Schema.NullOr(Schema.String),\n dirty: Schema.Boolean,\n counts_by_type: Schema.Record(Schema.String, Count),\n archived_count: Count,\n edges: Count,\n /** True when the index's watermark IS the current HEAD. A row count cannot answer this. */\n index_fresh: Schema.Boolean,\n embedder_up: Schema.Boolean,\n last_sleep: Schema.NullOr(\n Schema.Struct({\n run_id: Schema.String,\n status: Schema.String,\n started_at: Schema.String\n })\n )\n })\n})\n\n/**\n * The toolkit. Exactly fourteen: design.md §8's thirteen plus `memory_write_batch`.\n *\n * Order is the read order of the table in §8, which is also roughly the order an agent needs them:\n * write and read, then the three retrieval shapes, then the graph operations, then the trace plane,\n * then status.\n *\n * The batch sits SECOND, directly after `memory_write`, rather than appended at the end. `tools/list`\n * publishes this order and an agent reads it top-down, so the tool `memory_write`'s own description\n * points at is the very next entry. A pointer whose target is thirteen tools away is one an agent\n * reads after it has already decided how to write.\n */\nexport const MemhtmlToolkit = Toolkit.make(\n MemoryWrite,\n MemoryWriteBatch,\n MemoryRead,\n MemorySearch,\n MemoryRecall,\n MemoryCorrect,\n MemoryLink,\n MemoryNeighbors,\n MemoryArchive,\n MemoryReinforce,\n MemoryList,\n TraceSearch,\n TraceLinks,\n MemoryStatus\n)\n\n/**\n * The tool names, derived from the toolkit rather than restated.\n *\n * Two lists would drift: a placeholder list that once said fourteen names and a toolkit that now\n * builds thirteen would leave a test asserting the list and proving nothing about the server.\n */\nexport const TOOL_NAMES = Object.keys(MemhtmlToolkit.tools) as ReadonlyArray<\n keyof typeof MemhtmlToolkit.tools\n>\n\nexport type ToolName = (typeof TOOL_NAMES)[number]\n","import {\n archiveMemory,\n type BatchOpReport,\n type BatchWriteResult,\n batchWrite,\n claimFromProse,\n codeFor,\n correctMemory,\n type EmbedderShape,\n type layerApp,\n linkMemories,\n listMemories,\n messageFor,\n neighborsOf,\n proseTail,\n readMemory,\n recallMemories,\n reinforceMemories,\n searchMemories,\n searchTraces,\n statusReport,\n traceLinks,\n type WriteParams,\n writeMemory\n} from \"@memhtml/cli\"\nimport { InvalidMemory } from \"@memhtml/contracts/errors\"\nimport type { MemoryDoc } from \"@memhtml/html\"\nimport { Effect, type Layer } from \"effect\"\n\nimport { batchAbortFailure, type ToolFailure, toToolFailure } from \"./failure.js\"\nimport { MemhtmlToolkit } from \"./tools.js\"\n\n/**\n * The handlers: decode → call the shared use case → shape the result. Nothing else.\n *\n * Every handler calls the SAME function the CLI command calls, which is what makes `memory_search`\n * and `memhtml search` provably one query rather than two that agree today. A handler that reached for a\n * repository directly would be a second implementation of the thing the operations module exists to\n * be the only copy of.\n *\n * Parameter names are snake_case because they are the MCP wire contract; the operations take\n * camelCase. That rename is the handlers' whole remaining job.\n */\n\n/** The services a handler reaches for: the app layer's own output. */\ntype AppServices = Layer.Success<ReturnType<typeof layerApp>>\n\n/**\n * Every handler's error translation, applied once.\n *\n * MCP has one error channel and it is prose, so a typed error's STRUCTURE cannot survive the\n * boundary. Everything a caller acts on can, folded into the one string the protocol carries:\n * `toToolFailure` composes the stable code, the reason with its actionable payload fields, and\n * suggestions phrased as tool calls this agent can make. Nothing leaks a driver message, a git argv,\n * or a memory body, because the reason is `messageFor`'s and each error class dropped those at its\n * adapter edge precisely so a tool response could not carry corpus content.\n *\n * This used to build an `AiError`, which was the bug. `McpServer` catches `AiError` FIRST and\n * rewrites it to a generic internal-error sentence unless its reason is a parameter-validation error\n * (`McpServer.ts:831-838`), so every typed failure this server produced reached its agent with the\n * content removed. A `ToolFailure` is what each tool's `failure:` schema declares, which puts it on the\n * branch that passes `.message` through verbatim. The two halves only work together: dropping the\n * declaration in `tools.ts` re-masks everything this function builds, and the wire test in\n * `tests-integration` is what holds that pair honest.\n *\n * The error type is now `ToolFailure` for every handler, and `kit.toLayer` checks it, so a handler\n * that failed with a raw domain error would be a compile error rather than a masked response. This is\n * the single place the wire failure is produced.\n */\nconst handled = <A, E, R>(effect: Effect.Effect<A, E, R>): Effect.Effect<A, ToolFailure, R> =>\n effect.pipe(Effect.mapError(toToolFailure))\n\n/**\n * The head metadata as a flat string record.\n *\n * Flattened rather than typed per key: the wire schema is `Record<string, string>` because the head's\n * optional metas are genuinely open at the edges. A format version can add `memhtml-*` names, and a\n * client that had to know the closed set would break on the first addition. Numbers are stringified\n * because that is what the `<meta content>` attribute holds; a consumer that wants the number reads\n * the typed field on `memory_search` or `memory_list` instead.\n */\nconst metaRecord = (doc: MemoryDoc): Readonly<Record<string, string>> => {\n const out: Record<string, string> = {}\n for (const [key, value] of Object.entries(doc.metas)) {\n if (value === undefined) continue\n out[key] = typeof value === \"string\" ? value : String(value)\n }\n for (const entity of doc.entities) out[`entity:${entity}`] = \"true\"\n for (const tag of doc.tags) out[`tag:${tag}`] = \"true\"\n return out\n}\n\n/**\n * An explicit `null` as an absent value.\n *\n * The parameter schemas accept `null` as well as absence, and `Optional` in `tools.ts` says why: the\n * derived JSON Schema advertises `null`, and a client that reads the schema and sends\n * `{\"workspace\": null}` for \"no workspace\" is doing the documented thing. The operations layer speaks\n * `undefined` for \"not supplied\" because `exactOptionalPropertyTypes` distinguishes an absent key from\n * a present one, so the two vocabularies meet HERE, once, rather than at each of fourteen call sites.\n */\nconst opt = <A>(value: A | null | undefined): A | undefined => value ?? undefined\n\n/** Absent optional array as an empty one, so a handler never passes `undefined` downstream. */\nconst arr = <A>(value: ReadonlyArray<A> | null | undefined): ReadonlyArray<A> => value ?? []\n\n/**\n * The article a write authored, from whichever of the two parameters arrived, or else a refusal.\n *\n * `body` and `article_html` are two ways to author ONE article, so exactly one of them is the whole\n * rule, and it is enforced here rather than in the schema on purpose. A `Schema.Union` of two structs\n * derives a JSON Schema `anyOf` over the FULL parameter object twice, so a client reading\n * `tools/list` sees two near-identical thirteen-field shapes, and a decode failure against a union\n * names neither branch's actual problem. A runtime refusal costs the round trip a bad call already\n * deserved and spends it on prose that states the rule.\n *\n * Both-supplied is refused rather than resolved by precedence, the tempting shortcut. A\n * caller that sent both meant one of them, and silently rendering the other writes a memory whose\n * content the caller did not choose, into a git commit, indexed, retrievable. Neither-supplied is\n * refused for the same reason it cannot be defaulted: an article with no claim has no `<mark>`, so\n * `files.gist` would be empty on every disclosure tier.\n *\n * A blank string counts as absent, on both sides. Template-driven clients do fill unset fields with\n * `\"\"`, and such a call would otherwise read as \"supplied both\" when it supplied one.\n *\n * On the markup path `claim` is `\"\"` and `body` is empty, and neither derivation runs: the template\n * uses `articleHtml` verbatim, so a claim derived from prose that does not exist would be a second,\n * invisible authoring decision. The `<mark>` inside the markup IS the claim, and the parser extracts\n * it into `files.gist` on the first index pass. Markup whose `<mark>` is missing OR EMPTY is the\n * STORE's refusal (`packages/store/src/store.ts`'s render gate, over `@memhtml/html` constraint 1)\n * rather than this function's. The XOR is the only rule the wire boundary owns.\n *\n * The prose path derives through `claimFromProse`/`proseTail`, imported from `@memhtml/cli` so this door\n * and `memhtml apply` split prose the same way. A second copy here would let the same body produce\n * different gists depending on which door wrote it.\n */\ninterface Authored {\n readonly claim: string\n readonly body: ReadonlyArray<string>\n readonly articleHtml: string | undefined\n}\n\nconst authored = (\n body: string | null | undefined,\n articleHtml: string | null | undefined\n): Effect.Effect<Authored, InvalidMemory> => {\n const prose = opt(body)\n const markup = opt(articleHtml)\n const hasProse = prose !== undefined && prose.trim() !== \"\"\n const hasMarkup = markup !== undefined && markup.trim() !== \"\"\n if (hasProse === hasMarkup) {\n return Effect.fail(\n InvalidMemory.make({\n reason: `exactly one of body or article_html is required, and ${hasProse ? \"both were supplied\" : \"neither was supplied\"}`\n })\n )\n }\n return Effect.succeed(\n hasMarkup\n ? { claim: \"\", body: [], articleHtml: markup }\n : {\n claim: claimFromProse(prose as string),\n body: proseTail(prose as string),\n articleHtml: undefined\n }\n )\n}\n\n/** One `memory_write_batch` op as it arrives on the wire, before the XOR has been resolved. */\ninterface BatchOpParams {\n readonly title: string\n readonly body?: string | null | undefined\n readonly article_html?: string | null | undefined\n readonly memory_type: string\n readonly path?: string | null | undefined\n readonly workspace?: string | null | undefined\n readonly tags?: ReadonlyArray<string> | null | undefined\n readonly entities?: ReadonlyArray<string> | null | undefined\n readonly importance?: number | null | undefined\n readonly confidence?: number | null | undefined\n readonly session_id?: string | null | undefined\n readonly prompt_id?: string | null | undefined\n readonly turn_uuid?: string | null | undefined\n}\n\n/**\n * One op's wire-name-to-operation-name rename, given the article its XOR already resolved to.\n *\n * The same rename `memory_write`'s handler performs, over the same field list. The ops carry a whole\n * `memory_write` payload (D7), so a second spelling of this mapping would be the drift the shared\n * `writeFields` in `tools.ts` exists to make impossible on the schema side.\n */\nconst writeParamsOf = (op: BatchOpParams, article: Authored): WriteParams => ({\n title: op.title,\n claim: article.claim,\n body: article.body,\n articleHtml: article.articleHtml,\n memoryType: op.memory_type,\n path: opt(op.path),\n workspace: opt(op.workspace),\n tags: arr(op.tags),\n entities: arr(op.entities),\n importance: opt(op.importance),\n confidence: opt(op.confidence),\n sessionId: opt(op.session_id),\n promptId: opt(op.prompt_id),\n turnUuid: opt(op.turn_uuid)\n})\n\n/**\n * An op's XOR refusal as that op's own report, through the SAME `codeFor`/`messageFor` pair\n * `operations.ts`'s `reportFailure` uses.\n *\n * Not a second mapping of the error: a per-op `code` is part of the batch payload's contract, and\n * `memhtml apply` and `memory_write_batch` reporting different codes for one refused op is exactly the\n * drift the shared-use-case rule exists to prevent. The XOR is the one refusal the operations layer\n * cannot produce, being a wire-vocabulary rule about two parameters that layer never sees, since\n * `WriteParams` takes an already-resolved `claim`/`body`/`articleHtml`. So this is the one place\n * a report is built outside `batchWrite`, and it is built with `batchWrite`'s own functions.\n */\nconst xorReport = (index: number, error: InvalidMemory): BatchOpReport => ({\n index,\n ok: false,\n code: codeFor(error),\n error: messageFor(error)\n})\n\n/**\n * The first op that FAILED, as opposed to one that was skipped or deduped.\n *\n * `batchWrite`'s atomic abort reports the offending op with its code and every other op as `skipped`\n * (`operations.ts:545-548` for a decode refusal, `store.ts:693-705` for a render-gate one), so an\n * aborted batch is recognizable by exactly this: one report with `ok: false` and `skipped` unset. A\n * check on `summary.skipped > 0` alone would also match a batch that had nothing to abort.\n */\nconst firstFailure = (reports: ReadonlyArray<BatchOpReport>): BatchOpReport | undefined =>\n reports.find((report) => !report.ok && report.skipped !== true && report.code !== undefined)\n\n/** One op's report as the wire shape: every field present, absent ones as `null`. */\nconst wireReport = (report: BatchOpReport) => ({\n index: report.index,\n ok: report.ok,\n path: report.path ?? null,\n deduped: report.deduped === true,\n existing_path: report.existingPath ?? null,\n code: report.code ?? null,\n error: report.error ?? null,\n skipped: report.skipped === true,\n /**\n * The conflict assist's finding, `batchIndex` renamed to `batch_index`. That rename is the whole of\n * the handlers' remaining job, applied one level deeper than usual because this is the first nested\n * struct on the batch's wire shape. `memhtml apply`'s own `opPayload` performs the same rename onto\n * the same names, so the two doors' payloads stay byte-comparable.\n *\n * A conflict says nothing about `ok`, `path`, or `skipped`, and this function is where that is\n * visible: nothing above changes when the field is populated.\n */\n conflict:\n report.conflict === undefined\n ? null\n : {\n path: report.conflict.path,\n batch_index: report.conflict.batchIndex,\n claim: report.conflict.claim\n },\n consolidated_into: report.consolidatedInto ?? null,\n superseded_path: report.supersededPath ?? null\n})\n\n/**\n * The batch's counts, over the merged report array.\n *\n * The same one-pass derivation `operations.ts`'s `summarize` performs, and it has to be re-derived here\n * rather than taken from `batchWrite` for one reason: in continue mode this handler's own XOR refusals\n * are reports `batchWrite` never saw, so its summary describes a SHORTER op list. Taking it would\n * publish `total` less than `results.length`, a summary a client cannot reconcile with the array it\n * came with. On the atomic path there are no such refusals and this returns `batchWrite`'s own numbers.\n */\nconst summarize = (\n results: ReadonlyArray<BatchOpReport>\n): BatchWriteResult[\"summary\"] & { readonly total: number } => {\n let written = 0\n let deduped = 0\n let failed = 0\n let skipped = 0\n let consolidated = 0\n for (const result of results) {\n // The same partition `operations.ts` makes: a batch-internal loser's value survived at\n // another slot and no file of its own was attempted, so it is neither written nor failed.\n if (result.consolidatedInto !== undefined) consolidated += 1\n else if (result.skipped === true) skipped += 1\n else if (!result.ok) failed += 1\n else if (result.deduped === true) deduped += 1\n else written += 1\n }\n return { total: results.length, written, deduped, failed, skipped, consolidated }\n}\n\n/**\n * The handler layer for the toolkit.\n *\n * `kit.toLayer({ … })` is checked against the toolkit's own parameter and success schemas, so a\n * handler returning the wrong shape is a compile error rather than a decode failure on a live call.\n */\nexport const ToolHandlers: Layer.Layer<\n Layer.Success<ReturnType<typeof MemhtmlToolkit.toLayer>>,\n never,\n AppServices\n> = MemhtmlToolkit.toLayer({\n memory_write: (params) =>\n handled(\n Effect.gen(function* () {\n const article = yield* authored(params.body, params.article_html)\n const result = yield* writeMemory({\n title: params.title,\n claim: article.claim,\n body: article.body,\n articleHtml: article.articleHtml,\n memoryType: params.memory_type,\n path: opt(params.path),\n workspace: opt(params.workspace),\n tags: arr(params.tags),\n entities: arr(params.entities),\n importance: opt(params.importance),\n confidence: opt(params.confidence),\n sessionId: opt(params.session_id),\n promptId: opt(params.prompt_id),\n turnUuid: opt(params.turn_uuid)\n })\n return {\n path: result.path,\n created: result.created,\n deduped: result.deduped,\n existing_path: result.existingPath ?? null\n }\n })\n ),\n\n /**\n * The batch: resolve every op's XOR, call `batchWrite` ONCE, report every op in input order.\n *\n * **The XOR runs per op, up front, before `batchWrite` is called at all.** It is the wire boundary's\n * only rule and it is a rule about two PARAMETERS. `WriteParams` takes an already-resolved\n * `claim`/`body`/`articleHtml`, so an op that supplied both is a call the operations layer has no way\n * to recognize. Resolving it here also means the store's phase-1 validation sees only ops that could\n * possibly be written, which is what keeps \"the atomic abort happens before any file exists\" true of\n * the XOR too.\n *\n * **Then the modes diverge, and each one matches `batchWrite`'s own semantics for the failure class\n * it already handles**, a malformed `memory_type`, which is likewise a per-op decode refusal:\n *\n * - CONTINUE: each XOR refusal becomes that op's failed report, ONLY the survivors go to\n * `batchWrite`, and the survivors' reports are spliced back at their ORIGINAL indices. `originOf`\n * is what makes that possible: `batchWrite` indexes results in the array it was handed, so a\n * survivor at position 0 of a two-op call may be op 0 or op 1 of a three-op one, and reporting its\n * own index would shift every later op by the number of refusals before it. The result is a\n * SUCCESS: every op is present in `results`, in input order, which is the contract D3 states and\n * the only shape an agent can index by.\n * - ATOMIC (the default): the first refused op aborts, and the abort reaches the agent through the\n * ERROR channel as `batchAbortFailure`. An XOR refusal short-circuits before `batchWrite` is\n * called at all, since an atomic batch with a refused op writes nothing by definition and the call\n * would be a round trip whose only outcome is the abort. A refusal `batchWrite` itself produced,\n * such as a malformed `memory_type` or an op the store's render gate refused, comes back as an\n * aborted RESULT, and is converted at the same seam.\n *\n * **That conversion is the one non-obvious thing here, and it was a real bug caught by a test.** An\n * aborted `batchWrite` returns a well-formed result: every op reported, one of them failed, the rest\n * `skipped`, `commitSha: null`. Returning it verbatim is a SUCCESS response for a call that wrote\n * nothing, so the XOR path (an error) and the render-gate path (a success) would be two channels for\n * one outcome, and `BATCH_GUIDANCE`'s promise that \"the first refused op aborts the whole call … and\n * the failure names the offending op\" would be false for every refusal the handler did not itself\n * detect. `firstFailure` finds the offending op in the returned reports and `batchAbortFailure`\n * composes the one message both paths use.\n */\n memory_write_batch: (params) =>\n handled(\n Effect.gen(function* () {\n const continueOnError = params.continue_on_error === true\n const reports: Array<BatchOpReport | undefined> = params.ops.map(() => undefined)\n const survivors: Array<WriteParams> = []\n /** Survivor position in what `batchWrite` was handed → this caller's own op index. */\n const originOf: Array<number> = []\n\n for (const [index, op] of params.ops.entries()) {\n const article = yield* Effect.result(authored(op.body, op.article_html))\n if (article._tag === \"Failure\") {\n const report = xorReport(index, article.failure)\n /**\n * Composed rather than `Effect.fail(article.failure)`, which would reach the agent as the\n * singular's own message: it names the rule but not WHICH of twenty ops broke it, and says\n * nothing about `continue_on_error`.\n */\n if (!continueOnError) {\n return yield* Effect.fail(\n batchAbortFailure(index, report.code ?? \"ERR_INVALID_MEMORY\", report.error ?? \"\")\n )\n }\n reports[index] = report\n continue\n }\n originOf.push(index)\n survivors.push(writeParamsOf(op, article.success))\n }\n\n const batch = yield* batchWrite({\n ops: survivors,\n continueOnError,\n /**\n * The flag reaches `batchWrite` unchanged, which is the only correct place for the assist to\n * live: `memhtml apply --detect-conflicts` gets the same findings from the same code, so the two\n * doors cannot disagree about what a conflict is.\n *\n * The survivors-only consequence is real and it is right. An op this handler already refused\n * for the XOR is not in `survivors`, so it gets no conflict report, and it also has no claim\n * to derive one FROM: on the both-supplied path there is no way to tell which of the two the\n * caller meant, and on the neither-supplied path there is no claim at all. A finding invented\n * for such an op would name a slot the caller never asserted.\n */\n detectConflicts: params.detect_conflicts === true,\n // Threaded unchanged for the same reason the flag above is: `memhtml apply --consolidate`\n // resolves the same slots from the same code, so the two doors cannot disagree about\n // which value won.\n ...(params.consolidate !== undefined && params.consolidate !== null\n ? { consolidate: params.consolidate }\n : {}),\n sessionId: opt(params.session_id),\n promptId: opt(params.prompt_id),\n turnUuid: opt(params.turn_uuid)\n })\n\n // An atomic batch `batchWrite` aborted: one op failed, so the whole call failed, and it reaches\n // the agent through the same error channel and the same message as the XOR refusal above.\n if (!continueOnError) {\n const failed = firstFailure(batch.results)\n if (failed !== undefined) {\n return yield* Effect.fail(\n batchAbortFailure(\n originOf[failed.index] ?? failed.index,\n failed.code ?? \"ERR_INVALID_MEMORY\",\n failed.error ?? \"\"\n )\n )\n }\n }\n\n /**\n * Splice each survivor's report back at its ORIGINAL index, and translate the conflict's\n * `batchIndex` through the SAME map, which is the non-obvious half.\n *\n * `batchWrite` saw only `survivors`, so an intra-batch conflict it found names a position in\n * THAT array. In continue mode with an XOR-refused op before the conflicting pair, survivor 1\n * is the caller's op 2, so reporting the raw number would name a different op than the one the\n * assist actually matched, and it would name it plausibly enough that nobody would notice. The\n * outer `index` has always needed this translation for exactly the same reason; the conflict is\n * a second index in the same space and needs it too.\n *\n * `originOf[…] ?? conflict.batchIndex` mirrors the fallback three lines above rather than\n * dropping the conflict: an untranslatable index is impossible here (every survivor has an\n * origin, by construction of the loop that built both arrays), and if it somehow were not, a\n * caller is better served by a suspicious number than by a finding silently deleted.\n */\n for (const report of batch.results) {\n const index = originOf[report.index]\n if (index === undefined) continue\n const conflict = report.conflict\n const translated =\n conflict === undefined || conflict.batchIndex === null\n ? { ...report, index }\n : {\n ...report,\n index,\n conflict: {\n ...conflict,\n batchIndex: originOf[conflict.batchIndex] ?? conflict.batchIndex\n }\n }\n // `consolidatedInto` is a second index in `batchWrite`'s survivor space and takes the\n // same translation the conflict's `batchIndex` does, for the same reason: an XOR-refused\n // op before the consolidated pair would otherwise make the pointer name the wrong op.\n reports[index] =\n translated.consolidatedInto === undefined\n ? translated\n : {\n ...translated,\n consolidatedInto:\n originOf[translated.consolidatedInto] ?? translated.consolidatedInto\n }\n }\n\n /**\n * An op with no report of its own was never reached. Unreachable on the atomic path, which has\n * already failed by here, so this is continue mode's own case: `skipped`, the same word\n * `batchWrite` uses, so the two doors describe one outcome in one vocabulary.\n */\n const results = reports.map(\n (report, index) => report ?? ({ index, ok: false, skipped: true } satisfies BatchOpReport)\n )\n return {\n results: results.map(wireReport),\n summary: summarize(results),\n commit_sha: batch.commitSha\n }\n })\n ),\n\n memory_read: (params) =>\n handled(\n Effect.gen(function* () {\n const result = yield* readMemory(params.path, { sessionId: opt(params.session_id) })\n return {\n path: result.path,\n title: result.doc.title,\n body: result.doc.article.bodyText,\n gist: result.doc.article.gist,\n memory_type: result.doc.metas.memoryType,\n meta: metaRecord(result.doc),\n links: result.doc.links.map((link) => ({ rel: link.rel, href: link.href })),\n archived: result.doc.metas.status === \"archived\",\n warnings: result.doc.warnings\n }\n })\n ),\n\n memory_search: (params) =>\n handled(\n Effect.gen(function* () {\n const result = yield* searchMemories({\n query: params.query,\n limit: opt(params.limit),\n memoryTypes: opt(params.memory_types),\n workspace: opt(params.workspace),\n tags: opt(params.tags),\n entity: opt(params.entity),\n includeArchived: opt(params.include_archived),\n asOf: opt(params.as_of)\n })\n return {\n hits: result.hits.map((hit) => ({\n path: hit.path,\n title: hit.title,\n gist: hit.gist,\n memory_type: hit.memoryType,\n score: hit.score,\n confidence: hit.confidence,\n updated_at: hit.updatedAt,\n snippet: hit.snippet,\n entities: hit.entities,\n superseded_by: hit.supersededBy\n })),\n degraded: result.degraded,\n arms: result.arms,\n entity_scope: result.entityScope,\n scope_empty: result.scopeEmpty\n }\n })\n ),\n\n memory_recall: (params) =>\n handled(\n Effect.gen(function* () {\n const pack = yield* recallMemories({\n query: params.query,\n budgetChars: opt(params.budget_chars),\n workspace: opt(params.workspace)\n })\n /**\n * `lateral` is the union of both folds' index lines.\n *\n * It holds what did not fit the budget, surfaced with its claim and its path so an agent\n * can drill down deliberately, and it is not a third retrieval arm. Dropping it would make\n * a truncated pack indistinguishable from a small corpus.\n */\n return {\n sections: {\n arcs: pack.arcs.disclosed.map((entry) => ({\n path: entry.path,\n title: entry.title,\n gist: entry.gist,\n body: entry.body\n })),\n memories: pack.memories.disclosed.map((entry) => ({\n path: entry.path,\n title: entry.title,\n gist: entry.gist,\n body: entry.body\n })),\n lateral: [...pack.arcs.indexLines, ...pack.memories.indexLines].map((line) => ({\n path: line.path,\n title: line.title,\n gist: line.gist\n }))\n },\n spent_chars: pack.spentChars,\n truncated: pack.truncated,\n degraded: pack.degraded\n }\n })\n ),\n\n memory_correct: (params) =>\n handled(\n Effect.gen(function* () {\n const article = yield* authored(params.body, params.article_html)\n const result = yield* correctMemory({\n targetPath: params.target_path,\n title: params.title,\n claim: article.claim,\n body: article.body,\n articleHtml: article.articleHtml,\n reason: params.reason,\n sessionId: opt(params.session_id)\n })\n /**\n * `superseded` names the target's ARCHIVE path, which is where the file is once the commit\n * lands, and it is what the new file's `memhtml-supersedes` link points at. Reporting the\n * pre-archive path would hand back a path with no file behind it.\n */\n return {\n path: result.path,\n superseded: [result.archivedPath],\n archived: [result.archivedPath]\n }\n })\n ),\n\n memory_link: (params) =>\n handled(\n Effect.gen(function* () {\n const result = yield* linkMemories(params.src_path, params.rel, params.dst_path)\n return {\n // True whether or not this call was the one that wrote the link: the edge exists either\n // way, and `addLink` is idempotent on the pair. A false here would make a re-link look\n // like a failure.\n ok: true,\n rel: result.rel,\n src_path: result.srcPath,\n dst_path: result.dstPath\n }\n })\n ),\n\n memory_neighbors: (params) =>\n handled(\n Effect.gen(function* () {\n const result = yield* neighborsOf({\n path: params.path,\n depth: opt(params.depth),\n rels: opt(params.rels)\n })\n return { nodes: result.nodes, edges: result.edges }\n })\n ),\n\n memory_archive: (params) =>\n handled(\n Effect.gen(function* () {\n const result = yield* archiveMemory(params.path, params.reason)\n return { path: result.path, archive_path: result.archivePath }\n })\n ),\n\n memory_reinforce: (params) =>\n handled(\n Effect.gen(function* () {\n const result = yield* reinforceMemories(params.paths, params.signal)\n return { bumped: result.bumped, cooled_down: result.cooledDown }\n })\n ),\n\n memory_list: (params) =>\n handled(\n Effect.gen(function* () {\n const result = yield* listMemories({\n memoryType: opt(params.memory_type),\n workspace: opt(params.workspace),\n tag: opt(params.tag),\n entity: opt(params.entity),\n para: opt(params.para),\n limit: opt(params.limit),\n cursor: opt(params.cursor)\n })\n return {\n files: result.files.map((file) => ({\n path: file.path,\n title: file.title,\n memory_type: file.memoryType,\n gist: file.gist,\n workspace: file.workspace,\n para: file.para,\n confidence: file.confidence,\n importance: file.importance,\n archived: file.archived,\n updated_at: file.updatedAt\n })),\n next_cursor: result.nextCursor\n }\n })\n ),\n\n trace_search: (params) =>\n handled(\n Effect.gen(function* () {\n const result = yield* searchTraces({\n query: params.query,\n cwd: opt(params.cwd),\n since: opt(params.since),\n limit: opt(params.limit)\n })\n return {\n sessions: result.sessions.map((session) => ({\n session_id: session.sessionId,\n slug: session.slug,\n cwd: session.cwd,\n started_at: session.startedAt,\n prompt_count: session.promptCount,\n first_prompt: session.firstPrompt,\n ai_title: session.aiTitle\n }))\n }\n })\n ),\n\n trace_links: (params) =>\n handled(\n Effect.gen(function* () {\n const result = yield* traceLinks({\n sessionId: opt(params.session_id),\n path: opt(params.path)\n })\n return {\n links: result.links.map((link) => ({\n path: link.path,\n session_id: link.sessionId,\n prompt_id: link.promptId,\n turn_uuid: link.turnUuid,\n link_kind: link.linkKind,\n at: link.at\n }))\n }\n })\n ),\n\n memory_status: () =>\n handled(\n Effect.gen(function* () {\n const report = yield* statusReport()\n return {\n head_sha: report.headSha,\n dirty: report.dirty,\n counts_by_type: report.countsByType,\n archived_count: report.archivedCount,\n edges: report.edges,\n index_fresh: report.indexFresh,\n embedder_up: report.embedderUp,\n last_sleep:\n report.lastSleep === null\n ? null\n : {\n run_id: report.lastSleep.runId,\n status: report.lastSleep.status,\n started_at: report.lastSleep.startedAt\n }\n }\n })\n )\n})\n\n/** Re-exported so a caller wiring a test layer names the same type the handlers require. */\nexport type { AppServices, EmbedderShape }\n","import { readFile } from \"node:fs/promises\"\nimport { join } from \"node:path\"\n\nimport { Roots, readMemory } from \"@memhtml/cli\"\nimport { SLEEP_REPORTS_DIR } from \"@memhtml/store\"\nimport { Effect, Layer, Schema } from \"effect\"\nimport { McpSchema, McpServer } from \"effect/unstable/ai\"\n\n/**\n * The two resources, design.md §8.\n *\n * A resource is for CITATION-grade drill-down: a client that got a path from `memory_search` can\n * fetch `memhtml://file/<path>` and show a human the file behind an answer, without spending a tool call\n * and without the tool response having had to carry the whole body.\n *\n * `McpSchema.param` names each template parameter, so `tools/list`'s sibling `resources/templates`\n * publishes `{path}` and `{run-id}` as named rather than positional holes.\n */\n\n/** `memhtml://file/{path}`: one memory's rendered content. */\nconst pathParam = McpSchema.param(\"path\", Schema.String)\n\n/** `memhtml://sleep/{run-id}`: one sleep run's committed HTML report. */\nconst runIdParam = McpSchema.param(\"run-id\", Schema.String)\n\n/**\n * A memory file, by path.\n *\n * The BODY is returned, not the raw HTML file. A client asking a resource for a citation wants the\n * text a human reads; the markup is the storage format, and handing back a full document with a head\n * full of `memhtml-*` metas would spend a client's rendering budget on bookkeeping. The metadata is\n * available through `memory_read`, which is the tool for exactly that.\n *\n * A missing path fails the read rather than answering with an empty resource: a citation that\n * silently resolves to nothing is worse than one that says the file is gone.\n *\n * This read BUMPS salience, through the same `readMemory` the `memory_read` tool calls. The bump is\n * deliberate: the caller named one specific path, which is a chosen open. A client\n * fetching the file behind an answer is making the same statement an agent makes with `memory_read`,\n * and the plane should not be able to tell them apart.\n */\nexport const FileResource = McpServer.resource`memhtml://file/${pathParam}`({\n name: \"Memory file\",\n description:\n \"One memory's title, claim, and body text, by repo-root-relative path. For showing a human the file behind an answer.\",\n mimeType: \"text/plain\",\n content: (_uri, path) =>\n Effect.gen(function* () {\n const result = yield* readMemory(path)\n return [\n `# ${result.doc.title}`,\n \"\",\n result.doc.article.gist,\n \"\",\n result.doc.article.bodyText\n ].join(\"\\n\")\n }).pipe(Effect.orDie)\n})\n\n/**\n * A sleep run's report, by run id.\n *\n * The report is a COMMITTED file under `.memhtml/sleep/`, so this resource reads the tree rather than the\n * database: the report is the durable artifact of a run and the `sleep_runs` row is reporting\n * convenience. A run id arrives as `sleep/2026-08-02`, and the file is named for its last segment.\n */\nexport const SleepResource = McpServer.resource`memhtml://sleep/${runIdParam}`({\n name: \"Sleep run report\",\n description:\n \"One sleep run's committed HTML report: per-phase counts, commits, and what the run changed.\",\n mimeType: \"text/html\",\n content: (_uri, runId) =>\n Effect.gen(function* () {\n const roots = yield* Roots\n // The last segment only: a run id is `sleep/<date>` and the file is `<date>.html`, so joining\n // the whole id would look for `.memhtml/sleep/sleep/<date>.html`.\n const name = runId.split(\"/\").at(-1) ?? runId\n const path = join(roots.memhtmlRoot, SLEEP_REPORTS_DIR, `${name}.html`)\n return yield* Effect.tryPromise({\n try: () => readFile(path, \"utf8\"),\n catch: (cause) => cause\n })\n }).pipe(Effect.orDie)\n})\n\n/** Both resources as one layer, for the server to provide. */\nexport const Resources = Layer.mergeAll(FileResource, SleepResource)\n\n/** The templates, for a test to assert the surface without a handshake. */\nexport const RESOURCE_TEMPLATES = [\"memhtml://file/{path}\", \"memhtml://sleep/{run-id}\"] as const\n","import { layerApp } from \"@memhtml/cli\"\nimport { Layer, Logger } from \"effect\"\nimport { McpProtocol, McpServer } from \"effect/unstable/ai\"\n\nimport { ToolHandlers } from \"./handlers.js\"\nimport { Resources } from \"./resources.js\"\nimport { MemhtmlToolkit } from \"./tools.js\"\n\nexport const SERVER_NAME = \"memhtml\"\nexport const SERVER_VERSION = \"0.2.2\" // x-release-please-version\n\n/**\n * The server as one layer: fourteen tools, two resources, over the CLI's own `AppLive`.\n *\n * The same composition the CLI builds, deliberately. An MCP server with its own layer graph would be\n * a second set of answers to which database file, which git root, and which vector space. An agent\n * whose `memory_write` landed in one repo while its operator's `memhtml search` read another would\n * be very hard to diagnose from either side.\n *\n * **`Logger.LogToStderr` is required here.** Effect's default logger writes to stdout, and stdout\n * on this transport is the NDJSON-RPC stream, so one log line would corrupt the frame a client is\n * mid-parse on. The CLI sets the same reference for the same reason, one fd over.\n *\n * **There is no server-level `instructions` here, because effect provides no way to set one.** MCP\n * defines an `instructions` field on the initialize response for exactly the cross-tool guidance\n * this server wants to give (when to batch, the three doors, the commit duty), and effect does not\n * emit it. Verified against 4.0.0-beta.107 in the dependency's own declarations: `McpSchema`\n * DECLARES `instructions: optional(Schema.String)` on the initialize result, while `layerStdio`'s\n * options are `{name, version, protocols, extensions}`, so there is not even an argument to pass,\n * and the handler that builds the result supplies none.\n *\n * **TOOL DESCRIPTIONS are this server's only guidance channel.** That consequence is recorded here,\n * beside the field a maintainer would come looking for, rather than in a doc. It is why\n * `BATCH_GUIDANCE` and `ARTICLE_HTML_CONTRACT` in `tools.ts` are shared constants appended to every\n * description they apply to, and why they read as prose to an agent rather than as reference notes to a\n * maintainer. Do not patch the dependency; revisit this when effect wires the field, at which point the\n * duplicated prose can move up here.\n */\nexport const layerServer = (repoOverride?: string | undefined) =>\n Layer.mergeAll(McpServer.toolkit(MemhtmlToolkit), Resources).pipe(\n Layer.provide(ToolHandlers),\n Layer.provide(\n McpServer.layerStdio({\n name: SERVER_NAME,\n version: SERVER_VERSION,\n // The protocol revision is now the caller's to state, and `v2025_06_18` is the only adapter\n // this dependency ships. Naming it here means a future revision is an explicit, reviewable\n // choice rather than a default that moves the wire format under a shipped client.\n protocols: [McpProtocol.v2025_06_18]\n })\n ),\n Layer.provide(layerApp(repoOverride)),\n Layer.provide(Layer.succeed(Logger.LogToStderr)(true))\n )\n","#!/usr/bin/env node\nimport { NodeRuntime, NodeStdio } from \"@effect/platform-node\"\nimport { Layer } from \"effect\"\n\nimport { layerServer } from \"./server.js\"\n\n/**\n * The stdio entry point. stdout belongs to the MCP framing from here on: every log in the graph is\n * already routed to stderr by `layerServer`, and nothing in this file writes.\n *\n * `Layer.launch` runs the server for the process's lifetime rather than building the layer and\n * returning. The transport IS the program, and a built-then-released layer would close stdin out\n * from under the client mid-session.\n */\nLayer.launch(layerServer().pipe(Layer.provide(NodeStdio.layer))).pipe(NodeRuntime.runMain)\n"],"mappings":"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EACN,MAAM;EACN,aAAa;EACb,QAAQ;EACR,YAAY;CAChB;CACA;EACI,MAAM;EACN,MAAM;EACN,aAAa;CACjB;CACA;EACI,MAAM;EACN,MAAM;EACN,aAAa;EACb,YAAY;CAChB;CACA;EACI,MAAM;EACN,MAAM;EAIN,aAAa;CACjB;CACA;EACI,MAAM;EACN,MAAM;EACN,aAAa;EACb,SAAS;CACb;CACA;EACI,MAAM;EACN,MAAM;EACN,aAAa;CACjB;AACJ;;;;;;;;;;;AAWA,MAAa,WAAW;CACpB;EACI,MAAM;EACN,SAAS;EACT,MAAM,CAAC;EACP,OAAO,CAAC;EACR,eAAe,CAAC,cAAc;CAClC;CACA;EACI,MAAM;EACN,SAAS;EACT,MAAM,CAAC;EACP,OAAO,CAAC;EACR,eAAe,CAAC,WAAW;CAC/B;CACA;EACI,MAAM;EACN,SAAS;EACT,MAAM,CAAC;EACP,OAAO;GACH;IACI,MAAM;IACN,MAAM;IACN,aAAa;IACb,UAAU;GACd;GACA;IACI,MAAM;IACN,MAAM;IACN,aAAa;GACjB;GACA;IACI,MAAM;IACN,MAAM;IACN,aAAa;IACb,YAAY;GAChB;GACA;IACI,MAAM;IACN,MAAM;IACN,aAAa;GACjB;GACA;IACI,MAAM;IACN,MAAM;IACN,aAAa;IACb,QAAQ;IACR,UAAU;GACd;GACA;IACI,MAAM;IACN,MAAM;IACN,aAAa;GACjB;GACA;IAAE,MAAM;IAAa,MAAM;IAAU,aAAa;GAAyC;GAC3F;IACI,MAAM;IACN,MAAM;IACN,aAAa;GACjB;GACA;IACI,MAAM;IACN,MAAM;IACN,aAAa;IACb,YAAY;GAChB;GACA;IACI,MAAM;IACN,MAAM;IACN,aAAa;GACjB;GACA;IAAE,MAAM;IAAc,MAAM;IAAU,aAAa;GAAwC;GAC3F;IACI,MAAM;IACN,MAAM;IACN,aAAa;GACjB;GACA;IAAE,MAAM;IAAa,MAAM;IAAU,aAAa;GAAkC;GACpF;IAAE,MAAM;IAAa,MAAM;IAAU,aAAa;GAAgC;EACtF;EACA,eAAe,CAAC,gBAAgB;CACpC;CACA;EACI,MAAM;EACN,SAAS;EAC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"}
1
+ {"version":3,"file":"memhtml-mcp.mjs","names":["isTagged","text","nowSecond","summarize"],"sources":["../../apps/cli/dist/serve.js","../../apps/cli/dist/config.js","../../apps/cli/dist/extraction.js","../../apps/cli/dist/api-layer.js","../../apps/cli/dist/errors.js","../../apps/cli/dist/operations.js","../../apps/cli/dist/commands.js","../../apps/cli/dist/prose.js","../../apps/cli/dist/apply.js","../../apps/cli/dist/doctor.js","../../apps/cli/dist/run.js","../../apps/mcp/src/failure.ts","../../apps/mcp/src/tools.ts","../../apps/mcp/src/handlers.ts","../../apps/mcp/src/resources.ts","../../apps/mcp/src/server.ts","../../apps/mcp/src/bin.ts"],"sourcesContent":["import { spawn } from \"node:child_process\";\nimport { access } from \"node:fs/promises\";\nimport { fileURLToPath } from \"node:url\";\nimport { StorageFailure } from \"@memhtml/contracts/errors\";\nimport { Effect } from \"effect\";\n/** An explicit path to the server, for a deployment that does not keep the two apps side by side. */\nexport const MCP_BIN_VAR = \"MEMHTML_MCP_BIN\";\n/**\n * Where the `memhtml-mcp` entry point sits relative to this module, in each layout that ships.\n *\n * Resolved by PATH rather than by `require.resolve`, which the dependency direction forces.\n * `@memhtml/mcp` depends on `@memhtml/cli` for the composition root, so `@memhtml/cli` cannot depend on\n * `@memhtml/mcp` without a cycle, and node resolution can only find a package that is a dependency.\n * (`require.resolve(\"@memhtml/mcp/bin\")` from here raises `MODULE_NOT_FOUND`, which is how this was\n * found.)\n *\n * Two candidates, tried in order, because the two apps are one build and where that build puts them\n * differs:\n *\n * - `./memhtml-mcp.mjs` — the PUBLISHED package, where both bins are entry points of one bundle and\n * land beside each other in `dist/`.\n * - `../../mcp/dist/bin.js` — the workspace, where `apps/cli/dist/serve.js` reaches\n * `apps/mcp/dist/bin.js` two directories up and across.\n *\n * Both are real directories rather than pnpm symlinks into the store, so each walk is stable where it\n * applies. {@link MCP_BIN_VAR} still overrides for a deployment that separates them.\n */\nconst MCP_CANDIDATES = [\"./memhtml-mcp.mjs\", \"../../mcp/dist/bin.js\"];\nexport const mcpEntryPoint = () => Effect.gen(function* () {\n const override = process.env[MCP_BIN_VAR];\n if (override !== undefined && override.trim() !== \"\")\n return override.trim();\n for (const candidate of MCP_CANDIDATES) {\n const path = fileURLToPath(new URL(candidate, import.meta.url));\n const present = yield* Effect.tryPromise({\n try: () => access(path),\n catch: () => \"absent\"\n }).pipe(Effect.as(true), Effect.orElseSucceed(() => false));\n if (present)\n return path;\n }\n // A build that produced the CLI but not the server is the one thing that lands here, so the\n // message names the fix rather than the missing path.\n return yield* Effect.fail(StorageFailure.make({\n operation: `serve.resolveMcp: run \\`pnpm build\\`, or set ${MCP_BIN_VAR}`\n }));\n});\nexport const serveMcp = (memhtmlRoot) => Effect.gen(function* () {\n const entry = yield* mcpEntryPoint();\n return yield* Effect.callback((resume) => {\n const child = spawn(process.execPath, [entry], {\n stdio: \"inherit\",\n env: { ...process.env, MEMHTML_ROOT: memhtmlRoot }\n });\n child.on(\"error\", () => resume(Effect.fail(StorageFailure.make({ operation: \"serve.spawn\" }))));\n child.on(\"exit\", (code, signal) => resume(Effect.succeed({\n server: entry,\n exitCode: code ?? 0,\n signal: signal ?? null\n })));\n // Killing the child on interruption is what keeps a `memhtml serve mcp` that the operator\n // Ctrl-C'd from leaving an orphaned server holding the repo's database open.\n return Effect.sync(() => {\n child.kill();\n });\n });\n});\n//# sourceMappingURL=serve.js.map","import { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { expandRoot } from \"@memhtml/store\";\nimport { Config } from \"effect\";\nimport { MCP_BIN_VAR } from \"./serve.js\";\nexport const CONFIG_VARS = [\n {\n name: \"MEMHTML_ROOT\",\n description: \"The memory repo's root: a git repository holding the corpus and `.memhtml/`.\",\n fallback: join(\"~\", \"memhtml\")\n },\n {\n name: \"MEMHTML_TRACE_ROOT\",\n description: \"Where `memhtml trace index` reads Claude Code transcripts from. Read-only; never written.\",\n fallback: join(\"~\", \".claude\")\n },\n {\n name: \"MEMHTML_AWS_REGION\",\n description: \"The Bedrock region for embeddings and the sleep cycle's four LLM phases.\",\n fallback: \"us-east-1\"\n },\n {\n name: \"AWS_BEARER_TOKEN_BEDROCK\",\n description: \"Bedrock bearer token, read by the AWS SDK itself. Absent means the default credential chain; retrieval then degrades to the lexical floor rather than failing.\",\n fallback: null\n },\n {\n name: \"MEMHTML_EMBED\",\n description: \"`off` disables the embedder entirely. An explicit opt-out, distinct from a missing credential: a missing credential degrades one search at call time, `off` degrades every search, and an operator reading this manifest needs those to be different states.\",\n fallback: \"on\"\n },\n {\n name: \"MEMHTML_LLM\",\n description: \"`off` makes the four LLM sleep phases report `no model bound` and stay `ok`, so a credential-free run is honest rather than red.\",\n fallback: \"on\"\n },\n {\n name: \"MEMHTML_EXTRACT_ENTITIES\",\n description: \"`on` adds one GPT-5.6 Luna call per write batch that extracts `memhtml-entity` metas the ops did not declare. Opt-in, unlike MEMHTML_EMBED, because it changes what a write STORES: extracted entities land in the files as if authored, and the write itself never waits on or fails with the model. A failed extraction is a logged warning and an unextracted batch.\",\n fallback: \"off\"\n },\n {\n /**\n * The name is imported rather than retyped: this row and the `process.env` read at serve.ts:50\n * must name the same string, and a literal here would let a rename disclose a variable nothing\n * reads.\n */\n name: MCP_BIN_VAR,\n description: \"An explicit path to the `memhtml-mcp` entry point, read only by the `memhtml serve mcp` supervisor. Absent means the sibling-path default. The two apps ship as one build, so `apps/cli/dist/serve.js` finds `apps/mcp/dist/bin.js` two directories over. An operator sets it for a split deployment that does not keep the apps side by side; it locates the server rather than configuring the store, so it changes no retrieval behavior.\",\n fallback: null\n }\n];\n/**\n * `MEMHTML_ROOT`. Re-exported from `@memhtml/store`'s own config rather than redeclared, because the\n * store's config expands a leading `~`. This value arrives from a shell profile, an MCP client\n * config, and a cron line, and only the shell expands tildes on its own.\n */\nexport const MemhtmlRoot = Config.string(\"MEMHTML_ROOT\").pipe(Config.withDefault(join(\"~\", \"memhtml\")), Config.map(expandRoot));\n/**\n * `MEMHTML_TRACE_ROOT`, defaulting to `~/.claude`.\n *\n * A parameter rather than a constant so the trace indexer is drivable against a fixture tree and\n * against an archived copy, which is also what keeps real transcripts out of the test suite.\n */\nexport const TraceRoot = Config.string(\"MEMHTML_TRACE_ROOT\").pipe(Config.withDefault(join(homedir(), \".claude\")), Config.map(expandRoot));\n//# sourceMappingURL=config.js.map","import { ModelUnavailable } from \"@memhtml/contracts/errors\";\nimport { wrapAsData } from \"@memhtml/llm\";\nimport { Effect } from \"effect\";\n/**\n * GPT-5.6 Luna, the fast high-volume model on the mantle endpoint. A constant rather than config\n * because the schema below is tested against this model's strict-mode behavior. Changing the model\n * is a code change with a test run, not an env var.\n */\nexport const EXTRACTION_MODEL_ID = \"openai.gpt-5.6-luna\";\n/** Entity types the prompt offers. Downstream the vocabulary is open: `unknown:` is a valid store type. */\nconst ENTITY_TYPES = [\"person\", \"org\", \"service\", \"place\", \"work\", \"concept\", \"event\"];\n/**\n * The strict output schema. `additionalProperties: false` and `required` on every level because\n * the Responses API's `strict: true` demands both, and a lax schema invites the model to answer\n * with prose keys the parser would then be guessing at.\n */\nconst RESPONSE_SCHEMA = {\n type: \"object\",\n properties: {\n items: {\n type: \"array\",\n items: {\n type: \"object\",\n properties: {\n index: { type: \"integer\" },\n entities: {\n type: \"array\",\n items: {\n type: \"object\",\n properties: {\n type: { type: \"string\", enum: [...ENTITY_TYPES] },\n name: { type: \"string\" }\n },\n required: [\"type\", \"name\"],\n additionalProperties: false\n }\n }\n },\n required: [\"index\", \"entities\"],\n additionalProperties: false\n }\n }\n },\n required: [\"items\"],\n additionalProperties: false\n};\nconst INSTRUCTIONS = \"Extract the named entities each memory mentions. \" +\n \"An entity is a specific nameable thing a later search would look up: a person, an \" +\n \"organization, a service or system, a place, a titled work, a defined concept, or a named \" +\n \"event. Skip generic nouns, dates, and quantities. Use the memory's own spelling for the \" +\n \"name. Return one result per input index, with an empty entities array when a memory names \" +\n \"nothing.\";\n/** The request body for one batch. Exported for the wire test, where the schema is the contract. */\nexport const requestBodyOf = (modelId, items) => JSON.stringify({\n model: modelId,\n instructions: INSTRUCTIONS,\n input: wrapAsData(\"memories\", JSON.stringify(items.map((item, index) => ({ index, title: item.title, text: item.text })))),\n text: {\n format: {\n type: \"json_schema\",\n name: \"entities\",\n strict: true,\n schema: RESPONSE_SCHEMA\n }\n }\n});\n/**\n * Decode one Responses-API payload into index-aligned `type:name` arrays.\n *\n * Total over unknown input: every malformed shape returns `undefined` and the caller maps that to\n * `ModelUnavailable`. A payload this code cannot read carries no answer, and treating it as\n * \"no entities\" would record a model failure as a fact about the corpus.\n */\nexport const entitiesOf = (payload, expected) => {\n const text = outputTextOf(payload);\n if (text === undefined)\n return undefined;\n let parsed;\n try {\n parsed = JSON.parse(text);\n }\n catch {\n return undefined;\n }\n const items = parsed.items;\n if (!Array.isArray(items))\n return undefined;\n const results = Array.from({ length: expected }, () => []);\n for (const item of items) {\n const index = item.index;\n const entities = item.entities;\n if (typeof index !== \"number\" || !Number.isInteger(index) || index < 0 || index >= expected) {\n continue;\n }\n if (!Array.isArray(entities))\n continue;\n results[index] = entities.flatMap((entity) => {\n const type = entity.type;\n const name = entity.name;\n if (typeof type !== \"string\" || typeof name !== \"string\")\n return [];\n const trimmedName = name.trim();\n return trimmedName === \"\" ? [] : [`${type}:${trimmedName}`];\n });\n }\n return results;\n};\n/** The assistant message text out of a Responses payload, or `undefined` off-shape. */\nconst outputTextOf = (payload) => {\n const output = payload.output;\n if (!Array.isArray(output))\n return undefined;\n for (const entry of output) {\n if (entry.type !== \"message\")\n continue;\n const content = entry.content;\n if (!Array.isArray(content))\n continue;\n for (const part of content) {\n const text = part.text;\n if (part.type === \"output_text\" && typeof text === \"string\") {\n return text;\n }\n }\n }\n return undefined;\n};\n/**\n * Per-call ceiling. Generous against the probed ~1s because a batch of 256 ops is a bigger\n * prompt than the probe's one sentence, and a late abort costs only this batch's entities. The\n * write itself is unaffected.\n */\nconst EXTRACT_TIMEOUT_MS = 60_000;\n/** The extractor over a transport. The transport owns the endpoint; this owns prompt and parse. */\nexport const makeEntityExtractor = (transport, modelId) => ({\n extract: (items) => items.length === 0\n ? Effect.succeed([])\n : Effect.gen(function* () {\n const payload = yield* Effect.tryPromise({\n try: (signal) => {\n const timeout = AbortSignal.timeout(EXTRACT_TIMEOUT_MS);\n return transport.post(requestBodyOf(modelId, items), AbortSignal.any([signal, timeout]));\n },\n catch: (cause) => ModelUnavailable.make({\n modelId,\n reason: cause instanceof Error ? `${cause.name}: ${cause.message}` : String(cause)\n })\n });\n const entities = entitiesOf(payload, items.length);\n if (entities === undefined) {\n return yield* Effect.fail(ModelUnavailable.make({ modelId, reason: \"unreadable extraction payload\" }));\n }\n return entities;\n })\n});\n/**\n * The production transport: bearer-token fetch against the mantle endpoint.\n *\n * A non-2xx status is a rejection carrying the status and the body's first line, because mantle\n * reports quota and auth failures as structured JSON the operator needs verbatim. Folding it into\n * a generic message was the mistake the embeddings lane made first.\n */\nexport const fetchMantleTransport = (region, token) => ({\n post: async (body, signal) => {\n const response = await fetch(`https://bedrock-mantle.${region}.api.aws/openai/v1/responses`, {\n method: \"POST\",\n headers: { Authorization: `Bearer ${token}`, \"Content-Type\": \"application/json\" },\n body,\n signal\n });\n const text = await response.text();\n if (!response.ok) {\n throw new Error(`mantle ${response.status}: ${text.slice(0, 200)}`);\n }\n return JSON.parse(text);\n }\n});\n//# sourceMappingURL=extraction.js.map","import { readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { hasConsolidatorCredentials, makeConsolidator } from \"@memhtml/consolidator\";\nimport { StorageFailure } from \"@memhtml/contracts/errors\";\nimport { DatabaseService, Indexer, IndexGit, IndexRecorder, MIGRATIONS_DIR, makeDatabase, makeGitPort, makeIndexer, makeIndexRecorder, makeRetrieval, Retrieval, STATE_MIGRATIONS_DIR } from \"@memhtml/index\";\nimport { EMBED_DIM, EMBED_WATERMARK, Embeddings, EmbeddingsLive, ModelClient, ModelClientLive } from \"@memhtml/llm\";\nimport { makeSleep, Sleep } from \"@memhtml/sleep\";\nimport { Git, INDEX_DB_PATH, makeGit, makeStore, STATE_DB_PATH, Store } from \"@memhtml/store\";\nimport { Config, Context, Effect, Layer } from \"effect\";\nimport { MemhtmlRoot, TraceRoot } from \"./config.js\";\nimport { EXTRACTION_MODEL_ID, fetchMantleTransport, makeEntityExtractor } from \"./extraction.js\";\n/**\n * The service tags, re-exported from the composition root.\n *\n * A handler imports its services from here rather than from six packages, so \"which tag does this\n * come from\" is answered once. `IndexGit` is the case that needs care. `@memhtml/store` publishes\n * `memhtml/Git` for its `GitShape` and `@memhtml/index` publishes `memhtml/IndexGit` for a different\n * shape, and the two appearing side by side in this list keeps them from being confused.\n */\nexport { DatabaseService, Indexer, IndexGit, IndexRecorder, Retrieval } from \"@memhtml/index\";\nexport { Embeddings, ModelClient } from \"@memhtml/llm\";\nexport { Sleep } from \"@memhtml/sleep\";\nexport { Git, Store } from \"@memhtml/store\";\nexport const Roots = Context.Service(\"memhtml/Roots\");\n/**\n * The roots layer. `repoOverride` is `--repo`, and it wins over `MEMHTML_ROOT` so an operator running\n * against a second repo does not have to mutate their environment to do it.\n */\nexport const layerRoots = (repoOverride) => Layer.effect(Roots)(Effect.gen(function* () {\n const fromConfig = yield* MemhtmlRoot;\n const traceRoot = yield* TraceRoot;\n const memhtmlRoot = repoOverride !== undefined && repoOverride.trim() !== \"\" ? repoOverride.trim() : fromConfig;\n return { memhtmlRoot, traceRoot };\n})).pipe(Layer.orDie);\n/**\n * The database, rooted in the repo's `.memhtml/`.\n *\n * Both planes on one connection, always. The salience retrieval arm `LEFT JOIN`s `state.access`\n * in the same statement as `main.files`, so a connection without the attachment silently drops\n * that arm. `DatabaseShape.hasState` is what the arm registry consults, and it is `false` only\n * for a caller that deliberately asked for the index alone.\n */\nexport const layerDatabase = Layer.effect(DatabaseService)(Effect.gen(function* () {\n const roots = yield* Roots;\n return yield* makeDatabase(join(roots.memhtmlRoot, INDEX_DB_PATH), MIGRATIONS_DIR, {\n path: join(roots.memhtmlRoot, STATE_DB_PATH),\n migrationsDir: STATE_MIGRATIONS_DIR\n });\n})).pipe(Layer.orDie);\n/** Git over the repo root. The store's shape, under the store's own tag. */\nexport const layerGit = Layer.effect(Git)(Effect.gen(function* () {\n const roots = yield* Roots;\n return makeGit(roots.memhtmlRoot);\n}));\n/**\n * The indexer's git port, over the store's git service.\n *\n * `readFile` is `Effect.tryPromise` rather than `Effect.promise`. `Effect.promise` turns an ENOENT\n * into a defect, and a defect travels past the `Effect.catch` the indexer wraps each projection in.\n * An absent path would then kill the fiber mid-update instead of becoming the counted skip the indexer\n * already handles. An agent listing a path it just archived is the normal case, which makes this\n * the difference between a working `index update` and a crash on an ordinary day.\n */\nexport const layerIndexGit = Layer.effect(IndexGit)(Effect.gen(function* () {\n const roots = yield* Roots;\n const git = yield* Git;\n return makeGitPort({\n git,\n readFile: (path) => Effect.tryPromise({\n try: () => readFile(join(roots.memhtmlRoot, path), \"utf8\"),\n catch: (cause) => cause\n }),\n fail: (operation) => Effect.fail(StorageFailure.make({ operation: `git.${operation}` }))\n });\n}));\n/** The recorder: the dedupe lookup the store gates writes on, and the session-link writer. */\nexport const layerRecorder = Layer.effect(IndexRecorder)(Effect.gen(function* () {\n const db = yield* DatabaseService;\n return makeIndexRecorder(db);\n}));\n/**\n * The store, with the recorder's dedupe hook attached.\n *\n * `onMove` mirrors `state.access.path` across an archive. Cross-database foreign keys do not\n * exist, so the mirror is an explicit call at the one place a path can change; without it every\n * eviction leaves an orphan access row and the salience arm stops finding the memory it describes.\n */\nexport const layerStore = Layer.effect(Store)(Effect.gen(function* () {\n const git = yield* Git;\n const recorder = yield* IndexRecorder;\n const db = yield* DatabaseService;\n return makeStore(git, {\n dedupeLookup: recorder.activePathForHash,\n onMove: (from, to) => db.run(\"UPDATE state.access SET path = ? WHERE path = ?\", [to, from]).pipe(\n // A move whose mirror fails must not fail the move. The archive commit has already\n // landed, and the orphan row is what `memhtml doctor` reports. Losing the commit to a\n // bookkeeping error would be worse than an orphan.\n Effect.catch((error) => Effect.logWarning(`state.access mirror missed ${from} -> ${to}: ${error.operation}`)))\n });\n}));\nexport const Embedder = Context.Service(\"memhtml/Embedder\");\n/**\n * Bedrock embeddings when the region resolves, absent when `MEMHTML_EMBED` is `off`.\n *\n * The switch is an explicit opt-out rather than credential sniffing. A missing credential is\n * discovered at call time and degrades one search. A deliberate `off` degrades every search, and\n * an operator reading `memhtml manifest` needs those to be different states.\n */\nexport const layerEmbedder = Layer.effect(Embedder)(Effect.gen(function* () {\n const enabled = yield* Config.string(\"MEMHTML_EMBED\").pipe(Config.withDefault(\"on\"), Config.map((value) => value.trim().toLowerCase() !== \"off\"));\n if (!enabled)\n return { document: undefined, query: undefined };\n const embeddings = yield* Embeddings;\n return { document: embeddings, query: embeddings };\n})).pipe(Layer.orDie);\n/** A layer supplying the embedder ports directly, for a test that wants a deterministic vector. */\nexport const layerEmbedderFrom = (embedder) => Layer.succeed(Embedder)(embedder);\n/** The indexer, over the database and the git port. */\nexport const layerIndexer = Layer.effect(Indexer)(Effect.gen(function* () {\n const db = yield* DatabaseService;\n const git = yield* IndexGit;\n const embedder = yield* Embedder;\n return makeIndexer({\n db,\n git,\n embedWatermark: EMBED_WATERMARK,\n embedDim: EMBED_DIM,\n embeddings: embedder.document,\n // Wall-clock through the Effect clock would need an Effect here; the indexer wants a plain\n // thunk for `indexed_at`. A test that must pin the instant builds the indexer directly.\n now: () => new Date().toISOString()\n });\n}));\n/** Retrieval, over the database and the query embedder. */\nexport const layerRetrieval = Layer.effect(Retrieval)(Effect.gen(function* () {\n const db = yield* DatabaseService;\n const embedder = yield* Embedder;\n return makeRetrieval({ db, embeddings: embedder.query });\n}));\nexport const ModelPort = Context.Service(\"memhtml/ModelPort\");\nexport const layerModelPort = Layer.effect(ModelPort)(Effect.gen(function* () {\n const enabled = yield* Config.string(\"MEMHTML_LLM\").pipe(Config.withDefault(\"on\"), Config.map((value) => value.trim().toLowerCase() !== \"off\"));\n if (!enabled)\n return { model: undefined };\n return { model: yield* ModelClient };\n})).pipe(Layer.orDie);\n/** A layer supplying the model port directly, for a test that scripts the model's answers. */\nexport const layerModelFrom = (model) => Layer.succeed(ModelPort)({ model });\nexport const ExtractorPort = Context.Service(\"memhtml/ExtractorPort\");\nexport const layerExtractorPort = Layer.effect(ExtractorPort)(Effect.gen(function* () {\n const enabled = yield* Config.string(\"MEMHTML_EXTRACT_ENTITIES\").pipe(Config.withDefault(\"off\"), Config.map((value) => value.trim().toLowerCase() === \"on\"));\n if (!enabled)\n return { extractor: undefined };\n const region = yield* Config.string(\"MEMHTML_AWS_REGION\").pipe(Config.withDefault(\"us-east-1\"));\n const token = yield* Config.string(\"AWS_BEARER_TOKEN_BEDROCK\").pipe(Config.withDefault(\"\"));\n if (token === \"\") {\n yield* Effect.logWarning(\"MEMHTML_EXTRACT_ENTITIES=on but AWS_BEARER_TOKEN_BEDROCK is absent; writes proceed unextracted\");\n return { extractor: undefined };\n }\n return {\n extractor: makeEntityExtractor(fetchMantleTransport(region, token), EXTRACTION_MODEL_ID)\n };\n})).pipe(Layer.orDie);\n/** A layer supplying the extractor directly, for a test that scripts the extraction answers. */\nexport const layerExtractorFrom = (extractor) => Layer.succeed(ExtractorPort)({ extractor });\nexport const ConsolidatorPortService = Context.Service(\"memhtml/ConsolidatorPort\");\n/**\n * Two gates, both cheap, both before anything is spawned.\n *\n * `MEMHTML_LLM=off` is the same explicit opt-out `layerModelPort` reads, and it covers the consolidator\n * too, because an operator who turned the models off did not mean \"except the expensive agent\".\n *\n * `hasConsolidatorCredentials` is the credential preflight, read here as well as inside the client.\n * The redundancy is deliberate and the two reads do different jobs. This one decides whether the phase\n * sees a consolidator at all, so a credential-free environment gets `detail: \"no consolidator bound\"`,\n * the same shape the other three LLM phases report with no model, rather than a bound port that\n * fails on every call and reports a degradation. CI has no credentials and must read as skipped rather\n * than degraded.\n *\n * The check cannot be skipped in favor of the client's own, because the provider is lazy.\n * `createAmazonBedrock` and `provider(modelId)` both succeed with zero credentials and nothing fails\n * until the first request (verified in T-EVE-1's probe, recorded at\n * `apps/consolidator/src/contract.ts:301-319`).\n *\n * **`env` is a parameter, and it has to be.** `Config` reads its values through a `ConfigProvider`,\n * which a test substitutes, while `hasConsolidatorCredentials` reads `process.env` directly, and\n * effect's default provider snapshots `process.env` at module load (probed 2026-08-08: mutating\n * `process.env.MEMHTML_LLM` after importing `effect` changes nothing `Config.string` returns). A test\n * that set both by mutation would read a stale snapshot for one gate and a live object for the other,\n * and the two gates would disagree about which environment they are in. Threading the credential\n * environment through as an argument makes both injectable from one call. See\n * `apps/cli/tests/consolidator-wiring.test.ts`, where that disagreement produced a false defect\n * before this parameter existed.\n *\n * **It now requires `RootsShape`, for `traceRoot`.** That is how transcripts reach the agent. The\n * consolidator mounts the trace root read-only rather than sending transcripts as a model message\n * (`apps/consolidator/src/client.ts`, `manifestFor`, records what the superseded path actually did).\n * The root is `MEMHTML_TRACE_ROOT` and this file is where config becomes services, so it is read from the\n * same `Roots` service `memhtml trace index` scans with. One resolution of one variable is what\n * keeps the mounted tree and the indexed `traces` rows describing the same directory. A second\n * `Config.string(\"MEMHTML_TRACE_ROOT\")` here would be a second place the `~/.claude` default lives.\n */\nexport const layerConsolidatorPort = (env = process.env) => Layer.effect(ConsolidatorPortService)(Effect.gen(function* () {\n const roots = yield* Roots;\n const enabled = yield* Config.string(\"MEMHTML_LLM\").pipe(Config.withDefault(\"on\"), Config.map((value) => value.trim().toLowerCase() !== \"off\"));\n if (!enabled)\n return { consolidator: undefined };\n if (!hasConsolidatorCredentials(env)) {\n yield* Effect.logDebug(\"trace consolidation unbound: no Bedrock credentials in the environment\");\n return { consolidator: undefined };\n }\n /**\n * The client is built over the same environment the gate just read. A client over ambient\n * `process.env` while the gate read an injected one would pass the gate and fail at the call,\n * which is the degradation-instead-of-skip outcome this gate exists to prevent.\n */\n return { consolidator: makeConsolidator({ env, traceRoot: roots.traceRoot }) };\n})).pipe(Layer.orDie);\n/** A layer supplying the consolidator directly, for a test that scripts its candidates. */\nexport const layerConsolidatorFrom = (consolidator) => Layer.succeed(ConsolidatorPortService)({ consolidator });\n/**\n * The sleep runner over the same services every other command uses.\n *\n * `@memhtml/sleep` deliberately ships no `SleepLive` that resolves its own git, database, and model.\n * A layer that built its own would open a second connection to one database file and a second git\n * wrapper on one root, and the run would then curate a corpus the indexer is not describing.\n */\nexport const layerSleep = Layer.effect(Sleep)(Effect.gen(function* () {\n const git = yield* Git;\n const store = yield* Store;\n const db = yield* DatabaseService;\n const indexer = yield* Indexer;\n const modelPort = yield* ModelPort;\n const consolidatorPort = yield* ConsolidatorPortService;\n return makeSleep({\n git,\n store,\n db,\n indexer,\n model: modelPort.model,\n consolidator: consolidatorPort.consolidator\n });\n}));\n/**\n * Everything above the embedder and the model, as one layer requiring only the roots and those two.\n *\n * Written top-down because that is what `Layer.provideMerge(that)` means. It feeds `that`'s output\n * into `self`'s requirements, so the consumer is `self` and each `.pipe` step below adds the level\n * beneath it. Chaining in dependency order instead, with the database first, reads naturally and is\n * wrong. It would provide git to the database and leave `GitShape` in the final requirement set, which\n * typechecks as an unsatisfied layer rather than failing where the mistake is.\n *\n * Split out from `layerApp` so a test provides a deterministic embedder and a real temp repo with\n * no Bedrock anywhere in the graph. The composition under test is then the same composition\n * production runs, which a hand-assembled test wiring would not be.\n */\nexport const layerCore = Layer.mergeAll(layerSleep, layerRetrieval).pipe(Layer.provideMerge(Layer.mergeAll(layerIndexer, layerStore)), Layer.provideMerge(Layer.mergeAll(layerIndexGit, layerRecorder)), Layer.provideMerge(Layer.mergeAll(layerDatabase, layerGit)));\n/**\n * The production graph: roots from config, Bedrock behind both model ports, everything else over\n * them. `repoOverride` is `--repo`.\n *\n * This is the one composition production runs. `memhtml serve mcp` runs the same one in a child\n * process, so an MCP tool and its CLI twin cannot be looking at different databases.\n */\nexport const layerApp = (repoOverride) => layerCore.pipe(Layer.provideMerge(Layer.mergeAll(layerRoots(repoOverride), layerEmbedder.pipe(Layer.provide(EmbeddingsLive), Layer.orDie), layerModelPort.pipe(Layer.provide(ModelClientLive), Layer.orDie), layerExtractorPort, \n/**\n * `layerRoots` is provided to the consolidator port explicitly rather than merged beside it.\n * The consolidator needs `traceRoot` to mount, and a sibling in one `mergeAll` is not a\n * dependency. The roots layer is built once with `repoOverride` and fed in, so a `--repo`\n * run and the mounted trace root cannot come from two different resolutions.\n */\nlayerConsolidatorPort().pipe(Layer.provide(layerRoots(repoOverride))))));\n/**\n * The graph a test provides: the real composition, with the embedder and the model injected.\n *\n * Same `layerCore`, so a test exercises the wiring production uses rather than a parallel one. The\n * only substituted edges are the two that reach the network.\n */\nexport const layerAppWith = (options) => layerCore.pipe(Layer.provideMerge(Layer.mergeAll(layerRoots(options.repo), layerEmbedderFrom(options.embedder), layerModelFrom(options.model), layerConsolidatorFrom(options.consolidator), layerExtractorFrom(options.extractor))));\n//# sourceMappingURL=api-layer.js.map","import { fail } from \"./envelope.js\";\nconst isTagged = (value) => typeof value === \"object\" &&\n value !== null &&\n typeof value._tag === \"string\";\nconst text = (value) => (typeof value === \"string\" ? value : undefined);\nconst paths = (value) => Array.isArray(value) ? value.filter((entry) => typeof entry === \"string\") : [];\n/**\n * The code for a tag.\n *\n * `GitFailure` lives in `@memhtml/store` rather than `@memhtml/contracts`. It is the one error class\n * outside the shared contracts package, because it carries a git subcommand name and only the store\n * speaks git. It maps to `ERR_GIT` here at the CLI edge, the only place the two vocabularies meet.\n *\n * `EmbedModelMismatch` is a plain class rather than a schema error (it predates the contracts\n * package), so it arrives with the same `_tag` shape and needs no special case.\n */\nexport const codeFor = (error) => {\n if (!isTagged(error))\n return \"ERR_UNKNOWN\";\n switch (error._tag) {\n case \"GitFailure\":\n return \"ERR_GIT\";\n case \"StorageFailure\":\n return \"ERR_STORAGE\";\n case \"InvalidMemory\":\n return \"ERR_INVALID_MEMORY\";\n case \"PathNotFound\":\n return \"ERR_PATH_NOT_FOUND\";\n case \"WriteConflict\":\n return \"ERR_WRITE_CONFLICT\";\n case \"DirtyTree\":\n return \"ERR_DIRTY_TREE\";\n case \"DuplicateContent\":\n return \"ERR_DUPLICATE_CONTENT\";\n case \"ModelUnavailable\":\n return \"ERR_MODEL_UNAVAILABLE\";\n case \"EmbedModelMismatch\":\n return \"ERR_EMBED_MODEL_MISMATCH\";\n case \"DiscriminationFailed\":\n return \"ERR_DISCRIMINATION_FAILED\";\n default:\n return \"ERR_UNKNOWN\";\n }\n};\n/**\n * The human message for a failure.\n *\n * Deliberately narrow. Every payload field named here is one a caller can act on: a path to\n * re-read, two shas to reconcile, a model to check. The message omits the driver's own text, the\n * SQL, the git argv, and any memory body. Each typed error class already dropped those at its\n * adapter edge so a tool response could not carry corpus content, and reconstructing them here\n * would undo that.\n */\nexport const messageFor = (error) => {\n if (!isTagged(error))\n return String(error);\n switch (error._tag) {\n case \"GitFailure\":\n return `git ${text(error.command) ?? \"command\"} failed (exit ${String(error.exitCode)})`;\n case \"StorageFailure\":\n return `storage operation failed: ${text(error.operation) ?? \"unknown\"}`;\n case \"InvalidMemory\":\n return `invalid memory: ${text(error.reason) ?? \"unstated reason\"}`;\n case \"PathNotFound\":\n return `no memory at ${text(error.path) ?? \"the given path\"}`;\n case \"WriteConflict\":\n return `write conflict on ${text(error.path) ?? \"a path\"}: ours ${text(error.ourSha) ?? \"?\"}, theirs ${text(error.theirSha) ?? \"?\"}`;\n case \"DirtyTree\":\n return `the working tree has uncommitted changes: ${paths(error.paths).join(\", \")}`;\n case \"DuplicateContent\":\n return `this content already lives at ${text(error.existingPath) ?? \"another path\"}`;\n case \"ModelUnavailable\":\n return `bedrock refused ${text(error.modelId) ?? \"the model\"}: ${text(error.reason) ?? \"no reason given\"}`;\n case \"EmbedModelMismatch\":\n return `the index was built in vector space ${text(error.stored) ?? \"?\"}, configured is ${text(error.configured) ?? \"?\"}`;\n case \"LlmContractViolation\":\n return `the model broke its structured-output contract: ${text(error.reason) ?? \"no reason given\"}`;\n case \"DiscriminationFailed\":\n return text(error.reason) ?? \"the discrimination gate refused\";\n default:\n return `unexpected failure: ${error._tag}`;\n }\n};\n/**\n * What to do about a failure, as commands the caller can run.\n *\n * A suggestion is part of the contract. An agent that receives `ERR_INDEX_STALE` and a\n * `memhtml index update` suggestion can recover in one step without a round trip to a human. Absent\n * suggestions are an empty array rather than a null, so a parser never branches on presence.\n *\n * A record rather than a `switch`, which is what closes the drift class. Every `memhtml …` string\n * below names a command from the table in `commands.ts`, and a rename there used to leave a stale\n * suggestion here that nothing failed on. A record's keys and arms are both walkable, so the suite\n * can enumerate every tag, run every suggestion through the real `parseArgv`, and fail on a name the\n * table does not hold. A `switch` cannot expose any of that to a test.\n *\n * Validated in the test rather than here on purpose: `errors.ts` importing `commands.ts` closes the\n * cycle `commands.ts` → `operations.ts` → `errors.ts` (commands.ts:8, operations.ts:35) and leaves\n * `AUTHORABLE_RELS` undefined in `commands.ts`'s module body under an operations-first import order.\n */\nexport const SUGGESTIONS = {\n PathNotFound: () => [\"memhtml search <what you were looking for>\", \"memhtml list\"],\n WriteConflict: (error) => [\n `memhtml read ${text(error.path) ?? \"<path>\"}`,\n \"re-apply the change to current content\"\n ],\n DirtyTree: () => [\"git -C $MEMHTML_ROOT status\", \"commit or stash the changes, then retry\"],\n DuplicateContent: (error) => [`memhtml read ${text(error.existingPath) ?? \"<path>\"}`],\n EmbedModelMismatch: () => [\"memhtml index rebuild --embed\"],\n ModelUnavailable: () => [\"retry: search still works on the lexical floor\", \"memhtml status\"],\n InvalidMemory: () => [\"memhtml manifest\"],\n // No `--json`: it is a global flag defaulting to true (commands.ts:36-42), so naming it here only\n // gave the suggestion a second way to go stale.\n DiscriminationFailed: () => [\n \"memhtml eval discriminate\",\n \"memhtml sleep review\",\n \"git branch -D <run-id>\"\n ]\n};\nexport const suggestionsFor = (error) => {\n if (!isTagged(error))\n return [];\n return SUGGESTIONS[error._tag]?.(error) ?? [];\n};\n/** A typed failure as an envelope. The one call every command's error path makes. */\nexport const failureFor = (error) => fail(codeFor(error), messageFor(error), suggestionsFor(error));\n//# sourceMappingURL=errors.js.map","import { isEdgeRel, MEMORY_RELS, relClassFor, TASK_RELS } from \"@memhtml/contracts/edges\";\nimport { InvalidMemory } from \"@memhtml/contracts/errors\";\nimport { normalizePath } from \"@memhtml/contracts/paths\";\nimport { isTaskStatus, isWritableMemoryType, TASK_STATUSES, WRITABLE_MEMORY_TYPES } from \"@memhtml/contracts/types\";\nimport { frameKeyOf, REINFORCE_SIGNALS } from \"@memhtml/domain\";\nimport { isValidDatetime, setMeta } from \"@memhtml/html\";\nimport { DatabaseService, Indexer, IndexRecorder, persistScanned, Retrieval, readIndexState, readWatermark, reinforce, sanitizeFtsQuery } from \"@memhtml/index\";\nimport { EMBED_WATERMARK } from \"@memhtml/llm\";\nimport { attemptIo, commitSubject, Store } from \"@memhtml/store\";\nimport { mergeTailExtract, scanTraceRoot } from \"@memhtml/traces\";\nimport { Effect } from \"effect\";\nimport { ExtractorPort, Roots } from \"./api-layer.js\";\nimport { codeFor, messageFor } from \"./errors.js\";\n/**\n * The use cases, one per tool. Every CLI command and every MCP tool is a thin adapter over exactly\n * one of these, which makes `memhtml search` and `memory_search` provably the same query\n * rather than two implementations that agree today.\n *\n * Nothing here parses argv or builds an envelope. A function takes decoded parameters, returns a\n * typed result, and fails with a typed error. The adapters own the shape of the wire.\n */\n/** Wall-clock as an ISO-8601 UTC second, through the Effect clock so a test can pin it. */\nconst nowSecond = Effect.clockWith((clock) => Effect.map(clock.currentTimeMillis, (millis) => `${new Date(millis).toISOString().slice(0, 19)}Z`));\n/** Drop `undefined`-valued keys, so `exactOptionalPropertyTypes` sees an absent key. */\nconst defined = (input) => {\n const out = {};\n for (const [key, value] of Object.entries(input))\n if (value !== undefined)\n out[key] = value;\n return out;\n};\n/**\n * Narrow an untrusted memory-type string.\n *\n * `arc` is refused even though it is a valid storage type. An arc is synthesized by the sleep\n * cycle from many memories, so an agent naming one directly would be asserting a conclusion the\n * corpus has not earned. The vocabulary the tool exposes is therefore narrower than the CHECK\n * constraint by exactly that one value.\n */\nexport const decodeWritableType = (value) => WRITABLE_MEMORY_TYPES.includes(value)\n ? Effect.succeed(value)\n : Effect.fail(InvalidMemory.make({\n reason: `unknown memory type: ${value}. One of: ${WRITABLE_MEMORY_TYPES.join(\", \")}`\n }));\n/**\n * The rels a CALLER may author: the nine memory rels plus the two task rels.\n *\n * The two classes the vocabulary withholds are the ones the system mints itself. A `person` edge is\n * written by sleep's person-links phase against `resources/people/*`, and `from_session` is written\n * by the write path from the provenance a caller already supplied. Authoring either by hand would put\n * a hand-guessed row where a derivation belongs.\n */\nexport const AUTHORABLE_RELS = [...MEMORY_RELS, ...TASK_RELS];\n/**\n * Narrow an untrusted rel to one a caller may author.\n *\n * A `blocks` edge between two tasks is a legitimate authored assertion, so the task class is in and\n * refusing it would leave the task graph writable by nothing. Whether the rel agrees with its\n * endpoints is not this function's business. `@memhtml/store`'s `linkMemories` reads both files'\n * `memhtml-type` and refuses a mismatch, and it is the only layer that can see the endpoints at all.\n *\n * `memory_link`'s MCP schema stays memory-rels-only (`MemoryRelSchema`, `apps/mcp/src/tools.ts`) and\n * refuses a task rel at decode. That is one narrow surface for agents and one wider one for the\n * operator, with the store's endpoint guard governing both.\n */\nexport const decodeAuthorableRel = (value) => isEdgeRel(value) && AUTHORABLE_RELS.includes(value)\n ? Effect.succeed(value)\n : Effect.fail(InvalidMemory.make({\n reason: `unknown rel: ${value}. One of: ${AUTHORABLE_RELS.join(\", \")}`\n }));\n/** Narrow an untrusted task status. */\nexport const decodeTaskStatus = (value) => isTaskStatus(value)\n ? Effect.succeed(value)\n : Effect.fail(InvalidMemory.make({\n reason: `unknown task status: ${value}. One of: ${TASK_STATUSES.join(\", \")}`\n }));\n/**\n * Narrow an untrusted due date, using the FORMAT's own validator.\n *\n * `isValidDatetime` rather than a local regex or `Date.parse`, because `files.due_at` is compared and\n * ordered as a string. `2026-8-9` and `Aug 9 2026` both parse as instants and neither sorts alongside\n * `2026-08-09`, so the overdue query would silently miss them. Reusing the parser's own validator is\n * also what keeps this refusal and the parser's violation from drifting apart.\n */\nexport const decodeDueAt = (value) => isValidDatetime(value)\n ? Effect.succeed(value)\n : Effect.fail(InvalidMemory.make({\n reason: `due date is not an ISO date or datetime: ${value}. Expected YYYY-MM-DD or YYYY-MM-DDThh:mm:ssZ`\n }));\n/** Narrow an untrusted reinforcement signal. */\nexport const decodeSignal = (value) => REINFORCE_SIGNALS.includes(value)\n ? Effect.succeed(value)\n : Effect.fail(InvalidMemory.make({\n reason: `unknown signal: ${value}. One of: ${REINFORCE_SIGNALS.join(\", \")}`\n }));\n/**\n * Record the session link for an operation that touched a path.\n *\n * Fire-and-log rather than fail-the-call. The link is a note about what happened, and losing the\n * memory over a failed note about it would invert the priority. The file's own head already\n * carries `memhtml-session`/`memhtml-prompt`/`memhtml-turn`, so the durable half of the link survives even\n * when this row does not.\n */\nconst recordLink = (path, linkKind, provenance, at) => Effect.gen(function* () {\n if (provenance.sessionId === undefined || provenance.sessionId === \"\")\n return;\n const recorder = yield* IndexRecorder;\n yield* recorder\n .recordLink({\n path,\n sessionId: provenance.sessionId,\n linkKind,\n at,\n ...defined({ promptId: provenance.promptId, turnUuid: provenance.turnUuid })\n })\n .pipe(Effect.catch((error) => Effect.logWarning(`session link not recorded for ${path}: ${error.operation}`)));\n});\n/**\n * Bring the index up to the commit a write just made.\n *\n * `indexer.update()` rather than `indexPaths([…])`, and the difference changes behavior twice:\n *\n * 1. **`indexPaths` cannot express a rename.** Every correction and every archive is a `git mv`, and\n * an index that handled one as \"index the destination\" leaves the source row live. The archived\n * memory stays in `memhtml list`, `files` gains a row the tree does not have, and the chunk rows the\n * move exists to preserve are duplicated under two paths. `update()` reads `diff --name-status -M`,\n * sees the `R`, and re-points the row, which keeps the embedding and drops nothing.\n * 2. **`indexPaths` never records the watermark.** `index_state.head_sha` is what makes\n * \"the index describes the current commit\" answerable at all, so a write path that skipped it\n * would leave `memhtml status` reporting `index_fresh: false` forever and `index update` re-deriving\n * from a stale base.\n *\n * The cost is one `git diff` over one commit, which is what the watermark exists to bound. On the\n * very first write the watermark is absent and `update()` falls through to a full rebuild. That is\n * correct, and cheap on a corpus that has one file in it.\n */\nconst reindex = () => Effect.gen(function* () {\n const indexer = yield* Indexer;\n return yield* indexer.update({ embed: true });\n});\n/**\n * Decode untrusted write parameters into the store's `WriteInput`.\n *\n * Shared by {@link writeMemory} and {@link batchWrite}, and the sharing matters. A batch that\n * re-derived this would be a second decode of the same vocabulary, and the two would agree today\n * and drift the first time a field is added. This is `symspec`'s lesson stated as code: the batch\n * folds the singular's own decode rather than a parallel one.\n *\n * The two task metas are decoded here, before any file is rendered, and only for a task.\n * `@memhtml/html`'s parser refuses `memhtml-task-status` on a non-task and refuses a `memhtml-due` that is not\n * an ISO datetime, so a bad value passed through would render a file the indexer then declines to\n * project. That file is present in the tree, absent from every search, and visible only as a log\n * line. Deciding here turns it into a typed `InvalidMemory` before the commit.\n */\nconst toWriteInput = (params, at) => Effect.gen(function* () {\n const memoryType = yield* decodeWritableType(params.memoryType);\n const taskStatus = memoryType === \"task\" && params.taskStatus !== undefined && params.taskStatus !== \"\"\n ? yield* decodeTaskStatus(params.taskStatus)\n : undefined;\n const dueAt = memoryType === \"task\" && params.dueAt !== undefined && params.dueAt !== \"\"\n ? yield* decodeDueAt(params.dueAt)\n : undefined;\n return {\n title: params.title,\n claim: params.claim,\n memoryType,\n at,\n ...defined({\n body: params.body,\n articleHtml: params.articleHtml,\n path: params.path,\n workspace: params.workspace,\n tags: params.tags,\n entities: params.entities,\n importance: params.importance,\n confidence: params.confidence,\n sessionId: params.sessionId,\n promptId: params.promptId,\n turnUuid: params.turnUuid,\n taskStatus,\n dueAt\n })\n };\n});\n/**\n * Write one memory: render, dedupe, commit, index.\n *\n * On a dedupe nothing is indexed, because nothing changed. A dedupe writes no file, stages nothing,\n * and commits nothing, so the tree is byte-identical and the index already describes it.\n */\nexport const writeMemory = (params) => Effect.gen(function* () {\n const store = yield* Store;\n const at = yield* nowSecond;\n const result = yield* store.writeMemory(yield* toWriteInput(params, at));\n if (result.created)\n yield* reindex();\n yield* recordLink(result.path, \"wrote\", params, at);\n return result;\n});\n/**\n * A typed failure as a per-op report, through the same `codeFor`/`messageFor` every envelope error\n * takes.\n *\n * Mapped here rather than in each door, deliberately. A per-op code is part of the batch's payload\n * rather than of the envelope, so two doors shaping it independently is two mappings that agree today.\n * `memhtml apply` and `memory_write_batch` reporting different codes for the same refused op is\n * the drift the shared-use-case rule exists to prevent.\n */\nconst reportFailure = (index, error) => ({\n index,\n ok: false,\n code: codeFor(error),\n error: messageFor(error)\n});\n/**\n * The `detect_conflicts` assist: which claim, if any, each op's own claim contradicts.\n *\n * **Propose-only, and that is the design rather than a v1 limitation.** The function returns a report\n * per op index and writes nothing, stages nothing, and refuses nothing, because sometimes the\n * contradiction is the answer. A memory recording that a runbook step changed necessarily contradicts\n * the memory stating the old step, and an assist that auto-archived, applied last-wins, or blocked the\n * write would destroy the pair a later reader needs to see the change in. The caller decides: write\n * anyway, `memory_correct` the match, or skip.\n *\n * **One query for the whole batch.** Every op's frame key is collected first and `activeFramesFor` is\n * called once with all of them. The signature takes an array so a caller cannot loop, and a\n * per-op lookup would be the quadratic-write-cost pattern this codebase has already paid for once.\n *\n * **Two match sources, checked in that order.** The store answers for active non-task memories. Its\n * predicate, and 0009's index, exclude archived rows and tasks, because an archived claim is not a\n * competing assertion and an open to-do phrased as a claim is working state rather than knowledge. Then\n * come the batch's own earlier ops, folded as this loop walks them in order. Two ops in one call can\n * occupy the same slot, and neither is in the store yet, so nothing but this fold can see that pair. A\n * store match wins when an op has both, because the store's memory is a fact already in the corpus\n * while the earlier op is one this same call is about to create.\n *\n * **A later op reports on an earlier one, never the reverse.** The fold is asymmetric on purpose. Op 3\n * matching op 1 tells a caller \"you are about to restate something you just said\", which is actionable\n * with op 3 still in hand. Reporting it on op 1 as well would name a conflict with something that did\n * not exist when op 1 was written, and would double one finding into two.\n *\n * **A lookup failure degrades to no conflicts.** The assist is a note about the writes, so losing the\n * memories over a failed note about them would invert the priority as it would for\n * {@link recordLink} and {@link bumpAccess}, with the same `Effect.catch` → `logWarning` → neutral value.\n * The write path never sees this function's failure, which makes \"the assist cannot block a\n * write\" true structurally rather than by review.\n */\nconst detectFrameConflicts = (ops) => Effect.gen(function* () {\n /**\n * `frameKeyOf(op.claim)` per op, computed once and kept alongside the index.\n *\n * On the `article_html` path `claim` is `\"\"` by construction, because both doors leave it empty and\n * the `<mark>` inside the markup is the claim. `frameKeyOf` therefore returns null and a markup op\n * gets no assist. Deriving one here would mean parsing every op's article at the ops layer, a second\n * render of bytes the store is about to render anyway. The boundary is stated in the tool\n * description instead of hidden behind a duplicate parse.\n */\n const keyed = [];\n for (const [index, op] of ops.entries()) {\n const key = frameKeyOf(op.claim);\n if (key !== null)\n keyed.push({ index, key, claim: op.claim });\n }\n if (keyed.length === 0)\n return new Map();\n const recorder = yield* IndexRecorder;\n const live = yield* recorder\n .activeFramesFor(keyed.map((entry) => entry.key))\n .pipe(Effect.catch((error) => Effect.logWarning(`conflict assist skipped: ${error.operation}`).pipe(Effect.as(new Map()))));\n const conflicts = new Map();\n /** frame key → the first op in this batch to occupy it. Built as the loop walks in order. */\n const seen = new Map();\n for (const entry of keyed) {\n const [stored] = live.get(entry.key) ?? [];\n const earlier = seen.get(entry.key);\n if (stored !== undefined) {\n conflicts.set(entry.index, {\n path: stored.path,\n batchIndex: null,\n claim: stored.gist\n });\n }\n else if (earlier !== undefined) {\n conflicts.set(entry.index, {\n path: null,\n batchIndex: earlier.index,\n claim: earlier.claim\n });\n }\n if (earlier === undefined)\n seen.set(entry.key, { index: entry.index, claim: entry.claim });\n }\n return conflicts;\n});\n/**\n * Fold last-wins over the caller's op array, before the decode fold, so a batch-internal loser\n * never reaches disk. The surviving value simply occupies the earliest slot with that key.\n *\n * Not derived from {@link detectFrameConflicts}' output, although the walk mirrors it. A store\n * match wins there, masking the batch-internal pair the plan needs, and the plan needs both: the\n * batch collision decides which value writes, and the store match decides what that write supersedes.\n *\n * The slot rule: the first occupant of a key keeps its position and later ops with the same key\n * replace its content (`plannedOps[slot] = laterOp`, provenance and all, since the surviving value\n * is the later op's own statement). The occupant-tracking never moves, so a third restatement\n * replaces the slot again, last wins, at a stable position a caller can index by.\n *\n * Fail-closed on both of the rule's own guards: a null frame key is never consolidated, and a\n * failed store lookup degrades to batch-internal consolidation only, through the same\n * `Effect.catch` → `logWarning` → neutral-shape path the conflict assist takes, because an opt-in\n * consolidation must not become a new way to lose writes.\n */\nconst planLastWins = (ops) => Effect.gen(function* () {\n /** frame key → the slot (earliest occupant's index) that carries this key's surviving value. */\n const slotOf = new Map();\n /** slot index → the op whose value currently occupies it. */\n const content = new Map();\n const losers = new Map();\n /** Slot indices in caller order, keyed and keyless alike. */\n const order = [];\n for (const [index, op] of ops.entries()) {\n const key = frameKeyOf(op.claim);\n if (key === null) {\n // No frame shape, no slot. The rule's guards fail closed, so this op is never touched.\n order.push(index);\n content.set(index, op);\n continue;\n }\n const slot = slotOf.get(key);\n if (slot === undefined) {\n slotOf.set(key, index);\n order.push(index);\n content.set(index, op);\n continue;\n }\n content.set(slot, op);\n losers.set(index, slot);\n }\n const pendingSupersede = new Map();\n if (slotOf.size > 0) {\n const recorder = yield* IndexRecorder;\n // One query for every surviving key, for detectFrameConflicts' reason: a per-slot lookup is\n // the quadratic-write-cost shape this codebase has already paid for once.\n const live = yield* recorder\n .activeFramesFor([...slotOf.keys()])\n .pipe(Effect.catch((error) => Effect.logWarning(`consolidation store lookup skipped: ${error.operation}`).pipe(Effect.as(new Map()))));\n for (const [key, slot] of slotOf) {\n const [stored] = live.get(key) ?? [];\n if (stored !== undefined)\n pendingSupersede.set(slot, stored.path);\n }\n }\n return {\n ops: order.flatMap((index) => {\n const op = content.get(index);\n return op === undefined ? [] : [{ index, op }];\n }),\n losers,\n pendingSupersede\n };\n});\n/**\n * Loser reports for a last-wins plan, derived from the winner slots' own final reports.\n *\n * A loser reports `ok` with `consolidatedInto` only when its slot's write landed, which means the\n * surviving value is on disk and the pointer names where. A slot that was skipped or refused took the\n * loser's value down with it, so the loser reports `skipped`, which is the retryable outcome and\n * the one an atomic abort already means: nothing of this op reached disk.\n */\nconst withConsolidation = (results, plan) => {\n if (plan === null || plan.losers.size === 0)\n return results;\n return results.map((report, index) => {\n const slot = plan.losers.get(index);\n if (slot === undefined)\n return report;\n const winner = results[slot];\n return winner?.ok === true && winner.skipped !== true\n ? { index, ok: true, consolidatedInto: slot }\n : { index, ok: false, skipped: true };\n });\n};\n/**\n * Write N memories: one commit, one reindex, per-op results in input order.\n *\n * **Two folds, not one.** Decode is the operations layer's job and the store never sees it, so a\n * malformed `memory_type` on op 4 has to be caught here. This function therefore folds decode\n * over the ops and hands the store only what decoded. The store then folds the render gate, dedup,\n * and path claim over that, and this function splices the two result sets back into one array in\n * the caller's index space. Anything less and a decode failure would either be invisible per-op or\n * would shift every later op's index by one.\n *\n * **One reindex, gated on a file having been written** (G4). The indexer's `update()` reads\n * `git diff` over one commit, so a batch that committed once costs one diff. A dedupe-only\n * batch, which commits nothing, skips it entirely, because moving the watermark for a commit that\n * never happened is what `writeMemory`'s own `if (result.created)` guard exists to avoid.\n *\n * **The conflict assist is a third pass and it is read-only** (AC-1-2). It runs before the store's\n * fold, over the ops as the caller sent them, and its findings are merged into the reports at the\n * end, so it observes the batch and never participates in it. Nothing downstream of\n * {@link detectFrameConflicts} branches on its result: the same files are written, the same commit is\n * made, and the same ops are refused whether the flag is on or off. That is what propose-only means,\n * and it is checkable by reading this function rather than by trusting a description.\n */\nexport const batchWrite = (params) => Effect.gen(function* () {\n const continueOnError = params.continueOnError === true;\n const store = yield* Store;\n const at = yield* nowSecond;\n /**\n * The assist, over the caller's own op array and before anything is written.\n *\n * Over `params.ops` rather than the decoded `inputs` below, so a conflict is reported in the\n * caller's index space directly and needs no `originOf` translation. An op the store then\n * refuses still gets its finding, which is the more useful order, because a caller told both \"this\n * op is malformed\" and \"it also contradicts X\" fixes one thing.\n *\n * Not gated on the ops being valid, and deliberately so. `frameKeyOf` is a pure lexical function\n * over a string, so it has nothing to refuse and cannot fail on an op the decode is about to reject.\n */\n const conflicts = params.detectConflicts === true\n ? yield* detectFrameConflicts(params.ops)\n : new Map();\n /**\n * The consolidation plan, before the decode fold and in the caller's index space. A\n * batch-internal loser is excluded from everything downstream, so its value never earns a file.\n * The surviving value sits at the earliest slot with its key, so every later report and\n * conflict finding stays at the index the caller sent.\n */\n const plan = params.consolidate === \"last-wins\" ? yield* planLastWins(params.ops) : null;\n const planned = plan === null ? [...params.ops.entries()].map(([index, op]) => ({ index, op })) : plan.ops;\n /**\n * Fold 1, decode. `Effect.result` rather than letting the failure escape, because a decode\n * refusal is this op's result and not the batch's.\n */\n const reports = params.ops.map(() => undefined);\n const inputs = [];\n /** Store-result position → caller's op index, since the store never sees a skipped op. */\n const originOf = [];\n let decodeAborted = false;\n for (const { index, op } of planned) {\n const decoded = yield* Effect.result(toWriteInput({ ...op, ...provenanceOf(params, op) }, at));\n if (decoded._tag === \"Failure\") {\n reports[index] = reportFailure(index, decoded.failure);\n if (!continueOnError) {\n decodeAborted = true;\n break;\n }\n continue;\n }\n originOf.push(index);\n inputs.push(decoded.success);\n }\n /**\n * An atomic decode abort touches the store at all. Nothing was written, so every other op,\n * including the ones that decoded, reports `skipped`, matching the store's own abort semantics\n * exactly rather than inventing a second one.\n */\n if (decodeAborted) {\n const results = withConsolidation(merged(reports, conflicts), plan);\n return { results, summary: summarize(results), commitSha: null };\n }\n /**\n * The extraction assist: one model call over the decoded ops, extracted entities unioned into\n * each op's own `entities` before anything is written, so they land as ordinary `memhtml-entity`\n * metas and the git tree, rather than the index, is what remembers them.\n *\n * After the decode fold because a refused op must not reach the prompt, and before the store\n * because the render is what serializes the metas. Failure costs exactly this batch's\n * extracted entities. The port being absent, the model being down, and an unreadable payload\n * all take the same logged-warning path, and the write itself never waits on a retry.\n * `entities: []` is what every write produced before this assist existed.\n */\n const extractor = (yield* ExtractorPort).extractor;\n if (extractor !== undefined && inputs.length > 0) {\n const items = inputs.map((input) => ({\n title: input.title,\n text: input.articleHtml !== undefined\n ? input.articleHtml\n : [input.claim, ...(input.body ?? [])].join(\"\\n\")\n }));\n const outcome = yield* Effect.result(extractor.extract(items));\n if (outcome._tag === \"Failure\") {\n yield* Effect.logWarning(`entity extraction skipped for this batch: ${outcome.failure.reason}`);\n }\n else {\n for (const [index, extracted] of outcome.success.entries()) {\n const input = inputs[index];\n if (input === undefined || extracted.length === 0)\n continue;\n const declared = input.entities ?? [];\n const union = [...declared, ...extracted.filter((entity) => !declared.includes(entity))];\n inputs[index] = { ...input, entities: union };\n }\n }\n }\n // Fold 2, the store: render gate, dedup against the folded state, one commit.\n const batch = yield* store.writeMemories(inputs, { continueOnError });\n for (const entry of batch.results) {\n const index = originOf[entry.index];\n if (index === undefined)\n continue;\n reports[index] =\n entry.ok || entry.skipped === true\n ? {\n index,\n ok: entry.ok,\n ...defined({\n path: entry.path,\n deduped: entry.deduped,\n existingPath: entry.existingPath,\n skipped: entry.skipped\n })\n }\n : reportFailure(index, entry.error);\n }\n // One reindex, after the commit, only when a file was actually written.\n if (batch.writtenPaths.length > 0)\n yield* reindex();\n for (const path of batch.writtenPaths)\n yield* recordLink(path, \"wrote\", params, at);\n /**\n * The store-supersede pass, after a successful batch commit: every surviving slot whose frame\n * key a live memory occupied archives that memory, in one `supersedeMemories` call.\n *\n * A slot qualifies when its report is `ok` with a path, including a dedupe, where the path is\n * the pre-existing file that already carries this slot's value. The stored occupant still\n * states the losing value, so superseding it is still correct. A slot that failed or was\n * skipped wrote nothing, so there is nothing for its occupant to lose to.\n *\n * `Effect.result` rather than a bare yield, because a failed supersede must not fail a batch whose\n * memories already landed. The degradation is annotate-only: `supersededPath` is omitted, the\n * warning says why, and the corpus is merely unconsolidated, which is what every batch produced\n * before this flag existed. On success there is one extra reindex, because archive paths moved.\n */\n if (plan !== null && plan.pendingSupersede.size > 0) {\n const pairs = [];\n const winnerOf = new Map();\n for (const [slot, storedPath] of plan.pendingSupersede) {\n const report = reports[slot];\n if (report === undefined || !report.ok || report.skipped === true)\n continue;\n if (report.path === undefined)\n continue;\n // A slot whose content deduped onto the occupant itself is a restatement rather than a\n // supersession. Winner and loser are one file, and archiving it would lose the value.\n if (report.path === storedPath)\n continue;\n pairs.push({ winnerPath: report.path, loserPath: storedPath });\n winnerOf.set(storedPath, slot);\n }\n if (pairs.length > 0) {\n const outcome = yield* Effect.result(store.supersedeMemories(pairs));\n if (outcome._tag === \"Failure\") {\n yield* Effect.logWarning(`consolidation supersede skipped: ${messageFor(outcome.failure)}`);\n }\n else {\n for (const entry of outcome.success.archived) {\n const slot = winnerOf.get(entry.loserPath);\n const report = slot === undefined ? undefined : reports[slot];\n if (slot === undefined || report === undefined)\n continue;\n reports[slot] = { ...report, supersededPath: entry.archivePath };\n }\n if (outcome.success.archived.length > 0)\n yield* reindex();\n }\n }\n }\n /**\n * An op the store aborted before reaching has no result of its own, and neither does one whose\n * decode succeeded in a batch the store then aborted. Both are `skipped`. Losers pick up their\n * `consolidatedInto` pointer last, from their winner slot's own final report.\n */\n const results = withConsolidation(merged(reports, conflicts), plan);\n return {\n results,\n summary: summarize(results),\n commitSha: batch.commitSha\n };\n});\n/**\n * Per-op provenance falls back to the batch's own.\n *\n * The batch call carries the session the agent is in, and an op may name its own (a `memhtml apply`\n * file replaying a previous session's writes). Per-op wins, because it is the more specific statement\n * about where that one memory came from.\n */\nconst provenanceOf = (params, op) => defined({\n sessionId: op.sessionId ?? params.sessionId,\n promptId: op.promptId ?? params.promptId,\n turnUuid: op.turnUuid ?? params.turnUuid\n});\n/**\n * The reports as their final array: an unreported op becomes `skipped`, and every op picks up the\n * assist's finding for its index.\n *\n * One function for both exit paths, the atomic decode abort and the normal return, because they had\n * already grown two copies of the same `?? skipped` fill and a third responsibility spliced into only\n * one of them is how a batch that aborted would silently lose its conflict findings. The abort path\n * needs them because nothing was written. A caller told \"op 2 is malformed\" and also \"op 0\n * contradicts areas/x.html\" can fix both before retrying, rather than discovering the second on the\n * next round trip.\n *\n * Merging here rather than at each report's construction site also keeps the assist out of the\n * write path. The reports are already final when the conflicts are attached, so there is no point at\n * which a conflict could be read by anything that decides an outcome.\n */\nconst merged = (reports, conflicts) => reports.map((report, index) => {\n const base = report ?? { index, ok: false, skipped: true };\n const conflict = conflicts.get(index);\n return conflict === undefined ? base : { ...base, conflict };\n});\n/** The counts, derived from the reports in one pass so they cannot disagree with them. */\nconst summarize = (results) => {\n let written = 0;\n let deduped = 0;\n let failed = 0;\n let skipped = 0;\n let consolidated = 0;\n for (const result of results) {\n // A batch-internal loser is neither written nor failed. Its value survived at another slot,\n // and no file of its own was ever attempted, so it partitions into its own count.\n if (result.consolidatedInto !== undefined)\n consolidated += 1;\n else if (result.skipped === true)\n skipped += 1;\n else if (!result.ok)\n failed += 1;\n else if (result.deduped === true)\n deduped += 1;\n else\n written += 1;\n }\n return { total: results.length, written, deduped, failed, skipped, consolidated };\n};\n/**\n * Read one memory, optionally recording that the session read it.\n *\n * The access bump lives here and nowhere else on the retrieval side, because salience accumulates\n * evidence that someone chose a memory and a ranker's guess is not a choice. An explicit open names\n * one path, through this call and the `memhtml://file/{path}` resource that funnels through it, which is\n * the strongest signal short of a write. A path merely returned by search or recall was the ranker's own\n * suggestion, and bumping it builds a rich-get-richer loop: today's top five rank higher\n * tomorrow while the memory that should displace them never breaks in to earn a first bump.\n *\n * `bumpAccess` sits beside `recordLink` deliberately. Both are notes about the read, both swallow their\n * own failures, and neither may cost the caller the memory it asked for.\n */\nexport const readMemory = (path, provenance = {}) => Effect.gen(function* () {\n const store = yield* Store;\n const result = yield* store.readMemory(path);\n yield* recordLink(result.path, \"read\", provenance, yield* nowSecond);\n yield* bumpAccess([result.path]);\n return result;\n});\n/**\n * Ranked search. The retrieval service sanitizes the query text itself in `fts-query.ts`, so this\n * function never MATCHes user prose and neither does any caller of it.\n *\n * **No access bump, and the omission is the rule rather than an oversight.** A hit is the ranker's\n * guess about what the caller wanted, so counting it as salience would let the ranking teach itself.\n * A memory in today's top five would rank higher tomorrow purely for having been listed, and the\n * memory that should displace it never appears and so never earns a first bump. The cooldown does not\n * help, because it bounds one query replayed within 900 seconds, while the drift it would have to\n * bound operates across days. Salience moves when a caller opens a path ({@link readMemory}) or names\n * an outcome ({@link reinforceMemories}).\n */\nexport const searchMemories = (params) => Effect.gen(function* () {\n const retrieval = yield* Retrieval;\n return yield* retrieval.search(params);\n});\n/**\n * A context pack under a character budget.\n *\n * No access bump either, for {@link searchMemories}' reason. A disclosed body is still the ranker's\n * choice of what to spend the budget on rather than the caller's choice of what to read.\n */\nexport const recallMemories = (params) => Effect.gen(function* () {\n const retrieval = yield* Retrieval;\n return yield* retrieval.recall(params);\n});\n/**\n * Bump access bookkeeping for paths a caller chose to open. A missing state plane makes this a no-op.\n *\n * `reinforce` is the one SQL writer for `state.access` and this helper does not become a second one.\n * It moves callers to that writer rather than moving the write here.\n */\nconst bumpAccess = (paths) => Effect.gen(function* () {\n if (paths.length === 0)\n return;\n const db = yield* DatabaseService;\n if (!db.hasState)\n return;\n yield* reinforce(db, paths, \"neutral\", yield* nowSecond).pipe(Effect.catch((error) => Effect.logWarning(`access bookkeeping missed: ${error.operation}`).pipe(Effect.as({ bumped: [], cooledDown: [] }))));\n});\n/**\n * Supersede a memory: the new file and the archived target land in one commit.\n *\n * The type defaults to the target's own. A correction that silently changed the type would move\n * the memory to a different retention profile and a different PARA directory, and that is a second\n * decision the caller did not make.\n */\nexport const correctMemory = (params) => Effect.gen(function* () {\n const store = yield* Store;\n const target = yield* store.readMemory(params.targetPath);\n const requested = params.memoryType ?? target.doc.metas.memoryType;\n const memoryType = yield* decodeWritableType(requested);\n const at = yield* nowSecond;\n const result = yield* store.correctMemory(params.targetPath, {\n title: params.title,\n claim: params.claim,\n memoryType,\n at,\n ...defined({\n body: params.body,\n articleHtml: params.articleHtml,\n reason: params.reason,\n sessionId: params.sessionId,\n promptId: params.promptId,\n turnUuid: params.turnUuid\n })\n });\n // A correction is an add and a rename in one commit, so it needs the diff-driven path. The\n // archived target's row has to move rather than be re-added under a new name beside its old one.\n yield* reindex();\n yield* recordLink(result.path, \"corrected\", params, at);\n return result;\n});\n/**\n * Add an authored edge. Idempotent on `(rel, href)`, so a re-run commits nothing.\n *\n * The rel is decoded against {@link AUTHORABLE_RELS}, the memory class plus the task class, so\n * `memhtml link a.html blocks b.html` reaches the task graph while a person or provenance rel, both of\n * which the system mints itself, stays unauthorable.\n */\nexport const linkMemories = (srcPath, rel, dstPath) => Effect.gen(function* () {\n const edgeRel = yield* decodeAuthorableRel(rel);\n const store = yield* Store;\n const src = normalizePath(srcPath);\n const result = yield* store.linkMemories(src, edgeRel, dstPath);\n // `addLink` is idempotent on the pair, so a re-link commits nothing and there is nothing to\n // index. Re-deriving a diff for a no-op would move the watermark for a commit that never was.\n if (result.commitSha !== null)\n yield* reindex();\n return { ...result, srcPath: src, dstPath: normalizePath(dstPath), rel: edgeRel };\n});\n/** Soft-evict: `git mv` into `archive/<YYYY>/` with the archive stamps. Never a delete. */\nexport const archiveMemory = (path, reason) => Effect.gen(function* () {\n const store = yield* Store;\n const result = yield* store.archiveMemory(path, reason);\n // An archive is a pure rename. Handled as two independent paths it would leave the source row\n // live and duplicate the chunks. The diff path re-points the row and keeps the vector.\n yield* reindex();\n return result;\n});\n/** Bump access bookkeeping deliberately, with a caller-chosen signal. */\nexport const reinforceMemories = (paths, signal) => Effect.gen(function* () {\n const decoded = yield* decodeSignal(signal);\n const db = yield* DatabaseService;\n const at = yield* nowSecond;\n if (!db.hasState) {\n return { bumped: [], cooledDown: paths, signal: decoded };\n }\n const result = yield* reinforce(db, paths, decoded, at);\n return { ...result, signal: decoded };\n});\n/**\n * The memory graph around one path, to a fixed depth of at most two hops.\n *\n * **Two fixed-depth joins in a `UNION ALL`, deliberately not a recursive CTE.** The depth is\n * bounded at 2 by the tool's contract, so recursion buys nothing and costs the one thing a graph\n * query must not have here: an unbounded worst case on a corpus whose `relates_to` edges are\n * mined by the sleep cycle and can be dense. A fixed join is also index-covered by `edges_src`\n * and `edges_dst`, which a recursive walk is not.\n *\n * **Both directions, and `derived = 0 ∪ derived = 1`.** An edge is an assertion about a pair, and\n * which file happens to hold the `<link>` is authorship rather than direction of meaning. A\n * neighborhood that read only outbound edges would show a superseding memory its target and hide\n * from the target that it had been superseded. Derived edges are included because lateral retrieval\n * is what they are for. `derived` is still reported per node so a caller can tell a\n * sleep-mined suspicion from an authored assertion.\n *\n * `edge_class = 'memory'` on every join. A person edge entering here would put\n * `resources/people/*` into a memory neighborhood, and the class column exists to make that\n * structurally impossible.\n */\nexport const neighborsOf = (params) => Effect.gen(function* () {\n const db = yield* DatabaseService;\n const center = normalizePath(params.path);\n const depth = Math.min(2, Math.max(1, Math.trunc(params.depth ?? 1)));\n const rels = (params.rels ?? []).filter((rel) => isEdgeRel(rel) && relClassFor(rel) === \"memory\");\n const relFilter = rels.length > 0 ? ` AND e.rel IN (${rels.map(() => \"?\").join(\", \")})` : \"\";\n const relFilter2 = rels.length > 0 ? ` AND e2.rel IN (${rels.map(() => \"?\").join(\", \")})` : \"\";\n /**\n * Hop 1 is the center's own edges, either direction. Hop 2 walks one further from each hop-1\n * node and excludes the center, so a two-cycle does not report the center as its own neighbor\n * at distance 2.\n */\n const hopOne = `\n SELECT e.dst_path AS path, e.rel AS rel, e.derived AS derived, 1 AS hop\n FROM edges e\n WHERE e.src_path = ?1 AND e.edge_class = 'memory'${relFilter}\n UNION ALL\n SELECT e.src_path AS path, e.rel AS rel, e.derived AS derived, 1 AS hop\n FROM edges e\n WHERE e.dst_path = ?1 AND e.edge_class = 'memory'${relFilter}`;\n const hopTwo = `\n SELECT e2.dst_path AS path, e2.rel AS rel, e2.derived AS derived, 2 AS hop\n FROM edges e\n JOIN edges e2 ON e2.src_path = e.dst_path\n WHERE e.src_path = ?1 AND e.edge_class = 'memory' AND e2.edge_class = 'memory'\n AND e2.dst_path <> ?1${relFilter}${relFilter2}\n UNION ALL\n SELECT e2.src_path AS path, e2.rel AS rel, e2.derived AS derived, 2 AS hop\n FROM edges e\n JOIN edges e2 ON e2.dst_path = e.src_path\n WHERE e.dst_path = ?1 AND e.edge_class = 'memory' AND e2.edge_class = 'memory'\n AND e2.src_path <> ?1${relFilter}${relFilter2}`;\n const walk = depth === 1 ? hopOne : `${hopOne}\\n UNION ALL${hopTwo}`;\n /**\n * `min(hop)` per path: a node reachable both directly and via a detour is a 1-hop neighbor,\n * and reporting it twice would let one memory occupy two slots in a bounded answer.\n *\n * The join onto `files` is an inner join, so an edge pointing at a path the tree does not hold\n * contributes nothing. A dangling href is `memhtml doctor`'s finding rather than a titleless node.\n */\n const rows = yield* db.all(`SELECT w.path AS path, f.title AS title, min(w.hop) AS hop,\n min(w.rel) AS rel, max(w.derived) AS derived\n FROM (${walk}) w\n JOIN files f ON f.path = w.path\n GROUP BY w.path\n ORDER BY hop ASC, w.path ASC`, \n // The rel list binds once per occurrence of the filter, in textual order: hop 1 uses it\n // twice, hop 2 uses it four times. Getting this count wrong is a bind mismatch rather than a\n // wrong answer, so it fails loudly.\n [\n center,\n ...(depth === 1\n ? [...rels, ...rels]\n : [...rels, ...rels, ...rels, ...rels, ...rels, ...rels])\n ]);\n const nodes = rows.map((row) => ({\n path: row.path,\n title: row.title,\n hop: row.hop,\n rel: row.rel\n }));\n return { center, depth, nodes, edges: nodes.length };\n});\n/**\n * Page the corpus by facet.\n *\n * Keyset pagination on `path` rather than `LIMIT/OFFSET`. `files.path` is the primary key and it also\n * moves, because eviction is a `git mv`, so an offset page taken while a sleep cycle archives a file\n * would skip a row or repeat one. A cursor on the path itself is stable against that.\n */\nexport const listMemories = (params) => Effect.gen(function* () {\n const db = yield* DatabaseService;\n const limit = Math.min(500, Math.max(1, Math.trunc(params.limit ?? 50)));\n const conditions = [];\n const values = [];\n if (params.includeArchived !== true)\n conditions.push(\"f.archived = 0\");\n if (params.memoryType !== undefined && params.memoryType !== \"\") {\n const memoryType = yield* decodeWritableType(params.memoryType);\n conditions.push(\"f.memory_type = ?\");\n values.push(memoryType);\n }\n if (params.workspace !== undefined && params.workspace !== \"\") {\n conditions.push(\"f.workspace = ?\");\n values.push(params.workspace);\n }\n if (params.para !== undefined && params.para !== \"\") {\n conditions.push(\"f.para = ?\");\n values.push(params.para);\n }\n if (params.tag !== undefined && params.tag !== \"\") {\n conditions.push(\"EXISTS (SELECT 1 FROM file_tags t WHERE t.path = f.path AND t.tag = ?)\");\n values.push(params.tag);\n }\n if (params.entity !== undefined && params.entity !== \"\") {\n // The entity arrives as `type:name` and the table splits it at the first colon, so the\n // comparison rebuilds the reference rather than making the caller know the split.\n conditions.push(\"EXISTS (SELECT 1 FROM file_entities e WHERE e.path = f.path AND e.entity_type || ':' || e.entity_name = ?)\");\n values.push(params.entity);\n }\n if (params.cursor !== undefined && params.cursor !== \"\") {\n conditions.push(\"f.path > ?\");\n values.push(normalizePath(params.cursor));\n }\n const where = conditions.length === 0 ? \"\" : `WHERE ${conditions.join(\" AND \")}`;\n const rows = yield* db.all(`SELECT f.path, f.title, f.memory_type, f.gist, f.workspace, f.para,\n f.confidence, f.importance, f.archived, f.updated_at\n FROM files f ${where} ORDER BY f.path ASC LIMIT ?`, [...values, limit + 1]);\n // One row over the limit is fetched to decide whether a next page exists, then dropped. A\n // separate COUNT would be a second statement over the same predicate and could disagree with\n // this one under a concurrent write.\n const page = rows.slice(0, limit);\n const nextCursor = rows.length > limit ? (page.at(-1)?.path ?? null) : null;\n return {\n files: page.map((row) => ({\n path: row.path,\n title: row.title,\n memoryType: row.memory_type,\n gist: row.gist,\n workspace: row.workspace,\n para: row.para,\n confidence: row.confidence,\n importance: row.importance,\n archived: row.archived === 1,\n updatedAt: row.updated_at\n })),\n nextCursor\n };\n});\n/**\n * Move a task to a new status.\n *\n * **`setMeta`, never parse→serialize.** The editors splice by source offset, so the article's bytes\n * cannot move on a status change, and neither can `memhtml-content-hash`, the dedupe key, or any chunk\n * id hanging off it. A round trip through the serializer drops a `<pre>` newline per write, which\n * would re-embed a task for a one-word edit and break the hash the file claims for itself.\n *\n * **`done` routes through `store.archiveMemory`**, which is a `git mv`. That is the design decision\n * that keeps `done` off the `memhtml-status` axis. Finishing a task stamps the status and moves the file\n * under `archive/<YYYY>/`, so \"what did I finish\" is the archive tree plus `git log` rather than a\n * fifth value every archive, correction, and publish path would have to learn. The stamp is written\n * before the move so both land in one commit and `git log --follow` reads through it.\n *\n * `indexer.update()` afterwards rather than `indexPaths`, because the `done` transition is a rename and\n * `indexPaths` cannot express one. It would leave the pre-archive row live, duplicate the chunks\n * under two paths, and skip the watermark (finding from T9, stated at {@link reindex}).\n */\nexport const setTaskStatus = (params) => Effect.gen(function* () {\n const status = yield* decodeTaskStatus(params.status);\n const store = yield* Store;\n const path = normalizePath(params.path);\n const at = yield* nowSecond;\n // Read through the parser. A `memhtml task status` on a memory file would otherwise stamp a meta the\n // format refuses on that type, producing a file the indexer then declines to project.\n const existing = yield* store.readMemory(path);\n if (existing.doc.metas.memoryType !== \"task\") {\n return yield* Effect.fail(InvalidMemory.make({\n reason: `${path} is a ${existing.doc.metas.memoryType} memory, not a task: only a task carries memhtml-task-status`\n }));\n }\n /**\n * A no-op status change writes nothing and commits nothing, so a re-run is free and the tree\n * stays byte-identical. The `memhtml-updated` stamp is skipped along with it, because a fresh\n * timestamp with no status change would claim the task moved when it did not.\n */\n if (existing.doc.metas.taskStatus === status) {\n return {\n path,\n taskStatus: status,\n archived: false,\n commitSha: null,\n unchanged: true\n };\n }\n const stamped = setMeta(setMeta(existing.html, \"memhtml-task-status\", status), \"memhtml-updated\", at);\n yield* attemptIo(`task.write:${path}`, async () => {\n const { writeFile } = await import(\"node:fs/promises\");\n const { join } = await import(\"node:path\");\n await writeFile(join(store.root, path), stamped, \"utf8\");\n });\n if (status !== \"done\") {\n yield* store.git.add([path]);\n const commit = yield* store.git.commit(commitSubject(\"task\", `${status} ${path}`));\n yield* reindex();\n return {\n path,\n taskStatus: status,\n archived: false,\n commitSha: commit.sha,\n unchanged: false\n };\n }\n /**\n * `archiveMemory` stages the `git mv` and commits, and it reads the file from disk, so the\n * `memhtml-task-status: done` stamp written just above travels with the move rather than needing a\n * second commit. `git mv` carries a working-tree modification with it (probed live 2026-08-02:\n * the staged blob is the pre-edit content and the worktree keeps the edit), and `archiveMemory`\n * re-writes the stamped bytes at the destination before staging, so the committed file holds\n * both the archive stamps and the done status.\n */\n const archived = yield* store.archiveMemory(path, params.reason ?? `task ${status}`);\n yield* reindex();\n return {\n path,\n taskStatus: status,\n archived: true,\n archivePath: archived.archivePath,\n commitSha: archived.commitSha,\n unchanged: false\n };\n});\n/**\n * The task working set: a direct indexed scan, deliberately not retrieval.\n *\n * No RRF, no MMR, no embedding. A to-do list is not a ranking problem. An agent asking \"what is\n * open\" wants every row in a stable order, and a relevance score over working state would make the\n * answer depend on a query the caller does not have. The partial index `files_task_status`\n * (`WHERE memory_type='task' AND archived=0`) is what makes the default scan cheap.\n *\n * `blockedBy` is one correlated subquery over `edges`, filtered to `edge_class='task'` and\n * `rel='blocks'`. **The class filter is redundant with the rel filter today and is kept anyway.**\n * Probed live 2026-08-02: `0008_tasks.sql`'s per-class CHECKs refuse `blocks` under `memory`,\n * `person`, and `provenance`, so `rel='blocks'` already implies the class, and a mutation removing the\n * class predicate leaves every test green. It stays because the class column is what every\n * memory-graph query filters on, and a reader who saw this one query trust the rel alone would learn\n * the wrong rule about how the firewall is enforced.\n *\n * `group_concat` over an ordered subselect, probed 2026-08-12 on node 24.19.0. The inner `ORDER BY`\n * is preserved, and `char(10)` is the separator because a path cannot contain a newline while it can\n * contain a comma.\n *\n * The join is deliberately not an inner join onto `files`. A blocker whose file left the tree still\n * blocks, and hiding it here would make a permanently-blocked task look ready. `memhtml doctor` reports\n * that as a finding, and this function reports the edge as the corpus states it.\n */\nexport const listTasks = (params) => Effect.gen(function* () {\n const db = yield* DatabaseService;\n const limit = Math.min(500, Math.max(1, Math.trunc(params.limit ?? 50)));\n const conditions = [\"f.memory_type = 'task'\"];\n const values = [];\n if (params.includeArchived !== true)\n conditions.push(\"f.archived = 0\");\n if (params.status !== undefined && params.status !== \"\") {\n const status = yield* decodeTaskStatus(params.status);\n conditions.push(\"f.task_status = ?\");\n values.push(status);\n }\n if (params.workspace !== undefined && params.workspace !== \"\") {\n conditions.push(\"f.workspace = ?\");\n values.push(params.workspace);\n }\n if (params.dueBefore !== undefined && params.dueBefore !== \"\") {\n const dueBefore = yield* decodeDueAt(params.dueBefore);\n /**\n * `substr(…, 1, 10)` on both sides, so the comparison is one of calendar days.\n *\n * The case it fixes, established by enumeration 2026-08-02: a due date stored as a bare day\n * against a bound carrying a time on that same day. Whole-string,\n * `\"2026-08-25\" < \"2026-08-25T09:00:00Z\"` is true, because the shorter string is a prefix and\n * sorts first, so a task due sometime on the 25th would be reported late at 09:00 on the 25th.\n * A day-granularity deadline is not late until the day is over, and truncating both sides is\n * what says so. Every other combination of the two forms agrees either way.\n */\n conditions.push(\"f.due_at IS NOT NULL AND substr(f.due_at, 1, 10) < substr(?, 1, 10)\");\n values.push(dueBefore);\n }\n if (params.cursor !== undefined && params.cursor !== \"\") {\n conditions.push(\"f.path > ?\");\n values.push(normalizePath(params.cursor));\n }\n const rows = yield* db.all(`SELECT f.path, f.title, f.task_status, f.due_at, f.workspace, f.archived, f.updated_at,\n (SELECT group_concat(b.src_path, char(10)) FROM (\n SELECT e.src_path AS src_path FROM edges e\n WHERE e.dst_path = f.path AND e.edge_class = 'task' AND e.rel = 'blocks'\n ORDER BY e.src_path ASC) b) AS blocked_by\n FROM files f\n WHERE ${conditions.join(\" AND \")}\n ORDER BY f.path ASC LIMIT ?`, [...values, limit + 1]);\n const page = rows.slice(0, limit);\n const nextCursor = rows.length > limit ? (page.at(-1)?.path ?? null) : null;\n return {\n tasks: page.map((row) => ({\n path: row.path,\n title: row.title,\n taskStatus: row.task_status,\n dueAt: row.due_at,\n workspace: row.workspace,\n archived: row.archived === 1,\n updatedAt: row.updated_at,\n blockedBy: row.blocked_by === null ? [] : row.blocked_by.split(\"\\n\")\n })),\n nextCursor\n };\n});\n/**\n * `mergeTailExtract` as the merger `persistScanned` requires.\n *\n * The two shapes differ in exactly one field, and the difference is real rather than cosmetic.\n * `SessionExtract` carries `counters`, the parse bookkeeping of one scan, and the persisted\n * `traces` row does not, because those are facts about a read rather than about the session.\n * `readStoredExtract` therefore cannot reconstruct them, and the merge is handed zeros for the\n * stored side. The merged counters then describe this scan alone, which is the only reading\n * available. Inventing a stored value would produce a number that claims to count lines nobody\n * read.\n *\n * Every other field the merge reads is present on both shapes, so this adapter is total.\n */\nconst ZERO_COUNTERS = {\n parsedLines: 0,\n droppedLines: 0,\n droppedNoSession: 0,\n skippedTypeLines: 0,\n unknownTypeLines: 0\n};\nconst tailMerger = (stored, tail) => mergeTailExtract({ ...stored, counters: ZERO_COUNTERS }, { ...tail, counters: ZERO_COUNTERS });\n/**\n * Scan the trace root and persist what changed.\n *\n * {@link tailMerger} is passed as the tail merger, and this is the only correct way to call\n * `persistScanned` on a `tail` action. A tail's extract describes the appended slice, so its\n * `first_prompt` is a mid-conversation prompt, its `started_at` is later than the session's, and\n * its prompt ordinals restart at 0. `persistScanned` takes the merger as a parameter precisely so\n * that \"never upsert a tail extract directly\" is a type-level obligation rather than a convention.\n */\nexport const indexTraces = () => Effect.gen(function* () {\n const roots = yield* Roots;\n const db = yield* DatabaseService;\n const at = yield* nowSecond;\n const report = yield* scanTraceRoot(roots.traceRoot, readWatermark(db));\n let sessionsWritten = 0;\n let promptsWritten = 0;\n let merged = 0;\n for (const scanned of report.files) {\n const outcome = yield* persistScanned(db, scanned, tailMerger, at);\n if (outcome.action !== \"skip\")\n sessionsWritten += 1;\n if (outcome.merged)\n merged += 1;\n promptsWritten += outcome.promptsWritten;\n }\n return {\n traceRoot: roots.traceRoot,\n filesSeen: report.files.length,\n skipped: report.skipped,\n tailed: report.tailed,\n rescanned: report.rescanned,\n bytesRead: report.bytesRead,\n sessionsWritten,\n promptsWritten,\n tailsMerged: merged\n };\n});\n/**\n * FTS over session first-prompts and AI titles.\n *\n * The query goes through the same sanitizer the memory arms use, and it has to. An apostrophe is a\n * hard driver error rather than an empty result, and \"what did I ask about don't-repeat-yourself\"\n * is an ordinary trace query. An empty sanitized query returns the most recent sessions rather\n * than nothing, because a caller with no terms wants a listing and an empty MATCH is not a listing.\n *\n * This is the trace plane and it stops here. No memory table is named, and nothing in the\n * retrieval assembler names `traces`. The firewall is by table name, in both directions.\n */\nexport const searchTraces = (params) => Effect.gen(function* () {\n const db = yield* DatabaseService;\n const match = sanitizeFtsQuery(params.query);\n const limit = Math.min(200, Math.max(1, Math.trunc(params.limit ?? 20)));\n const conditions = [];\n const values = [];\n /**\n * The MATCH names `traces_fts` rather than a column of `traces`. The index is an external-content\n * FTS5 table, so it is joined in by rowid and only reached when there is something to match. Without\n * a query the statement never mentions it, which is what keeps a bare listing a plain table scan.\n */\n const matched = match !== \"\";\n const from = matched\n ? \"FROM traces_fts JOIN traces t ON t.rowid = traces_fts.rowid\"\n : \"FROM traces t\";\n if (matched) {\n conditions.push(\"traces_fts MATCH ?\");\n values.push(match);\n }\n if (params.cwd !== undefined && params.cwd !== \"\") {\n conditions.push(\"t.cwd = ?\");\n values.push(params.cwd);\n }\n if (params.since !== undefined && params.since !== \"\") {\n conditions.push(\"t.started_at >= ?\");\n values.push(params.since);\n }\n const where = conditions.length === 0 ? \"\" : `WHERE ${conditions.join(\" AND \")}`;\n // A matched query orders by relevance, ascending because FTS5's bm25 is negative-is-better.\n // Without a match there is no relevance to order by and recency is the useful order.\n const order = matched ? \"ORDER BY bm25(traces_fts)\" : \"ORDER BY t.started_at DESC\";\n const rows = yield* db.all(`SELECT t.session_id, t.slug, t.cwd, t.started_at, t.prompt_count, t.first_prompt, t.ai_title\n ${from} ${where} ${order} LIMIT ?`, [...values, limit]);\n return {\n sessions: rows.map((row) => ({\n sessionId: row.session_id,\n slug: row.slug,\n cwd: row.cwd,\n startedAt: row.started_at,\n promptCount: row.prompt_count,\n firstPrompt: row.first_prompt,\n aiTitle: row.ai_title\n })),\n degraded: match === \"\"\n };\n});\n/**\n * The memory-session links, from either side.\n *\n * Both parameters absent is a refusal rather than an unbounded scan of every link ever recorded. A\n * tool whose no-argument form returns the whole table is a tool an agent calls by accident.\n */\nexport const traceLinks = (params) => Effect.gen(function* () {\n const hasSession = params.sessionId !== undefined && params.sessionId !== \"\";\n const hasPath = params.path !== undefined && params.path !== \"\";\n if (!hasSession && !hasPath) {\n return yield* Effect.fail(InvalidMemory.make({ reason: \"trace links needs a session_id or a path\" }));\n }\n const db = yield* DatabaseService;\n const conditions = [];\n const values = [];\n if (hasSession) {\n conditions.push(\"l.session_id = ?\");\n values.push(params.sessionId);\n }\n if (hasPath) {\n conditions.push(\"l.path = ?\");\n values.push(normalizePath(params.path));\n }\n const rows = yield* db.all(`SELECT l.path, l.session_id, l.prompt_id, l.turn_uuid, l.link_kind, l.at\n FROM memory_session_links l\n WHERE ${conditions.join(\" AND \")}\n ORDER BY l.at DESC, l.path ASC`, values);\n return {\n links: rows.map((row) => ({\n path: row.path,\n sessionId: row.session_id,\n promptId: row.prompt_id,\n turnUuid: row.turn_uuid,\n linkKind: row.link_kind,\n at: row.at\n }))\n };\n});\n/**\n * Corpus health, in one call.\n *\n * `indexFresh` compares the recorded watermark to `HEAD`, which is the only answer that means\n * anything. The index is a projection of a commit, so \"fresh\" means \"the commit it describes is the\n * commit we are on\". A count of rows would say the index exists rather than that it is current.\n *\n * `embedderUp` is read off the stored watermark rather than by probing Bedrock. A status call that\n * made a network request would fail for a reason unrelated to the corpus, and what a caller\n * needs to know is whether the vectors in this index are usable.\n */\nexport const statusReport = () => Effect.gen(function* () {\n const store = yield* Store;\n const db = yield* DatabaseService;\n const headSha = yield* store.git.revParseHead();\n const dirty = yield* store.dirtyPaths();\n const state = yield* readIndexState(db).pipe(Effect.orElseSucceed(() => undefined));\n const byType = yield* countRows(db, \"SELECT memory_type AS k, count(*) AS n FROM files WHERE archived = 0 GROUP BY memory_type\");\n const archivedCount = yield* countOne(db, \"SELECT count(*) AS n FROM files WHERE archived = 1\");\n const edges = yield* countOne(db, \"SELECT count(*) AS n FROM edges\");\n const derivedEdges = yield* countOne(db, \"SELECT count(*) AS n FROM edges WHERE derived = 1\");\n const embeddings = yield* countOne(db, \"SELECT count(*) AS n FROM embeddings\");\n const chunks = yield* countOne(db, \"SELECT count(*) AS n FROM chunks\");\n const traces = yield* countOne(db, \"SELECT count(*) AS n FROM traces\");\n const lastSleep = yield* db\n .get(\"SELECT run_id, status, started_at FROM sleep_runs ORDER BY started_at DESC LIMIT 1\")\n .pipe(Effect.orElseSucceed(() => undefined));\n return {\n root: store.root,\n headSha,\n dirty: dirty.length > 0,\n dirtyPaths: dirty,\n countsByType: byType,\n archivedCount,\n edges,\n derivedEdges,\n chunks,\n embeddings,\n traces,\n indexFresh: state?.head_sha !== null && state?.head_sha === headSha,\n indexHeadSha: state?.head_sha ?? null,\n embedModel: state?.embed_model ?? null,\n // A stored watermark that disagrees with the configured one means every cosine in this index\n // is against a different vector space. Reporting it as \"up\" would be the silent half-migration\n // the indexer refuses at write time.\n embedderUp: state !== undefined && state.embed_model === EMBED_WATERMARK && embeddings > 0,\n hasState: db.hasState,\n lastSleep: lastSleep === undefined\n ? null\n : { runId: lastSleep.run_id, status: lastSleep.status, startedAt: lastSleep.started_at }\n };\n});\n/** One scalar count, `0` when the table is unreachable. */\nconst countOne = (db, sql) => db.get(sql).pipe(Effect.map((row) => row?.n ?? 0));\n/** A `GROUP BY` into a record. An absent key means zero, so the caller never reads a null. */\nconst countRows = (db, sql) => db\n .all(sql)\n .pipe(Effect.map((rows) => Object.fromEntries(rows.map((row) => [row.k, row.n]))));\n/** Re-exported so the write path's type guard is usable by a caller building tool schemas. */\nexport { isWritableMemoryType };\n//# sourceMappingURL=operations.js.map","import { MEMORY_RELS } from \"@memhtml/contracts/edges\";\nimport { TASK_STATUSES, WRITABLE_MEMORY_TYPES } from \"@memhtml/contracts/types\";\nimport { REINFORCE_SIGNALS } from \"@memhtml/domain\";\nimport { SLEEP_PHASES } from \"@memhtml/sleep\";\nimport { CONFIG_VARS } from \"./config.js\";\nimport { ERROR_CODES } from \"./envelope.js\";\nimport { AUTHORABLE_RELS } from \"./operations.js\";\n/** Flags every command accepts. Listed once so the manifest cannot drift from behavior. */\nexport const GLOBAL_FLAGS = [\n {\n name: \"json\",\n type: \"boolean\",\n description: \"Emit the typed JSON envelope on stdout (default; logs go to stderr).\",\n default: true\n },\n {\n name: \"dense\",\n type: \"boolean\",\n description: \"Minify JSON and drop null fields, for pasting into a context window.\",\n default: false\n },\n {\n name: \"repo\",\n type: \"string\",\n description: \"Path to the memory repo. Defaults to $MEMHTML_ROOT.\",\n default: \"\"\n }\n];\n/** Flags every retrieval command shares, so `search` and `recall` cannot scope differently. */\nconst SCOPE_FLAGS = [\n {\n name: \"type\",\n type: \"string\",\n description: \"Restrict to one memory type. Repeatable; each occurrence broadens (ANY-of).\",\n values: WRITABLE_MEMORY_TYPES,\n repeatable: true\n },\n {\n name: \"workspace\",\n type: \"string\",\n description: \"Restrict to one workspace. STRICT: a scoped query never returns a memory with no workspace.\"\n },\n {\n name: \"tag\",\n type: \"string\",\n description: \"Restrict to memories carrying any of these tags. Repeatable; each broadens.\",\n repeatable: true\n },\n {\n name: \"entity\",\n type: \"string\",\n // Singular, unlike --tag, because the scope exists to chain one hop off a hit's own entity list,\n // which is one reference at a time. Same spelling `memhtml list --entity` takes, so the two are\n // one vocabulary rather than two facets that happen to share a word.\n description: \"Restrict to memories carrying one `type:name` entity reference, e.g. service:checkout-api, the form a hit's `entities` publishes, so a hop is a copy. A scope matching nothing returns no hits and says so; it never widens.\"\n },\n {\n name: \"include-archived\",\n type: \"boolean\",\n description: \"Include archived memories. Eviction is a `git mv`, so they still exist.\",\n default: false\n },\n {\n name: \"as-of\",\n type: \"string\",\n description: \"Point-in-time view: returns what was believed valid at this ISO instant, including since-superseded memories (marked superseded_by). The validity window is coalesce(valid_from, event_at, created_at) <= as-of < valid_until.\"\n }\n];\n/**\n * The single source of parsing, validation, and the manifest. A command lands here\n * before it lands anywhere else, so `memhtml manifest` and `memhtml agents-doc` describe\n * what the binary actually accepts rather than what someone remembered to document.\n *\n * A subcommand is one entry with a space in its name (`index rebuild`), not a nested tree.\n * Flattening keeps `nearest()` able to suggest across the whole surface, a typo in the noun\n * (`memhtml indx rebuild`) and a typo in the verb (`memhtml index rebiuld`) both get a candidate, and\n * keeps one table driving parsing, the manifest, and the generated doc.\n */\nexport const COMMANDS = [\n {\n name: \"manifest\",\n summary: \"Emit this CLI's full machine-readable contract.\",\n args: [],\n flags: [],\n responseTypes: [\"cli.manifest\"]\n },\n {\n name: \"init\",\n summary: \"Scaffold a memory repo at --repo/$MEMHTML_ROOT: git init, PARA dirs, merge driver.\",\n args: [],\n flags: [],\n responseTypes: [\"repo.init\"]\n },\n {\n name: \"write\",\n summary: \"Write one memory. Content-hash duplicates return the existing path, uncommitted.\",\n args: [],\n flags: [\n {\n name: \"title\",\n type: \"string\",\n description: \"The memory's title. Becomes the <title> and the filename slug.\",\n required: true\n },\n {\n name: \"claim\",\n type: \"string\",\n description: \"The one load-bearing sentence. Becomes the <mark> span and files.gist. Exactly one of --claim or --article-html.\"\n },\n {\n name: \"body\",\n type: \"string\",\n description: \"A prose paragraph after the claim. Repeatable, one <p> each.\",\n repeatable: true\n },\n {\n name: \"article-html\",\n type: \"string\",\n description: \"Raw <article> markup used verbatim in place of --claim/--body. Must contain exactly one <mark> in the first <p> or <li>; the first <time datetime> becomes the memory's event time. The store refuses format violations before any commit. Exactly one of --claim or --article-html.\"\n },\n {\n name: \"type\",\n type: \"string\",\n description: \"The memory type. `arc` is absent: an arc is synthesized by sleep.\",\n values: WRITABLE_MEMORY_TYPES,\n required: true\n },\n {\n name: \"path\",\n type: \"string\",\n description: \"An explicit path override. Ignored when it is not a valid memory path.\"\n },\n { name: \"workspace\", type: \"string\", description: \"Routes the memory to projects/<slug>/.\" },\n {\n name: \"tag\",\n type: \"string\",\n description: \"A tag. Repeatable; the first one routes an unplaced resource memory.\"\n },\n {\n name: \"entity\",\n type: \"string\",\n description: \"A `type:name` entity reference, e.g. service:checkout-api. Repeatable.\",\n repeatable: true\n },\n {\n name: \"importance\",\n type: \"int\",\n description: \"1-10, a display ordinal. The retention scorer divides by 10.\"\n },\n { name: \"confidence\", type: \"string\", description: \"0-1. 1.0 is an unqualified assertion.\" },\n {\n name: \"session-id\",\n type: \"string\",\n description: \"The Claude Code session. Stamped into the head AND indexed as a link.\"\n },\n { name: \"prompt-id\", type: \"string\", description: \"The prompt within that session.\" },\n { name: \"turn-uuid\", type: \"string\", description: \"The turn within that session.\" }\n ],\n responseTypes: [\"memory.written\"]\n },\n {\n name: \"apply\",\n summary: \"Write many memories from a JSONL op stream: ONE commit, ONE index update, per-op results.\",\n args: [],\n flags: [\n {\n name: \"file\",\n type: \"string\",\n description: \"The JSONL file to read. One complete JSON object per line. Omit it (or pass `-`) to read the stream from stdin.\"\n },\n {\n name: \"continue-on-error\",\n type: \"boolean\",\n description: \"Best-effort: a refused op is reported and skipped while every surviving op lands in the one commit. Atomic by default. The first refused op aborts the batch and nothing is written.\",\n default: false\n },\n {\n name: \"detect-conflicts\",\n type: \"boolean\",\n description: \"Report each op's frame-matches as a per-op `conflict`: the ACTIVE memory (or the earlier op) whose claim occupies the same subject-and-relation slot. PROPOSE-ONLY: every op still writes exactly as it would have, because sometimes the contradiction is the answer. You decide: write anyway, `memhtml correct` the match, or drop the line.\",\n default: false\n },\n {\n name: \"consolidate\",\n type: \"string\",\n values: [\"last-wins\"],\n description: \"Resolve frame-key matches instead of only reporting them: `--consolidate last-wins` makes the LATER op's value win a shared claim slot (one file, written at the FIRST index that claimed the slot, with each later restatement reporting `consolidated_into` naming that slot) and archives a stored ACTIVE memory a surviving slot displaces, reported as `superseded_path`. Off by default; claims with no frame shape are never consolidated.\"\n },\n {\n name: \"session-id\",\n type: \"string\",\n description: \"The Claude Code session for every op that names none. A line's own `session_id` wins over this.\"\n },\n { name: \"prompt-id\", type: \"string\", description: \"The prompt within that session.\" },\n { name: \"turn-uuid\", type: \"string\", description: \"The turn within that session.\" }\n ],\n responseTypes: [\"batch.applied\"]\n },\n {\n name: \"read\",\n summary: \"Read one memory: its metas, links, article, and format warnings.\",\n args: [{ name: \"path\", description: \"Repo-root-relative path to the memory.\", required: true }],\n flags: [\n {\n name: \"session-id\",\n type: \"string\",\n description: \"Records a `read` session link, so provenance is queryable both ways.\"\n }\n ],\n responseTypes: [\"memory.detail\"]\n },\n {\n name: \"search\",\n summary: \"Ranked search: four RRF arms plus MMR. Degrades to the lexical floor.\",\n args: [{ name: \"query\", description: \"Prose. Never a query language.\", required: true }],\n flags: [\n ...SCOPE_FLAGS,\n { name: \"limit\", type: \"int\", description: \"Hits to return.\", default: 10 }\n ],\n responseTypes: [\"memory.hits\"]\n },\n {\n name: \"recall\",\n summary: \"A disclosure pack under a character budget: arcs and memories folded separately.\",\n args: [{ name: \"query\", description: \"Prose.\", required: true }],\n flags: [\n ...SCOPE_FLAGS,\n {\n name: \"budget\",\n type: \"int\",\n description: \"Characters of quoted body. Arcs get their own envelope on top.\",\n default: 16_000\n }\n ],\n responseTypes: [\"recall.pack\"]\n },\n {\n name: \"correct\",\n summary: \"Supersede a memory: write the new file and archive the target in ONE commit.\",\n args: [{ name: \"target\", description: \"The memory being corrected.\", required: true }],\n flags: [\n { name: \"title\", type: \"string\", description: \"The new memory's title.\", required: true },\n {\n name: \"claim\",\n type: \"string\",\n description: \"The corrected claim. Exactly one of --claim or --article-html.\"\n },\n {\n name: \"body\",\n type: \"string\",\n description: \"A prose paragraph. Repeatable.\",\n repeatable: true\n },\n {\n name: \"article-html\",\n type: \"string\",\n description: \"Raw <article> markup for the superseding memory, used verbatim in place of --claim/--body. Must contain exactly one <mark> in the first <p> or <li>; the first <time datetime> becomes the memory's event time. The store refuses format violations before any commit. Exactly one of --claim or --article-html.\"\n },\n {\n name: \"type\",\n type: \"string\",\n description: \"The new memory's type. Defaults to the target's.\",\n values: WRITABLE_MEMORY_TYPES\n },\n { name: \"reason\", type: \"string\", description: \"Why the correction was made.\" },\n { name: \"session-id\", type: \"string\", description: \"Records a `corrected` session link.\" }\n ],\n responseTypes: [\"memory.corrected\"]\n },\n {\n name: \"link\",\n summary: \"Add an authored edge to the source file and commit it. Idempotent.\",\n args: [\n { name: \"src\", description: \"The asserting memory or task.\", required: true },\n {\n name: \"rel\",\n // The task rels are authorable here rather than in `memory_link`, because a `blocks` edge\n // between two tasks is a real authored assertion, while a person or provenance rel is minted.\n description: `One of: ${AUTHORABLE_RELS.join(\", \")}. A task rel needs two tasks; a memory rel refuses a task endpoint.`,\n required: true\n },\n { name: \"dst\", description: \"The memory or task being pointed at.\", required: true }\n ],\n flags: [],\n responseTypes: [\"memory.linked\"]\n },\n {\n name: \"neighbors\",\n summary: \"The memory graph around one path, to a fixed depth of at most two hops.\",\n args: [{ name: \"path\", description: \"The center of the neighborhood.\", required: true }],\n flags: [\n { name: \"depth\", type: \"int\", description: \"1 or 2. Never more.\", default: 1 },\n {\n name: \"rel\",\n type: \"string\",\n description: \"Restrict to these rels. Repeatable.\",\n values: MEMORY_RELS,\n repeatable: true\n }\n ],\n responseTypes: [\"memory.neighbors\"]\n },\n {\n name: \"archive\",\n summary: \"Soft-evict: `git mv` into archive/<YYYY>/ with the archive stamps. Never a delete.\",\n args: [{ name: \"path\", description: \"The memory to archive.\", required: true }],\n flags: [{ name: \"reason\", type: \"string\", description: \"Why it was evicted.\", required: true }],\n responseTypes: [\"memory.archived\"]\n },\n {\n name: \"reinforce\",\n summary: \"Bump access bookkeeping, gated by a 900-second per-path cooldown.\",\n args: [\n { name: \"path\", description: \"A memory path. Repeat the argument for more.\", required: true }\n ],\n flags: [\n {\n name: \"signal\",\n type: \"string\",\n description: \"`neutral` bumps access without claiming the memory was right.\",\n values: REINFORCE_SIGNALS,\n default: \"neutral\"\n }\n ],\n responseTypes: [\"memory.reinforced\"]\n },\n {\n name: \"list\",\n summary: \"Page through the corpus by type, workspace, tag, entity, or PARA bucket.\",\n args: [],\n flags: [\n {\n name: \"type\",\n type: \"string\",\n description: \"One memory type.\",\n values: WRITABLE_MEMORY_TYPES\n },\n { name: \"workspace\", type: \"string\", description: \"One workspace.\" },\n { name: \"tag\", type: \"string\", description: \"One tag.\" },\n { name: \"entity\", type: \"string\", description: \"One `type:name` entity reference.\" },\n {\n name: \"para\",\n type: \"string\",\n description: \"One PARA bucket.\",\n values: [\"projects\", \"areas\", \"resources\", \"archive\"]\n },\n { name: \"limit\", type: \"int\", description: \"Rows per page.\", default: 50 },\n {\n name: \"cursor\",\n type: \"string\",\n description: \"The `next_cursor` from the previous page: the last path returned.\"\n },\n {\n name: \"include-archived\",\n type: \"boolean\",\n description: \"Include archived memories.\",\n default: false\n }\n ],\n responseTypes: [\"memory.list\"]\n },\n /**\n * The task family: CRUDL over the 10th memory type, without retrieval.\n *\n * Sugar over the same use cases everything else uses. `task add` is `writeMemory` with\n * `--type task`, and `task status` is one head meta plus (for `done`) the archive machinery. The\n * design intent is that an agent works tasks with `Read`, `Edit`, and `ls` as readily as with these.\n * A task is a file in a directory, and this family exists so the common moves are one call rather\n * than three.\n */\n {\n name: \"task add\",\n summary: \"Open a task: a `task` memory in projects/<ws>/tasks/ or areas/inbox/tasks/.\",\n args: [],\n flags: [\n {\n name: \"title\",\n type: \"string\",\n description: \"What the task is. Becomes the <title> and the filename slug.\",\n required: true\n },\n {\n name: \"claim\",\n type: \"string\",\n description: \"The task statement, as the <mark> span. Defaults to --title.\"\n },\n {\n name: \"body\",\n type: \"string\",\n description: \"A prose paragraph of working notes. Repeatable, one <p> each.\",\n repeatable: true\n },\n {\n name: \"status\",\n type: \"string\",\n description: \"The opening status. `todo` unless you are recording work already underway.\",\n values: TASK_STATUSES,\n default: \"todo\"\n },\n {\n name: \"due\",\n type: \"string\",\n description: \"An ISO date or datetime deadline. Compared as a string, so the form matters.\"\n },\n {\n name: \"workspace\",\n type: \"string\",\n description: \"Routes the task to projects/<slug>/tasks/.\"\n },\n {\n name: \"tag\",\n type: \"string\",\n description: \"A tag. Repeatable; tags scope search but never route a task.\",\n repeatable: true\n },\n {\n name: \"entity\",\n type: \"string\",\n description: \"A `type:name` entity reference. Repeatable.\",\n repeatable: true\n },\n {\n name: \"session-id\",\n type: \"string\",\n description: \"The Claude Code session that opened the task.\"\n },\n { name: \"prompt-id\", type: \"string\", description: \"The prompt within that session.\" },\n { name: \"turn-uuid\", type: \"string\", description: \"The turn within that session.\" }\n ],\n responseTypes: [\"task.written\"]\n },\n {\n name: \"task status\",\n summary: \"Move a task's status. `done` stamps AND archives it, in one commit.\",\n args: [\n { name: \"path\", description: \"The task file.\", required: true },\n { name: \"status\", description: `One of: ${TASK_STATUSES.join(\", \")}.`, required: true }\n ],\n flags: [\n {\n name: \"reason\",\n type: \"string\",\n description: \"Why it closed. Recorded on the archive commit when the status is `done`.\"\n }\n ],\n responseTypes: [\"task.updated\"]\n },\n {\n name: \"task list\",\n summary: \"The task working set: a direct indexed scan with blockers, never ranked retrieval.\",\n args: [],\n flags: [\n {\n name: \"status\",\n type: \"string\",\n description: \"One task status.\",\n values: TASK_STATUSES\n },\n { name: \"workspace\", type: \"string\", description: \"One workspace.\" },\n {\n name: \"due-before\",\n type: \"string\",\n description: \"An ISO date. Returns tasks due strictly before it, by calendar day.\"\n },\n { name: \"limit\", type: \"int\", description: \"Rows per page.\", default: 50 },\n {\n name: \"cursor\",\n type: \"string\",\n description: \"The `next_cursor` from the previous page: the last path returned.\"\n },\n {\n name: \"include-archived\",\n type: \"boolean\",\n description: \"Include finished tasks. `done` archives, so they are otherwise absent.\",\n default: false\n }\n ],\n responseTypes: [\"task.list\"]\n },\n {\n name: \"index rebuild\",\n summary: \"Rebuild index.db from the git tree at HEAD. Destroys nothing outside .memhtml/.\",\n args: [],\n flags: [\n {\n name: \"embed\",\n type: \"boolean\",\n description: \"Fill missing vectors from Bedrock. --no-embed makes the rebuild instant.\",\n default: true\n }\n ],\n responseTypes: [\"index.report\"]\n },\n {\n name: \"index update\",\n summary: \"Index only what moved since the recorded watermark, plus the dirty working tree.\",\n args: [],\n flags: [\n { name: \"embed\", type: \"boolean\", description: \"Fill missing vectors.\", default: true }\n ],\n responseTypes: [\"index.report\"]\n },\n {\n name: \"index status\",\n summary: \"The index watermark, the vector space it was built in, and its row counts.\",\n args: [],\n flags: [],\n responseTypes: [\"index.report\"]\n },\n {\n name: \"trace index\",\n summary: \"Scan $MEMHTML_TRACE_ROOT for Claude Code transcripts, reading only what changed.\",\n args: [],\n flags: [],\n responseTypes: [\"trace.report\"]\n },\n {\n name: \"trace search\",\n summary: \"FTS over session first-prompts and AI titles. Never enters memory retrieval.\",\n args: [{ name: \"query\", description: \"Prose.\", required: true }],\n flags: [\n { name: \"cwd\", type: \"string\", description: \"Restrict to sessions from this directory.\" },\n { name: \"since\", type: \"string\", description: \"ISO-8601 lower bound on started_at.\" },\n { name: \"limit\", type: \"int\", description: \"Sessions to return.\", default: 20 }\n ],\n responseTypes: [\"trace.sessions\"]\n },\n {\n name: \"trace links\",\n summary: \"The memory-session links, from either side.\",\n args: [],\n flags: [\n { name: \"session-id\", type: \"string\", description: \"Every memory this session touched.\" },\n { name: \"path\", type: \"string\", description: \"Every session that touched this memory.\" }\n ],\n responseTypes: [\"trace.links\"]\n },\n {\n name: \"sleep run\",\n summary: \"The nightly curation cycle: 15 phases, each an isolated commit on a review branch.\",\n args: [],\n flags: [\n {\n name: \"date\",\n type: \"string\",\n description: \"The run date, `YYYY-MM-DD`. Defaults to today. Names the branch.\"\n },\n {\n name: \"phases\",\n type: \"string\",\n description: `Comma-separated subset. All 15 by default: ${SLEEP_PHASES.join(\", \")}.`\n },\n {\n name: \"dry-run\",\n type: \"boolean\",\n description: \"Report per-phase counts and commit nothing.\",\n default: false\n }\n ],\n responseTypes: [\"sleep.report\"]\n },\n {\n name: \"sleep resume\",\n summary: \"Re-run only the phases with no Memhtml-Phase trailer on the branch.\",\n args: [{ name: \"run-id\", description: \"The run id, e.g. sleep/2026-08-02.\", required: true }],\n flags: [],\n responseTypes: [\"sleep.report\"]\n },\n {\n name: \"sleep review\",\n summary: \"Per-phase counts, the commit list, diff --stat, and a per-file classification.\",\n args: [{ name: \"run-id\", description: \"The run id.\", required: true }],\n flags: [\n { name: \"diff\", type: \"boolean\", description: \"Include the raw diff.\", default: false }\n ],\n responseTypes: [\"sleep.review\"]\n },\n {\n name: \"sleep merge\",\n summary: \"Fast-forward main to the run's branch, after the discrimination gate passes.\",\n args: [{ name: \"run-id\", description: \"The run id.\", required: true }],\n flags: [\n {\n name: \"skip-gate\",\n type: \"boolean\",\n description: \"Merge without re-running discrimination. A deliberate, logged override, never a default.\",\n default: false\n }\n ],\n responseTypes: [\"sleep.merge\"]\n },\n {\n name: \"sleep status\",\n summary: \"The latest sleep run and its per-phase outcomes.\",\n args: [],\n flags: [],\n responseTypes: [\"sleep.report\"]\n },\n {\n name: \"status\",\n summary: \"Corpus health: HEAD, dirty state, counts by type, edges, index freshness.\",\n args: [],\n flags: [],\n responseTypes: [\"status.health\"]\n },\n {\n name: \"publish\",\n summary: \"Regenerate the per-directory index.html listings and sitemap.xml, and commit them.\",\n args: [],\n flags: [],\n responseTypes: [\"publish.report\"]\n },\n {\n name: \"doctor\",\n summary: \"Corpus health: dangling hrefs, orphan state rows, inbox depth, vocabulary, staleness.\",\n args: [],\n flags: [\n {\n name: \"fix\",\n type: \"boolean\",\n description: \"Repair dangling hrefs and prune orphan access rows. The other findings need a decision.\",\n default: false\n }\n ],\n responseTypes: [\"doctor.report\"]\n },\n {\n name: \"eval discriminate\",\n summary: \"The refusable retrieval gate: every probe must outrank its own wrong-fact twins.\",\n args: [],\n flags: [\n {\n name: \"mode\",\n type: \"string\",\n description: \"`fake` is the deterministic embedder CI measures; `live` needs AWS_BEARER_TOKEN_BEDROCK and refuses loudly without it.\",\n values: [\"fake\", \"live\"],\n default: \"fake\"\n },\n {\n name: \"seed\",\n type: \"int\",\n description: \"The fixture corpus seed. A failing run is reproducible from this number.\"\n },\n { name: \"size\", type: \"int\", description: \"Base memories to generate.\", default: 200 },\n {\n name: \"probes\",\n type: \"int\",\n description: \"Probes to run. Design §5 wants ≥30.\",\n default: 36\n },\n {\n name: \"mrr-floor\",\n type: \"string\",\n description: \"Mean-reciprocal-rank floor. Lowering it is a deliberate, visible choice.\",\n default: \"0.85\"\n }\n ],\n responseTypes: [\"eval.discrimination\"]\n },\n /**\n * Code-mode (ROADMAP item 7b): one script, one execution, one envelope.\n *\n * The flag surface answers three questions a script cannot answer for itself, and nothing else.\n *\n * **How does the script arrive?** Three doors, exactly one per call, enforced in `validate` so a\n * wrong combination is exit 2. `--file` for a script under version control, `--script` for the\n * inline one-liner an agent composes, and a bare `memhtml exec` (or `-`) for stdin, which is the same\n * three-door shape and the same `-` spelling `memhtml apply` already uses for its op stream, so an\n * agent that learned one learned both. `--script` rather than a positional argument, because the\n * positional slot on a two-word command is where a run-id or a path goes on every other command\n * here, and a multi-line program in that slot would read as one.\n *\n * **How long may it run?** `--timeout-ms`, bounded and defaulted, because the guest is a QuickJS\n * worker with no reaper of its own and an unbounded script holds the CLI process open. The\n * millisecond unit is in the flag name rather than left to a note, since `--timeout 30` is\n * ambiguous by a factor of a thousand.\n *\n * **Which tree does it see?** `--sha`, defaulting to `HEAD`. Never the live working tree, which is\n * a containment decision rather than a convenience. A mounted `$MEMHTML_ROOT` exposes `.memhtml/index.db`\n * to the guest, whose `sqlite3` reads it happily (probed 2026-08-09: a read-only mount is no barrier\n * to a reader). A gitignored file is absent from a detached worktree, so pinning a commit is what\n * keeps the ranked planes out of reach, and the read-only mount is the second layer rather than\n * the only one. A pin also makes the answer reproducible. `sha` rides back in the envelope, so a\n * rerun is exact.\n *\n * There is deliberately no flag for the guest's own opt-ins. `javascript` is on because `js-exec`\n * is the feature. `python` and `network` are off and unofferable, so no invocation can turn either\n * on. `apps/cli/src/exec.ts` carries the mechanism and the egress probe.\n */\n {\n name: \"exec\",\n summary: \"Run a read-only traversal script over the corpus in a sandbox: multi-hop in ONE execution.\",\n args: [],\n flags: [\n {\n name: \"file\",\n type: \"string\",\n description: \"The script to run, as a path on the HOST. Omit it (or pass `-`) to read the script from stdin. Mutually exclusive with `--script`.\"\n },\n {\n name: \"script\",\n type: \"string\",\n description: \"The script source, inline. Mutually exclusive with `--file` and with reading stdin.\"\n },\n {\n name: \"timeout-ms\",\n type: \"int\",\n description: \"Wall-clock bound on the script. Exceeding it is `exitCode` 124 with `timedOut: true`, not an error envelope. Capped at 600000.\",\n default: 30000\n },\n {\n name: \"sha\",\n type: \"string\",\n description: \"The commit to mount, materialized as a detached worktree. Defaults to HEAD. Never the live working tree, whose gitignored .memhtml/index.db a worktree omits.\"\n }\n ],\n responseTypes: [\"exec.report\"]\n },\n {\n name: \"state export\",\n summary: \"Write .memhtml/state/access.jsonl, the only durable copy of the state plane, and commit.\",\n args: [],\n flags: [],\n responseTypes: [\"state.export\"]\n },\n {\n name: \"state import\",\n summary: \"Replay the committed sidecar into state.db. Counters merge by max, never last-wins.\",\n args: [],\n flags: [],\n responseTypes: [\"state.import\"]\n },\n {\n name: \"agents-doc\",\n summary: \"Regenerate AGENTS.md from this command table. --check fails on drift.\",\n args: [],\n flags: [\n {\n name: \"check\",\n type: \"boolean\",\n description: \"Compare the committed doc to the regenerated one and fail on a difference.\",\n default: false\n },\n { name: \"out\", type: \"string\", description: \"Where to write. Defaults to ./AGENTS.md.\" }\n ],\n responseTypes: [\"agents.doc\"]\n },\n {\n name: \"serve mcp\",\n summary: \"Run the `memhtml-mcp` stdio server: 14 tools and 2 resources over this same repo.\",\n args: [],\n flags: [],\n responseTypes: [\"serve.exit\"]\n }\n];\nexport const COMMAND_NAMES = COMMANDS.map((command) => command.name);\n/**\n * The example op line, quoted verbatim into the `when-to-batch` block.\n *\n * A constant rather than a literal inside the prose, because a test parses it. An example an agent\n * copies has to be valid JSONL, and it stays valid because the doc and the parser read the\n * same bytes. A prose-only example drifts silently the first time a field is renamed.\n */\nexport const GUIDE_OP_EXAMPLE = '{\"op\":\"write\",\"title\":\"One writer and many readers share the index\",\"type\":\"semantic\",\"body\":\"WAL admits a single writer at a time and any number of concurrent readers, so a CLI command and a running `memhtml serve mcp` can work against one store.\",\"tag\":\"infra\"}';\n/**\n * The guide: what an agent reads on its first call, before it has written anything.\n *\n * Prose, in a structured field, authored here beside `COMMANDS`, which is the design (spec\n * D8/G6). The manifest carries it on a bare `memhtml`, `memhtml help`, `memhtml --help`, and `memhtml manifest`, and\n * `memhtml agents-doc` renders these same strings into `AGENTS.md`, so the doc and the live answer cannot\n * disagree. Prose kept in a separate Markdown file would be a second copy that drifts, and prose kept\n * only in `AGENTS.md` would be invisible to an agent that never opens the repo.\n *\n * Written for an LLM agent mid-task rather than for an operator browsing: complete sentences, action\n * first, and every claim true of this build rather than of the design. A guide that describes an\n * intention is worse than no guide, because an agent acts on it.\n */\nexport const GUIDE = [\n {\n topic: \"first-call\",\n body: \"You are reading this CLI's manifest: every command, argument, flag, response type, error code, \" +\n \"and environment variable the binary accepts. A bare `memhtml`, `memhtml help`, `memhtml --help`, and \" +\n \"`memhtml manifest` all return it, and all four answer on a machine with no repo, no database, and \" +\n \"no credentials, so this is also the liveness check when something else has failed. \" +\n \"Every command writes exactly ONE JSON envelope to stdout and nothing else; logs go to stderr. \" +\n \"A success is `{apiVersion, type, data}` and a failure is `{apiVersion, error, code, suggestions}`. \" +\n \"Branch on `code`, never on the `error` prose: the codes and response types are append-only and a \" +\n \"shipped one never changes meaning, while the prose changes freely as wording improves. \" +\n \"Exit 0 is success, exit 2 is a usage error you fix by changing the call, exit 1 is a runtime \" +\n \"failure you fix by changing the repo or the environment. Add `--dense` to any command to get \" +\n \"minified JSON with null fields dropped, which is what you want when the output goes into a prompt.\"\n },\n {\n topic: \"write-surfaces\",\n body: \"There are three ways to put a memory into the corpus, and they are all legitimate. \" +\n \"First, this CLI: `memhtml write` for one memory, `memhtml apply` for many. \" +\n \"Second, the MCP server: `memhtml serve mcp` speaks stdio with 14 tools and 2 resources over this \" +\n \"same repo, and it is the door to use when you are already an MCP client. \" +\n \"Third, editing files under $MEMHTML_ROOT directly with your normal file tools: the git tree IS the \" +\n \"system of record and `.memhtml/index.db` is only a projection of it, so a hand-written or hand-edited \" +\n \"memory file is as real as one this CLI wrote. `memhtml index update` projects uncommitted working-tree \" +\n \"changes as well as committed ones, so a dirty edit is searchable before you commit it. \" +\n \"What you take on by editing directly is everything the write path would have done for you: the \" +\n \"file must satisfy the format (run `memhtml doctor`, and `memhtml read <path>` reports per-file format \" +\n \"warnings), you own choosing a path that does not collide, you own noticing that the content \" +\n \"already exists somewhere else, and you own the commit. The nightly `memhtml sleep run` refuses to \" +\n \"start on a dirty tree, so an uncommitted edit blocks curation until it is committed or stashed. \" +\n \"A CLI command and a running `memhtml serve mcp` may share one store: the index is WAL SQLite, \" +\n \"which admits one writer at a time and any number of concurrent readers, so a second writer \" +\n \"waits its turn rather than failing. The one thing to keep clear of is `memhtml sleep run`, and \" +\n \"for a git reason rather than a database one: a run holds a checked-out `sleep/<date>` branch, \" +\n \"so a write landing during it commits onto that branch and is merged as if it were curation or \" +\n \"lost when the branch is dropped.\"\n },\n {\n topic: \"when-to-batch\",\n body: \"Writing more than about three memories in one task? Call `memhtml apply` once with a JSONL op stream \" +\n \"instead of running `memhtml write` N times. A batch stages every file, makes ONE commit, and \" +\n \"reindexes ONCE, where N separate writes make N commits and pay N index passes over N diffs. \" +\n \"Pass the stream as `memhtml apply --file ops.jsonl`, or pipe it: `memhtml apply -` and a bare `memhtml apply` \" +\n \"both read stdin. One complete JSON object per line, no wrapping array, no pretty-printing. A \" +\n \"line looks like this:\\n\" +\n `${GUIDE_OP_EXAMPLE}\\n` +\n \"`op` is `write` (the only verb in the vocabulary today), `title` and `type` are required, and each \" +\n \"op carries the same optional fields `memhtml write` takes, in snake_case: `path`, `workspace`, `tag`, \" +\n \"`entity`, `importance`, `confidence`, `session_id`, `prompt_id`, `turn_uuid`. \" +\n \"The whole file is validated for shape before ANY op executes, so a malformed line 7 is exit 2 \" +\n \"naming line 7 with nothing written. A failed apply costs you nothing but the call. \" +\n \"You get one result per op in INPUT ORDER, each naming its own `index`, so you can match results \" +\n \"back to the lines you sent. \" +\n \"A batch is ATOMIC by default: the first refused op aborts the whole batch, no file is written, no \" +\n \"commit is made, and the surviving ops report `skipped: true`. Pass `--continue-on-error` for \" +\n \"best-effort instead, and a refused op comes back as one failed result carrying its own `code` and \" +\n \"`error` while every op that succeeded lands in the one commit. \" +\n \"A duplicate is never an error: an op whose exact content is already stored comes back `ok: true` \" +\n \"with `deduped: true` and the existing path, so re-applying a file you already applied is safe and \" +\n \"writes nothing. `commit_sha` is null exactly when nothing was committed: a batch that only \" +\n \"deduped, or one that aborted.\"\n },\n {\n topic: \"conflicts\",\n body: \"Pass `--detect-conflicts` to `memhtml apply` and each result gains a `conflict` field naming what \" +\n \"that op's claim contradicts. Dedupe catches an op whose content is IDENTICAL to something stored; \" +\n \"this catches an op that says something DIFFERENT about the same thing, the case dedupe is blind \" +\n \"to and the one that actually rots a corpus. \" +\n \"The match is grammatical, not semantic: a claim is split into a frame (the subject and relation, \" +\n \"up to its last `of`/`is`/`in`/`to`/`by`/`as`) and a value, and two claims conflict when they share \" +\n \"a frame. `The pool ceiling is 64` and `The pool ceiling is 128` share `the pool ceiling is`. \" +\n \"`conflict.path` names an ACTIVE memory already holding that slot; `conflict.batch_index` names an \" +\n \"EARLIER op in this same call, which is the case nothing else can see because neither op is stored \" +\n \"yet; `conflict.claim` is the other claim's own text, so you can decide without a second read. \" +\n \"It is null when nothing matched, and also when the claim has no frame shape. The rule refuses \" +\n \"frames under three tokens and values over six, so short claims and claims trailed by a clause are \" +\n \"deliberately unmatched rather than loosely matched. On a line using `article_html` instead of \" +\n \"`body` it is always null, because the claim lives inside your markup and is not read until the \" +\n \"store renders it. \" +\n \"THE ASSIST NEVER CHANGES WHAT IS WRITTEN. An op carrying a conflict is written exactly as it \" +\n \"would have been without the flag: nothing is archived, nothing is refused, and later does not win. \" +\n \"That is deliberate, because sometimes the contradiction IS the answer. A memory recording that a \" +\n \"runbook step changed necessarily contradicts the memory stating the old step, and a system that \" +\n \"resolved that for you would delete the pair a reader needs in order to see the change at all. \" +\n \"You decide per conflict: keep both (they are about different things, or both are true), \" +\n \"`memhtml correct <path>` instead (the new claim supersedes the old one, and the old one stays readable \" +\n \"under archive/), or drop the line (you were about to restate something already stored). \" +\n \"Archived memories never match, so a superseded claim stops contradicting the claim that superseded it.\\n\" +\n \"When you have already decided that later wins (a re-scrape, a settings sync, any stream where \" +\n \"each line is the newest statement of its slot), pass `--consolidate last-wins` (the batch tool's \" +\n '`consolidate: \"last-wins\"`) and the batch RESOLVES those matches instead of reporting them. ' +\n \"Ops sharing a frame key write ONE file carrying the LATER value at the FIRST index that claimed \" +\n \"the slot; each later restatement reports `consolidated_into` naming that slot and the summary \" +\n \"counts it under `consolidated`, neither written nor failed. A stored ACTIVE memory occupying a \" +\n \"surviving slot is archived with a supersedes link from the new file, its archive path reported \" +\n \"as `superseded_path`, the same chain `memhtml correct` leaves, so ancestry reads identically. \" +\n \"OFF by default, and the key is the conflict rule's own: the frame split is a rule measured in \" +\n \"the eval harness before it was believed and ported verbatim into `@memhtml/domain`'s frame.ts, \" +\n \"which detection and consolidation share, so anything the rule refuses to key (short frames, \" +\n \"clause values) is never consolidated, and what you saw reported with `--detect-conflicts` is \" +\n \"exactly what this flag would have acted on.\\n\" +\n \"Every supersede, `memhtml correct` and `--consolidate last-wins` alike, also stamps a VALIDITY \" +\n \"WINDOW, in the same one commit. The superseded memory gains `memhtml-valid-until` set to the \" +\n \"moment the new fact became true (the winner's own `memhtml-valid-from`, else its first \" +\n \"`<time datetime>`, else the operation's instant), and the winner gains `memhtml-valid-from` at \" +\n \"that same moment, so one window closes exactly where the next opens. Min-wins: a memory \" +\n \"already stating an EARLIER `memhtml-valid-until` keeps it, because a fact cannot outlive its \" +\n \"earliest stated bound. That is what `--as-of` on `memhtml search` reads: pass an ISO instant and \" +\n \"the result is what was believed valid AT THAT MOMENT. Since-superseded memories return, each \" +\n \"marked `superseded_by` naming what replaced it, and facts not yet valid then are absent. \" +\n \"History is read from the files, not replayed from git, so it survives a full index rebuild.\"\n },\n {\n topic: \"authoring\",\n body: \"Every write authors the article in exactly one of two ways, and supplying both or neither is \" +\n \"refused. Either you write prose and the template owns the markup (`--claim` is the one \" +\n \"load-bearing sentence and becomes the `<mark>` claim span and `files.gist`, and each `--body` \" +\n \"is one paragraph after it) or you supply `--article-html` and own the markup yourself. \" +\n \"On a `memhtml apply` line the prose form is the `body` field, whose first sentence becomes the claim, \" +\n \"and the markup form is `article_html`. \" +\n \"When you supply markup you own two constraints. It must contain EXACTLY ONE `<mark>`, and that \" +\n \"`<mark>` must sit in the article's first `<p>` or `<li>` and not inside an `<aside>` or \" +\n \"`<details>`. The claim leads the article and is never a caveat or behind a fold. And the first \" +\n \"`<time datetime>` in your markup becomes the memory's event time, which is what recency ranks \" +\n \"on, so a memory about something that happened last year should say so rather than being ranked \" +\n \"as today's news. \" +\n \"Markup is checked before anything is written: the store renders your article, runs the format \" +\n \"check, and refuses with the list of violations before it creates a file, stages it, or commits. \" +\n \"A refused write leaves the tree byte-identical, so a failed attempt costs nothing and you can fix \" +\n \"the markup and retry. \" +\n \"Code goes in the prose path as a fenced block: a body paragraph that is entirely a ``` fence \" +\n \"becomes <figure><pre><code>, whitespace preserved verbatim, and the fence's info string \" +\n \"(```ts) is stamped as data-lang and promoted to a `lang:ts` entity, so `memhtml list --entity \" +\n \"lang:ts` finds every memory carrying TypeScript. A blank line inside a fence does NOT split \" +\n 'paragraphs. On the markup path write the same <figure><pre><code data-lang=\"ts\"> yourself; ' +\n \"never `class` (forbidden) and never `lang=` (that attribute names human languages).\"\n },\n {\n topic: \"code-mode\",\n body: \"Answering a question that takes MORE THAN ONE HOP through the corpus? Write it as a script and \" +\n \"run `memhtml exec` once, instead of spending a tool call per hop. Supersedence ancestry, live \" +\n \"contradiction pairs, orphan census, entity co-occurrence, 'which of these 40 paths has no \" +\n \"backlink': each of those is one traversal in code and N round trips through `memhtml read` and \" +\n \"`memhtml neighbors`. Measured on a 305-file corpus: a full census in 598ms, and 410 edges resolved \" +\n \"into 201 chains, longest 8 hops, in one execution at 430ms. \" +\n \"The script runs under QuickJS in a sandbox with the corpus mounted READ-ONLY at `/mnt/memhtml`, and \" +\n \"a helper is already seeded for you at `/workspace/lib/corpus.mjs`. Import it: \" +\n '`import { corpus, backlinks, chain, edges } from \"/workspace/lib/corpus.mjs\"`. `corpus()` ' +\n \"returns a Map keyed by root-absolute path (the SAME string an edge's href holds, so \" +\n \"`memories.get(link.href)` resolves with no path juggling) and each value carries `claim`, \" +\n \"`memoryType`, `status`, `tags`, `entities`, `links`, `facets`, `citations`, `eventAt`, and a \" +\n \"`document` escape hatch for any selector the fields do not cover. \" +\n \"Print your answer as JSON on stdout with `console.log`; it comes back verbatim in `data.stdout`, \" +\n \"so keep it small and structured rather than dumping the corpus. \" +\n \"THREE THINGS IT CANNOT DO, by design. It cannot write: the corpus is read-only and a write \" +\n \"answers EROFS, so every write still goes through `memhtml write` / `memhtml apply`, which own commits, \" +\n \"dedup, and conflict detection. It cannot rank: no cosine, no RRF, no salience, and no index \" +\n \"database. For ranked retrieval shell out to `memhtml search --json` and parse its envelope, which \" +\n \"the one-envelope-per-command contract already makes a code-mode API. And it cannot reach the \" +\n \"network: there is no curl and the guest's `fetch` refuses on call. \" +\n \"The intended opening move is ranked retrieval FIRST, code-mode second: `memhtml search` or \" +\n \"`memhtml recall` to get the handful of paths the ranking stack says matter, then `memhtml exec` to walk, \" +\n \"join, count, and filter from there. Starting in code-mode means starting with a full-corpus scan \" +\n \"and no relevance signal. \" +\n \"A non-zero `exitCode` in the response is YOUR script failing, not the command failing. Read \" +\n \"`data.stderr` for the diagnostic and the exit code is still 0. A script that runs past \" +\n \"`--timeout-ms` (default 30000) comes back `exitCode: 124` with `timedOut: true`. \" +\n \"The tree you get is a pinned commit, HEAD by default, named in `data.sha`, so an answer is \" +\n \"reproducible with `--sha`, and an uncommitted edit is NOT visible to the script.\"\n }\n];\nexport const GUIDE_TOPICS = GUIDE.map((block) => block.topic);\n/**\n * Derived from `COMMANDS` and `GLOBAL_FLAGS` by walking them, so adding a flag\n * updates the manifest automatically. A hand-written manifest drifts the first\n * time someone adds a flag and forgets to edit it.\n */\nexport const buildManifest = () => ({\n name: \"memhtml\",\n version: \"0.2.3\", // x-release-please-version\n summary: \"Read, write, and curate the git-backed memory repo.\",\n apiVersion: \"1\",\n /**\n * The prose an agent needs before the command table means anything, so it is listed before it.\n * A manifest that opened with 33 command specifications makes an agent infer the workflow from a\n * surface, while `guide` states it.\n */\n guide: GUIDE,\n globalFlags: GLOBAL_FLAGS,\n errorCodes: ERROR_CODES,\n config: CONFIG_VARS,\n responseTypes: [...new Set(COMMANDS.flatMap((command) => command.responseTypes))],\n commands: COMMANDS.map((command) => ({\n name: command.name,\n summary: command.summary,\n args: command.args,\n flags: command.flags,\n responseTypes: command.responseTypes,\n supportsJson: true,\n supportsDense: true\n }))\n});\n//# sourceMappingURL=commands.js.map","/**\n * Prose → claim derivation: the single implementation both write doors use.\n *\n * The tools take `{title, body}` because that is what a model produces, and the format needs a\n * `<mark>` claim plus one `<p>` per paragraph. Turning the first into the second is a text heuristic,\n * and it lives here for two reasons. It was duplicated once, as `claimOf`/`restOf` in `apps/mcp` and\n * `claimFromProse`/`proseTail` in `apps/cli`, the same regex in two packages. A sentence-splitting\n * rule that drifts between the doors also makes `memhtml apply` and `memory_write_batch` derive different\n * claims from the same body, so the gist of a memory would depend on which door wrote it.\n *\n * It does not live in `@memhtml/html`, which owns markup and the format's own rules. \"Where does a\n * sentence end\" is a guess about natural-language prose, and the format states no such constraint. It\n * is not in `operations.ts` either, because that module holds the use cases both doors call, and this\n * is a text helper they apply before calling one.\n *\n * The derivation is defense in depth now; it was once the only guard. `@memhtml/html` constraint 1 now\n * rejects an empty `<mark>` outright, so a door that skipped this would be stopped by the store's\n * render gate instead of landing a file with an empty `files.gist`. What is left here is the\n * authoring convenience the doors exist to provide: a JSONL line and an MCP call carry no `claim`\n * field, so the door derives one instead of asking an author to restate the body's first sentence.\n */\nimport { closesFence, fenceOpeningOf } from \"@memhtml/html\";\n/**\n * Split prose into paragraphs on blank lines, dropping the empties. Inside a fenced code block a\n * blank line is content, so the split skips it there. Without that carve-out, a snippet containing a\n * blank line splits into two paragraphs, neither of which is a complete fence, and both land as\n * escaped backtick text instead of the `<figure><pre><code>` an intact fence renders as.\n *\n * The fence grammar comes from `@memhtml/html` (`fenceOpeningOf`/`closesFence`) rather than a second\n * copy here. The splitter deciding \"this is one block\" and the template deciding \"this is a fence\"\n * must be the same judgment, or the doors drift the way the claim derivation once did.\n */\nconst paragraphsOf = (prose) => {\n const parts = [[]];\n let opening;\n for (const line of prose.split(\"\\n\")) {\n const current = parts.at(-1);\n if (opening === undefined && line.trim() === \"\") {\n if (current.length > 0)\n parts.push([]);\n continue;\n }\n current.push(line);\n if (opening === undefined) {\n opening = fenceOpeningOf(line);\n }\n else if (closesFence(line, opening)) {\n opening = undefined;\n }\n }\n return parts.map((lines) => lines.join(\"\\n\").trim()).filter((part) => part !== \"\");\n};\n/**\n * The claim: the first sentence of the prose.\n *\n * The first sentence is where a model puts the assertion. Taking the title instead would make every\n * gist a restatement of the filename, which is the one thing a Tier-1 disclosure line must not be.\n * Prose with no sentence terminator is its own claim in full. A fragment is still an assertion, and\n * rejecting it would reject the shortest legitimate memory there is.\n */\nexport const claimFromProse = (prose) => {\n const trimmed = prose.trim();\n const match = /^(.*?[.!?])(\\s|$)/s.exec(trimmed);\n return (match?.[1] ?? trimmed).trim();\n};\n/**\n * The prose after the claim, as paragraphs. Empty when the claim was the whole body.\n *\n * The first element becomes the claim paragraph's own tail rather than a second `<p>`, which is\n * `articleHtmlFor`'s contract in `@memhtml/html`'s template. A one-paragraph body therefore yields\n * exactly one `<p>` with the `<mark>` inside it, which is what constraint 1 requires.\n */\nexport const proseTail = (prose) => {\n const remainder = prose.trim().slice(claimFromProse(prose).length).trim();\n return remainder === \"\" ? [] : paragraphsOf(remainder);\n};\n//# sourceMappingURL=prose.js.map","import { readFile } from \"node:fs/promises\";\nimport { fail } from \"./envelope.js\";\nimport { claimFromProse, proseTail } from \"./prose.js\";\n/**\n * `memhtml apply`'s own layer: JSONL text in, decoded ops or a usage failure out.\n *\n * Separated from `run.ts` because everything here is a decision about one untrusted text format, and\n * because AC-6-4's contract is that the whole file is judged before any op executes. That makes\n * this a pure function from text to either an op list or a refusal, testable without a repo.\n *\n * The refusals are `Failure` values rather than thrown errors for the reason `validate` returns one:\n * the exit code is the contract. A usage error is exit 2 and a runtime error is exit 1, and a\n * malformed line is a usage error, because the caller wrote a bad file and the corpus is fine.\n */\n/**\n * The op vocabulary, v1.\n *\n * Writes only, per spec D4. `op` is carried on the wire anyway so v2 can add `correct`/`link`/\n * `archive` without a format break. An unknown value is refused with this list attached rather than\n * ignored, because a file of `{\"op\":\"wrote\",…}` lines that applied nothing and exited 0 is the silent\n * failure the whole pre-validation pass exists to prevent.\n */\nexport const APPLY_OPS = [\"write\"];\n/**\n * Every field a line may carry, mapped to the `WriteParams` field it becomes.\n *\n * A table rather than a hand-written decode, so the snake_case → camelCase rename is stated once and\n * the unknown-field check below is derived from it. The MCP tool's parameters use exactly these\n * snake_case names (`apps/mcp/src/tools.ts`), so an agent that learned the field names from one door\n * can write a JSONL file for the other without translating.\n */\nconst SCALAR_FIELDS = {\n title: \"title\",\n type: \"memoryType\",\n body: \"body\",\n article_html: \"articleHtml\",\n path: \"path\",\n workspace: \"workspace\",\n importance: \"importance\",\n confidence: \"confidence\",\n session_id: \"sessionId\",\n prompt_id: \"promptId\",\n turn_uuid: \"turnUuid\",\n status: \"taskStatus\",\n due: \"dueAt\"\n};\n/** Fields that accept a string or an array of strings, and always become an array. */\nconst LIST_FIELDS = { tag: \"tags\", tags: \"tags\", entity: \"entities\", entities: \"entities\" };\n/** `op` is the discriminator rather than a `WriteParams` field, so it is legal and never mapped. */\nconst KNOWN_FIELDS = new Set([\n \"op\",\n ...Object.keys(SCALAR_FIELDS),\n ...Object.keys(LIST_FIELDS)\n]);\n/** A usage failure naming the offending line, 1-based as a text editor counts. */\nconst lineError = (code, line, reason, suggestions = []) => fail(code, `${APPLY_DOC}: line ${line}: ${reason}`, suggestions);\n/** The prefix every apply refusal carries, so a caller can tell a file error from a corpus error. */\nconst APPLY_DOC = \"memhtml apply\";\n/** One line's parsed JSON as a record, or the refusal. */\nconst objectAt = (text, line) => {\n let value;\n try {\n value = JSON.parse(text);\n }\n catch (error) {\n return lineError(\"ERR_INVALID_FLAG\", line, `not valid JSON (${error instanceof Error ? error.message : String(error)}). Every line is one complete JSON object; a pretty-printed object spanning several lines is not JSONL`, [\n 'memhtml apply --file ops.jsonl, one object per line: {\"op\":\"write\",\"title\":\"…\",\"type\":\"semantic\",\"body\":\"…\"}'\n ]);\n }\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n return lineError(\"ERR_INVALID_FLAG\", line, `parsed as ${Array.isArray(value) ? \"an array\" : typeof value}, not a JSON object`);\n }\n return value;\n};\nconst isFailure = (value) => typeof value === \"object\" && value !== null && \"code\" in value && \"error\" in value;\n/** A field that must be a non-empty string, or the refusal naming it. */\nconst requiredString = (record, field, line) => {\n const value = record[field];\n if (value === undefined) {\n return lineError(\"ERR_MISSING_ARGUMENT\", line, `missing required field \\`${field}\\``);\n }\n if (typeof value !== \"string\" || value.trim() === \"\") {\n return lineError(\"ERR_INVALID_FLAG\", line, `\\`${field}\\` must be a non-empty string, got ${value === null ? \"null\" : typeof value}`);\n }\n return value;\n};\n/** A list field as an array of strings: a bare string is a one-element list, as `--tag` is. */\nconst strings = (value, field, line) => {\n if (typeof value === \"string\")\n return value === \"\" ? [] : [value];\n if (!Array.isArray(value)) {\n return lineError(\"ERR_INVALID_FLAG\", line, `\\`${field}\\` must be a string or an array of strings, got ${typeof value}`);\n }\n const out = [];\n for (const entry of value) {\n if (typeof entry !== \"string\") {\n return lineError(\"ERR_INVALID_FLAG\", line, `\\`${field}\\` holds a ${typeof entry} where every element must be a string`);\n }\n if (entry !== \"\")\n out.push(entry);\n }\n return out;\n};\n/** A numeric field, accepting the JSON number or a numeric string. */\nconst numeric = (value, field, line) => {\n const parsed = typeof value === \"number\" ? value : Number(value);\n if (typeof value !== \"number\" && typeof value !== \"string\") {\n return lineError(\"ERR_INVALID_FLAG\", line, `\\`${field}\\` must be a number, got ${typeof value}`);\n }\n if (!Number.isFinite(parsed)) {\n return lineError(\"ERR_INVALID_FLAG\", line, `\\`${field}\\` is not a finite number: ${String(value)}`);\n }\n return parsed;\n};\n/**\n * One line as a `WriteParams`, or the refusal naming the line.\n *\n * The shape rules AC-6-4 puts at this door and nowhere else: the line parses, it declares an op in\n * the vocabulary, and it carries `title` and `type` as non-empty strings. Everything past that is\n * the operations layer's decode (is `type` in the vocabulary) or the store's render gate (is the\n * markup valid). Those are checked per op and reported per op, and they are not duplicated here,\n * because a second copy of the type vocabulary is a second thing to update when it moves.\n */\nconst opAt = (record, line) => {\n for (const field of Object.keys(record)) {\n if (!KNOWN_FIELDS.has(field)) {\n return lineError(\"ERR_INVALID_FLAG\", line, `unknown field \\`${field}\\`. Fields: ${[...KNOWN_FIELDS].sort().join(\", \")}`);\n }\n }\n const op = record.op;\n if (op === undefined) {\n return lineError(\"ERR_MISSING_ARGUMENT\", line, `missing required field \\`op\\`. One of: ${APPLY_OPS.join(\", \")}`);\n }\n if (typeof op !== \"string\" || !APPLY_OPS.includes(op)) {\n return lineError(\"ERR_INVALID_FLAG\", line, `\\`op\\` must be one of: ${APPLY_OPS.join(\", \")}, got ${JSON.stringify(op)}`);\n }\n const title = requiredString(record, \"title\", line);\n if (isFailure(title))\n return title;\n const memoryType = requiredString(record, \"type\", line);\n if (isFailure(memoryType))\n return memoryType;\n const params = { title, memoryType, claim: \"\" };\n for (const [field, target] of Object.entries(SCALAR_FIELDS)) {\n const value = record[field];\n if (value === undefined || value === null)\n continue;\n if (field === \"title\" || field === \"type\")\n continue;\n if (target === \"importance\" || target === \"confidence\") {\n const parsed = numeric(value, field, line);\n if (isFailure(parsed))\n return parsed;\n params[target] = parsed;\n continue;\n }\n if (typeof value !== \"string\") {\n return lineError(\"ERR_INVALID_FLAG\", line, `\\`${field}\\` must be a string, got ${typeof value}`);\n }\n params[target] = value;\n }\n for (const [field, target] of Object.entries(LIST_FIELDS)) {\n const value = record[field];\n if (value === undefined || value === null)\n continue;\n const parsed = strings(value, field, line);\n if (isFailure(parsed))\n return parsed;\n params[target] = [...(params[target] ?? []), ...parsed];\n }\n /**\n * `body` prose becomes claim + tail; `article_html` is used verbatim and leaves `claim` empty.\n *\n * The XOR itself is not enforced here. The store's render gate owns it per op, so a batch with one\n * bad op reports that op and not the whole file. This branch owns the claim instead: the JSONL wire\n * has no `claim` field, so a prose line's claim is derived rather than restated by its author (see\n * {@link claimFromProse}, the one copy both doors share).\n *\n * Skipping this no longer lands a bad file. `@memhtml/html` constraint 1 rejects an empty `<mark>`, so\n * the render gate would stop the op instead of committing a file with an empty `files.gist`. The\n * derivation is what makes a prose line valid in the first place. It is no longer the only thing\n * standing between a missing claim and a silent write.\n */\n const prose = typeof params.body === \"string\" ? params.body : undefined;\n if (prose !== undefined && prose.trim() !== \"\") {\n params.claim = claimFromProse(prose);\n params.body = proseTail(prose);\n }\n else if (prose !== undefined) {\n delete params.body;\n }\n return params;\n};\n/**\n * Decode a whole JSONL document, refusing on the first bad line.\n *\n * Every line is judged before any op runs (AC-6-4), and that ordering is the contract rather than an\n * implementation detail. An apply that executed lines 1-6 and then refused line 7 would leave a\n * commit behind for a call that reported failure, and the caller's only recovery would be to work out\n * which prefix landed. Refusing first costs nothing, since no service has been touched yet.\n *\n * Blank lines are skipped rather than refused, because a file written by `printf '%s\\n'` or a heredoc\n * ends in one. The line numbers still count them, so an error naming line 7 means the seventh\n * line of the file the caller can open in an editor.\n */\nexport const decodeApply = (text) => {\n const ops = [];\n const lines = text.split(\"\\n\");\n for (const [at, raw] of lines.entries()) {\n const line = at + 1;\n if (raw.trim() === \"\")\n continue;\n const record = objectAt(raw, line);\n if (isFailure(record))\n return { ok: false, failure: record };\n const op = opAt(record, line);\n if (isFailure(op))\n return { ok: false, failure: op };\n ops.push(op);\n }\n if (ops.length === 0) {\n return {\n ok: false,\n failure: fail(\"ERR_MISSING_ARGUMENT\", `${APPLY_DOC}: no ops. The input held no non-blank lines, so there is nothing to write`, [\n \"memhtml apply --file ops.jsonl\",\n 'printf \\'%s\\\\n\\' \\'{\"op\":\"write\",\"title\":\"A fact\",\"type\":\"semantic\",\"body\":\"The thing that happened.\"}\\' | memhtml apply -'\n ])\n };\n }\n return { ok: true, ops };\n};\n/**\n * Read the JSONL text for one invocation: `--file <path>`, or stdin.\n *\n * **The stdin seam.** `run()` takes this reader as an injectable parameter and defaults to\n * {@link readStdin}, so `bin.ts` needs no edit and a test supplies the text directly. Reading stdin\n * in `bin.ts` instead would make the entry point parse argv to discover whether\n * the command it is about to dispatch even wants stdin, and would put an I/O decision in the one file\n * whose whole job is \"call run, write the envelope, exit\".\n */\nexport const applyText = async (file, stdin) => {\n if (file !== undefined && file.trim() !== \"\") {\n try {\n return await readFile(file, \"utf8\");\n }\n catch (error) {\n return fail(\"ERR_PATH_NOT_FOUND\", `${APPLY_DOC}: cannot read --file ${file}: ${error instanceof Error ? error.message : String(error)}`, [`ls ${file}`, \"memhtml apply - < ops.jsonl\"]);\n }\n }\n return await stdin();\n};\n/**\n * `process.stdin` as text, and nothing when a human is at a terminal.\n *\n * The TTY check is what makes a bare `memhtml apply` with no `--file` and no pipe answer with the\n * empty-input usage error instead of hanging forever waiting on a keyboard. An agent invoking this\n * without a pipe gets an envelope; a hang would get a timeout and no diagnosis.\n */\nexport const readStdin = async () => {\n if (process.stdin.isTTY === true)\n return \"\";\n const chunks = [];\n for await (const chunk of process.stdin) {\n chunks.push(typeof chunk === \"string\" ? Buffer.from(chunk) : chunk);\n }\n return Buffer.concat(chunks).toString(\"utf8\");\n};\n/**\n * The `batch.applied` payload: the operation's result, renamed to the wire's snake_case.\n *\n * snake_case here and camelCase in `memory.written` is a real inconsistency, and it comes from the\n * spec (D6 names `commit_sha`). It was taken on purpose, because it makes this payload byte-comparable\n * with `memory_write_batch`'s over MCP. An agent that has parsed one has parsed the other, and a batch\n * result is the payload most likely to be handled by shared code across the two doors.\n *\n * Absent fields are `null` rather than missing. `deduped` and `skipped` are always booleans. An agent\n * branching on `deduped === true` should not have to also handle the key being absent, and `--dense`\n * strips the nulls for the context-window case anyway.\n */\nconst opPayload = (report) => ({\n index: report.index,\n ok: report.ok,\n path: report.path ?? null,\n deduped: report.deduped === true,\n existing_path: report.existingPath ?? null,\n code: report.code ?? null,\n error: report.error ?? null,\n skipped: report.skipped === true,\n /**\n * What this op's claim contradicts, when `--detect-conflicts` was passed and something matched.\n *\n * The inner field names are snake_case (`batch_index`) for the same reason the outer ones are: this\n * payload is byte-comparable with `memory_write_batch`'s, so shared code across the two doors reads\n * one shape. Null when the flag was off, when nothing matched, or when the claim has no frame\n * shape. An op carrying a conflict was still written, because the field is a report rather than a\n * refusal.\n */\n conflict: report.conflict === undefined\n ? null\n : {\n path: report.conflict.path,\n batch_index: report.conflict.batchIndex,\n claim: report.conflict.claim\n },\n /**\n * The two `--consolidate last-wins` outcomes, null everywhere else, including when the flag was\n * off. That is the same \"absent is null\" rule every field above follows, and the same shape\n * `memory_write_batch` publishes.\n */\n consolidated_into: report.consolidatedInto ?? null,\n superseded_path: report.supersededPath ?? null\n});\nexport const applyPayload = (result) => ({\n results: result.results.map(opPayload),\n summary: result.summary,\n commit_sha: result.commitSha\n});\n//# sourceMappingURL=apply.js.map","import { mkdir, writeFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport { isEdgeRel } from \"@memhtml/contracts/edges\";\nimport { INBOX_DIR, normalizePath, TASKS_SUBDIR } from \"@memhtml/contracts/paths\";\nimport { checkMemory } from \"@memhtml/html\";\nimport { DatabaseService, STATE_SCHEMA } from \"@memhtml/index\";\nimport { EMBED_WATERMARK } from \"@memhtml/llm\";\nimport { allPaths, applyHeadEdits, archivedFormOf, danglingEdges, hrefFor, link, meta, unlink } from \"@memhtml/sleep\";\nimport { attemptIo, commitSubject, readFileOrNull } from \"@memhtml/store\";\nimport { Effect } from \"effect\";\nimport { Git, Store } from \"./api-layer.js\";\n/**\n * `memhtml doctor`: the corpus's own health check, and `--fix` for the two findings a repair can settle\n * without a judgement call.\n *\n * Eight checks, and each one is a claim the design makes about the corpus rather than a lint:\n *\n * 1. **Dangling `<link>` hrefs**: an authored edge pointing at a path the tree does not hold. Design\n * §2.3 has no foreign key on `edges` deliberately (a `<link>` may name a file the indexer has not\n * reached), so a LEFT JOIN is the only thing that finds these.\n * 2. **Orphan state rows**: a `state.access` row whose path left the tree. There are no\n * cross-database foreign keys, so the store mirrors a move explicitly and an interrupted mirror\n * leaves a row describing nothing.\n * 3. **Inbox depth**: design §2.1 rule 6 routes an unplaceable memory to `areas/inbox/` and says\n * doctor reports the depth as a health signal. A deep inbox is a placement rule that stopped\n * matching what agents write, which nothing else surfaces.\n * 4. **Vocabulary warnings**: format constraint 6. An element outside the closed vocabulary still\n * indexes, so the only way it ever becomes visible is here.\n * 5. **Index staleness**: the index is a projection of a commit, so \"fresh\" means the commit it\n * describes is the commit we are on. Plus the vector-space watermark, because a stored space that\n * differs from the configured one makes every cosine in the index incomparable.\n * 6. **Overdue tasks**: a task is default-excluded from search and skipped by every sleep phase, so\n * nothing else in the system will ever mention that a deadline passed. Doctor is the only surface\n * that reads `due_at`.\n * 7. **Stale task blockers**: a `blocks` edge whose blocker is archived or absent. This is the one\n * task-graph state no single file can reveal, since each file individually is valid and the pair\n * is a task waiting on something that will never move.\n * 8. **Task inbox depth**: a task in `areas/inbox/tasks/` is work with no project, and a task inbox\n * is meant to be drained rather than accumulated.\n *\n * **`--fix` repairs exactly two of the eight, and the repair logic is imported from the sleep\n * integrity phase rather than re-ported.** `archivedFormOf` decides whether a dangling target moved\n * to the archive or is genuinely gone, and `applyHeadEdits`/`link`/`unlink`/`meta` are the byte-splice\n * editors that change one head line without touching the article. A parse→serialize round trip drops\n * a `<pre>` newline per write, so a \"repair\" through the serializer would move the content hash of\n * every file it touched. A second implementation of either would be the consumer-side reimplementation\n * of producer semantics the fleet has paid for repeatedly.\n *\n * The other six report and do not repair. An inbox memory or task needs a human or an agent to decide\n * where it belongs, a vocabulary warning needs the author's intent, and a stale index needs\n * `memhtml index update`, which doctor names in its own suggestions rather than running behind the\n * operator's back. An overdue task needs the work done or the deadline moved, and a stale blocker\n * needs someone to decide whether the blocked task is actually ready.\n */\n/** How deep the inbox may get before doctor calls it a finding. */\nexport const INBOX_WARN_DEPTH = 20;\n/**\n * How many unplaced tasks may sit in `areas/inbox/tasks/` before doctor calls it a finding.\n *\n * Lower than {@link INBOX_WARN_DEPTH} because the two crowds mean different things. An unplaced\n * memory is a placement rule that stopped matching. An unplaced task is work with no project, which\n * is the state a to-do list rots in, and a task inbox is meant to be drained rather than accumulated.\n */\nexport const INBOX_TASK_WARN_DEPTH = 10;\n/** Every `state.access` path the index has no `files` row for. */\nconst orphanAccess = (db) => db.hasState\n ? db\n .all(`SELECT a.path AS path FROM ${STATE_SCHEMA}.access a\n LEFT JOIN files f ON f.path = a.path\n WHERE f.path IS NULL ORDER BY a.path ASC`)\n .pipe(Effect.map((rows) => rows.map((row) => row.path)), Effect.orElseSucceed(() => []))\n : Effect.succeed([]);\n/** How many ACTIVE memories sit in the inbox. An archived one is no longer awaiting placement. */\nconst inboxDepth = (db) => db\n .get(\"SELECT count(*) AS n FROM files WHERE archived = 0 AND path LIKE ? || '/%'\", [INBOX_DIR])\n .pipe(Effect.map((row) => row?.n ?? 0), Effect.orElseSucceed(() => 0));\n/**\n * How many ACTIVE tasks sit in the task inbox.\n *\n * `memory_type = 'task'` as well as the path prefix, because `areas/inbox/tasks/` is a directory and\n * a directory is not a type: a hand-authored memory filed there would inflate the task count and make\n * the finding say something it does not mean.\n */\nconst inboxTaskDepth = (db) => db\n .get(`SELECT count(*) AS n FROM files\n WHERE archived = 0 AND memory_type = 'task' AND path LIKE ? || '/%'`, [`${INBOX_DIR}/${TASKS_SUBDIR}`])\n .pipe(Effect.map((row) => row?.n ?? 0), Effect.orElseSucceed(() => 0));\n/**\n * Open tasks past their deadline.\n *\n * `substr(due_at, 1, 10)` states that the comparison is one of calendar days. The bound is always a\n * bare `YYYY-MM-DD` (today, from the clock), and enumerated against that bound the truncation changes\n * no answer, because a time-bearing due date on the bound's own day sorts after it either way. It\n * stays as the statement of intent. `listTasks`' `--due-before` takes a caller-supplied bound that may\n * carry a time, where the truncation does change answers, and one form across both queries keeps\n * \"overdue\" meaning the same thing in the two places an operator reads it.\n *\n * **`archived = 0` and `task_status <> 'done'` both change the result** (mutation-verified\n * 2026-08-02). A finished task's deadline is history, and reporting it would make the finding grow\n * forever and never reach zero.\n */\nconst overdueTasks = (db, today) => db\n .all(`SELECT path, task_status, due_at FROM files\n WHERE memory_type = 'task' AND archived = 0 AND due_at IS NOT NULL\n AND substr(due_at, 1, 10) < ? AND task_status <> 'done'\n ORDER BY due_at ASC, path ASC`, [today])\n .pipe(Effect.map((rows) => rows.map((row) => ({ path: row.path, taskStatus: row.task_status, dueAt: row.due_at }))), Effect.orElseSucceed(() => []));\n/**\n * Open tasks whose blocker can never close them.\n *\n * A `blocks` edge points blocker → blocked, so the blocked task is the edge's `dst_path`. A LEFT JOIN\n * rather than an inner one, because the two failure modes differ. An archived blocker is finished work\n * whose edge nobody cleared, and a missing one is an edge whose source has no `files` row. Either way\n * the blocked task waits on something that will never move. This is the one task-graph state no single\n * file can reveal, since each file is individually valid and only the pair is wrong.\n *\n * **The `archived` arm is the reachable one, and `missing` is defense in depth.** Probed and\n * mutation-confirmed 2026-08-02: deleting a blocker's file makes `indexer.update` clear\n * `edges WHERE src_path = ?` in the same batch, so an edge cannot outlive its source file and the\n * `missing` branch has nothing to find. Removing that `DELETE` turns the branch on, and it stays for\n * that reason. `edges` carries no foreign key deliberately, so a future writer of edge rows would not\n * inherit the indexer's discipline.\n *\n * **`edge_class = 'task'` is redundant with `rel = 'blocks'` today.** The migration's per-class CHECKs\n * refuse `blocks` under every other class, so a mutation dropping it leaves the suite green. It is kept\n * because every memory-graph query filters on the class column, and a reader who saw this one trust\n * the rel alone would learn the wrong rule about where the firewall lives.\n *\n * Report-only. Clearing the edge is an authoring decision, since the blocked task may be genuinely\n * ready or the blocker may have been archived prematurely. `--fix` guessing between those would rewrite\n * a plan.\n */\nconst staleBlockers = (db) => db\n .all(`SELECT t.path AS path, e.src_path AS blocker_path,\n CASE WHEN b.path IS NULL THEN 'missing' ELSE 'archived' END AS blocker_state\n FROM files t\n JOIN edges e ON e.dst_path = t.path AND e.edge_class = 'task' AND e.rel = 'blocks'\n LEFT JOIN files b ON b.path = e.src_path\n WHERE t.memory_type = 'task' AND t.archived = 0 AND t.task_status <> 'done'\n AND (b.path IS NULL OR b.archived = 1)\n ORDER BY t.path ASC, e.src_path ASC`)\n .pipe(Effect.map((rows) => rows.map((row) => ({\n path: row.path,\n blockerPath: row.blocker_path,\n blockerState: row.blocker_state === \"missing\" ? \"missing\" : \"archived\"\n}))), Effect.orElseSucceed(() => []));\n/**\n * Re-read every active file and collect its format warnings.\n *\n * Re-read rather than taken from the index, because a warning is not a stored column. The indexer\n * counts a parse failure and projects what it can, and constraint 6 is deliberately non-fatal. Doctor\n * is the one caller that wants the list, so it is the one caller that pays for the read.\n */\nconst collectWarnings = (root, paths) => Effect.gen(function* () {\n const warnings = [];\n const unparseable = [];\n for (const path of paths) {\n const html = yield* readFileOrNull(join(root, path)).pipe(Effect.orElseSucceed(() => null));\n if (html === null) {\n unparseable.push(path);\n continue;\n }\n const checked = checkMemory(html);\n if (checked.violations.length > 0)\n unparseable.push(path);\n if (checked.warnings.length > 0)\n warnings.push({ path, warnings: checked.warnings });\n }\n return { warnings, unparseable };\n});\n/** The year a run's repairs partition archive lookups under: the current calendar year. */\nconst currentYear = Effect.clockWith((clock) => Effect.map(clock.currentTimeMillis, (millis) => new Date(millis).getUTCFullYear()));\n/** Today as `YYYY-MM-DD`, through the Effect clock so a test can pin what \"overdue\" means. */\nconst todayDate = Effect.clockWith((clock) => Effect.map(clock.currentTimeMillis, (millis) => new Date(millis).toISOString().slice(0, 10)));\n/** An ISO-8601 UTC second, for the `memhtml-updated` stamp a repair writes. */\nconst nowSecond = Effect.clockWith((clock) => Effect.map(clock.currentTimeMillis, (millis) => `${new Date(millis).toISOString().slice(0, 19)}Z`));\n/**\n * Repair the dangling hrefs and prune the orphan access rows.\n *\n * The href repair mirrors the integrity phase exactly, using its own `archivedFormOf` and its own\n * head editors. A dangling target that moved under `archive/<YYYY>/` gets its href rewritten, so the\n * edge still says something true. A target with no file anywhere gets the link dropped with a\n * warning, because the edge asserts a relationship to nothing and leaving it would produce the same\n * finding on every rebuild forever.\n *\n * Remove-then-add on the same file in one pass, so a repair replaces one line rather than dropping a\n * line and appending another elsewhere in the head. A re-run is then a no-op: once the href points at\n * the archive path the removal matches nothing and the addition is already present.\n */\nconst repair = (root, findings, orphans) => Effect.gen(function* () {\n const git = yield* Git;\n const db = yield* DatabaseService;\n const at = yield* nowSecond;\n let rewritten = 0;\n let dropped = 0;\n const touched = [];\n for (const finding of findings) {\n if (!isEdgeRel(finding.rel))\n continue;\n const rel = finding.rel;\n const absolute = join(root, finding.srcPath);\n const html = yield* readFileOrNull(absolute).pipe(Effect.orElseSucceed(() => null));\n if (html === null)\n continue;\n const edits = finding.rewriteTo === null\n ? [unlink(rel, hrefFor(finding.dstPath)), meta(\"memhtml-updated\", at)]\n : [\n unlink(rel, hrefFor(finding.dstPath)),\n link(rel, hrefFor(finding.rewriteTo)),\n meta(\"memhtml-updated\", at)\n ];\n const edited = applyHeadEdits(html, edits);\n if (edited === html)\n continue;\n if (finding.rewriteTo === null) {\n yield* Effect.logWarning(`doctor dropped a dangling ${rel} from ${finding.srcPath}: target has no file`);\n }\n yield* attemptIo(`doctor.write:${finding.srcPath}`, async () => {\n await mkdir(dirname(absolute), { recursive: true });\n await writeFile(absolute, edited, \"utf8\");\n }).pipe(Effect.orElseSucceed(() => undefined));\n touched.push(finding.srcPath);\n if (finding.rewriteTo === null)\n dropped += 1;\n else\n rewritten += 1;\n }\n let prunedAccessRows = 0;\n if (orphans.length > 0 && db.hasState) {\n /**\n * One statement per path rather than an `IN` list, because the list is unbounded. A driver\n * parameter limit reached mid-prune would fail the whole batch and leave every row in place.\n */\n for (const path of orphans) {\n const done = yield* db\n .run(`DELETE FROM ${STATE_SCHEMA}.access WHERE path = ?`, [path])\n .pipe(Effect.as(true), Effect.orElseSucceed(() => false));\n if (done)\n prunedAccessRows += 1;\n }\n }\n let commitSha = null;\n if (touched.length > 0) {\n yield* git.add(touched);\n const commit = yield* git.commit(commitSubject(\"link\", `repair ${rewritten + dropped} dangling links`));\n commitSha = commit.sha;\n }\n return { rewritten, dropped, prunedAccessRows, commitSha };\n});\n/**\n * Run the health check, optionally repairing.\n *\n * The findings are gathered before any repair and the report carries the pre-repair lists alongside\n * `repaired`, which is what makes a `--fix` run auditable. An operator reads what was wrong and what\n * was done about it in one envelope, rather than a clean report that says nothing happened.\n */\nexport const doctor = (options) => Effect.gen(function* () {\n const git = yield* Git;\n const store = yield* Store;\n const db = yield* DatabaseService;\n const headSha = yield* git.revParseHead().pipe(Effect.orElseSucceed(() => null));\n const dirty = yield* store.dirtyPaths().pipe(Effect.orElseSucceed(() => []));\n const state = yield* db\n .get(\"SELECT head_sha, embed_model FROM index_state WHERE id = 1\")\n .pipe(Effect.orElseSucceed(() => undefined));\n const known = new Set((yield* allPaths(db).pipe(Effect.orElseSucceed(() => []))).map((row) => row.path));\n const year = yield* currentYear;\n const edges = yield* danglingEdges(db).pipe(Effect.orElseSucceed(() => []));\n const dangling = edges.map((edge) => {\n const dstPath = normalizePath(edge.dst_path);\n return {\n srcPath: edge.src_path,\n rel: edge.rel,\n dstPath,\n rewriteTo: archivedFormOf(dstPath, known, year) ?? null\n };\n });\n const orphanAccessRows = yield* orphanAccess(db);\n const depth = yield* inboxDepth(db);\n const taskDepth = yield* inboxTaskDepth(db);\n const overdue = yield* overdueTasks(db, yield* todayDate);\n const stale = yield* staleBlockers(db);\n const active = yield* db\n .all(\"SELECT path FROM files WHERE archived = 0 ORDER BY path ASC\")\n .pipe(Effect.orElseSucceed(() => []));\n const { warnings, unparseable } = yield* collectWarnings(git.root, active.map((row) => row.path));\n const repaired = options.fix ? yield* repair(git.root, dangling, orphanAccessRows) : undefined;\n const indexFresh = state?.head_sha !== null && state?.head_sha === headSha;\n const embedModelMatches = state?.embed_model === EMBED_WATERMARK;\n return {\n root: git.root,\n /**\n * `healthy` is computed from the findings and not from the repair. A `--fix` run that repaired\n * everything still reports the corpus as it was found. A command that flipped itself green by\n * fixing what it found would make \"doctor is clean\" unfalsifiable.\n */\n healthy: dangling.length === 0 &&\n orphanAccessRows.length === 0 &&\n depth <= INBOX_WARN_DEPTH &&\n /**\n * The task inbox counts toward `healthy` for the same reason the memory inbox does: an\n * unplaced item is a routing signal. `overdueTasks` and `staleBlockers` are excluded, because\n * those two are facts about the work rather than defects in the corpus. A repo whose owner is\n * late on a to-do is structurally sound, and folding them in would make `healthy: false` the\n * normal state and stop anyone reading the flag at all. Every other finding here is a defect\n * in the corpus; those two describe work that has fallen behind.\n */\n taskDepth <= INBOX_TASK_WARN_DEPTH &&\n warnings.length === 0 &&\n unparseable.length === 0 &&\n indexFresh &&\n embedModelMatches,\n dangling,\n orphanAccessRows,\n inboxDepth: depth,\n inboxCrowded: depth > INBOX_WARN_DEPTH,\n inboxTaskDepth: taskDepth,\n inboxTasksCrowded: taskDepth > INBOX_TASK_WARN_DEPTH,\n overdueTasks: overdue,\n staleBlockers: stale,\n warnings,\n unparseable,\n indexFresh,\n indexHeadSha: state?.head_sha ?? null,\n headSha,\n embedModelMatches,\n storedEmbedModel: state?.embed_model ?? null,\n configuredEmbedModel: EMBED_WATERMARK,\n dirty,\n ...(repaired === undefined ? {} : { repaired })\n };\n});\n//# sourceMappingURL=doctor.js.map","import { discriminationGate, runDiscrimination } from \"@memhtml/eval\";\nimport { initRepo } from \"@memhtml/store\";\nimport { Effect, Logger } from \"effect\";\nimport { runAgentsDoc } from \"./agents-doc.js\";\nimport { Git, Indexer, layerApp, Sleep } from \"./api-layer.js\";\nimport { applyPayload, applyText, decodeApply, readStdin } from \"./apply.js\";\nimport { buildManifest, COMMAND_NAMES, COMMANDS, GLOBAL_FLAGS } from \"./commands.js\";\nimport { MemhtmlRoot } from \"./config.js\";\nimport { doctor } from \"./doctor.js\";\nimport { API_VERSION, EXIT_OK, EXIT_RUNTIME, EXIT_USAGE, fail, nearest, render, succeed } from \"./envelope.js\";\nimport { failureFor } from \"./errors.js\";\nimport { DEFAULT_TIMEOUT_MS, execCommand, MAX_TIMEOUT_MS, readScript } from \"./exec.js\";\nimport * as ops from \"./operations.js\";\nimport { publish } from \"./publish.js\";\nimport { serveMcp } from \"./serve.js\";\nimport { stateExport, stateImport } from \"./state.js\";\nimport { indexReport, sleepPhases, sleepRunReport } from \"./views.js\";\nconst KNOWN_FLAGS = new Set([\n ...GLOBAL_FLAGS.map((flag) => flag.name),\n ...COMMANDS.flatMap((command) => command.flags.map((flag) => flag.name))\n]);\n/**\n * The two-word command names, longest first.\n *\n * A subcommand is matched greedily so `index status` beats `index`, and the leftover tokens become\n * positionals. Matching the shorter name first would make `memhtml index status` a call to a\n * hypothetical `index` command with `status` as an argument, which is a wrong answer rather than an\n * error.\n */\nconst COMPOUND_NAMES = COMMAND_NAMES.filter((name) => name.includes(\" \")).sort((left, right) => right.length - left.length);\n/**\n * `--flag value`, `--flag=value`, `--no-flag`, and bare `--flag`.\n *\n * Every flag's value is an array, because several flags are repeatable (`--tag`, `--entity`,\n * `--body`) and a map of scalars would silently keep only the last occurrence, so a write with three\n * entities would store one. Non-repeatable flags read `.at(-1)`, so a duplicate is last-wins rather\n * than an error, which is what a shell user retyping a flag expects.\n */\nexport const parseArgv = (argv) => {\n const positional = [];\n const flags = new Map();\n const push = (name, value) => {\n const existing = flags.get(name);\n if (existing === undefined)\n flags.set(name, [value]);\n else\n existing.push(value);\n };\n let index = 0;\n while (index < argv.length) {\n const token = argv[index];\n if (token.startsWith(\"--\")) {\n const body = token.slice(2);\n const eq = body.indexOf(\"=\");\n if (eq !== -1) {\n push(body.slice(0, eq), body.slice(eq + 1));\n index += 1;\n continue;\n }\n // `--no-embed` is how a boolean defaulting to true is turned off. Without it, a flag whose\n // default is `true` would be unsettable from a shell.\n if (body.startsWith(\"no-\") && KNOWN_FLAGS.has(body.slice(3))) {\n push(body.slice(3), false);\n index += 1;\n continue;\n }\n const next = argv[index + 1];\n if (next !== undefined && !next.startsWith(\"--\")) {\n push(body, next);\n index += 2;\n continue;\n }\n push(body, true);\n index += 1;\n continue;\n }\n positional.push(token);\n index += 1;\n }\n const joined = positional.join(\" \");\n const compound = COMPOUND_NAMES.find((name) => joined === name || joined.startsWith(`${name} `));\n if (compound !== undefined) {\n const consumed = compound.split(\" \").length;\n return { command: compound, positional: positional.slice(consumed), flags };\n }\n return { command: positional[0] ?? \"\", positional: positional.slice(1), flags };\n};\n/** A flag's last value as a string, or `undefined` when it was not given. */\nconst str = (parsed, name) => {\n const value = parsed.flags.get(name)?.at(-1);\n return value === undefined || typeof value === \"boolean\" ? undefined : value;\n};\n/** Every value a repeatable flag was given, in order. Empty when absent. */\nconst list = (parsed, name) => (parsed.flags.get(name) ?? []).flatMap((value) => typeof value === \"string\" && value !== \"\" ? [value] : []);\n/** A flag as a boolean: bare `--flag` is true, `--no-flag` is false, `--flag=false` is false. */\nconst bool = (parsed, name, fallback) => {\n const value = parsed.flags.get(name)?.at(-1);\n if (value === undefined)\n return fallback;\n if (typeof value === \"boolean\")\n return value;\n return value !== \"false\" && value !== \"0\" && value !== \"no\";\n};\n/** A flag as an integer, or `undefined` when absent or unparseable. */\nconst int = (parsed, name) => {\n const raw = str(parsed, name);\n if (raw === undefined)\n return undefined;\n const value = Number.parseInt(raw, 10);\n return Number.isFinite(value) ? value : undefined;\n};\n/** A flag as a finite number in a range, or `undefined`. */\nconst num = (parsed, name) => {\n const raw = str(parsed, name);\n if (raw === undefined)\n return undefined;\n const value = Number.parseFloat(raw);\n return Number.isFinite(value) ? value : undefined;\n};\n/** The scope every retrieval command shares, so `search` and `recall` cannot diverge. */\nconst scopeOf = (parsed) => ({\n memoryTypes: list(parsed, \"type\"),\n workspace: str(parsed, \"workspace\"),\n tags: list(parsed, \"tag\"),\n entity: str(parsed, \"entity\"),\n includeArchived: bool(parsed, \"include-archived\", false),\n asOf: str(parsed, \"as-of\")\n});\n/** Session provenance, from the three flags every write-path command accepts. */\nconst provenanceOf = (parsed) => ({\n sessionId: str(parsed, \"session-id\"),\n promptId: str(parsed, \"prompt-id\"),\n turnUuid: str(parsed, \"turn-uuid\")\n});\n/**\n * Dispatch one parsed invocation against the provided services.\n *\n * Every arm is decode → call → name the response type. No arm builds an envelope, catches an error,\n * or writes to a stream. Those happen once in {@link run}, which keeps thirty-one commands\n * from having thirty-one slightly different failure shapes.\n *\n * `applyOps` is the one piece of state an arm cannot derive from `parsed`. Reading a file or draining\n * stdin is async I/O whose failures are usage errors (exit 2), and `run` has already done it and\n * refused before reaching here. It is passed in rather than read here, so the `apply` arm stays what\n * every other arm is: one call to a shared use case.\n */\nconst dispatch = (parsed, applyOps = []) => {\n switch (parsed.command) {\n case \"manifest\":\n return Effect.succeed([\"cli.manifest\", buildManifest()]);\n case \"init\":\n return Effect.gen(function* () {\n const git = yield* Git;\n const result = yield* initRepo(git);\n return [\"repo.init\", result];\n });\n case \"write\":\n return Effect.gen(function* () {\n const result = yield* ops.writeMemory({\n title: str(parsed, \"title\") ?? \"\",\n // `claim` is \"\" exactly when `--article-html` supplied the article instead, which\n // `validate` has already proven is the only way to get here without a claim. The\n // template ignores `claim` entirely on that branch (`@memhtml/html` template.ts:88-91).\n claim: str(parsed, \"claim\") ?? \"\",\n body: list(parsed, \"body\"),\n articleHtml: str(parsed, \"article-html\"),\n memoryType: str(parsed, \"type\") ?? \"\",\n path: str(parsed, \"path\"),\n workspace: str(parsed, \"workspace\"),\n tags: list(parsed, \"tag\"),\n entities: list(parsed, \"entity\"),\n importance: int(parsed, \"importance\"),\n confidence: num(parsed, \"confidence\"),\n ...provenanceOf(parsed)\n });\n return [\"memory.written\", result];\n });\n /**\n * The batch door. One call to the shared `batchWrite`, and the per-op `code`/`error` it returns\n * are not re-mapped here. The operation already ran them through the same `codeFor`/`messageFor`\n * every envelope error takes, so this door and `memory_write_batch` cannot report\n * different codes for one refused op.\n */\n case \"apply\":\n return Effect.gen(function* () {\n const result = yield* ops.batchWrite({\n ops: applyOps,\n continueOnError: bool(parsed, \"continue-on-error\", false),\n detectConflicts: bool(parsed, \"detect-conflicts\", false),\n // `validate` has already refused any value outside the flag's closed vocabulary, so the\n // narrowing here cannot silently drop a caller's ask.\n ...(str(parsed, \"consolidate\") === \"last-wins\"\n ? { consolidate: \"last-wins\" }\n : {}),\n ...provenanceOf(parsed)\n });\n return [\"batch.applied\", applyPayload(result)];\n });\n case \"read\":\n return Effect.gen(function* () {\n const result = yield* ops.readMemory(parsed.positional[0] ?? \"\", provenanceOf(parsed));\n return [\n \"memory.detail\",\n {\n path: result.path,\n title: result.doc.title,\n metas: result.doc.metas,\n entities: result.doc.entities,\n tags: result.doc.tags,\n links: result.doc.links,\n gist: result.doc.article.gist,\n body: result.doc.article.bodyText,\n html: result.doc.article.html,\n archived: result.doc.metas.status === \"archived\",\n warnings: result.doc.warnings\n }\n ];\n });\n case \"search\":\n return Effect.gen(function* () {\n const result = yield* ops.searchMemories({\n query: parsed.positional[0] ?? \"\",\n limit: int(parsed, \"limit\"),\n ...scopeOf(parsed)\n });\n return [\"memory.hits\", result];\n });\n case \"recall\":\n return Effect.gen(function* () {\n const pack = yield* ops.recallMemories({\n query: parsed.positional[0] ?? \"\",\n budgetChars: int(parsed, \"budget\"),\n ...scopeOf(parsed)\n });\n return [\"recall.pack\", pack];\n });\n case \"correct\":\n return Effect.gen(function* () {\n const result = yield* ops.correctMemory({\n targetPath: parsed.positional[0] ?? \"\",\n title: str(parsed, \"title\") ?? \"\",\n claim: str(parsed, \"claim\") ?? \"\",\n body: list(parsed, \"body\"),\n articleHtml: str(parsed, \"article-html\"),\n memoryType: str(parsed, \"type\"),\n reason: str(parsed, \"reason\"),\n ...provenanceOf(parsed)\n });\n return [\"memory.corrected\", result];\n });\n case \"link\":\n return Effect.gen(function* () {\n const result = yield* ops.linkMemories(parsed.positional[0] ?? \"\", parsed.positional[1] ?? \"\", parsed.positional[2] ?? \"\");\n return [\"memory.linked\", result];\n });\n case \"neighbors\":\n return Effect.gen(function* () {\n const result = yield* ops.neighborsOf({\n path: parsed.positional[0] ?? \"\",\n depth: int(parsed, \"depth\"),\n rels: list(parsed, \"rel\")\n });\n return [\"memory.neighbors\", result];\n });\n case \"archive\":\n return Effect.gen(function* () {\n const result = yield* ops.archiveMemory(parsed.positional[0] ?? \"\", str(parsed, \"reason\") ?? \"\");\n return [\"memory.archived\", result];\n });\n case \"reinforce\":\n return Effect.gen(function* () {\n // Every positional is a path: `memhtml reinforce a.html b.html --signal positive` is the shape\n // an agent reaching for the MCP tool's `paths` array writes on a command line.\n const result = yield* ops.reinforceMemories(parsed.positional, str(parsed, \"signal\") ?? \"neutral\");\n return [\"memory.reinforced\", result];\n });\n case \"list\":\n return Effect.gen(function* () {\n const result = yield* ops.listMemories({\n memoryType: str(parsed, \"type\"),\n workspace: str(parsed, \"workspace\"),\n tag: str(parsed, \"tag\"),\n entity: str(parsed, \"entity\"),\n para: str(parsed, \"para\"),\n limit: int(parsed, \"limit\"),\n cursor: str(parsed, \"cursor\"),\n includeArchived: bool(parsed, \"include-archived\", false)\n });\n return [\"memory.list\", result];\n });\n case \"task add\":\n return Effect.gen(function* () {\n const title = str(parsed, \"title\") ?? \"\";\n const result = yield* ops.writeMemory({\n title,\n // The claim defaults to the title, because a task's statement and its name are usually the\n // same sentence, and a required second phrasing would be restated verbatim every time.\n claim: str(parsed, \"claim\") ?? title,\n body: list(parsed, \"body\"),\n memoryType: \"task\",\n workspace: str(parsed, \"workspace\"),\n tags: list(parsed, \"tag\"),\n entities: list(parsed, \"entity\"),\n taskStatus: str(parsed, \"status\"),\n dueAt: str(parsed, \"due\"),\n ...provenanceOf(parsed)\n });\n return [\n \"task.written\",\n {\n path: result.path,\n created: result.created,\n deduped: result.deduped,\n // Two open tasks with identical bodies are two real work items, so the dedup carve-out\n // means this is normally false. It is reported anyway, because a caller cannot tell a\n // fresh file from a returned one without it.\n existingPath: result.existingPath ?? null,\n taskStatus: str(parsed, \"status\") ?? \"todo\",\n dueAt: str(parsed, \"due\") ?? null,\n commitSha: result.commitSha\n }\n ];\n });\n case \"task status\":\n return Effect.gen(function* () {\n const result = yield* ops.setTaskStatus({\n path: parsed.positional[0] ?? \"\",\n status: parsed.positional[1] ?? \"\",\n reason: str(parsed, \"reason\")\n });\n return [\"task.updated\", { ...result, archivePath: result.archivePath ?? null }];\n });\n case \"task list\":\n return Effect.gen(function* () {\n const result = yield* ops.listTasks({\n status: str(parsed, \"status\"),\n workspace: str(parsed, \"workspace\"),\n dueBefore: str(parsed, \"due-before\"),\n limit: int(parsed, \"limit\"),\n cursor: str(parsed, \"cursor\"),\n includeArchived: bool(parsed, \"include-archived\", false)\n });\n return [\"task.list\", result];\n });\n case \"index rebuild\":\n return Effect.gen(function* () {\n const indexer = yield* Indexer;\n const report = yield* indexer.rebuild({ embed: bool(parsed, \"embed\", true) });\n return [\"index.report\", { mode: \"rebuild\", ...report }];\n });\n case \"index update\":\n return Effect.gen(function* () {\n const indexer = yield* Indexer;\n const report = yield* indexer.update({ embed: bool(parsed, \"embed\", true) });\n return [\"index.report\", { mode: \"update\", ...report }];\n });\n case \"index status\":\n return Effect.gen(function* () {\n const report = yield* indexReport();\n return [\"index.report\", report];\n });\n case \"trace index\":\n return Effect.gen(function* () {\n const report = yield* ops.indexTraces();\n return [\"trace.report\", report];\n });\n case \"trace search\":\n return Effect.gen(function* () {\n const result = yield* ops.searchTraces({\n query: parsed.positional[0] ?? \"\",\n cwd: str(parsed, \"cwd\"),\n since: str(parsed, \"since\"),\n limit: int(parsed, \"limit\")\n });\n return [\"trace.sessions\", result];\n });\n case \"trace links\":\n return Effect.gen(function* () {\n const result = yield* ops.traceLinks({\n sessionId: str(parsed, \"session-id\"),\n path: str(parsed, \"path\")\n });\n return [\"trace.links\", result];\n });\n case \"sleep run\":\n return Effect.gen(function* () {\n const sleep = yield* Sleep;\n const phases = yield* sleepPhases(str(parsed, \"phases\"));\n const report = yield* sleep.run({\n date: str(parsed, \"date\") ?? (yield* today),\n ...(phases === undefined ? {} : { phases }),\n dryRun: bool(parsed, \"dry-run\", false)\n });\n return [\"sleep.report\", sleepRunReport(report)];\n });\n case \"sleep resume\":\n return Effect.gen(function* () {\n const sleep = yield* Sleep;\n const report = yield* sleep.resume(parsed.positional[0] ?? \"\");\n return [\"sleep.report\", sleepRunReport(report)];\n });\n case \"sleep review\":\n return Effect.gen(function* () {\n const sleep = yield* Sleep;\n const report = yield* sleep.review(parsed.positional[0]);\n const withDiff = bool(parsed, \"diff\", false);\n if (!withDiff)\n return [\"sleep.review\", report];\n // The raw diff is fetched here rather than inside `review`, because it is the one field whose\n // size is unbounded, and a review that always carried it would make the default response\n // unusable in a context window.\n const git = yield* Git;\n const diff = yield* git\n .run([\"diff\", `${report.baseSha}..${report.headSha}`])\n .pipe(Effect.orElseSucceed(() => \"\"));\n return [\"sleep.review\", { ...report, diff }];\n });\n case \"sleep merge\":\n return Effect.gen(function* () {\n const sleep = yield* Sleep;\n const skipGate = bool(parsed, \"skip-gate\", false);\n if (skipGate) {\n yield* Effect.logWarning(\"sleep merge --skip-gate: merging without re-running discrimination\");\n }\n /**\n * **The discrimination gate, composed here.** A sleep run that degrades retrieval quality\n * cannot land. `@memhtml/sleep` takes the gate as a parameter and supplies none, so a package\n * that cannot import the eval also cannot silently default it. The composition is visible in\n * this wiring or it does not exist.\n *\n * `discriminationGate` fails on an inversion, `merge` wraps it in `Effect.result`, and the\n * failure becomes `refusal: \"gate-failed\"` with `main` never moving.\n *\n * `fake` mode, always. The gate measures the ranking stack against its own generated fixture\n * corpus, so a live-Bedrock run would make a nightly merge conditional on a network call and\n * on credentials being present at 3am. The deterministic embedder's cosine relations are\n * a pure function of the text, which is the property a regression gate needs. A\n * cron whose merge silently skipped its gate because a token expired is the failure this\n * arrangement prevents.\n */\n const report = yield* sleep.merge(parsed.positional[0] ?? \"\", skipGate ? {} : { preMergeGate: discriminationGate().pipe(Effect.asVoid) });\n return [\"sleep.merge\", report];\n });\n case \"publish\":\n return Effect.gen(function* () {\n const report = yield* publish();\n return [\"publish.report\", report];\n });\n case \"doctor\":\n return Effect.gen(function* () {\n const report = yield* doctor({ fix: bool(parsed, \"fix\", false) });\n return [\"doctor.report\", report];\n });\n case \"state export\":\n return Effect.gen(function* () {\n const report = yield* stateExport();\n return [\"state.export\", report];\n });\n case \"state import\":\n return Effect.gen(function* () {\n const report = yield* stateImport();\n return [\"state.import\", report];\n });\n case \"sleep status\":\n return Effect.gen(function* () {\n const sleep = yield* Sleep;\n const report = yield* sleep.review();\n return [\n \"sleep.report\",\n {\n runId: report.runId,\n branch: report.branch,\n baseSha: report.baseSha,\n headSha: report.headSha,\n phases: report.phases,\n commits: report.commits.length\n }\n ];\n });\n case \"status\":\n return Effect.gen(function* () {\n const report = yield* ops.statusReport();\n return [\"status.health\", report];\n });\n default:\n // Unreachable while every COMMANDS entry has a case. A new spec with no handler surfaces\n // here as a usage error rather than an empty stdout.\n return Effect.fail({ _tag: \"UnhandledCommand\", command: parsed.command });\n }\n};\n/** Today as `YYYY-MM-DD`, through the Effect clock so a test can pin the run date. */\nconst today = Effect.clockWith((clock) => Effect.map(clock.currentTimeMillis, (millis) => new Date(millis).toISOString().slice(0, 10)));\n/**\n * An unknown command, with candidates measured against the whole typed invocation.\n *\n * `parseArgv` only matches a compound name exactly, so a typo in either word of `memhtml index rebuild`\n * leaves `command` holding the first token alone and every remaining token in `positional`.\n * Measuring `\"index\"` against the flat name list scores `init` at 2 and `index rebuild` at 8, so the\n * suggestion an operator needs loses to one they did not ask for. Re-joining the tokens makes\n * the distance a comparison of the two things: `\"index rebiuld\"` is 2 from `index rebuild` and 12\n * from `init`.\n *\n * Both are offered, the joined form first, because the typo could be in either half. A one-word\n * invocation joins to itself, so the single-command path is unchanged.\n */\nconst unknownCommand = (parsed) => {\n const typed = [parsed.command, ...parsed.positional].join(\" \").trim();\n const candidates = [\n ...nearest(typed, COMMAND_NAMES),\n ...nearest(parsed.command, COMMAND_NAMES)\n ].filter((name, at, all) => all.indexOf(name) === at);\n return fail(\"ERR_UNKNOWN_COMMAND\", `unknown command: ${typed === \"\" ? parsed.command : typed}`, candidates.slice(0, 3));\n};\n/**\n * The commands where the article body comes from either a claim or pre-authored markup, never both.\n *\n * Listed here rather than expressed in `FlagSpec`, because `FlagSpec` has one `required: boolean` and\n * no notion of a conditional. Inventing a table field for a rule that holds on two commands\n * would put a second, weaker copy of this check into the manifest for every command that does not\n * need it. Both flag descriptions state the rule, so `memhtml manifest` still carries it.\n */\nconst EITHER_CLAIM_OR_ARTICLE = new Set([\"write\", \"correct\"]);\n/**\n * `memhtml exec` takes at most one script door, and a bound inside the cap.\n *\n * Here for the reason `claimOrArticle` is: `validate`'s return becomes exit 2 and a failure raised in\n * `dispatch` becomes exit 1, so \"you passed the wrong flags\" must be decided before any service is\n * built. `.erpaval/solutions/api-patterns/xor-params-and-mcp-error-masking.md` records the rule.\n *\n * At most one rather than exactly one, because zero doors is legal and means stdin, the same shape\n * `memhtml apply` has, where a bare invocation drains the pipe. A missing script is not a usage error\n * here. An empty one is, and that check sits beside the read in {@link run} because reading is async.\n *\n * `--timeout-ms` is checked for a positive integer within the cap. Zero and negatives are refused\n * rather than clamped, because just-bash treats a non-positive `maxJsTimeoutMs` as no bound at all, so\n * `--timeout-ms 0` would read as \"be quick\" and mean \"run forever\".\n */\nconst execFlags = (parsed) => {\n if (parsed.command !== \"exec\")\n return undefined;\n const doors = [\n str(parsed, \"file\") === undefined ? undefined : \"--file\",\n str(parsed, \"script\") === undefined ? undefined : \"--script\"\n ].filter((door) => door !== undefined);\n if (doors.length > 1) {\n return fail(\"ERR_INVALID_FLAG\", \"exec takes at most one of --file or --script, not both: two scripts cannot both be the one that runs\", [\n \"memhtml exec --file traverse.mjs\",\n \"memhtml exec --script 'console.log(1)'\",\n \"cat s.mjs | memhtml exec\"\n ]);\n }\n // A `-` positional is the explicit stdin spelling, so it cannot sit beside a door either.\n if (doors.length === 1 && parsed.positional[0] === \"-\") {\n return fail(\"ERR_INVALID_FLAG\", `exec cannot read stdin and ${doors[0]} in the same call: \\`-\\` names stdin as the script source`, [\"cat s.mjs | memhtml exec\", `memhtml exec ${doors[0]} …`]);\n }\n const raw = str(parsed, \"timeout-ms\");\n if (raw !== undefined) {\n const timeout = int(parsed, \"timeout-ms\");\n if (timeout === undefined || timeout <= 0 || timeout > MAX_TIMEOUT_MS) {\n return fail(\"ERR_INVALID_FLAG\", `--timeout-ms must be a positive integer of at most ${MAX_TIMEOUT_MS}: a non-positive bound is no bound at all, which is the one thing a sandbox may not be`, [`memhtml exec --timeout-ms ${DEFAULT_TIMEOUT_MS}`]);\n }\n }\n return undefined;\n};\n/**\n * Exactly one of `--claim` / `--article-html`.\n *\n * Checked here rather than in the dispatch arm, because the exit code is the contract. `validate`'s\n * return is emitted as exit 2 ({@link EXIT_USAGE}), while a failure raised inside `dispatch` travels\n * through `failureFor` and becomes exit 1. Supplying the wrong flags is a usage error, and a shell\n * caller branching on the code must not see it as a runtime one.\n *\n * Two codes for two conditions, each following the convention already in this function. An absent\n * required flag is `ERR_MISSING_ARGUMENT` (as below), and a flag present but unusable as given is\n * `ERR_INVALID_FLAG` (as above, and in the closed-vocabulary check). Neither is newly minted.\n */\nconst claimOrArticle = (parsed) => {\n if (!EITHER_CLAIM_OR_ARTICLE.has(parsed.command))\n return undefined;\n const hasClaim = str(parsed, \"claim\") !== undefined;\n const hasArticle = str(parsed, \"article-html\") !== undefined;\n if (hasClaim && hasArticle) {\n return fail(\"ERR_INVALID_FLAG\", `${parsed.command} takes exactly one of --claim or --article-html, not both: --article-html is the whole article, so a --claim beside it would be silently discarded`, [\n `memhtml ${parsed.command} --claim <sentence>`,\n `memhtml ${parsed.command} --article-html '<p>…</p>'`\n ]);\n }\n if (!hasClaim && !hasArticle) {\n return fail(\"ERR_MISSING_ARGUMENT\", `${parsed.command} requires exactly one of --claim or --article-html`, [\n `memhtml ${parsed.command} --claim <sentence>`,\n `memhtml ${parsed.command} --article-html '<p>…</p>'`\n ]);\n }\n return undefined;\n};\n/**\n * Validate a parsed invocation against its spec. Usage errors only; nothing here touches a service.\n *\n * Returning the failure rather than throwing keeps the exit code decision in one place. A usage\n * error is exit 2 and a runtime error is exit 1, and a validator that emitted its own envelope would\n * have to know that too.\n */\nconst validate = (parsed) => {\n for (const name of parsed.flags.keys()) {\n if (!KNOWN_FLAGS.has(name)) {\n return fail(\"ERR_INVALID_FLAG\", `unknown flag: --${name}`, nearest(name, [...KNOWN_FLAGS]));\n }\n }\n const spec = COMMANDS.find((command) => command.name === parsed.command);\n if (spec === undefined)\n return unknownCommand(parsed);\n const missingArgs = spec.args.filter((arg, position) => arg.required && parsed.positional[position] === undefined);\n if (missingArgs.length > 0) {\n return fail(\"ERR_MISSING_ARGUMENT\", `${spec.name} requires: ${missingArgs.map((arg) => arg.name).join(\", \")}`, [`memhtml ${spec.name} <${missingArgs[0]?.name}>`]);\n }\n const missingFlags = spec.flags.filter((flag) => flag.required === true && parsed.flags.get(flag.name) === undefined);\n if (missingFlags.length > 0) {\n return fail(\"ERR_MISSING_ARGUMENT\", `${spec.name} requires: ${missingFlags.map((flag) => `--${flag.name}`).join(\", \")}`, missingFlags.map((flag) => `memhtml ${spec.name} --${flag.name} <value>`));\n }\n // Presence rules together: the unconditionally-required flags above, then the two conditional\n // rules the table cannot express, then the value checks below.\n const eitherOr = claimOrArticle(parsed);\n if (eitherOr !== undefined)\n return eitherOr;\n const exec = execFlags(parsed);\n if (exec !== undefined)\n return exec;\n /**\n * A closed-vocabulary flag is checked here rather than at the service, so a typo answers with the\n * whole vocabulary and never touches the database. Every value of a repeatable flag is checked, not\n * only the last one, so a `--type` list with one bad entry is a usage error rather than a silently\n * narrowed search.\n */\n for (const flag of spec.flags) {\n if (flag.values === undefined)\n continue;\n for (const value of parsed.flags.get(flag.name) ?? []) {\n if (typeof value !== \"string\")\n continue;\n if (!flag.values.includes(value)) {\n return fail(\"ERR_INVALID_FLAG\", `--${flag.name} must be one of: ${flag.values.join(\", \")}`, nearest(value, flag.values));\n }\n }\n }\n return undefined;\n};\n/**\n * Returns the rendered envelope and an exit code rather than writing to the process, so tests\n * assert on the exact bytes an agent would parse.\n *\n * `layer` is injectable for that reason. A test supplies the real composition over a temp\n * repo and a deterministic embedder, and every assertion below then describes the shipped path.\n *\n * `stdin` is injectable for the same reason one step further out. `memhtml apply` reads a JSONL stream\n * from a pipe, and a test that had to spawn a process and write to its descriptor to exercise the\n * stdin path would be an integration test of the shell rather than of this function. The default\n * reads `process.stdin`, so `bin.ts` needs no knowledge of which commands want input.\n */\nexport const run = async (argv, layer, stdin = readStdin) => {\n const parsed = parseArgv(argv);\n const dense = bool(parsed, \"dense\", false);\n const emit = (payload, exitCode) => ({\n stdout: render(payload, dense),\n exitCode\n });\n if (parsed.command === \"\" || parsed.command === \"help\") {\n return emit(succeed(\"cli.manifest\", buildManifest()), EXIT_OK);\n }\n const invalid = validate(parsed);\n if (invalid !== undefined)\n return emit(invalid, EXIT_USAGE);\n /**\n * The two self-describing commands answer without building the app layer.\n *\n * `manifest` matters most here. It is the first call an agent makes and it must answer on a\n * machine with no repo, no database, and no credentials. Building the layer first would make the\n * self-description conditional on the thing it describes being already working.\n *\n * `agents-doc` is here because building the layer has a side effect. `layerDatabase` opens\n * `$MEMHTML_ROOT/.memhtml/index.db`, creating the directory and running every migration. A doc generator\n * that scaffolded a memory repo as a side effect of rendering Markdown would create `~/memhtml`\n * on any machine that ran `memhtml agents-doc --check` in CI. It reads only the command table, so it\n * has no reason to touch the app graph at all.\n */\n if (parsed.command === \"manifest\") {\n return emit(succeed(\"cli.manifest\", buildManifest()), EXIT_OK);\n }\n if (parsed.command === \"agents-doc\") {\n return Effect.runPromise(runAgentsDoc({ check: bool(parsed, \"check\", false), out: str(parsed, \"out\") }).pipe(Effect.map((data) => emit(succeed(\"agents.doc\", data), EXIT_OK)), Effect.catch((error) => Effect.succeed(emit(failureFor(error), EXIT_RUNTIME))), Effect.provideService(Logger.LogToStderr, true)));\n }\n /**\n * `serve mcp` must not build the app layer either, and here the reason is the database.\n *\n * The supervisor's only job is to spawn the server and wait. Building `layerApp` first would open\n * `$MEMHTML_ROOT/.memhtml/index.db` and run its migrations in the parent. That is a second writer\n * against the store the child exists to serve, held open for as long as the child lives, by a process\n * that never issues a query. The parent needs the resolved repo root, which is config rather than a\n * service.\n *\n * Nothing is emitted until the child exits, because stdout belongs to the child from the moment it\n * is spawned. The `serve.exit` envelope describes how the server ended, and it is written after\n * the descriptors are the parent's again.\n */\n if (parsed.command === \"serve mcp\") {\n return Effect.runPromise(Effect.gen(function* () {\n const override = str(parsed, \"repo\");\n const configured = yield* MemhtmlRoot;\n const memhtmlRoot = override !== undefined && override.trim() !== \"\" ? override.trim() : configured;\n return yield* serveMcp(memhtmlRoot);\n }).pipe(Effect.map((data) => emit(succeed(\"serve.exit\", data), EXIT_OK)), Effect.catch((error) => Effect.succeed(emit(failureFor(error), EXIT_RUNTIME))), Effect.catchCause((cause) => Effect.succeed(emit(fail(\"ERR_UNKNOWN\", `unexpected failure: ${String(cause)}`, []), EXIT_RUNTIME))), Effect.provideService(Logger.LogToStderr, true)));\n }\n /**\n * `eval discriminate` does not build the app layer either, for the reason the command above gives.\n * The gate measures the ranking stack against its own generated fixture corpus in a temp directory\n * with an in-memory database, and reads the operator's `index.db` not at all. Building `layerApp`\n * would open and migrate a store this command never queries, and an operator checking the gate is\n * typically doing it while `memhtml-mcp` serves that store.\n *\n * **Exit 1 on a failed gate**, with `ERR_DISCRIMINATION_FAILED`. A gate that exited 0 and\n * left the verdict inside the payload would be a gate every shell caller forgets to read. The\n * exit code is what stops a pipeline.\n */\n if (parsed.command === \"eval discriminate\") {\n const requested = (str(parsed, \"mode\") ?? \"fake\");\n return Effect.runPromise(runDiscrimination({\n mode: requested,\n ...(int(parsed, \"seed\") === undefined ? {} : { seed: int(parsed, \"seed\") }),\n ...(int(parsed, \"size\") === undefined ? {} : { size: int(parsed, \"size\") }),\n ...(int(parsed, \"probes\") === undefined ? {} : { probes: int(parsed, \"probes\") }),\n ...(num(parsed, \"mrr-floor\") === undefined ? {} : { mrrFloor: num(parsed, \"mrr-floor\") })\n }).pipe(Effect.map((outcome) => outcome.passed\n ? emit(succeed(\"eval.discrimination\", outcome), EXIT_OK)\n : {\n stdout: render(succeed(\"eval.discrimination\", outcome), dense),\n exitCode: EXIT_RUNTIME\n }), Effect.catchCause((cause) => Effect.succeed(emit(fail(\"ERR_UNKNOWN\", `unexpected failure: ${String(cause)}`, []), EXIT_RUNTIME))), Effect.provideService(Logger.LogToStderr, true)));\n }\n /**\n * `memhtml exec` does not build the app layer either, for the reason two commands over.\n *\n * The command reads a git tree and nothing else. It materializes a commit as a detached worktree and\n * mounts that directory read-only. It never queries `index.db`, so building `layerApp` would open and\n * migrate a database it does not use, on the path an agent reaches for while `memhtml serve mcp` is\n * serving the repo. Nothing here can be reached through `dispatch`, because `dispatch`'s service set\n * is the app layer's.\n *\n * **Non-zero `exitCode` in the payload is still exit 0 for the process**, and that split is the\n * contract. A failing script is a report with `stderr` an agent reads and fixes. The CLI's exit 1 is\n * reserved for the runtime failing to run the script at all (no repo, unreadable sha, absent helper).\n * Collapsing the two would make an agent unable to tell a bad selector from a broken install, and\n * would bury the script's own diagnostic inside an `error` string.\n *\n * The script is read here rather than in `runExec`, so a missing or empty script is exit 2 like every\n * other input error. `runExec` takes source, never a path.\n */\n if (parsed.command === \"exec\") {\n const inline = str(parsed, \"script\");\n const file = parsed.positional[0] === \"-\" ? undefined : str(parsed, \"file\");\n const script = inline !== undefined ? inline : file === undefined ? await stdin() : await readScript(file);\n if (typeof script !== \"string\")\n return emit(script, EXIT_USAGE);\n if (script.trim() === \"\") {\n return emit(fail(\"ERR_MISSING_ARGUMENT\", \"exec needs a script: a blank one would report an empty answer rather than an error\", [\n \"memhtml exec --script 'console.log(1)'\",\n \"memhtml exec --file traverse.mjs\",\n \"cat s.mjs | memhtml exec\"\n ]), EXIT_USAGE);\n }\n const override = str(parsed, \"repo\");\n return Effect.runPromise(Effect.gen(function* () {\n const configured = yield* MemhtmlRoot;\n const memhtmlRoot = override !== undefined && override.trim() !== \"\" ? override.trim() : configured;\n return yield* execCommand({\n script,\n memhtmlRoot,\n sha: str(parsed, \"sha\"),\n timeoutMs: int(parsed, \"timeout-ms\")\n });\n }).pipe(Effect.map((report) => emit(succeed(\"exec.report\", report), EXIT_OK)), Effect.catch((error) => Effect.succeed(emit(failureFor(error), EXIT_RUNTIME))), Effect.catchCause((cause) => Effect.succeed(emit(fail(\"ERR_UNKNOWN\", `unexpected failure: ${String(cause)}`, []), EXIT_RUNTIME))), Effect.provideService(Logger.LogToStderr, true), Effect.scoped));\n }\n /**\n * `memhtml apply` checks the shape of its whole op stream before any service is built (AC-6-4).\n *\n * Here rather than in `validate` because reading a file is async and `validate` is a pure synchronous\n * function of the parsed argv. Here rather than in the dispatch arm because a refusal raised inside\n * `dispatch` travels through `failureFor` and becomes exit 1, while a malformed input file is a usage\n * error and must be exit 2. The caller wrote a bad file, and the corpus is fine.\n *\n * The ordering is the observable contract. Nothing is written for a file with a bad line, and at this\n * point nothing can have been, because the app layer has not been built, so no database is open and\n * no git command has run.\n */\n let applyOps = [];\n if (parsed.command === \"apply\") {\n // `memhtml apply -` is the explicit \"read stdin\" spelling, and the dash is not a path.\n const file = parsed.positional[0] === \"-\" ? undefined : str(parsed, \"file\");\n const text = await applyText(file, stdin);\n if (typeof text !== \"string\")\n return emit(text, EXIT_USAGE);\n const decoded = decodeApply(text);\n if (!decoded.ok)\n return emit(decoded.failure, EXIT_USAGE);\n applyOps = decoded.ops;\n }\n const program = dispatch(parsed, applyOps).pipe(Effect.map(([type, data]) => emit(succeed(type, data), EXIT_OK)), Effect.catch((error) => Effect.succeed(emit(failureFor(error), EXIT_RUNTIME))), \n // A defect is still an answer. An unexpected throw anywhere below would otherwise reach the\n // process as an unhandled rejection and print a stack trace onto stdout. Stdout is a parse\n // target, so it carries the envelope and nothing else.\n Effect.catchCause((cause) => Effect.succeed(emit(fail(\"ERR_UNKNOWN\", `unexpected failure: ${String(cause)}`, []), EXIT_RUNTIME))), Effect.provide(layer ?? layerApp(str(parsed, \"repo\"))), \n // Logs go to stderr, always. Effect's default logger writes to stdout, which would interleave\n // log lines with the envelope and break every parser.\n Effect.provideService(Logger.LogToStderr, true), Effect.scoped);\n return Effect.runPromise(program);\n};\n/** The envelope's api version, re-exported so a caller can assert on it without a second import. */\nexport { API_VERSION };\n//# sourceMappingURL=run.js.map","import { codeFor, messageFor } from \"@memhtml/cli\"\nimport { Schema } from \"effect\"\n\n/**\n * The MCP wire failure: one error class, declared on every tool, whose `.message` IS the response an\n * agent reads.\n *\n * **Why a declared class at all.** `McpServer` has three catch branches for a failed `tools/call`\n * (`McpServer.ts:831-847`, effect 4.0.0-beta.102) and only ONE of them lets prose through. An\n * `AiError`, which is what this module replaced, takes branch 1 and is rewritten to \"Tool execution\n * failed due to an internal server error\" unless its reason is a parameter-validation error. A value\n * the tool's own `failureSchema` accepts takes branch 2, where `error instanceof Error ? error.message`\n * passes the text through verbatim. The schema declaration is therefore the whole\n * difference between an agent that can recover and an agent that reads a sentence with no content in\n * it. `Effect.tapCause(Effect.logError)` runs before all three branches, so stderr logging is\n * unaffected either way.\n *\n * **Why the message is composed at construction.** `McpServer` reads `.message` and nothing else.\n * `code` and `suggestions` are not on the wire as fields, because MCP's tool-error channel is one\n * text block. So the three parts are folded into the string HERE, once, and the structured fields stay\n * for tests and for any future surface that can carry them. A consumer that wanted the code back out\n * reads the prefix, which is why the code comes first and is followed by a colon: `ERR_*` is a stable\n * vocabulary and the prose after it is not.\n *\n * **Why `Schema.TaggedError` rather than a hand-written `Error` subclass.** `Schema.is(failureSchema)`\n * is the branch-2 predicate, so the value has to be something a schema accepts, and it has to be an\n * `Error` for `.message` to be read. `Schema.TaggedError` is the one construction that is both: an\n * instance is `instanceof Error`, `Schema.is` accepts it, and `Schema.is` REJECTS a plain `Error`,\n * which is what keeps a genuine defect on branch 3 where it belongs. All three of those are asserted\n * in `tests/failure.test.ts`, so the construction cannot be swapped for one that loses any of them.\n */\nexport class ToolFailure extends Schema.TaggedError<ToolFailure>()(\"ToolFailure\", {\n /** The stable code, from the same `ERROR_CODES` vocabulary the CLI envelope publishes. */\n code: Schema.String,\n /** The composed wire text: code, reason, then suggestions. This is what the agent reads. */\n message: Schema.String,\n /** The suggestions, kept structured so a test can assert them without parsing prose. */\n suggestions: Schema.Array(Schema.String)\n}) {}\n\n/** A typed failure as it arrives here: a `_tag` plus whatever payload its class carries. */\ninterface TaggedError {\n readonly _tag: string\n readonly [field: string]: unknown\n}\n\nconst isTagged = (value: unknown): value is TaggedError =>\n typeof value === \"object\" &&\n value !== null &&\n typeof (value as { _tag?: unknown })._tag === \"string\"\n\nconst text = (value: unknown): string | undefined => (typeof value === \"string\" ? value : undefined)\n\n/**\n * What to do about a failure, phrased as calls this agent can actually make.\n *\n * The reader is an LLM mid-task holding fourteen tools and no shell. `suggestionsFor` in\n * `apps/cli/src/errors.ts:115-137` answers the same question for a human at a prompt and answers it in\n * `memhtml` commands and `git` invocations, every one of which is unreachable from here. A suggestion\n * an agent cannot execute costs more than none: it spends the model's attention on a plan that ends in\n * \"I don't have a terminal\", and the recovery that WAS available goes unmentioned. So this is a\n * deliberate parallel mapping rather than a reuse, and the rule it holds to is that every string names\n * a tool in the toolkit or an action inside the current call's own control.\n *\n * The path payloads are interpolated rather than left as `<path>` placeholders: the agent has to type\n * the argument, and a code that already knows the path and does not say it forces a `memory_list` the\n * response could have skipped.\n *\n * The FIRST suggestion is always the action, and any state the agent needs in order to trust that\n * action comes second. `toToolFailure` joins the list behind \"Try: \", so a list that opened with\n * \"nothing was written\" would put a fact where the reader is looking for a verb.\n *\n * `DirtyTree`, `GitFailure` and `StorageFailure` share the same answer, and it states the ceiling:\n * an agent cannot commit, stash, or repair a database from a tool call. `memory_status` is\n * the one read that distinguishes \"the repo is wedged\" from \"that one write raced\", and escalation is\n * the correct terminal move rather than a retry loop.\n */\nexport const mcpSuggestionsFor = (error: unknown): ReadonlyArray<string> => {\n if (!isTagged(error)) return []\n switch (error._tag) {\n case \"PathNotFound\":\n return [\n \"call memory_search with a query for what you were looking for\",\n \"call memory_list to page the corpus by type or workspace\"\n ]\n case \"WriteConflict\":\n return [\n `call memory_read on ${text(error.path) ?? \"that path\"} to get the current content`,\n \"re-apply your change to that content and retry the write\"\n ]\n case \"DuplicateContent\":\n return [\n `call memory_read on ${text(error.existingPath) ?? \"that path\"} — your content already lives there`,\n \"nothing was written and no commit was made, so there is nothing to clean up\"\n ]\n case \"InvalidMemory\":\n return [\n \"fix the violated constraint named above and call the same tool again\",\n \"nothing was written and no commit was made — the store refused at the render gate\"\n ]\n case \"ModelUnavailable\":\n return [\n \"retry — search degrades to the lexical floor without the embedder, so results are narrower but real\",\n \"call memory_status to see whether the embedder is up\"\n ]\n case \"EmbedModelMismatch\":\n return [\n \"keep working — memory_search still runs on the lexical, recency, and salience arms\",\n \"the vector arm stays unusable until an operator rebuilds the index\"\n ]\n case \"DirtyTree\":\n case \"GitFailure\":\n case \"StorageFailure\":\n return [\n \"call memory_status to see repo health: HEAD, dirty state, and index freshness\",\n \"report this to the operator if it persists — an agent cannot repair the repo from a tool call\"\n ]\n case \"DiscriminationFailed\":\n return [\"call memory_status to see when sleep last ran\", \"report this to the operator\"]\n default:\n return []\n }\n}\n\n/**\n * A reason ending in a sentence terminator, so the suggestions read as a second sentence.\n *\n * `messageFor` returns fragments without final punctuation because the CLI envelope carries the reason\n * in its own JSON field and the suggestions in another, so there is nothing to run together. Here the\n * three parts share one string, and \"no memory at areas/x.html Try: call memory_search\" is a sentence\n * an LLM has to re-parse.\n */\nconst sentence = (reason: string): string => (/[.!?]$/.test(reason) ? reason : `${reason}.`)\n\n/**\n * A typed domain failure as the wire failure.\n *\n * Total by construction, three times over: `codeFor` maps an unknown `_tag` to `ERR_UNKNOWN`,\n * `messageFor` maps it to a stated fallback, and `mcpSuggestionsFor` returns an empty array. So an\n * error class added upstream tomorrow reaches an agent as prose with a documented code rather than as\n * the internal-error string. That string is the failure mode this whole module exists to end, and it\n * would come straight back if the mapping could fall off the end.\n *\n * The reason text is `messageFor`'s and only `messageFor`'s: it excludes the driver's message, the\n * SQL, the git argv, and every memory body, because each error class dropped those at its adapter edge\n * so that a tool response could not carry corpus content. Enriching past it here would undo that at\n * the one boundary where the content leaves the process.\n */\nexport const toToolFailure = (error: unknown): ToolFailure => {\n /**\n * An already-composed failure passes through UNCHANGED, and that branch is what lets a handler\n * compose its own wire failure at all.\n *\n * `handled` in `handlers.ts` is `Effect.mapError(toToolFailure)` over every handler, so a handler that\n * fails with a `ToolFailure` it built itself arrives here too. `batchAbortFailure` is that case,\n * since it needs an op index no typed domain error carries. Without this branch it falls off the end\n * of `codeFor`'s switch (its `_tag` is `\"ToolFailure\"`, in no error vocabulary) and is rewritten to\n * `ERR_UNKNOWN: unexpected failure: ToolFailure`, the whole composed message replaced by its own\n * class name. That is the masking this module exists to end, arriving from the inside. Caught by the\n * batch abort tests; kept here rather than by exempting the batch handler from `handled`, since a\n * handler outside the one error translation is a handler that can leak an untranslated failure.\n */\n if (error instanceof ToolFailure) return error\n\n const code = codeFor(error)\n const suggestions = mcpSuggestionsFor(error)\n const reason = sentence(messageFor(error))\n return new ToolFailure({\n code,\n suggestions,\n message:\n suggestions.length === 0\n ? `${code}: ${reason}`\n : `${code}: ${reason} Try: ${suggestions.join(\"; \")}`\n })\n}\n\n/**\n * An atomic batch's abort as the wire failure, naming the op that caused it.\n *\n * **Why the error channel and not a success payload.** An atomic batch that aborted wrote nothing, made\n * no commit, and produced no path, so there is no result to return, and a success response carrying\n * `written: 0` is one an agent has to inspect to discover its call did nothing. Every other refusal on\n * this server is an error, so a batch that refused through the success channel would be the one tool\n * whose failures an agent could miss by not looking. `memory_write_batch`'s description promises exactly\n * this (\"the first refused op aborts the whole call … and the failure names the offending op as\n * ops[N]\"), and that promise is what this function makes true.\n *\n * **Why it is composed HERE rather than in the handler.** `failure.ts` is the single place the wire\n * failure is produced, and there are TWO atomic refusals that must be indistinguishable to a reader: the\n * handler's own per-op XOR check, and an op the store's render gate refused inside `batchWrite`. The\n * second arrives as a `BatchOpReport`, carrying a code and a reason STRING because `operations.ts`\n * already mapped the typed error and deliberately dropped it, so `toToolFailure` cannot be reached for\n * it. Two hand-composed messages would be two shapes for one outcome; one function is one shape.\n *\n * **Why the suggestions are the batch's own and not `mcpSuggestionsFor`'s.** The singular's advice for\n * `InvalidMemory` is \"fix the violated constraint and call the same tool again\", which is right and\n * incomplete here: the agent is holding N-1 ops that WOULD have landed, and the thing it most needs to\n * know is that `continue_on_error` exists. Both entries open with a verb, because the list is joined\n * behind \"Try: \".\n *\n * The `code` is the op's own, carried through unchanged from `codeFor` so the batch and the singular\n * report one refusal under one code. An agent branching on `ERR_INVALID_MEMORY` must not have to know\n * which door produced it.\n */\nexport const batchAbortFailure = (index: number, code: string, reason: string): ToolFailure => {\n const suggestions = [\n `fix ops[${index}] and call memory_write_batch again`,\n \"set continue_on_error to true to write the ops that would have succeeded\"\n ]\n return new ToolFailure({\n code,\n suggestions,\n message:\n `${code}: ops[${index}]: ${sentence(reason)} ` +\n `The batch is atomic, so nothing was written and no commit was made. ` +\n `Try: ${suggestions.join(\"; \")}`\n })\n}\n","import {\n DatabaseService,\n ExtractorPort,\n Indexer,\n IndexRecorder,\n Retrieval,\n Store\n} from \"@memhtml/cli\"\nimport { MEMORY_RELS } from \"@memhtml/contracts/edges\"\nimport { PARA_BUCKETS, WRITABLE_MEMORY_TYPES } from \"@memhtml/contracts/types\"\nimport { REINFORCE_SIGNALS } from \"@memhtml/domain\"\nimport { Schema } from \"effect\"\nimport { Tool, Toolkit } from \"effect/unstable/ai\"\n\nimport { ToolFailure } from \"./failure.js\"\n\n/**\n * The fourteen tools: design.md §8 verbatim, plus `memory_write_batch` (spec 004 D7).\n *\n * **`parameters` is always `Schema.Struct`, never `Schema.Class`.** A client sends a plain object\n * literal, and a class schema's decode expects an instance. The failure is a decode error on every\n * call, at runtime, for every tool. This is the one trap the whole surface is arranged around.\n *\n * **Sleep is deliberately absent.** It is a cron/operator action producing a reviewable branch, not\n * something an agent fires mid-conversation: a sleep run rewrites confidence across the corpus,\n * archives memories, and creates a branch a human is expected to read. `memhtml sleep run` is the\n * entry point, and if the fleet ever wants one here it is `sleep_status` (read-only). The write\n * side stays behind an operator.\n *\n * Every `success` schema is also a `Schema.Struct`, so `tools/list` publishes a JSON Schema the\n * client can validate a response against rather than an opaque object.\n *\n * **Every tool declares `failure: ToolFailure`, and the omission is a silent wire bug.** A tool with\n * no declared failure schema gets `Schema.Never` (`Tool.ts:1265`), so `McpServer`'s declared-failure\n * predicate rejects everything and every failure, typed domain error included, is rewritten to\n * \"Tool execution failed due to an internal server error\" before it reaches the caller\n * (`McpServer.ts:831-847`). The declaration is what puts a tool's failures on the branch that passes\n * prose through; see `failure.ts` for the mechanism. `failureMode` is left at its `\"error\"` default\n * on purpose: the error CHANNEL is what `McpServer` catches, and `\"return\"` would instead fold the\n * failure into the success union, where the server would see a successful call carrying a failure\n * payload no MCP client knows to read.\n */\n\n/** The eight types an agent may write. `arc` is system-written by the sleep cycle. */\nconst WritableType = Schema.Literals(WRITABLE_MEMORY_TYPES)\n\n/** The nine MEMORY-class rels. A person or provenance rel cannot be named here. */\nconst MemoryRelSchema = Schema.Literals(MEMORY_RELS)\n\n/**\n * A repo-root-relative path: `areas/oncall/rollback-order.html`.\n *\n * The git-tree form with no leading slash, which is `files.path`, and the ID of a memory. The\n * `<link href>` form in the HTML carries a leading slash and is converted at the store boundary, so\n * a tool never sees it.\n */\nconst MemoryPath = Schema.String\n\n/**\n * `Schema.Finite`, not `Schema.Number`, for every numeric field.\n *\n * `Number` derives a JSON Schema with an `anyOf` carrying a STRING branch, because `Infinity` and\n * `NaN` are not JSON numbers and the codec represents them as strings. Probed on this beta,\n * `Schema.Number` derives `{\"anyOf\":[{\"type\":\"number\"},{\"type\":\"string\",\"enum\":[\"Infinity\",\n * \"-Infinity\",\"NaN\"]}]}`. A client reading that sees a union where the tool wants a number. `Finite`\n * derives a clean `{\"type\":\"number\"}`.\n */\nconst Finite = Schema.Finite\n\n/** A count: a non-negative quantity. */\nconst Count = Schema.Int\n\n/**\n * An optional parameter that a client may also send explicitly as `null`.\n *\n * A bare `Schema.optional(X)` is a WIRE BUG here, and it is the kind a byte-comparison fixture\n * cannot see: the derived JSON Schema publishes `{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"null\"}]}` , telling\n * every client that `null` is acceptable, while the decoder rejects it with \"Expected string |\n * undefined, got null\" (both probed on effect 4.0.0-beta.102). So a client that read the schema and\n * did the obvious thing, sending `{\"workspace\": null}` for \"no workspace\", would get a decode error\n * on a call the published contract said was valid. Many clients serialize an absent optional exactly\n * that way.\n *\n * `optionalKey(NullOr(X))` makes the decoder accept all three forms a client can produce (absent,\n * a value, and `null`) and publishes the FLAT `{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"null\"}]}`.\n * `optional` rather than `optionalKey` would derive a nested `anyOf` wrapping that union in a second\n * one, which is the same contract spelled in a way a client has to unwrap twice to read.\n *\n * `null` and absent both mean \"not supplied\", which is what the handlers normalize to `undefined`.\n */\nconst Optional = <S extends Schema.Top>(schema: S) => Schema.optionalKey(Schema.NullOr(schema))\n\n/**\n * The services a tool handler may reach for, declared per tool.\n *\n * `Tool.make`'s `dependencies` is what moves a service from the handler's requirement set into the\n * TOOL's, so `kit.toLayer({…})` accepts a handler that yields `Store`, and the requirement then\n * surfaces on the layer where `layerApp` satisfies it. Without the declaration a handler that reads\n * a service is a type error, and the only ways out are casting the handler or building the services\n * inside it: the first loses the check that the app layer provides what the tools need, and the\n * second gives every tool call its own database connection.\n *\n * Each tool declares only what it actually uses, so a handler that grows a dependency has to say so,\n * which keeps `memory_search` provably unable to reach the store and write.\n *\n * A FUNCTION per set, not a shared constant: the option's type is a mutable array, so handing the\n * same array to fourteen tools would let one tool's construction mutate the dependency list of the\n * other thirteen.\n */\nconst READS = () => [DatabaseService]\n// ExtractorPort is in the write set because `batchWrite` reads it (the write-time entity assist);\n// the port resolves to `{ extractor: undefined }` unless MEMHTML_EXTRACT_ENTITIES=on.\nconst WRITES = () => [Store, Indexer, IndexRecorder, ExtractorPort]\nconst RETRIEVES = () => [Retrieval, DatabaseService]\n\n/**\n * The `article_html` contract, stated in the description of every tool that takes it.\n *\n * A description, not a doc comment on the parameter: `tools/list` publishes `description` and an\n * agent chooses and fills a tool from it, so a contract stated anywhere else is a contract the\n * caller never reads. And it has to be stated HERE rather than left to the store's refusal, because\n * `article_html` is the one parameter where the caller owns a format constraint. Every other\n * parameter is a value the template places itself. An agent that learns the `<mark>` rule from an\n * `InvalidMemory` on its first write has already spent a round trip on something the tool could\n * have told it.\n *\n * The four clauses are the ones a caller can actually violate: format.md constraint 1 (exactly one\n * `<mark>`, inside the first `<p>` or `<li>`), constraint 3 (no `class`, `style`, or `<script>`),\n * the closed vocabulary, and the `<time datetime>` rule. That last one is a CONSEQUENCE the caller\n * has to know about rather than a constraint: the first such element becomes `files.event_at`,\n * and the recency arm ranks episodic memories by it rather than by write time.\n */\nconst ARTICLE_HTML_CONTRACT =\n \"Supply EXACTLY ONE of `body` or `article_html`. Both or neither is refused. \" +\n \"`article_html` is raw <article> inner markup used verbatim, and the caller owns the format: exactly one <mark>, \" +\n \"inside the first <p> or the first <li>, and never inside <aside> or <details>; only elements from the closed \" +\n \"vocabulary in docs/format.md; no class attribute, no style attribute, no <script>, no event handlers. \" +\n 'The FIRST <time datetime=\"…\"> element becomes the memory\\'s event time, which is what the recency arm ranks ' +\n \"by, so an episodic memory about last week should carry last week's date, not today's. Markup that violates \" +\n \"the format is refused before any file is written or committed. \" +\n \"Code snippets: in `body` prose, a paragraph that is entirely a fenced code block (```ts … ```) becomes \" +\n '<figure><pre><code data-lang=\"ts\">, whitespace verbatim, and the language promotes to a `lang:ts` entity; ' +\n \"a blank line inside the fence does not split it. In `article_html`, author the same markup yourself: \" +\n \"data-lang, never class (forbidden) and never lang= (that names human languages).\"\n\n/**\n * When to batch and what a batch does, stated in the description of BOTH write tools.\n *\n * A shared constant for the same reason `ARTICLE_HTML_CONTRACT` is one: `memory_write` has to point at\n * `memory_write_batch` and `memory_write_batch` has to explain itself, and two hand-written versions of\n * one workflow drift the first time the semantics move. Written once, appended twice.\n *\n * And it lives in a DESCRIPTION because this server has nowhere else to put it. MCP has a server-level\n * `instructions` field for exactly this kind of cross-tool guidance, and effect 4.0.0-beta.102 never\n * emits it. See the comment in `server.ts` next to `layerStdio`. Tool descriptions are the only\n * channel, so a workflow rule that is not in one is a rule no agent reads.\n *\n * Every clause is something the caller decides or has to predict, and nothing else: the threshold that\n * makes batching worth it, the ordering guarantee it can index results by, the atomicity default it\n * would otherwise have to discover from a refusal, the flag that changes that default, and the two\n * outcomes an agent most often mistakes for errors, a dedupe and a per-op failure in continue mode.\n * The cost of leaving any of them out is a wrong assumption an agent acts on for the rest of the task.\n */\nconst BATCH_GUIDANCE =\n \"Call memory_write_batch ONCE rather than memory_write N times whenever this task will write more than about three memories: \" +\n \"a batch stages every file, makes ONE commit, and reindexes ONCE, so it costs less than N calls and leaves a history a reader can follow. \" +\n \"It returns one result per op in INPUT ORDER, each naming that op's index, its path, and whether it deduped. \" +\n \"A batch is ATOMIC by default: the first refused op aborts the whole call, no file is written and no commit is made, and the failure names the offending op as ops[N]. \" +\n \"Set continue_on_error to true for best-effort instead, and a refused op comes back as a failed result carrying its own code and reason while every surviving op lands in the one commit. \" +\n \"A duplicate is never a failure: an op whose exact content is already stored returns ok with deduped=true and the existing path. \" +\n \"Each op supplies EXACTLY ONE of body or article_html, the same rule memory_write follows.\"\n\n/**\n * The `detect_conflicts` assist, stated in `memory_write_batch`'s description.\n *\n * A constant beside `BATCH_GUIDANCE` for the same reason that one exists, and for one more: a test\n * asserts the whole string is present, so the semantics and the published prose cannot drift into two\n * versions. It is not appended to `memory_write`'s description. The singular has no such flag, and a\n * paragraph about a parameter a tool does not accept is a paragraph that makes an agent try to send it.\n *\n * Every clause is something a caller acts on. The distinction from dedupe, because an agent that\n * thought this was dedupe would stop checking. The RULE, because it is grammatical rather than semantic\n * and an agent expecting meaning-matching would trust a null it should not. The two match sources, since\n * the intra-batch one is invisible to every other tool. The nulls, all four of them, because each one is\n * an absence of information rather than an absence of conflict. And the propose-only contract WITH its\n * reason, the BEAM caveat, spelled out rather than asserted: an agent told only \"this does not block\"\n * will assume it is a v1 limitation and hand-roll the archiving the design deliberately refuses.\n */\nconst CONFLICT_GUIDANCE =\n \"Set detect_conflicts to true and each per-op result gains a `conflict` field naming what that op's claim CONTRADICTS. \" +\n \"This is not dedupe: dedupe catches an op whose content is IDENTICAL to something stored, while this catches an op that says something DIFFERENT about the same thing, the case dedupe is blind to, and the one that actually rots a corpus. \" +\n \"The match is grammatical rather than semantic. A claim splits into a frame (the subject and relation up to its LAST of/is/in/to/by/as) and a value, and two claims conflict when they share a frame: 'The pool ceiling is 64' and 'The pool ceiling is 128' both key on 'the pool ceiling is'. \" +\n \"conflict.path names an ACTIVE memory already holding that slot. conflict.batch_index names an EARLIER op in this same call, which no other tool can see because neither op is stored yet; it has no path for that reason. conflict.claim is the other claim's own text, so you can decide without a second call. \" +\n \"conflict is null when nothing matched, when detect_conflicts was absent, when the claim states no frame shape (the rule refuses frames under three tokens and values over six, so short claims and claims trailed by a clause are deliberately unmatched rather than loosely matched), and always on an op that used article_html. The claim is inside your markup there and is not read until the store renders it. \" +\n \"THE ASSIST NEVER CHANGES WHAT IS WRITTEN. An op carrying a conflict is written exactly as it would have been without the flag: nothing is archived, nothing is refused, later does not win, and the summary counts are unchanged. \" +\n \"That is deliberate, not a limitation. Sometimes the contradiction IS the answer. A memory recording that a runbook step changed necessarily contradicts the memory stating the old step, and a system that resolved that for you would destroy the pair a reader needs in order to see the change at all. \" +\n \"So YOU decide, per conflict: keep both (they are about different things, or both are true), call memory_correct on the named path instead (the new claim supersedes the old one, which stays readable under archive/), or drop the op. \" +\n \"Archived memories never match, so a superseded claim stops contradicting the claim that superseded it.\"\n\n/**\n * The `consolidate` opt-in, stated in `memory_write_batch`'s description.\n *\n * A third constant beside the two above and AFTER `CONFLICT_GUIDANCE` in the description, because it\n * is the acting counterpart of the assist: an agent has to know what a conflict IS before \"resolve it\n * last-wins\" means anything, and stating the flag first would make the propose-only contract above\n * read as contradicted two paragraphs later.\n */\nconst CONSOLIDATE_GUIDANCE =\n 'Set consolidate to \"last-wins\" and the batch RESOLVES frame-key matches instead of only reporting them: for ops sharing a claim slot (the same deterministic frame key the conflict rule uses), the LATER value wins. Exactly one file is written, at the FIRST index that claimed the slot, and every later restatement reports consolidated_into naming that slot instead of a path of its own. ' +\n \"A stored ACTIVE memory occupying a surviving slot is archived with a supersedes link from the new file, its archive path reported on the winner as superseded_path. \" +\n \"Off by default, and claims with no frame shape are never consolidated. The guards fail closed, so this only ever acts on claims the conflict rule would have matched.\"\n\n/**\n * The fields that author ONE memory, shared by `memory_write`'s parameters and `memory_write_batch`'s\n * op struct.\n *\n * D7 says the batch op is \"the same fields as memory_write\". Written twice, that is a claim two\n * literals make about each other and stop making the first time a field is added to one of them. An\n * agent that learned `tags` from `memory_write` and had it silently dropped by a batch op would get a\n * memory it could not find by the facet it filed it under. Shared, the widening is automatic and the\n * published schemas cannot disagree.\n *\n * A FUNCTION returning a fresh literal, matching `READS`/`WRITES` above: the field record is handed to\n * a schema constructor and nothing here should be able to observe another tool's construction.\n */\nconst writeFields = () => ({\n title: Schema.String,\n /**\n * Prose. The first sentence becomes the `<mark>` claim and the rest becomes one `<p>` per blank-line\n * paragraph. See `claimFromProse`/`proseTail` in `@memhtml/cli`'s `prose.ts`, the one copy this door and\n * `memhtml apply` share. Optional because `article_html` is the other way to author the same article, and\n * the handler refuses a call that names both or neither.\n */\n body: Optional(Schema.String),\n /** Pre-authored article markup, used verbatim in place of `body`. See the description's contract. */\n article_html: Optional(Schema.String),\n memory_type: WritableType,\n path: Optional(MemoryPath),\n workspace: Optional(Schema.String),\n tags: Optional(Schema.Array(Schema.String)),\n entities: Optional(Schema.Array(Schema.String)),\n importance: Optional(Count),\n confidence: Optional(Finite),\n session_id: Optional(Schema.String),\n prompt_id: Optional(Schema.String),\n turn_uuid: Optional(Schema.String)\n})\n\nconst MemoryWrite = Tool.make(\"memory_write\", {\n description:\n \"Write one memory to the corpus. Returns the existing path with deduped=true when an active memory already holds this exact content. A duplicate creates no file and no commit. \" +\n ARTICLE_HTML_CONTRACT +\n \" \" +\n BATCH_GUIDANCE,\n dependencies: WRITES(),\n parameters: Schema.Struct(writeFields()),\n failure: ToolFailure,\n success: Schema.Struct({\n path: MemoryPath,\n created: Schema.Boolean,\n deduped: Schema.Boolean,\n existing_path: Schema.NullOr(MemoryPath)\n })\n})\n\n/**\n * One op in a batch: a whole `memory_write` payload, with the tool name standing in for D4's `op`\n * discriminator.\n *\n * A nested `Schema.Struct`, which is what makes the array's `items` a published object schema with its\n * own `required`. Probed on effect 4.0.0-beta.102, `Schema.Array(Schema.Struct({…}))` derives the\n * struct INLINE under `items` rather than hoisting it into a `$defs` a client would have to resolve.\n * So `ops[].title` is as legible to a caller reading `tools/list` as `memory_write`'s own `title`, and\n * the `Optional` discipline carries in unchanged: an optional inside an op publishes the same FLAT\n * `{\"anyOf\":[{…},{\"type\":\"null\"}]}` and accepts absent, a value, or `null`.\n */\nconst BatchOp = Schema.Struct(writeFields())\n\n/** One op's outcome, mirroring `memhtml apply`'s own per-op payload field for field, in snake_case. */\nconst BatchOpResult = Schema.Struct({\n /** This op's position in the `ops` array the caller sent. Results come back in that order too. */\n index: Count,\n ok: Schema.Boolean,\n /**\n * Every field below is PRESENT and nullable rather than optional, for the reason `memory_write`'s\n * `existing_path` is: a client reading an absent key cannot tell \"this op did not dedupe\" from \"this\n * server does not report dedupes\", and an agent deciding whether to retry needs that distinction.\n */\n path: Schema.NullOr(MemoryPath),\n deduped: Schema.Boolean,\n existing_path: Schema.NullOr(MemoryPath),\n /** The stable `ERR_*` code for this op's refusal, null when it did not fail. */\n code: Schema.NullOr(Schema.String),\n error: Schema.NullOr(Schema.String),\n /**\n * True when this op was never attempted: an atomic abort reports every op other than the offending\n * one as skipped, which is how a caller tells \"refused\" from \"not reached\".\n */\n skipped: Schema.Boolean,\n /**\n * What this op's claim contradicts, when `detect_conflicts` was on and something matched. Null when\n * the flag was off, when nothing matched, or when the claim states no frame shape.\n *\n * `Schema.NullOr(Schema.Struct(…))`, present like every field above rather than optional: a client\n * reading an absent key cannot tell \"this op conflicts with nothing\" from \"this server does not\n * report conflicts\", and the two lead to opposite decisions.\n *\n * ONE struct with both source fields nullable rather than a union of two, so a client reads `claim`\n * unconditionally (that is the disagreement, and it is what the decision is made on) and then\n * whichever of `path`/`batch_index` is non-null. A `Schema.Union` would publish two near-identical\n * three-field shapes under an `anyOf` and force every consumer to discriminate before reading the\n * field it wanted, which is the same trap the `body`/`article_html` XOR avoids by not being a union.\n */\n conflict: Schema.NullOr(\n Schema.Struct({\n /** The ACTIVE memory already holding this frame key. Null for an intra-batch match. */\n path: Schema.NullOr(MemoryPath),\n /**\n * The EARLIER op in THIS call holding it. Null for a store match, and it has no path because\n * that op's file does not exist yet. The batch has not been written when the assist runs.\n */\n batch_index: Schema.NullOr(Count),\n /** The other claim's own text. */\n claim: Schema.String\n })\n ),\n /**\n * Set on a batch-internal LOSER under `consolidate: \"last-wins\"`: a later op with the same frame\n * key replaced this op's value before anything was written, and the number is the caller-space\n * index of the op whose position carries the surviving value. Null everywhere else, present like\n * every field above so a client can tell \"not consolidated\" from \"not reported\".\n */\n consolidated_into: Schema.NullOr(Count),\n /**\n * Set on a WINNER whose write superseded a live stored memory under `consolidate: \"last-wins\"`:\n * the loser's ARCHIVE path, where its bytes now live. Null when nothing stored occupied the\n * slot, and when the supersede degraded (the batch still wrote; the corpus is merely\n * unconsolidated).\n */\n superseded_path: Schema.NullOr(Schema.String)\n})\n\nconst MemoryWriteBatch = Tool.make(\"memory_write_batch\", {\n description:\n \"Write many memories in ONE commit: every op is validated first, every surviving file is staged, and the batch commits and reindexes exactly once. \" +\n \"commit_sha is null when nothing was written: an all-deduped batch, or an aborted one. \" +\n /**\n * Same order as `memory_write`'s (the article contract, then the batch workflow), so an agent that\n * has read one description finds the other's clauses where it expects them. Reversed here, the\n * guidance's closing XOR reminder would sit immediately before the full statement of that same rule,\n * which reads as a repetition rather than as two sections.\n */\n ARTICLE_HTML_CONTRACT +\n \" \" +\n BATCH_GUIDANCE +\n /**\n * LAST, after the workflow, and consolidation after the conflict rule it acts on. The guidance\n * states what a batch IS and an agent needs that before an optional assist over it means anything;\n * leading with the conflict rule would explain a field on a result shape the reader has not been\n * told about yet.\n */\n \" \" +\n CONFLICT_GUIDANCE +\n \" \" +\n CONSOLIDATE_GUIDANCE,\n dependencies: WRITES(),\n parameters: Schema.Struct({\n ops: Schema.Array(BatchOp),\n /** Best-effort mode: a refused op is reported and skipped, survivors land in the one commit. */\n continue_on_error: Optional(Schema.Boolean),\n /**\n * Report each op's frame-matches as a per-op `conflict`. Propose-only: it changes nothing about what\n * is written. `Optional` rather than defaulted-true because the assist costs one extra query per\n * batch, and a caller that did not ask for the field would be paying for an answer it does not read.\n */\n detect_conflicts: Optional(Schema.Boolean),\n /**\n * Opt-in deterministic last-wins consolidation over the conflict rule's own frame keys. A\n * `Literals` of one value rather than a boolean, so the vocabulary can widen (a `first-wins`, a\n * semantic mode) without a shipped `true` changing meaning under a caller.\n */\n consolidate: Optional(Schema.Literals([\"last-wins\"])),\n /**\n * Batch-level provenance: the session this call is being made in. An op that names its own wins,\n * because it is the more specific statement about where that one memory came from, which is what\n * lets a batch replay writes from an earlier session without relabelling them.\n */\n session_id: Optional(Schema.String),\n prompt_id: Optional(Schema.String),\n turn_uuid: Optional(Schema.String)\n }),\n failure: ToolFailure,\n success: Schema.Struct({\n results: Schema.Array(BatchOpResult),\n /** Derived from `results` in one pass, so the counts cannot disagree with the array. */\n summary: Schema.Struct({\n total: Count,\n written: Count,\n deduped: Count,\n failed: Count,\n skipped: Count,\n /** Batch-internal losers under `consolidate: \"last-wins\"`: neither written nor failed. */\n consolidated: Count\n }),\n commit_sha: Schema.NullOr(Schema.String)\n })\n})\n\nconst MemoryRead = Tool.make(\"memory_read\", {\n description:\n \"Read one memory in full: its head metadata, authored links, and complete article body. The only path to a <details> body, which recall never quotes. An explicit open of a named path COUNTS as salience. This is the read that moves the access plane, while a search or recall hit does not.\",\n /**\n * `DatabaseService` is here because an explicit open bumps the access plane: `readMemory` reaches the\n * state plane through `bumpAccess`, so the tool has to declare it or the handler is a type error. The\n * widening is the salience rule made visible in the dependency set. `memory_search` still cannot\n * reach it, which is what keeps a ranker's guess out of the plane.\n */\n dependencies: [Store, IndexRecorder, DatabaseService],\n parameters: Schema.Struct({\n path: MemoryPath,\n session_id: Optional(Schema.String)\n }),\n failure: ToolFailure,\n success: Schema.Struct({\n path: MemoryPath,\n title: Schema.String,\n body: Schema.String,\n gist: Schema.String,\n memory_type: Schema.String,\n meta: Schema.Record(Schema.String, Schema.String),\n links: Schema.Array(Schema.Struct({ rel: Schema.String, href: Schema.String })),\n archived: Schema.Boolean,\n warnings: Schema.Array(Schema.String)\n })\n})\n\nconst MemorySearch = Tool.make(\"memory_search\", {\n description:\n \"Ranked search over the corpus: lexical, vector, recency, and salience arms fused with RRF, then diversified. Each hit carries a `snippet`: the text of the file's best-matching chunk for this query (its opening chunk when the vector arm did not fire), truncated with a trailing `…` when cut. `degraded` is true when the vector arm did not fire, so the result came from fewer signals. Each hit also carries `entities` in `type:name` form; pass one of those values back as `entity` to make the next call the second hop of a chain. That is two calls, not a guess about spelling. An `entity` scope that matches nothing returns NO hits and says so through `scope_empty`: this tool never widens a scope it could not satisfy. `as_of` is a point-in-time view: pass an ISO instant and the result is what was believed valid at that moment, including since-superseded memories (marked superseded_by). Returning a path changes nothing: a hit is this ranker's guess, so it never bumps salience. Call memory_read to open the one you chose, and memory_reinforce to record whether it was right.\",\n dependencies: RETRIEVES(),\n parameters: Schema.Struct({\n query: Schema.String,\n limit: Optional(Count),\n memory_types: Optional(Schema.Array(WritableType)),\n workspace: Optional(Schema.String),\n tags: Optional(Schema.Array(Schema.String)),\n /**\n * One entity reference in `type:name` form, the same spelling `memory_list` takes and the same\n * spelling a hit's `entities` publishes, so a value read off a hit is a valid scope verbatim.\n */\n entity: Optional(Schema.String),\n include_archived: Optional(Schema.Boolean),\n /**\n * Point-in-time view: returns what was believed valid at this moment, including\n * since-superseded memories (marked superseded_by). The window is\n * `coalesce(valid_from, event_at, created_at) <= as_of < valid_until`. The supersede path\n * stamps both ends, so history is read from the files rather than replayed from git.\n */\n as_of: Optional(Schema.String)\n }),\n failure: ToolFailure,\n success: Schema.Struct({\n hits: Schema.Array(\n Schema.Struct({\n path: MemoryPath,\n title: Schema.String,\n gist: Schema.String,\n memory_type: Schema.String,\n /** The fused RRF score. Unitless and comparable only within one result set. */\n score: Finite,\n confidence: Finite,\n updated_at: Schema.String,\n /**\n * The best-matching chunk's text for THIS query (the vector arm's winning chunk, or the\n * file's opening chunk on the degraded path), truncated with a trailing `…` when cut.\n */\n snippet: Schema.String,\n /**\n * This memory's entity references in `type:name` form, sorted, possibly empty.\n *\n * The next hop's `entity` parameter, published in the form that parameter accepts: the whole\n * point is that a caller chains by COPYING a value rather than by reconstructing one.\n */\n entities: Schema.Array(Schema.String),\n /**\n * The path of the memory that superseded this one, or `null` when nothing has. Non-null\n * only for an archived hit, which reaches a result through `as_of` or\n * `include_archived`, so a point-in-time answer is legible as history. Present and\n * nullable like `consolidated_into`: a client must be able to tell \"not superseded\" from\n * \"this build does not report supersession\".\n */\n superseded_by: Schema.NullOr(Schema.String)\n })\n ),\n degraded: Schema.Boolean,\n arms: Schema.Array(Schema.String),\n /** The `entity` this search was scoped to, or `null` when it was not scoped by entity. */\n entity_scope: Schema.NullOr(Schema.String),\n /**\n * True when a scope was named, it narrowed the query, and nothing survived it.\n *\n * A boolean in every case, following `degraded`: this is the field that makes an empty scoped\n * result attributable to the scope, and it would be worth nothing if its absence had to be read\n * as `false`.\n */\n scope_empty: Schema.Boolean\n })\n})\n\nconst MemoryRecall = Tool.make(\"memory_recall\", {\n description:\n \"A context pack under a character budget: full bodies for what fits, one index line each for what does not. Arcs are folded under their own envelope so a synthesis cannot crowd out the evidence behind it.\",\n dependencies: RETRIEVES(),\n parameters: Schema.Struct({\n query: Schema.String,\n budget_chars: Optional(Count),\n workspace: Optional(Schema.String)\n }),\n failure: ToolFailure,\n success: Schema.Struct({\n sections: Schema.Struct({\n arcs: Schema.Array(\n Schema.Struct({\n path: MemoryPath,\n title: Schema.String,\n gist: Schema.String,\n body: Schema.String\n })\n ),\n memories: Schema.Array(\n Schema.Struct({\n path: MemoryPath,\n title: Schema.String,\n gist: Schema.String,\n body: Schema.String\n })\n ),\n /** What did not fit: claim plus path, for a deliberate drill-down. */\n lateral: Schema.Array(\n Schema.Struct({ path: MemoryPath, title: Schema.String, gist: Schema.String })\n )\n }),\n spent_chars: Count,\n truncated: Schema.Boolean,\n degraded: Schema.Boolean\n })\n})\n\nconst MemoryCorrect = Tool.make(\"memory_correct\", {\n description:\n \"Supersede a memory: write the corrected version and archive the target in ONE commit, linked in both directions. Never edits in place. The superseded memory stays readable under archive/. \" +\n ARTICLE_HTML_CONTRACT,\n dependencies: WRITES(),\n parameters: Schema.Struct({\n target_path: MemoryPath,\n title: Schema.String,\n /** The corrected prose; first sentence becomes the new `<mark>`. Exclusive with `article_html`. */\n body: Optional(Schema.String),\n /** Pre-authored markup for the superseding article, used verbatim. Exclusive with `body`. */\n article_html: Optional(Schema.String),\n reason: Schema.String,\n session_id: Optional(Schema.String)\n }),\n failure: ToolFailure,\n success: Schema.Struct({\n path: MemoryPath,\n superseded: Schema.Array(MemoryPath),\n archived: Schema.Array(MemoryPath)\n })\n})\n\nconst MemoryLink = Tool.make(\"memory_link\", {\n description:\n \"Assert an edge between two memories. Written into the source file's head, so it survives an index rebuild. Idempotent: re-linking the same pair commits nothing.\",\n dependencies: [Store, Indexer],\n parameters: Schema.Struct({\n src_path: MemoryPath,\n rel: MemoryRelSchema,\n dst_path: MemoryPath,\n strength: Optional(Finite)\n }),\n failure: ToolFailure,\n success: Schema.Struct({\n ok: Schema.Boolean,\n rel: Schema.String,\n src_path: MemoryPath,\n dst_path: MemoryPath\n })\n})\n\nconst MemoryNeighbors = Tool.make(\"memory_neighbors\", {\n description:\n \"The memory graph around one path, to at most two hops, in both directions. Includes sleep-mined edges: lateral retrieval is what they are for.\",\n dependencies: READS(),\n parameters: Schema.Struct({\n path: MemoryPath,\n depth: Optional(Count),\n rels: Optional(Schema.Array(MemoryRelSchema))\n }),\n failure: ToolFailure,\n success: Schema.Struct({\n nodes: Schema.Array(\n Schema.Struct({\n path: MemoryPath,\n title: Schema.String,\n /** 1-based distance from the center: 1 or 2, never 0. */\n hop: Count,\n rel: Schema.String\n })\n ),\n edges: Count\n })\n})\n\nconst MemoryArchive = Tool.make(\"memory_archive\", {\n description:\n \"Soft-evict a memory: `git mv` into archive/<YYYY>/ with the archive stamps. Nothing is ever deleted, and `git log --follow` reads straight through.\",\n dependencies: [Store, Indexer],\n parameters: Schema.Struct({\n path: MemoryPath,\n reason: Schema.String\n }),\n failure: ToolFailure,\n success: Schema.Struct({\n path: MemoryPath,\n archive_path: MemoryPath\n })\n})\n\nconst MemoryReinforce = Tool.make(\"memory_reinforce\", {\n description:\n \"Record that a memory helped or misled. Gated by a 900-second per-path cooldown, so a replayed query cannot inflate a memory's ranking; `cooled_down` lists the paths the cooldown held back.\",\n dependencies: READS(),\n parameters: Schema.Struct({\n paths: Schema.Array(MemoryPath),\n signal: Schema.Literals(REINFORCE_SIGNALS)\n }),\n failure: ToolFailure,\n success: Schema.Struct({\n bumped: Schema.Array(MemoryPath),\n cooled_down: Schema.Array(MemoryPath)\n })\n})\n\nconst MemoryList = Tool.make(\"memory_list\", {\n description:\n \"Page through the corpus by facet. `next_cursor` is a keyset on the path, so a page stays correct even while a sleep cycle archives files.\",\n dependencies: READS(),\n parameters: Schema.Struct({\n memory_type: Optional(WritableType),\n workspace: Optional(Schema.String),\n tag: Optional(Schema.String),\n entity: Optional(Schema.String),\n para: Optional(Schema.Literals(PARA_BUCKETS)),\n limit: Optional(Count),\n cursor: Optional(Schema.String)\n }),\n failure: ToolFailure,\n success: Schema.Struct({\n files: Schema.Array(\n Schema.Struct({\n path: MemoryPath,\n title: Schema.String,\n memory_type: Schema.String,\n gist: Schema.String,\n workspace: Schema.NullOr(Schema.String),\n para: Schema.String,\n confidence: Finite,\n importance: Count,\n archived: Schema.Boolean,\n updated_at: Schema.String\n })\n ),\n next_cursor: Schema.NullOr(Schema.String)\n })\n})\n\nconst TraceSearch = Tool.make(\"trace_search\", {\n description:\n \"Find past Claude Code sessions by what was asked in them. A read-only index over transcript files: no session content is stored, only pointers and capped heads.\",\n dependencies: READS(),\n parameters: Schema.Struct({\n query: Schema.String,\n cwd: Optional(Schema.String),\n since: Optional(Schema.String),\n limit: Optional(Count)\n }),\n failure: ToolFailure,\n success: Schema.Struct({\n sessions: Schema.Array(\n Schema.Struct({\n session_id: Schema.String,\n slug: Schema.String,\n cwd: Schema.NullOr(Schema.String),\n started_at: Schema.NullOr(Schema.String),\n prompt_count: Count,\n first_prompt: Schema.String,\n ai_title: Schema.NullOr(Schema.String)\n })\n )\n })\n})\n\nconst TraceLinks = Tool.make(\"trace_links\", {\n description:\n \"Which memories a session produced, or which sessions touched a memory. Needs a session_id or a path. Both absent is refused rather than returning every link ever recorded.\",\n dependencies: READS(),\n parameters: Schema.Struct({\n session_id: Optional(Schema.String),\n path: Optional(MemoryPath)\n }),\n failure: ToolFailure,\n success: Schema.Struct({\n links: Schema.Array(\n Schema.Struct({\n path: MemoryPath,\n session_id: Schema.String,\n prompt_id: Schema.NullOr(Schema.String),\n turn_uuid: Schema.NullOr(Schema.String),\n link_kind: Schema.String,\n at: Schema.String\n })\n )\n })\n})\n\nconst MemoryStatus = Tool.make(\"memory_status\", {\n description:\n \"Corpus health in one call: HEAD, dirty state, counts by type, edge totals, whether the index describes the current commit, and when sleep last ran.\",\n dependencies: [Store, DatabaseService],\n /**\n * `Tool.EmptyParams`, not `Schema.Struct({})`.\n *\n * Probed on effect 4.0.0-beta.102: an empty `Schema.Struct` derives\n * `{\"anyOf\":[{\"type\":\"object\"},{\"type\":\"array\"}]}`, a union with an ARRAY branch, because a struct\n * with no fields constrains nothing and the codec's encoded form admits both. A client reading that\n * cannot tell it should send `{}`, and a strict one may refuse to call the tool at all.\n * `Tool.EmptyParams` derives `{\"type\":\"object\",\"additionalProperties\":false}`, which says exactly\n * \"an object, and no fields\", the intent.\n */\n parameters: Tool.EmptyParams,\n failure: ToolFailure,\n success: Schema.Struct({\n head_sha: Schema.NullOr(Schema.String),\n dirty: Schema.Boolean,\n counts_by_type: Schema.Record(Schema.String, Count),\n archived_count: Count,\n edges: Count,\n /** True when the index's watermark IS the current HEAD. A row count cannot answer this. */\n index_fresh: Schema.Boolean,\n embedder_up: Schema.Boolean,\n last_sleep: Schema.NullOr(\n Schema.Struct({\n run_id: Schema.String,\n status: Schema.String,\n started_at: Schema.String\n })\n )\n })\n})\n\n/**\n * The toolkit. Exactly fourteen: design.md §8's thirteen plus `memory_write_batch`.\n *\n * Order is the read order of the table in §8, which is also roughly the order an agent needs them:\n * write and read, then the three retrieval shapes, then the graph operations, then the trace plane,\n * then status.\n *\n * The batch sits SECOND, directly after `memory_write`, rather than appended at the end. `tools/list`\n * publishes this order and an agent reads it top-down, so the tool `memory_write`'s own description\n * points at is the very next entry. A pointer whose target is thirteen tools away is one an agent\n * reads after it has already decided how to write.\n */\nexport const MemhtmlToolkit = Toolkit.make(\n MemoryWrite,\n MemoryWriteBatch,\n MemoryRead,\n MemorySearch,\n MemoryRecall,\n MemoryCorrect,\n MemoryLink,\n MemoryNeighbors,\n MemoryArchive,\n MemoryReinforce,\n MemoryList,\n TraceSearch,\n TraceLinks,\n MemoryStatus\n)\n\n/**\n * The tool names, derived from the toolkit rather than restated.\n *\n * Two lists would drift: a placeholder list that once said fourteen names and a toolkit that now\n * builds thirteen would leave a test asserting the list and proving nothing about the server.\n */\nexport const TOOL_NAMES = Object.keys(MemhtmlToolkit.tools) as ReadonlyArray<\n keyof typeof MemhtmlToolkit.tools\n>\n\nexport type ToolName = (typeof TOOL_NAMES)[number]\n","import {\n archiveMemory,\n type BatchOpReport,\n type BatchWriteResult,\n batchWrite,\n claimFromProse,\n codeFor,\n correctMemory,\n type EmbedderShape,\n type layerApp,\n linkMemories,\n listMemories,\n messageFor,\n neighborsOf,\n proseTail,\n readMemory,\n recallMemories,\n reinforceMemories,\n searchMemories,\n searchTraces,\n statusReport,\n traceLinks,\n type WriteParams,\n writeMemory\n} from \"@memhtml/cli\"\nimport { InvalidMemory } from \"@memhtml/contracts/errors\"\nimport type { MemoryDoc } from \"@memhtml/html\"\nimport { Effect, type Layer } from \"effect\"\n\nimport { batchAbortFailure, type ToolFailure, toToolFailure } from \"./failure.js\"\nimport { MemhtmlToolkit } from \"./tools.js\"\n\n/**\n * The handlers: decode → call the shared use case → shape the result. Nothing else.\n *\n * Every handler calls the SAME function the CLI command calls, which is what makes `memory_search`\n * and `memhtml search` provably one query rather than two that agree today. A handler that reached for a\n * repository directly would be a second implementation of the thing the operations module exists to\n * be the only copy of.\n *\n * Parameter names are snake_case because they are the MCP wire contract; the operations take\n * camelCase. That rename is the handlers' whole remaining job.\n */\n\n/** The services a handler reaches for: the app layer's own output. */\ntype AppServices = Layer.Success<ReturnType<typeof layerApp>>\n\n/**\n * Every handler's error translation, applied once.\n *\n * MCP has one error channel and it is prose, so a typed error's STRUCTURE cannot survive the\n * boundary. Everything a caller acts on can, folded into the one string the protocol carries:\n * `toToolFailure` composes the stable code, the reason with its actionable payload fields, and\n * suggestions phrased as tool calls this agent can make. Nothing leaks a driver message, a git argv,\n * or a memory body, because the reason is `messageFor`'s and each error class dropped those at its\n * adapter edge precisely so a tool response could not carry corpus content.\n *\n * This used to build an `AiError`, which was the bug. `McpServer` catches `AiError` FIRST and\n * rewrites it to a generic internal-error sentence unless its reason is a parameter-validation error\n * (`McpServer.ts:831-838`), so every typed failure this server produced reached its agent with the\n * content removed. A `ToolFailure` is what each tool's `failure:` schema declares, which puts it on the\n * branch that passes `.message` through verbatim. The two halves only work together: dropping the\n * declaration in `tools.ts` re-masks everything this function builds, and the wire test in\n * `tests-integration` is what holds that pair honest.\n *\n * The error type is now `ToolFailure` for every handler, and `kit.toLayer` checks it, so a handler\n * that failed with a raw domain error would be a compile error rather than a masked response. This is\n * the single place the wire failure is produced.\n */\nconst handled = <A, E, R>(effect: Effect.Effect<A, E, R>): Effect.Effect<A, ToolFailure, R> =>\n effect.pipe(Effect.mapError(toToolFailure))\n\n/**\n * The head metadata as a flat string record.\n *\n * Flattened rather than typed per key: the wire schema is `Record<string, string>` because the head's\n * optional metas are genuinely open at the edges. A format version can add `memhtml-*` names, and a\n * client that had to know the closed set would break on the first addition. Numbers are stringified\n * because that is what the `<meta content>` attribute holds; a consumer that wants the number reads\n * the typed field on `memory_search` or `memory_list` instead.\n */\nconst metaRecord = (doc: MemoryDoc): Readonly<Record<string, string>> => {\n const out: Record<string, string> = {}\n for (const [key, value] of Object.entries(doc.metas)) {\n if (value === undefined) continue\n out[key] = typeof value === \"string\" ? value : String(value)\n }\n for (const entity of doc.entities) out[`entity:${entity}`] = \"true\"\n for (const tag of doc.tags) out[`tag:${tag}`] = \"true\"\n return out\n}\n\n/**\n * An explicit `null` as an absent value.\n *\n * The parameter schemas accept `null` as well as absence, and `Optional` in `tools.ts` says why: the\n * derived JSON Schema advertises `null`, and a client that reads the schema and sends\n * `{\"workspace\": null}` for \"no workspace\" is doing the documented thing. The operations layer speaks\n * `undefined` for \"not supplied\" because `exactOptionalPropertyTypes` distinguishes an absent key from\n * a present one, so the two vocabularies meet HERE, once, rather than at each of fourteen call sites.\n */\nconst opt = <A>(value: A | null | undefined): A | undefined => value ?? undefined\n\n/** Absent optional array as an empty one, so a handler never passes `undefined` downstream. */\nconst arr = <A>(value: ReadonlyArray<A> | null | undefined): ReadonlyArray<A> => value ?? []\n\n/**\n * The article a write authored, from whichever of the two parameters arrived, or else a refusal.\n *\n * `body` and `article_html` are two ways to author ONE article, so exactly one of them is the whole\n * rule, and it is enforced here rather than in the schema on purpose. A `Schema.Union` of two structs\n * derives a JSON Schema `anyOf` over the FULL parameter object twice, so a client reading\n * `tools/list` sees two near-identical thirteen-field shapes, and a decode failure against a union\n * names neither branch's actual problem. A runtime refusal costs the round trip a bad call already\n * deserved and spends it on prose that states the rule.\n *\n * Both-supplied is refused rather than resolved by precedence, the tempting shortcut. A\n * caller that sent both meant one of them, and silently rendering the other writes a memory whose\n * content the caller did not choose, into a git commit, indexed, retrievable. Neither-supplied is\n * refused for the same reason it cannot be defaulted: an article with no claim has no `<mark>`, so\n * `files.gist` would be empty on every disclosure tier.\n *\n * A blank string counts as absent, on both sides. Template-driven clients do fill unset fields with\n * `\"\"`, and such a call would otherwise read as \"supplied both\" when it supplied one.\n *\n * On the markup path `claim` is `\"\"` and `body` is empty, and neither derivation runs: the template\n * uses `articleHtml` verbatim, so a claim derived from prose that does not exist would be a second,\n * invisible authoring decision. The `<mark>` inside the markup IS the claim, and the parser extracts\n * it into `files.gist` on the first index pass. Markup whose `<mark>` is missing OR EMPTY is the\n * STORE's refusal (`packages/store/src/store.ts`'s render gate, over `@memhtml/html` constraint 1)\n * rather than this function's. The XOR is the only rule the wire boundary owns.\n *\n * The prose path derives through `claimFromProse`/`proseTail`, imported from `@memhtml/cli` so this door\n * and `memhtml apply` split prose the same way. A second copy here would let the same body produce\n * different gists depending on which door wrote it.\n */\ninterface Authored {\n readonly claim: string\n readonly body: ReadonlyArray<string>\n readonly articleHtml: string | undefined\n}\n\nconst authored = (\n body: string | null | undefined,\n articleHtml: string | null | undefined\n): Effect.Effect<Authored, InvalidMemory> => {\n const prose = opt(body)\n const markup = opt(articleHtml)\n const hasProse = prose !== undefined && prose.trim() !== \"\"\n const hasMarkup = markup !== undefined && markup.trim() !== \"\"\n if (hasProse === hasMarkup) {\n return Effect.fail(\n InvalidMemory.make({\n reason: `exactly one of body or article_html is required, and ${hasProse ? \"both were supplied\" : \"neither was supplied\"}`\n })\n )\n }\n return Effect.succeed(\n hasMarkup\n ? { claim: \"\", body: [], articleHtml: markup }\n : {\n claim: claimFromProse(prose as string),\n body: proseTail(prose as string),\n articleHtml: undefined\n }\n )\n}\n\n/** One `memory_write_batch` op as it arrives on the wire, before the XOR has been resolved. */\ninterface BatchOpParams {\n readonly title: string\n readonly body?: string | null | undefined\n readonly article_html?: string | null | undefined\n readonly memory_type: string\n readonly path?: string | null | undefined\n readonly workspace?: string | null | undefined\n readonly tags?: ReadonlyArray<string> | null | undefined\n readonly entities?: ReadonlyArray<string> | null | undefined\n readonly importance?: number | null | undefined\n readonly confidence?: number | null | undefined\n readonly session_id?: string | null | undefined\n readonly prompt_id?: string | null | undefined\n readonly turn_uuid?: string | null | undefined\n}\n\n/**\n * One op's wire-name-to-operation-name rename, given the article its XOR already resolved to.\n *\n * The same rename `memory_write`'s handler performs, over the same field list. The ops carry a whole\n * `memory_write` payload (D7), so a second spelling of this mapping would be the drift the shared\n * `writeFields` in `tools.ts` exists to make impossible on the schema side.\n */\nconst writeParamsOf = (op: BatchOpParams, article: Authored): WriteParams => ({\n title: op.title,\n claim: article.claim,\n body: article.body,\n articleHtml: article.articleHtml,\n memoryType: op.memory_type,\n path: opt(op.path),\n workspace: opt(op.workspace),\n tags: arr(op.tags),\n entities: arr(op.entities),\n importance: opt(op.importance),\n confidence: opt(op.confidence),\n sessionId: opt(op.session_id),\n promptId: opt(op.prompt_id),\n turnUuid: opt(op.turn_uuid)\n})\n\n/**\n * An op's XOR refusal as that op's own report, through the SAME `codeFor`/`messageFor` pair\n * `operations.ts`'s `reportFailure` uses.\n *\n * Not a second mapping of the error: a per-op `code` is part of the batch payload's contract, and\n * `memhtml apply` and `memory_write_batch` reporting different codes for one refused op is exactly the\n * drift the shared-use-case rule exists to prevent. The XOR is the one refusal the operations layer\n * cannot produce, being a wire-vocabulary rule about two parameters that layer never sees, since\n * `WriteParams` takes an already-resolved `claim`/`body`/`articleHtml`. So this is the one place\n * a report is built outside `batchWrite`, and it is built with `batchWrite`'s own functions.\n */\nconst xorReport = (index: number, error: InvalidMemory): BatchOpReport => ({\n index,\n ok: false,\n code: codeFor(error),\n error: messageFor(error)\n})\n\n/**\n * The first op that FAILED, as opposed to one that was skipped or deduped.\n *\n * `batchWrite`'s atomic abort reports the offending op with its code and every other op as `skipped`\n * (`operations.ts:545-548` for a decode refusal, `store.ts:693-705` for a render-gate one), so an\n * aborted batch is recognizable by exactly this: one report with `ok: false` and `skipped` unset. A\n * check on `summary.skipped > 0` alone would also match a batch that had nothing to abort.\n */\nconst firstFailure = (reports: ReadonlyArray<BatchOpReport>): BatchOpReport | undefined =>\n reports.find((report) => !report.ok && report.skipped !== true && report.code !== undefined)\n\n/** One op's report as the wire shape: every field present, absent ones as `null`. */\nconst wireReport = (report: BatchOpReport) => ({\n index: report.index,\n ok: report.ok,\n path: report.path ?? null,\n deduped: report.deduped === true,\n existing_path: report.existingPath ?? null,\n code: report.code ?? null,\n error: report.error ?? null,\n skipped: report.skipped === true,\n /**\n * The conflict assist's finding, `batchIndex` renamed to `batch_index`. That rename is the whole of\n * the handlers' remaining job, applied one level deeper than usual because this is the first nested\n * struct on the batch's wire shape. `memhtml apply`'s own `opPayload` performs the same rename onto\n * the same names, so the two doors' payloads stay byte-comparable.\n *\n * A conflict says nothing about `ok`, `path`, or `skipped`, and this function is where that is\n * visible: nothing above changes when the field is populated.\n */\n conflict:\n report.conflict === undefined\n ? null\n : {\n path: report.conflict.path,\n batch_index: report.conflict.batchIndex,\n claim: report.conflict.claim\n },\n consolidated_into: report.consolidatedInto ?? null,\n superseded_path: report.supersededPath ?? null\n})\n\n/**\n * The batch's counts, over the merged report array.\n *\n * The same one-pass derivation `operations.ts`'s `summarize` performs, and it has to be re-derived here\n * rather than taken from `batchWrite` for one reason: in continue mode this handler's own XOR refusals\n * are reports `batchWrite` never saw, so its summary describes a SHORTER op list. Taking it would\n * publish `total` less than `results.length`, a summary a client cannot reconcile with the array it\n * came with. On the atomic path there are no such refusals and this returns `batchWrite`'s own numbers.\n */\nconst summarize = (\n results: ReadonlyArray<BatchOpReport>\n): BatchWriteResult[\"summary\"] & { readonly total: number } => {\n let written = 0\n let deduped = 0\n let failed = 0\n let skipped = 0\n let consolidated = 0\n for (const result of results) {\n // The same partition `operations.ts` makes: a batch-internal loser's value survived at\n // another slot and no file of its own was attempted, so it is neither written nor failed.\n if (result.consolidatedInto !== undefined) consolidated += 1\n else if (result.skipped === true) skipped += 1\n else if (!result.ok) failed += 1\n else if (result.deduped === true) deduped += 1\n else written += 1\n }\n return { total: results.length, written, deduped, failed, skipped, consolidated }\n}\n\n/**\n * The handler layer for the toolkit.\n *\n * `kit.toLayer({ … })` is checked against the toolkit's own parameter and success schemas, so a\n * handler returning the wrong shape is a compile error rather than a decode failure on a live call.\n */\nexport const ToolHandlers: Layer.Layer<\n Layer.Success<ReturnType<typeof MemhtmlToolkit.toLayer>>,\n never,\n AppServices\n> = MemhtmlToolkit.toLayer({\n memory_write: (params) =>\n handled(\n Effect.gen(function* () {\n const article = yield* authored(params.body, params.article_html)\n const result = yield* writeMemory({\n title: params.title,\n claim: article.claim,\n body: article.body,\n articleHtml: article.articleHtml,\n memoryType: params.memory_type,\n path: opt(params.path),\n workspace: opt(params.workspace),\n tags: arr(params.tags),\n entities: arr(params.entities),\n importance: opt(params.importance),\n confidence: opt(params.confidence),\n sessionId: opt(params.session_id),\n promptId: opt(params.prompt_id),\n turnUuid: opt(params.turn_uuid)\n })\n return {\n path: result.path,\n created: result.created,\n deduped: result.deduped,\n existing_path: result.existingPath ?? null\n }\n })\n ),\n\n /**\n * The batch: resolve every op's XOR, call `batchWrite` ONCE, report every op in input order.\n *\n * **The XOR runs per op, up front, before `batchWrite` is called at all.** It is the wire boundary's\n * only rule and it is a rule about two PARAMETERS. `WriteParams` takes an already-resolved\n * `claim`/`body`/`articleHtml`, so an op that supplied both is a call the operations layer has no way\n * to recognize. Resolving it here also means the store's phase-1 validation sees only ops that could\n * possibly be written, which is what keeps \"the atomic abort happens before any file exists\" true of\n * the XOR too.\n *\n * **Then the modes diverge, and each one matches `batchWrite`'s own semantics for the failure class\n * it already handles**, a malformed `memory_type`, which is likewise a per-op decode refusal:\n *\n * - CONTINUE: each XOR refusal becomes that op's failed report, ONLY the survivors go to\n * `batchWrite`, and the survivors' reports are spliced back at their ORIGINAL indices. `originOf`\n * is what makes that possible: `batchWrite` indexes results in the array it was handed, so a\n * survivor at position 0 of a two-op call may be op 0 or op 1 of a three-op one, and reporting its\n * own index would shift every later op by the number of refusals before it. The result is a\n * SUCCESS: every op is present in `results`, in input order, which is the contract D3 states and\n * the only shape an agent can index by.\n * - ATOMIC (the default): the first refused op aborts, and the abort reaches the agent through the\n * ERROR channel as `batchAbortFailure`. An XOR refusal short-circuits before `batchWrite` is\n * called at all, since an atomic batch with a refused op writes nothing by definition and the call\n * would be a round trip whose only outcome is the abort. A refusal `batchWrite` itself produced,\n * such as a malformed `memory_type` or an op the store's render gate refused, comes back as an\n * aborted RESULT, and is converted at the same seam.\n *\n * **That conversion is the one non-obvious thing here, and it was a real bug caught by a test.** An\n * aborted `batchWrite` returns a well-formed result: every op reported, one of them failed, the rest\n * `skipped`, `commitSha: null`. Returning it verbatim is a SUCCESS response for a call that wrote\n * nothing, so the XOR path (an error) and the render-gate path (a success) would be two channels for\n * one outcome, and `BATCH_GUIDANCE`'s promise that \"the first refused op aborts the whole call … and\n * the failure names the offending op\" would be false for every refusal the handler did not itself\n * detect. `firstFailure` finds the offending op in the returned reports and `batchAbortFailure`\n * composes the one message both paths use.\n */\n memory_write_batch: (params) =>\n handled(\n Effect.gen(function* () {\n const continueOnError = params.continue_on_error === true\n const reports: Array<BatchOpReport | undefined> = params.ops.map(() => undefined)\n const survivors: Array<WriteParams> = []\n /** Survivor position in what `batchWrite` was handed → this caller's own op index. */\n const originOf: Array<number> = []\n\n for (const [index, op] of params.ops.entries()) {\n const article = yield* Effect.result(authored(op.body, op.article_html))\n if (article._tag === \"Failure\") {\n const report = xorReport(index, article.failure)\n /**\n * Composed rather than `Effect.fail(article.failure)`, which would reach the agent as the\n * singular's own message: it names the rule but not WHICH of twenty ops broke it, and says\n * nothing about `continue_on_error`.\n */\n if (!continueOnError) {\n return yield* Effect.fail(\n batchAbortFailure(index, report.code ?? \"ERR_INVALID_MEMORY\", report.error ?? \"\")\n )\n }\n reports[index] = report\n continue\n }\n originOf.push(index)\n survivors.push(writeParamsOf(op, article.success))\n }\n\n const batch = yield* batchWrite({\n ops: survivors,\n continueOnError,\n /**\n * The flag reaches `batchWrite` unchanged, which is the only correct place for the assist to\n * live: `memhtml apply --detect-conflicts` gets the same findings from the same code, so the two\n * doors cannot disagree about what a conflict is.\n *\n * The survivors-only consequence is real and it is right. An op this handler already refused\n * for the XOR is not in `survivors`, so it gets no conflict report, and it also has no claim\n * to derive one FROM: on the both-supplied path there is no way to tell which of the two the\n * caller meant, and on the neither-supplied path there is no claim at all. A finding invented\n * for such an op would name a slot the caller never asserted.\n */\n detectConflicts: params.detect_conflicts === true,\n // Threaded unchanged for the same reason the flag above is: `memhtml apply --consolidate`\n // resolves the same slots from the same code, so the two doors cannot disagree about\n // which value won.\n ...(params.consolidate !== undefined && params.consolidate !== null\n ? { consolidate: params.consolidate }\n : {}),\n sessionId: opt(params.session_id),\n promptId: opt(params.prompt_id),\n turnUuid: opt(params.turn_uuid)\n })\n\n // An atomic batch `batchWrite` aborted: one op failed, so the whole call failed, and it reaches\n // the agent through the same error channel and the same message as the XOR refusal above.\n if (!continueOnError) {\n const failed = firstFailure(batch.results)\n if (failed !== undefined) {\n return yield* Effect.fail(\n batchAbortFailure(\n originOf[failed.index] ?? failed.index,\n failed.code ?? \"ERR_INVALID_MEMORY\",\n failed.error ?? \"\"\n )\n )\n }\n }\n\n /**\n * Splice each survivor's report back at its ORIGINAL index, and translate the conflict's\n * `batchIndex` through the SAME map, which is the non-obvious half.\n *\n * `batchWrite` saw only `survivors`, so an intra-batch conflict it found names a position in\n * THAT array. In continue mode with an XOR-refused op before the conflicting pair, survivor 1\n * is the caller's op 2, so reporting the raw number would name a different op than the one the\n * assist actually matched, and it would name it plausibly enough that nobody would notice. The\n * outer `index` has always needed this translation for exactly the same reason; the conflict is\n * a second index in the same space and needs it too.\n *\n * `originOf[…] ?? conflict.batchIndex` mirrors the fallback three lines above rather than\n * dropping the conflict: an untranslatable index is impossible here (every survivor has an\n * origin, by construction of the loop that built both arrays), and if it somehow were not, a\n * caller is better served by a suspicious number than by a finding silently deleted.\n */\n for (const report of batch.results) {\n const index = originOf[report.index]\n if (index === undefined) continue\n const conflict = report.conflict\n const translated =\n conflict === undefined || conflict.batchIndex === null\n ? { ...report, index }\n : {\n ...report,\n index,\n conflict: {\n ...conflict,\n batchIndex: originOf[conflict.batchIndex] ?? conflict.batchIndex\n }\n }\n // `consolidatedInto` is a second index in `batchWrite`'s survivor space and takes the\n // same translation the conflict's `batchIndex` does, for the same reason: an XOR-refused\n // op before the consolidated pair would otherwise make the pointer name the wrong op.\n reports[index] =\n translated.consolidatedInto === undefined\n ? translated\n : {\n ...translated,\n consolidatedInto:\n originOf[translated.consolidatedInto] ?? translated.consolidatedInto\n }\n }\n\n /**\n * An op with no report of its own was never reached. Unreachable on the atomic path, which has\n * already failed by here, so this is continue mode's own case: `skipped`, the same word\n * `batchWrite` uses, so the two doors describe one outcome in one vocabulary.\n */\n const results = reports.map(\n (report, index) => report ?? ({ index, ok: false, skipped: true } satisfies BatchOpReport)\n )\n return {\n results: results.map(wireReport),\n summary: summarize(results),\n commit_sha: batch.commitSha\n }\n })\n ),\n\n memory_read: (params) =>\n handled(\n Effect.gen(function* () {\n const result = yield* readMemory(params.path, { sessionId: opt(params.session_id) })\n return {\n path: result.path,\n title: result.doc.title,\n body: result.doc.article.bodyText,\n gist: result.doc.article.gist,\n memory_type: result.doc.metas.memoryType,\n meta: metaRecord(result.doc),\n links: result.doc.links.map((link) => ({ rel: link.rel, href: link.href })),\n archived: result.doc.metas.status === \"archived\",\n warnings: result.doc.warnings\n }\n })\n ),\n\n memory_search: (params) =>\n handled(\n Effect.gen(function* () {\n const result = yield* searchMemories({\n query: params.query,\n limit: opt(params.limit),\n memoryTypes: opt(params.memory_types),\n workspace: opt(params.workspace),\n tags: opt(params.tags),\n entity: opt(params.entity),\n includeArchived: opt(params.include_archived),\n asOf: opt(params.as_of)\n })\n return {\n hits: result.hits.map((hit) => ({\n path: hit.path,\n title: hit.title,\n gist: hit.gist,\n memory_type: hit.memoryType,\n score: hit.score,\n confidence: hit.confidence,\n updated_at: hit.updatedAt,\n snippet: hit.snippet,\n entities: hit.entities,\n superseded_by: hit.supersededBy\n })),\n degraded: result.degraded,\n arms: result.arms,\n entity_scope: result.entityScope,\n scope_empty: result.scopeEmpty\n }\n })\n ),\n\n memory_recall: (params) =>\n handled(\n Effect.gen(function* () {\n const pack = yield* recallMemories({\n query: params.query,\n budgetChars: opt(params.budget_chars),\n workspace: opt(params.workspace)\n })\n /**\n * `lateral` is the union of both folds' index lines.\n *\n * It holds what did not fit the budget, surfaced with its claim and its path so an agent\n * can drill down deliberately, and it is not a third retrieval arm. Dropping it would make\n * a truncated pack indistinguishable from a small corpus.\n */\n return {\n sections: {\n arcs: pack.arcs.disclosed.map((entry) => ({\n path: entry.path,\n title: entry.title,\n gist: entry.gist,\n body: entry.body\n })),\n memories: pack.memories.disclosed.map((entry) => ({\n path: entry.path,\n title: entry.title,\n gist: entry.gist,\n body: entry.body\n })),\n lateral: [...pack.arcs.indexLines, ...pack.memories.indexLines].map((line) => ({\n path: line.path,\n title: line.title,\n gist: line.gist\n }))\n },\n spent_chars: pack.spentChars,\n truncated: pack.truncated,\n degraded: pack.degraded\n }\n })\n ),\n\n memory_correct: (params) =>\n handled(\n Effect.gen(function* () {\n const article = yield* authored(params.body, params.article_html)\n const result = yield* correctMemory({\n targetPath: params.target_path,\n title: params.title,\n claim: article.claim,\n body: article.body,\n articleHtml: article.articleHtml,\n reason: params.reason,\n sessionId: opt(params.session_id)\n })\n /**\n * `superseded` names the target's ARCHIVE path, which is where the file is once the commit\n * lands, and it is what the new file's `memhtml-supersedes` link points at. Reporting the\n * pre-archive path would hand back a path with no file behind it.\n */\n return {\n path: result.path,\n superseded: [result.archivedPath],\n archived: [result.archivedPath]\n }\n })\n ),\n\n memory_link: (params) =>\n handled(\n Effect.gen(function* () {\n const result = yield* linkMemories(params.src_path, params.rel, params.dst_path)\n return {\n // True whether or not this call was the one that wrote the link: the edge exists either\n // way, and `addLink` is idempotent on the pair. A false here would make a re-link look\n // like a failure.\n ok: true,\n rel: result.rel,\n src_path: result.srcPath,\n dst_path: result.dstPath\n }\n })\n ),\n\n memory_neighbors: (params) =>\n handled(\n Effect.gen(function* () {\n const result = yield* neighborsOf({\n path: params.path,\n depth: opt(params.depth),\n rels: opt(params.rels)\n })\n return { nodes: result.nodes, edges: result.edges }\n })\n ),\n\n memory_archive: (params) =>\n handled(\n Effect.gen(function* () {\n const result = yield* archiveMemory(params.path, params.reason)\n return { path: result.path, archive_path: result.archivePath }\n })\n ),\n\n memory_reinforce: (params) =>\n handled(\n Effect.gen(function* () {\n const result = yield* reinforceMemories(params.paths, params.signal)\n return { bumped: result.bumped, cooled_down: result.cooledDown }\n })\n ),\n\n memory_list: (params) =>\n handled(\n Effect.gen(function* () {\n const result = yield* listMemories({\n memoryType: opt(params.memory_type),\n workspace: opt(params.workspace),\n tag: opt(params.tag),\n entity: opt(params.entity),\n para: opt(params.para),\n limit: opt(params.limit),\n cursor: opt(params.cursor)\n })\n return {\n files: result.files.map((file) => ({\n path: file.path,\n title: file.title,\n memory_type: file.memoryType,\n gist: file.gist,\n workspace: file.workspace,\n para: file.para,\n confidence: file.confidence,\n importance: file.importance,\n archived: file.archived,\n updated_at: file.updatedAt\n })),\n next_cursor: result.nextCursor\n }\n })\n ),\n\n trace_search: (params) =>\n handled(\n Effect.gen(function* () {\n const result = yield* searchTraces({\n query: params.query,\n cwd: opt(params.cwd),\n since: opt(params.since),\n limit: opt(params.limit)\n })\n return {\n sessions: result.sessions.map((session) => ({\n session_id: session.sessionId,\n slug: session.slug,\n cwd: session.cwd,\n started_at: session.startedAt,\n prompt_count: session.promptCount,\n first_prompt: session.firstPrompt,\n ai_title: session.aiTitle\n }))\n }\n })\n ),\n\n trace_links: (params) =>\n handled(\n Effect.gen(function* () {\n const result = yield* traceLinks({\n sessionId: opt(params.session_id),\n path: opt(params.path)\n })\n return {\n links: result.links.map((link) => ({\n path: link.path,\n session_id: link.sessionId,\n prompt_id: link.promptId,\n turn_uuid: link.turnUuid,\n link_kind: link.linkKind,\n at: link.at\n }))\n }\n })\n ),\n\n memory_status: () =>\n handled(\n Effect.gen(function* () {\n const report = yield* statusReport()\n return {\n head_sha: report.headSha,\n dirty: report.dirty,\n counts_by_type: report.countsByType,\n archived_count: report.archivedCount,\n edges: report.edges,\n index_fresh: report.indexFresh,\n embedder_up: report.embedderUp,\n last_sleep:\n report.lastSleep === null\n ? null\n : {\n run_id: report.lastSleep.runId,\n status: report.lastSleep.status,\n started_at: report.lastSleep.startedAt\n }\n }\n })\n )\n})\n\n/** Re-exported so a caller wiring a test layer names the same type the handlers require. */\nexport type { AppServices, EmbedderShape }\n","import { readFile } from \"node:fs/promises\"\nimport { join } from \"node:path\"\n\nimport { Roots, readMemory } from \"@memhtml/cli\"\nimport { SLEEP_REPORTS_DIR } from \"@memhtml/store\"\nimport { Effect, Layer, Schema } from \"effect\"\nimport { McpSchema, McpServer } from \"effect/unstable/ai\"\n\n/**\n * The two resources, design.md §8.\n *\n * A resource is for CITATION-grade drill-down: a client that got a path from `memory_search` can\n * fetch `memhtml://file/<path>` and show a human the file behind an answer, without spending a tool call\n * and without the tool response having had to carry the whole body.\n *\n * `McpSchema.param` names each template parameter, so `tools/list`'s sibling `resources/templates`\n * publishes `{path}` and `{run-id}` as named rather than positional holes.\n */\n\n/** `memhtml://file/{path}`: one memory's rendered content. */\nconst pathParam = McpSchema.param(\"path\", Schema.String)\n\n/** `memhtml://sleep/{run-id}`: one sleep run's committed HTML report. */\nconst runIdParam = McpSchema.param(\"run-id\", Schema.String)\n\n/**\n * A memory file, by path.\n *\n * The BODY is returned, not the raw HTML file. A client asking a resource for a citation wants the\n * text a human reads; the markup is the storage format, and handing back a full document with a head\n * full of `memhtml-*` metas would spend a client's rendering budget on bookkeeping. The metadata is\n * available through `memory_read`, which is the tool for exactly that.\n *\n * A missing path fails the read rather than answering with an empty resource: a citation that\n * silently resolves to nothing is worse than one that says the file is gone.\n *\n * This read BUMPS salience, through the same `readMemory` the `memory_read` tool calls. The bump is\n * deliberate: the caller named one specific path, which is a chosen open. A client\n * fetching the file behind an answer is making the same statement an agent makes with `memory_read`,\n * and the plane should not be able to tell them apart.\n */\nexport const FileResource = McpServer.resource`memhtml://file/${pathParam}`({\n name: \"Memory file\",\n description:\n \"One memory's title, claim, and body text, by repo-root-relative path. For showing a human the file behind an answer.\",\n mimeType: \"text/plain\",\n content: (_uri, path) =>\n Effect.gen(function* () {\n const result = yield* readMemory(path)\n return [\n `# ${result.doc.title}`,\n \"\",\n result.doc.article.gist,\n \"\",\n result.doc.article.bodyText\n ].join(\"\\n\")\n }).pipe(Effect.orDie)\n})\n\n/**\n * A sleep run's report, by run id.\n *\n * The report is a COMMITTED file under `.memhtml/sleep/`, so this resource reads the tree rather than the\n * database: the report is the durable artifact of a run and the `sleep_runs` row is reporting\n * convenience. A run id arrives as `sleep/2026-08-02`, and the file is named for its last segment.\n */\nexport const SleepResource = McpServer.resource`memhtml://sleep/${runIdParam}`({\n name: \"Sleep run report\",\n description:\n \"One sleep run's committed HTML report: per-phase counts, commits, and what the run changed.\",\n mimeType: \"text/html\",\n content: (_uri, runId) =>\n Effect.gen(function* () {\n const roots = yield* Roots\n // The last segment only: a run id is `sleep/<date>` and the file is `<date>.html`, so joining\n // the whole id would look for `.memhtml/sleep/sleep/<date>.html`.\n const name = runId.split(\"/\").at(-1) ?? runId\n const path = join(roots.memhtmlRoot, SLEEP_REPORTS_DIR, `${name}.html`)\n return yield* Effect.tryPromise({\n try: () => readFile(path, \"utf8\"),\n catch: (cause) => cause\n })\n }).pipe(Effect.orDie)\n})\n\n/** Both resources as one layer, for the server to provide. */\nexport const Resources = Layer.mergeAll(FileResource, SleepResource)\n\n/** The templates, for a test to assert the surface without a handshake. */\nexport const RESOURCE_TEMPLATES = [\"memhtml://file/{path}\", \"memhtml://sleep/{run-id}\"] as const\n","import { layerApp } from \"@memhtml/cli\"\nimport { Layer, Logger } from \"effect\"\nimport { McpProtocol, McpServer } from \"effect/unstable/ai\"\n\nimport { ToolHandlers } from \"./handlers.js\"\nimport { Resources } from \"./resources.js\"\nimport { MemhtmlToolkit } from \"./tools.js\"\n\nexport const SERVER_NAME = \"memhtml\"\nexport const SERVER_VERSION = \"0.2.3\" // x-release-please-version\n\n/**\n * The server as one layer: fourteen tools, two resources, over the CLI's own `AppLive`.\n *\n * The same composition the CLI builds, deliberately. An MCP server with its own layer graph would be\n * a second set of answers to which database file, which git root, and which vector space. An agent\n * whose `memory_write` landed in one repo while its operator's `memhtml search` read another would\n * be very hard to diagnose from either side.\n *\n * **`Logger.LogToStderr` is required here.** Effect's default logger writes to stdout, and stdout\n * on this transport is the NDJSON-RPC stream, so one log line would corrupt the frame a client is\n * mid-parse on. The CLI sets the same reference for the same reason, one fd over.\n *\n * **There is no server-level `instructions` here, because effect provides no way to set one.** MCP\n * defines an `instructions` field on the initialize response for exactly the cross-tool guidance\n * this server wants to give (when to batch, the three doors, the commit duty), and effect does not\n * emit it. Verified against 4.0.0-beta.107 in the dependency's own declarations: `McpSchema`\n * DECLARES `instructions: optional(Schema.String)` on the initialize result, while `layerStdio`'s\n * options are `{name, version, protocols, extensions}`, so there is not even an argument to pass,\n * and the handler that builds the result supplies none.\n *\n * **TOOL DESCRIPTIONS are this server's only guidance channel.** That consequence is recorded here,\n * beside the field a maintainer would come looking for, rather than in a doc. It is why\n * `BATCH_GUIDANCE` and `ARTICLE_HTML_CONTRACT` in `tools.ts` are shared constants appended to every\n * description they apply to, and why they read as prose to an agent rather than as reference notes to a\n * maintainer. Do not patch the dependency; revisit this when effect wires the field, at which point the\n * duplicated prose can move up here.\n */\nexport const layerServer = (repoOverride?: string | undefined) =>\n Layer.mergeAll(McpServer.toolkit(MemhtmlToolkit), Resources).pipe(\n Layer.provide(ToolHandlers),\n Layer.provide(\n McpServer.layerStdio({\n name: SERVER_NAME,\n version: SERVER_VERSION,\n // The protocol revision is now the caller's to state, and `v2025_06_18` is the only adapter\n // this dependency ships. Naming it here means a future revision is an explicit, reviewable\n // choice rather than a default that moves the wire format under a shipped client.\n protocols: [McpProtocol.v2025_06_18]\n })\n ),\n Layer.provide(layerApp(repoOverride)),\n Layer.provide(Layer.succeed(Logger.LogToStderr)(true))\n )\n","#!/usr/bin/env node\nimport { NodeRuntime, NodeStdio } from \"@effect/platform-node\"\nimport { Layer } from \"effect\"\n\nimport { layerServer } from \"./server.js\"\n\n/**\n * The stdio entry point. stdout belongs to the MCP framing from here on: every log in the graph is\n * already routed to stderr by `layerServer`, and nothing in this file writes.\n *\n * `Layer.launch` runs the server for the process's lifetime rather than building the layer and\n * returning. The transport IS the program, and a built-then-released layer would close stdin out\n * from under the client mid-session.\n */\nLayer.launch(layerServer().pipe(Layer.provide(NodeStdio.layer))).pipe(NodeRuntime.runMain)\n"],"mappings":"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EACN,MAAM;EACN,aAAa;EACb,QAAQ;EACR,YAAY;CAChB;CACA;EACI,MAAM;EACN,MAAM;EACN,aAAa;CACjB;CACA;EACI,MAAM;EACN,MAAM;EACN,aAAa;EACb,YAAY;CAChB;CACA;EACI,MAAM;EACN,MAAM;EAIN,aAAa;CACjB;CACA;EACI,MAAM;EACN,MAAM;EACN,aAAa;EACb,SAAS;CACb;CACA;EACI,MAAM;EACN,MAAM;EACN,aAAa;CACjB;AACJ;;;;;;;;;;;AAWA,MAAa,WAAW;CACpB;EACI,MAAM;EACN,SAAS;EACT,MAAM,CAAC;EACP,OAAO,CAAC;EACR,eAAe,CAAC,cAAc;CAClC;CACA;EACI,MAAM;EACN,SAAS;EACT,MAAM,CAAC;EACP,OAAO,CAAC;EACR,eAAe,CAAC,WAAW;CAC/B;CACA;EACI,MAAM;EACN,SAAS;EACT,MAAM,CAAC;EACP,OAAO;GACH;IACI,MAAM;IACN,MAAM;IACN,aAAa;IACb,UAAU;GACd;GACA;IACI,MAAM;IACN,MAAM;IACN,aAAa;GACjB;GACA;IACI,MAAM;IACN,MAAM;IACN,aAAa;IACb,YAAY;GAChB;GACA;IACI,MAAM;IACN,MAAM;IACN,aAAa;GACjB;GACA;IACI,MAAM;IACN,MAAM;IACN,aAAa;IACb,QAAQ;IACR,UAAU;GACd;GACA;IACI,MAAM;IACN,MAAM;IACN,aAAa;GACjB;GACA;IAAE,MAAM;IAAa,MAAM;IAAU,aAAa;GAAyC;GAC3F;IACI,MAAM;IACN,MAAM;IACN,aAAa;GACjB;GACA;IACI,MAAM;IACN,MAAM;IACN,aAAa;IACb,YAAY;GAChB;GACA;IACI,MAAM;IACN,MAAM;IACN,aAAa;GACjB;GACA;IAAE,MAAM;IAAc,MAAM;IAAU,aAAa;GAAwC;GAC3F;IACI,MAAM;IACN,MAAM;IACN,aAAa;GACjB;GACA;IAAE,MAAM;IAAa,MAAM;IAAU,aAAa;GAAkC;GACpF;IAAE,MAAM;IAAa,MAAM;IAAU,aAAa;GAAgC;EACtF;EACA,eAAe,CAAC,gBAAgB;CACpC;CACA;EACI,MAAM;EACN,SAAS;EAC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"}