@keb-org/consol 0.1.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,28 +1,64 @@
1
1
  # @keb-org/consol
2
2
 
3
- TypeScript SDK for the consol memory engine, for three uses:
3
+ The consol memory engine as one package: the MCP server (`npx -y @keb-org/consol`) and a TypeScript SDK.
4
+ The SDK has three uses:
4
5
 
5
- 1. **Local**: a vault on disk (or S3) in your own process. The SDK starts `consol stdio` as a child.
6
+ 1. **Local**: a vault on disk (or S3), with the engine running in your own process as a native addon.
6
7
  2. **Hosted client**: the same `Vault` methods against a backend over HTTP.
7
8
  3. **Backend**: `Consol.handle` behind your own HTTP route, so clients of any language can use it.
8
9
 
9
10
  Node >= 22 or Bun, no dependencies; `import` and `require` share one implementation. The behaviour
10
11
  is the same in every mode.
11
12
 
12
- ## The server binary and database
13
+ ## MCP server
13
14
 
14
- The package does not build the server. The SDK uses the first of these that exists:
15
+ `npx -y @keb-org/consol --vault PATH --agent NAME` serves MCP on stdio for one vault that already
16
+ exists (`consol create --vault PATH` makes it; the MCP never creates a vault). The engine runs in
17
+ its process through the same addon as the SDK, so it needs the database too: every `CONSOL_*`
18
+ variable of its process is an engine setting (`CONSOL_DB_URL` is required, `CONSOL_EMBED_*` turn on
19
+ embeddings). Engine logs go to stderr at the level of `RUST_LOG` (default `warn`).
15
20
 
16
- 1. the `binaryPath` option,
17
- 2. the `CONSOL_BIN` environment variable,
18
- 3. the platform package `@keb-org/consol-<platform>-<arch>`, resolved from the SDK's own location,
19
- 4. `consol` (`consol.exe` on Windows) on `PATH`.
21
+ | Flag | Variable | |
22
+ |---|---|---|
23
+ | `--vault` | `CONSOL_VAULT` | a directory or `s3://bucket/prefix/name`; with `--remote`, the vault's name on the backend |
24
+ | `--remote` | `CONSOL_REMOTE_URL` | a backend URL (use 3) to forward to, instead of opening a vault; its headers come from `CONSOL_REMOTE_HEADERS` (one `Name: value` per line) |
25
+ | `--agent` | `CONSOL_AGENT` | the default agent of every call; a call's `agent` argument overrides it |
26
+
27
+ In an MCP client, or in Claude Code (the options go before the name):
28
+
29
+ ```bash
30
+ claude mcp add --env CONSOL_DB_URL=postgres://postgres:pw@127.0.0.1:5432/postgres \
31
+ consol -- npx -y @keb-org/consol --vault /abs/path/vault --agent assistant
32
+ ```
33
+
34
+ ```json
35
+ { "mcpServers": { "consol": {
36
+ "command": "npx",
37
+ "args": ["-y", "@keb-org/consol", "--vault", "/abs/path/vault", "--agent", "assistant"],
38
+ "env": { "CONSOL_DB_URL": "postgres://postgres:pw@127.0.0.1:5432/postgres" }
39
+ } } }
40
+ ```
41
+
42
+ A hosted backend needs no database here:
43
+ `npx -y @keb-org/consol --remote https://api.acme.dev/v1/consol --vault demo --agent assistant`, with the
44
+ backend's credentials in `CONSOL_REMOTE_HEADERS`. The
45
+ tool list, the instructions and each result are the engine's, the same ones `consol serve` mounts
46
+ over streamable HTTP at `<path>/mcp`, which is unchanged.
47
+
48
+ ## The engine addon and database
49
+
50
+ The engine is the native addon `consol.node`, loaded the first time you start a vault or a
51
+ `Consol` (importing the SDK loads nothing). The SDK uses the first of these:
52
+
53
+ 1. the `addonPath` option,
54
+ 2. the `CONSOL_ADDON` environment variable,
55
+ 3. the platform package `@keb-org/consol-<platform>-<arch>`, resolved from the SDK's own location.
20
56
 
21
- A `binaryPath` or `CONSOL_BIN` that points nowhere fails at once with `binary_not_found` instead
22
- of falling through to the next place. Build the binary from the repository with
23
- `cargo build --release -p consol-cli`.
57
+ An `addonPath` or `CONSOL_ADDON` that points nowhere, or at a file that does not load, fails at
58
+ once with `addon_not_found` instead of falling through to the next place. The platform package
59
+ also ships the `consol` CLI as `bin/consol`.
24
60
 
25
- The engine needs a database, PostgreSQL with pgvector or CockroachDB: pass `db` or set
61
+ The engine still needs a database, PostgreSQL with pgvector or CockroachDB: pass `db` or set
26
62
  `CONSOL_DB_URL` in your process (the one `CONSOL_*` variable the SDK takes from its host).
27
63
 
28
64
  ## 1. Local
@@ -44,8 +80,8 @@ try {
44
80
  ```
45
81
 
46
82
  `createVault` fails with `vault_exists` when the directory already holds a vault, `openVault`
47
- (same options) with `vault_not_found` when it does not. Both start the server and check that its
48
- version matches the SDK's major.minor before they return.
83
+ (same options) with `vault_not_found` when it does not. Both open the engine and check that the
84
+ addon's version matches the SDK's major.minor before they return.
49
85
 
50
86
  ## 2. Hosted client
51
87
 
@@ -109,19 +145,25 @@ Each method takes one object with the tool's wire argument names, plus optional
109
145
 
110
146
  | Method | Arguments | Returns |
111
147
  |---|---|---|
112
- | `recall` | `{ query, include_history?, limit?, agent? }` | `RecallResult`: `text` is the progressive disclosure to give a model (cards by tier, intent block, hints); `candidates` are the same cards as data. Cards are leads: read a note before relying on it |
148
+ | `recall` | `{ query, agent? }` | `RecallResult`: `text` is the progressive disclosure to give a model (cards by tier, the blocks the query's symbols ask for, hints); `candidates` are the same cards as data. Cards are leads: read a note before relying on it |
113
149
  | `read` | `{ id, continuation?, agent? }` | `ReadResult`; `text` is the exact file |
114
- | `remember` | `{ id, content?, expected_version?, summary?, entities?, facets?, scope?, team?, valid_from?, valid_to?, reflection?, agent? }` | `RememberResult` |
115
- | `remove` | `{ id, expected_version, reflection?, agent? }` | `RemoveResult`: the note is archived, its body stays readable |
116
- | `reflect` | one mode: `{}` discovery, `{ ids }`, `{ work_ids }` or `{ decisions }`; `limit?`, `agent?` | `ReflectResult`; delivered sources carry their `text` |
150
+ | `remember` | `{ id, content?, meta?, expected_version?, token?, agent? }`; `meta` is `{ summary?, entities?, facets?, scope?, team?, valid_from?, valid_to? }` | `RememberResult` |
151
+ | `remove` | `{ id, expected_version, token?, agent? }` | `RemoveResult`: the note is archived, its body stays readable |
152
+ | `reflect` | `{ focus?, decisions?, agent? }`; call it with no arguments and follow `next` | `ReflectResult`: `next`, the delivered `items` (sources carry their `text`), every unresolved `writes` entry, the `blocked` tokens |
117
153
  | `callTool` | `(name, args)` with raw wire arguments | the raw `Reply`; tool errors come back as `reply.error` |
118
154
  | `close` | | final and idempotent |
119
155
 
120
156
  - `remember` without `expected_version` is create-only. To update, pass `source.content_version`
121
- from your last `read`; a stale one fails with `version_conflict`. Omitted or `null` fields keep
122
- their stored value, `""` or `[]` clears them.
123
- - `remember` and `remove` also take `{ resume: true, reflection, agent? }` to resume a pending
124
- reflection-bound write. `reflection` is `{ work_id, basis_hash, action_key }`.
157
+ from your last `read`; a stale one fails with `version_conflict`. Omitted or `null` `content` keeps
158
+ the body. In `meta` a key left out keeps its stored value, `null`, `""` or `[]` clears it (not
159
+ `scope` or `team`); a key the server does not know is ignored and listed in the receipt's
160
+ `ignored`.
161
+ - `token` on `remember` and `remove` is an item's work token from `reflect`: it binds that one write to the
162
+ work (the server derives the write's key). `{ token, agent? }` alone, with a write token from
163
+ `reflect().writes` or a write receipt, resumes that write.
164
+ - `reflect` holds nothing between calls: a cold `reflect()` lists every unresolved write, the open work and
165
+ the blocked work. Decide an item with `{ token, verdict, reason, refs?, outputs? }`, `verdict` one of the
166
+ item's `verdicts`; the server sizes each batch (about 64 KiB of source Markdown, at most 16 items).
125
167
  - `byte_range` values are UTF-8 byte offsets: `sliceUtf8(text, start, end)` slices a JS string by them.
126
168
  - `vault.serverVersion` is the server version of the last reply. An agent exists from its first
127
169
  `remember`; reading as an agent that wrote nothing finds nothing.
@@ -147,7 +189,7 @@ Markdown inline.
147
189
 
148
190
  The tool list is not on the wire. `consol schema` prints its JSON Schema: `oneOf` holds one
149
191
  envelope per tool, with the `args` schema and the description the model reads. An MCP client can
150
- use `consol mcp` instead.
192
+ use the MCP server of this package instead (above).
151
193
 
152
194
  ## Loading many notes
153
195
 
@@ -181,16 +223,16 @@ try {
181
223
  ```
182
224
 
183
225
  - `ConsolError.is(error)` also works across duplicate copies of the package, where `instanceof` fails.
184
- - `field` names the input at fault, relative to the call's arguments (`limit`,
185
- `decisions[2].temporal`), `tool` the tool, and `result` keeps the raw `Reply` of a failed tool
226
+ - `field` names the input at fault, relative to the call's arguments (`query`,
227
+ `decisions[2].refs[0]`), `tool` the tool, and `result` keeps the raw `Reply` of a failed tool
186
228
  call, including the durable state of a pending write (`error.result.result`).
187
229
  - Tool codes (contract section 4; accept unknown future ones): `invalid_argument`, `forbidden`,
188
230
  `not_found`, `already_exists`, `version_conflict`, `team_not_attached`, `secret_rejected`,
189
231
  `content_too_large`, `unsupported`, `work_not_found`, `basis_mismatch`, `already_decided`,
190
232
  `unresolved_writes`, `postcondition_failed`, `index_unavailable`, `storage_unavailable`, `vault_busy`, `internal`.
191
233
  Vault codes: `vault_exists`, `vault_not_found`.
192
- - SDK codes: `request_failed` (`status?`), `binary_not_found`, `incompatible_server` (server
193
- major.minor differs from the SDK's), `server_exited`, `timeout`, `aborted`, `closed`.
234
+ - SDK codes: `request_failed` (`status?`), `addon_not_found`, `incompatible_server` (addon or
235
+ server major.minor differs from the SDK's), `timeout`, `aborted`, `closed`.
194
236
  - `retryable: true` (`vault_busy`, `index_unavailable`, `storage_unavailable`, `request_failed` on a connect error or
195
237
  502/503/504) means the identical request may succeed later. The SDK never retries by itself.
196
238
 
@@ -198,22 +240,17 @@ try {
198
240
 
199
241
  - `close()` is final and idempotent: in-flight and later calls fail with `closed`, and concurrent
200
242
  calls share one completion. `await using vault = await openVault(...)` closes it for you.
201
- - An idle local vault does not keep Node or Bun alive: a script that forgets `close()` still
202
- exits, and its server exits with it.
203
243
  - Every call has a deadline: 300 s by default, `timeoutMs` per vault or per call, `0` disables it.
204
244
  `signal` aborts a call. Both only stop waiting: a write may still complete, so read the note
205
245
  before you retry.
206
- - When a local server crashes, in-flight calls fail with `server_exited`. The next call starts the
207
- server once more and opens the vault again (`vault_not_found` if it was deleted meanwhile).
208
- - Servers on one vault take turns on its lock, a lease in the database; a wait over 5 s fails with
209
- `vault_busy`.
246
+ - Engines on one vault, in this process or another, take turns on its lock, a lease in the
247
+ database; a wait over 5 s fails with `vault_busy`.
210
248
 
211
249
  ## Configuration and logging
212
250
 
213
- Options of `createVault`, `openVault` (local) and `Consol.start` become the server's environment,
214
- never its argv. Inherited `CONSOL_*` and `RUST_LOG` variables are dropped first (except
215
- `CONSOL_DB_URL` as the default for `db`), so a stray host variable cannot change the agent or the
216
- vault.
251
+ Options of `createVault`, `openVault` (local) and `Consol.start` become the engine's settings (the
252
+ `CONSOL_*` names). Your process's own `CONSOL_*` variables are not read (except `CONSOL_DB_URL` as
253
+ the default for `db`), so a stray host variable cannot change the agent or the vault.
217
254
 
218
255
  | Option | Meaning |
219
256
  |---|---|
@@ -221,16 +258,16 @@ vault.
221
258
  | `vaultRoot` / `vault` | `Consol.start`: a directory or `s3://` prefix of many vaults, or one vault |
222
259
  | `agent` | default agent of every call; a call's `agent` argument overrides it |
223
260
  | `db` | database URL (`CONSOL_DB_URL`): PostgreSQL with pgvector, or CockroachDB |
224
- | `embedding` | `{ url, model, apiKey?, dims?, queryPrefix?, batchSize?, timeoutMs? }` (`CONSOL_EMBED_*`); without it recall is lexical-only |
261
+ | `embedding` | `{ url, model, apiKey?, dims?, timeoutMs? }` (`CONSOL_EMBED_*`); without it recall is lexical-only |
225
262
  | `jev` | `{ url?, apiKey?, model?, auto? }` (`CONSOL_JEV_*`); experimental advice |
226
- | `env` | any other server variable, applied last |
227
- | `binaryPath` | server binary, see above |
263
+ | `env` | any other engine setting (a `CONSOL_*` name), applied last |
264
+ | `addonPath` | the `consol.node` to load, see above |
228
265
  | `timeoutMs` | default per-call deadline in ms |
229
- | `onLog` | `(line) => void`, receives each server stderr line |
266
+ | `onLog` | `(line) => void`, receives each engine log line |
267
+ | `logLevel` | which lines reach `onLog`, such as `info` or `consol_engine=debug` (default `warn`) |
230
268
 
231
- The server's stderr is captured and never written to your process's stderr. Pass `onLog` to see
232
- it, and `env: { RUST_LOG: 'debug' }` for more detail. A `server_exited` error carries the server's
233
- last lines in its message and in `stderrTail`.
269
+ The addon has one log sink per process: every engine's `onLog` receives the same lines at the
270
+ latest `logLevel` set, and the sink is released when the last of them closes.
234
271
 
235
272
  ## Types and stability
236
273
 
@@ -246,21 +283,22 @@ decisions, and every `jev`, `jev_triage` and `jev_advisory` field (typed as plai
246
283
  bun install
247
284
  bun run build # dist/index.js (CJS), dist/index.mjs (ESM wrapper), dist/*.d.ts
248
285
  bun run typecheck # tsc over src
249
- # the rest needs the real binary and a database:
250
- export CONSOL_BIN=/abs/consol CONSOL_DB_URL=postgres://...
251
- bun run test # pure checks, hosted-client stub, real-binary suite on Node, strict tsc incl. conformance
252
- bun run test:consumers # packed tarballs in Bun, Node ESM, Node CJS and platform-package projects
253
- node test/capture.mjs # recapture test/fixtures from the real server (without CONSOL_BIN it only regenerates the types check)
286
+ bun run test:offline # pure checks, hosted-client stub, fake-addon transport checks, strict tsc incl. conformance
287
+ # the rest needs the real addon and a database:
288
+ export CONSOL_ADDON=/abs/consol.node CONSOL_DB_URL=postgres://...
289
+ bun run test # the offline checks plus the real-addon suite on Node
290
+ CONSOL_BIN=/abs/consol bun run test:consumers # packed tarballs in Bun, Node ESM, Node CJS and platform-package projects
291
+ node test/capture.mjs # recapture test/fixtures from the real engine (without CONSOL_ADDON it only regenerates the types check)
254
292
  ```
255
293
 
256
294
  `bun run test`, not `bun test` (that is Bun's own runner). Consumer logs go to
257
295
  `consumers/<project>.log`.
258
296
 
259
- ## Publishing platform binaries
297
+ ## Publishing platform packages
260
298
 
261
299
  1. `bun run build`
262
- 2. `bun scripts/make-platform-package.mjs <binary> <platform> <arch> <outDir>` per target
263
- (win32-x64, darwin-arm64, darwin-x64, linux-x64, linux-arm64); the binary is stored as
264
- `bin/consol[.exe]` whatever its file name
300
+ 2. `bun scripts/make-platform-package.mjs <binary> <addon> <platform> <arch> <outDir>` per target
301
+ (win32-x64, darwin-arm64, darwin-x64, linux-x64, linux-arm64); the CLI binary is stored as
302
+ `bin/consol[.exe]` and the addon as `consol.node`, whatever their file names
265
303
  3. Publish each platform package, then `bun scripts/inject-optional-deps.mjs <publish-copy-of-package.json>`
266
304
  4. `bun publish` the main package (`publishConfig.access` is `public`)
package/dist/consol.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { type EngineOptions } from "./stdio";
1
+ import { type EngineOptions } from "./native";
2
2
  import type { CallOptions, Context } from "./transport";
3
3
  import type { Reply } from "./types";
4
4
  /** Exactly one of `vaultRoot` / `vault`, plus the engine settings (`db`, `embedding`, `jev`, ...). */
@@ -10,7 +10,7 @@ export interface ConsolOptions extends EngineOptions {
10
10
  }
11
11
  export declare function httpStatus(reply: Reply): number;
12
12
  /**
13
- * The engine in your own backend: one `consol stdio` child behind `handle`. A backend authorizes
13
+ * The engine in your own backend: the in-process engine behind `handle`. A backend authorizes
14
14
  * the request itself, then passes the result as `ctx`.
15
15
  *
16
16
  * SECURITY: `ctx` (the vaults the caller may use, the agent they are) must come from YOUR auth,
package/dist/errors.d.ts CHANGED
@@ -1,13 +1,12 @@
1
1
  import type { Reply } from "./types";
2
2
  /** Branchable error codes: wire tool codes (contract section 4) plus the SDK codes of section 6. */
3
- export type ConsolErrorCode = "invalid_argument" | "not_found" | "already_exists" | "version_conflict" | "team_not_attached" | "secret_rejected" | "content_too_large" | "unsupported" | "work_not_found" | "basis_mismatch" | "already_decided" | "unresolved_writes" | "postcondition_failed" | "index_unavailable" | "storage_unavailable" | "vault_busy" | "forbidden" | "vault_exists" | "vault_not_found" | "internal" | "request_failed" | "binary_not_found" | "incompatible_server" | "server_exited" | "timeout" | "aborted" | "closed" | (string & {});
3
+ export type ConsolErrorCode = "invalid_argument" | "not_found" | "already_exists" | "version_conflict" | "team_not_attached" | "secret_rejected" | "content_too_large" | "unsupported" | "work_not_found" | "basis_mismatch" | "already_decided" | "unresolved_writes" | "postcondition_failed" | "index_unavailable" | "storage_unavailable" | "vault_busy" | "forbidden" | "vault_exists" | "vault_not_found" | "internal" | "request_failed" | "addon_not_found" | "incompatible_server" | "timeout" | "aborted" | "closed" | (string & {});
4
4
  export interface ConsolErrorDetails {
5
5
  retryable?: boolean;
6
6
  field?: string;
7
7
  tool?: string;
8
8
  /** HTTP status of a `request_failed` / `forbidden` that came from the transport, not the engine. */
9
9
  status?: number;
10
- stderrTail?: string;
11
10
  result?: Reply;
12
11
  cause?: unknown;
13
12
  }
@@ -18,7 +17,6 @@ export declare class ConsolError extends Error {
18
17
  readonly field?: string;
19
18
  readonly tool?: string;
20
19
  readonly status?: number;
21
- readonly stderrTail?: string;
22
20
  /** Raw reply of a failed typed call: durable write state (`result.result`) survives the throw. */
23
21
  readonly result?: Reply;
24
22
  constructor(code: ConsolErrorCode, message: string, details?: ConsolErrorDetails);
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export { ConsolError, type ConsolErrorCode } from "./errors";
2
2
  export { Consol, httpStatus, type ConsolOptions } from "./consol";
3
3
  export type { CallOptions, Context } from "./transport";
4
- export type { EmbeddingOptions, EngineOptions, JevOptions } from "./stdio";
5
- export { Vault, createVault, openVault, sliceUtf8, type VaultOptions, type HostedOptions, type RecallArgs, type ReadArgs, type RememberArgs, type RemoveArgs, type ResumeArgs, type ReflectArgs, type ReflectionContext, type Decision, type InspectedSource, type InspectedSpan, type TemporalAssessment, type PairAssertion, } from "./vault";
6
- export type { Reply, ToolError, Unavailable, Status, ByteRange, Scope, SourceRef, RelationshipKind, Relationship, RecallRetrieval, SourceExcerpt, RecallCard, RecallResult, ReadResult, RememberResult, RemoveResult, ReflectResult, AffectedDoc, EntityAffectedGroup, WriteState, WriteFailureCode, WriteReceipt, WriteResolution, WriteOutcome, ReflectionWork, SourceWork, PairWork, HubWork, PromotionWork, DeliveredSource, CompleteDelivery, FailedDelivery, ReflectDelivery, DecisionReceipt, SourceDisposition, PairDisposition, } from "./types";
4
+ export type { EmbeddingOptions, EngineOptions, JevOptions } from "./native";
5
+ export { Vault, createVault, openVault, sliceUtf8, type VaultOptions, type HostedOptions, type RecallArgs, type ReadArgs, type RememberArgs, type RemoveArgs, type ResumeArgs, type ReflectArgs, type Decision, } from "./vault";
6
+ export type { Reply, ToolError, Unavailable, Status, ByteRange, Scope, SourceRef, RelationshipKind, Relationship, RecallRetrieval, SourceExcerpt, RecallCard, RecallResult, ReadResult, RememberResult, RemoveResult, ReflectResult, AffectedDoc, EntityAffectedGroup, WriteState, WriteFailureCode, WriteReceipt, WriteResolution, WriteOutcome, UnresolvedWrite, ReflectItem, SourceItem, PairItem, HubItem, PromotionItem, DeliveredSource, DecisionReceipt, FocusError, ReflectNext, } from "./types";