@panaversity/ksor 0.0.4 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,53 @@
1
1
  # @panaversity/ksor
2
2
 
3
+ ## 0.0.6
4
+
5
+ ### Patch Changes
6
+
7
+ - 3890ad2: fix: a scaffolded project has exactly two commands, one per surface —
8
+ `pnpm dev` for the site people read, `pnpm serve` for the record agents query
9
+ (it applies the schema, authorizes ingest, ingests, and serves). Neither asks
10
+ the reader to decide anything.
11
+
12
+ fix: the one-command script is no longer called `up`. `up` is
13
+ pnpm's own alias for `update`, so the script shipped in 0.0.5 was shadowed by
14
+ the package manager: an adopter following the runbook ran `pnpm up` expecting
15
+ to bring their record up and instead upgraded their dependencies. Anyone on
16
+ 0.0.5 should use `pnpm run schema && pnpm run grant && pnpm run ingest &&
17
+ pnpm run serve` until they re-scaffold.
18
+
19
+ ## 0.0.5
20
+
21
+ ### Patch Changes
22
+
23
+ - 995f002: feat: scaffolded projects ship a commented `.env.example` naming every
24
+ variable the agent surface needs — the DSN variable, the provider key, and
25
+ `KSOR_AUTH_DISABLED=1`, which a local run requires because `ksor serve` refuses
26
+ to boot unauthenticated. Copy it to `.env` and it is read automatically.
27
+
28
+ feat: standing up the agent surface is one command and one config block.
29
+ `ksor` now reads `./.env` automatically (Node-native, no dependency; a real
30
+ environment variable still wins), scaffolded projects get `pnpm up` —
31
+ schema → grant → ingest → serve — and `ksor schema --apply` is re-runnable
32
+ instead of failing on an already-provisioned database, so the whole sequence
33
+ is safe to repeat and doubles as the refresh after editing `knowledge/`.
34
+
35
+ fix: a scaffolded project deploys on the first try. The shipped `vercel.json`
36
+ pinned `--frozen-lockfile`, so an adopter's first Vercel import failed with
37
+ `ERR_PNPM_OUTDATED_LOCKFILE` — the scaffold declares a root dependency whose
38
+ stamped version the committed lockfile cannot record.
39
+
40
+ fix: the serve runbook no longer tells first-timers to declare
41
+ `retrieval.vector_floor: uncalibrated` before serving, which made every request
42
+ refuse until a floor was measured. Configuring the record needs one `database:`
43
+ block; the abstention gate is turned on deliberately, after it serves.
44
+
45
+ - 4e84cdf: fix: `ksor serve` reports its real version to MCP clients. In 0.0.4 every
46
+ client saw `serverInfo.version` of `0.0.0`: the gateway read the version from
47
+ an environment variable at module scope, and the CLI's static import evaluated
48
+ that module before the CLI could set the variable. The version now travels as
49
+ an argument, and a test drives the bundled binary to assert it.
50
+
3
51
  ## 0.0.4
4
52
 
5
53
  ### Patch Changes
package/dist/cli.mjs CHANGED
@@ -16,7 +16,7 @@ import { bodyLimit } from "hono/body-limit";
16
16
  import { parseArgs } from "node:util";
17
17
  import { readFile, readdir, stat } from "node:fs/promises";
18
18
  import { spawnSync } from "node:child_process";
19
- //#region ../content-gateway/dist/main-DxfT2qs3.mjs
19
+ //#region ../content-gateway/dist/main-MwJTMEjf.mjs
20
20
  /**
21
21
  * The pool checkout timed out — never retried: under saturation a retry is
22
22
  * a thundering herd aimed at the component already drowning.
@@ -2907,10 +2907,17 @@ async function runHttp(composition) {
2907
2907
  * released `ksor serve` announce 0.0.0 to every client (verified live by
2908
2908
  * probe; review 2026-08-20).
2909
2909
  */
2910
- const GATEWAY_VERSION = process.env["KSOR_GATEWAY_VERSION"] || "0.0.0";
2911
- async function main$1() {
2910
+ /**
2911
+ * Fallback only. The bundling CLI knows the PUBLISHED version and passes it to
2912
+ * `main(version)`; a module-level env read cannot work here, because this
2913
+ * module is evaluated by the CLI's static import BEFORE the CLI's own main()
2914
+ * body could set an env var (ESM evaluation order — the reason the previous
2915
+ * attempt at this shipped inert in 0.0.4).
2916
+ */
2917
+ const GATEWAY_VERSION = "0.0.0";
2918
+ async function main$1(version = GATEWAY_VERSION) {
2912
2919
  try {
2913
- await runHttp(await compose(path.resolve(process.env["KSOR_INSTANCE"] ?? "instance.md"), GATEWAY_VERSION));
2920
+ await runHttp(await compose(path.resolve(process.env["KSOR_INSTANCE"] ?? "instance.md"), version));
2914
2921
  } catch (error) {
2915
2922
  const message = error instanceof Error ? error.message : String(error);
2916
2923
  console.error(`error: ${message}`);
@@ -3043,7 +3050,7 @@ async function runScopedIn(pool, gucs, op, options = {}) {
3043
3050
  throw lastError;
3044
3051
  }
3045
3052
  //#endregion
3046
- //#region ../content/dist/commands-3cajARmF.mjs
3053
+ //#region ../content/dist/commands-D8yo1t8o.mjs
3047
3054
  /**
3048
3055
  * EVAL-LOCKED constants, quarried verbatim from the oracle
3049
3056
  * (sor-agentfactory @ b554f91, config.py) — changing any of these is a
@@ -6148,6 +6155,14 @@ async function schemaCommand(args) {
6148
6155
  if (instance === null) return fail$1(REFUSED, "schema: --apply needs --instance (the instance names the DSN env var; --dim alone names no database)");
6149
6156
  const dsn = resolveDsn(instance);
6150
6157
  if (typeof dsn === "number") return dsn;
6158
+ const already = await withPool(dsn, async (pool) => {
6159
+ const r = await pool.query("SELECT schema_version FROM schema_meta LIMIT 1").catch(() => null);
6160
+ return r === null ? null : String(r.rows[0]?.schema_version ?? "");
6161
+ });
6162
+ if (already !== null) {
6163
+ process.stdout.write(`schema: already applied (schema_meta ${already}) — nothing to do; drop the database to start over\n`);
6164
+ return 0;
6165
+ }
6151
6166
  await withPool(dsn, (pool) => applySchema(pool, dim));
6152
6167
  process.stdout.write(`schema: applied at dim ${dim} (database named by ${instance.dsnEnv})\n`);
6153
6168
  return 0;
@@ -6363,7 +6378,7 @@ function isEnvironmentError(value) {
6363
6378
  }
6364
6379
  //#endregion
6365
6380
  //#region src/init/materialize.ts
6366
- const EMITTED_NAMES = /* @__PURE__ */ new Map([["gitignore", ".gitignore"]]);
6381
+ const EMITTED_NAMES = /* @__PURE__ */ new Map([["gitignore", ".gitignore"], ["env.example", ".env.example"]]);
6367
6382
  const TEXT_EXTENSIONS = /* @__PURE__ */ new Set([
6368
6383
  ".md",
6369
6384
  ".json",
@@ -6674,7 +6689,20 @@ Verbs (dev and build exit 2 until they ship; the rest are implemented):\n init
6674
6689
 
6675
6690
  Exit codes: 1 refused · 2 designed but not implemented · 3 environment
6676
6691
  Docs: node_modules/${pkg.name}/docs · ${pkg.homepage}\n`;
6692
+ /**
6693
+ * Load `./.env` when one exists, so the served rung's variables (the DSN the
6694
+ * instance names, the provider key) live in a file the adopter already
6695
+ * gitignores instead of being exported by hand into every shell. Node does
6696
+ * this natively — no dependency — and a REAL environment variable still wins
6697
+ * over the file, so CI and production overrides behave as they should.
6698
+ */
6699
+ function loadDotEnv() {
6700
+ try {
6701
+ process.loadEnvFile();
6702
+ } catch {}
6703
+ }
6677
6704
  async function main(args) {
6705
+ loadDotEnv();
6678
6706
  if (args.includes("--help") || args.includes("-h")) {
6679
6707
  process.stdout.write(usage);
6680
6708
  return 0;
@@ -6706,8 +6734,7 @@ async function main(args) {
6706
6734
  return exitCodes.refused;
6707
6735
  }
6708
6736
  if (instance !== void 0) process.env["KSOR_INSTANCE"] = instance;
6709
- process.env["KSOR_GATEWAY_VERSION"] = pkg.version;
6710
- await main$1();
6737
+ await main$1(pkg.version);
6711
6738
  return 0;
6712
6739
  }
6713
6740
  if (verb === "ingest" || verb === "schema" || verb === "grant" || verb === "calibrate" || verb === "gc") return runContentCli(args.slice(args.indexOf(verb)));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@panaversity/ksor",
3
- "version": "0.0.4",
3
+ "version": "0.0.6",
4
4
  "description": "Knowledge System of Record — the authoritative, governed source of knowledge that humans and AI agents operate from. Name reserved; implementation in progress.",
5
5
  "keywords": [
6
6
  "abstention",
@@ -52,52 +52,95 @@ pnpm check # the format checker — run before handing off any knowledge c
52
52
  with honest abstention. It is the climbed rung — not required for `pnpm dev`.
53
53
  Stand it up in this order (each step's errors explain how to fix themselves):
54
54
 
55
- 1. **Configure `instance.md`.** Add the serve blocks to the frontmatter
56
- (`pnpm check` accepts them; the kernel validates their values):
55
+ 1. **Configure `instance.md`.** One block is required the name of the
56
+ environment variable holding your DSN (never the DSN itself):
57
57
 
58
58
  ```yaml
59
59
  database:
60
- dsn_env: KSOR_DB_URL # the NAME of the env var holding the DSN — never the DSN itself
61
- embedding:
62
- provider: gemini # default; the seam, not the vendor, is the contract
63
- model: gemini-embedding-001
64
- dim: 1536 # 2000 for the pgvector HNSW index
65
- retrieval:
66
- vector_floor: uncalibrated # see step 6; `uncalibrated` REFUSES every serve until you paste a number
60
+ dsn_env: KSOR_DB_URL
61
+ ```
62
+
63
+ That is enough. `embedding:` is optional and already defaults to
64
+ `provider: gemini`, `model: gemini-embedding-001`, `dim: 1536`; write it out
65
+ only to pin the space explicitly or to change it — and note that model and
66
+ dim are the PERSISTED identity of the embedding space, so changing either
67
+ later means re-embedding the whole corpus. Keep `dim` at or below 2000: the
68
+ pgvector HNSW index refuses more, and `gemini-embedding-001` can emit 3072.
69
+
70
+ Leave `retrieval:` out for now — the gate is off and the server says so.
71
+ Turning it on is step 4, AFTER the record is serving.
72
+
73
+ 2. **Copy `.env.example` to `.env`** and fill it in — `ksor` reads it
74
+ automatically, so nothing needs exporting, and `.env` is already gitignored.
75
+ A real environment variable still wins over the file, so CI and production
76
+ overrides behave normally.
77
+
78
+ ```sh
79
+ cp .env.example .env
80
+ ```
81
+
82
+ Three values matter:
83
+
84
+ - `KSOR_DB_URL` — the Postgres store named by `instance.md`'s `dsn_env`. It
85
+ needs the pgvector extension: `CREATE EXTENSION vector;`
86
+ - `GEMINI_API_KEY` — the embedding provider key.
87
+ - `KSOR_AUTH_DISABLED=1` — **required for a local run.** `ksor serve`
88
+ refuses to boot unauthenticated without it, deliberately, so a server is
89
+ never left open by accident. It binds loopback, where auth off is the
90
+ intended dev shape. A PUBLIC deployment configures the SSO door instead —
91
+ see the comments in `.env.example` and "Serving safely" below.
92
+
93
+ 3. **Bring it up — one command:**
94
+
95
+ ```sh
96
+ pnpm serve # schema → grant → ingest → serve
67
97
  ```
68
98
 
69
- 2. **Provision Postgres** with the `vector` extension (`CREATE EXTENSION vector`),
70
- e.g. a Neon database. Export the DSN under the name `dsn_env` chose, plus the
71
- provider key:
99
+ Every step is re-runnable, so this is also how you **refresh after editing
100
+ `knowledge/`**: an applied schema reports "already applied", an existing
101
+ grant reports "already granted", and ingest builds a fresh generation.
102
+
103
+ **`pnpm serve` is the only command this rung needs.** Run it the first
104
+ time, run it after editing `knowledge/`, run it to bring the server back —
105
+ it is always the right answer, so there is nothing to decide. Every step it
106
+ chains reports the state it found rather than failing: an applied schema
107
+ says "already applied", an existing grant says "already granted", and
108
+ unchanged chunks carry forward by content hash, so a rerun on an untouched
109
+ corpus makes **zero provider calls** (`embedded 0, carried N`).
110
+
111
+ What a rerun does spend is a generation — each one is created and activated,
112
+ and they accumulate. Reap them when you think of it, or on a schedule:
72
113
 
73
114
  ```sh
74
- export KSOR_DB_URL='postgresql://…' # the var instance.md names
75
- export GEMINI_API_KEY='…' # the embedding provider key
115
+ pnpm exec ksor gc --instance instance.md
76
116
  ```
77
117
 
78
- 3. **Apply the schema:** `pnpm schema` (creates tables, indexes, and the
79
- ingest role).
118
+ The individual verbs (`pnpm schema`, `pnpm grant`, `pnpm ingest`,
119
+ `pnpm serve`) exist for pipelines and split duties — a deploy step that
120
+ ingests while a different process serves, or a DBA who holds the credentials
121
+ that authorize ingest. Reach for them when something else runs the steps;
122
+ not as a daily choice.
80
123
 
81
- 4. **Authorize ingest:** `pnpm grant`writes the one row row-level security
82
- requires before any write to this corpus is allowed. Idempotent, and
83
- `pnpm exec ksor grant --instance instance.md --revoke` withdraws it.
124
+ 4. **Turn the abstention gate on deliberately, once it serves.** This is the
125
+ step that makes "not in this corpus" a real answer, and it is measured, never
126
+ guessed:
84
127
 
85
- This is a separate, named act on purpose: applying the schema and
86
- authorizing writes are different decisions, and a schema step that granted
87
- itself write access would make the tool its own authorizer. Apply the schema
88
- and ingest as the SAME Postgres login (the ingest role is granted to whoever
89
- applied the DDL).
128
+ ```sh
129
+ pnpm exec ksor calibrate --instance instance.md
130
+ ```
90
131
 
91
- 5. **Ingest:** `pnpm ingest` embeds `knowledge/` into a fresh generation and
92
- activates it (`--flip`). Safe to re-run (see the generation model below).
132
+ It prints a recommended `vector_floor` for THIS corpus in THIS embedding
133
+ space. Paste the number in and restart:
93
134
 
94
- 6. **Calibrate the abstention floor** (only if `vector_floor: uncalibrated`):
95
- `pnpm exec ksor calibrate --instance instance.md` prints a recommended
96
- `vector_floor` measurement; paste the number into `instance.md`'s `retrieval:`
97
- block and re-run. A corpus that declares no `retrieval:` block serves with the
98
- gate OFF (honest: it will not refuse out-of-corpus questions).
135
+ ```yaml
136
+ retrieval:
137
+ vector_floor: 0.55 # measured by ksor calibrate on <date>
138
+ ```
99
139
 
100
- 7. **Serve:** `pnpm serve`.
140
+ Never copy a floor from another corpus — recalibrate, and record the
141
+ measurement beside the number. Writing `vector_floor: uncalibrated` declares
142
+ the intent to gate WITHOUT a measurement, and every serve refuses until a
143
+ number replaces it; that is the fail-closed posture, not a starting point.
101
144
 
102
145
  ```sh
103
146
  pnpm schema # apply the DDL (once)
@@ -30,17 +30,23 @@ Postgres store (with pgvector) and an embedding provider key, so it is not
30
30
  part of `pnpm dev`. The ordered path is:
31
31
 
32
32
  ```sh
33
- export KSOR_DB_URL='postgresql://…' # the DSN var your instance.md names
34
- export GEMINI_API_KEY='…' # the embedding provider key
35
- pnpm schema # apply the database schema (once)
36
- pnpm grant # authorize ingest for this corpus (once)
37
- pnpm ingest # embed knowledge/ into a generation and activate it
38
- pnpm serve # run the MCP server over the record
33
+ cp .env.example .env # fill in KSOR_DB_URL, GEMINI_API_KEY, KSOR_AUTH_DISABLED=1
34
+ pnpm serve # schema grant → ingest → serve
39
35
  ```
40
36
 
41
- One setup step comes before this adding the `database:`/`embedding:` blocks
42
- to `instance.md` and there is more to know about the generation model and the
43
- fail-closed security posture. `AGENTS.md` "Serving to agents" is the
37
+ `ksor` reads `.env` automatically nothing to export. `KSOR_AUTH_DISABLED=1`
38
+ is required for a local run: serve refuses to boot unauthenticated on purpose,
39
+ so a server is never open by accident.
40
+
41
+ Add one block to `instance.md` first — `database: { dsn_env: KSOR_DB_URL }`,
42
+ the NAME of the variable, never the DSN. That is the whole required config:
43
+ `embedding:` already defaults to Gemini at 1536 dimensions, and leaving
44
+ `retrieval:` out starts you with the abstention gate off and honest about it
45
+ (turn it on afterwards with `ksor calibrate`, once the record is serving).
46
+
47
+ `pnpm serve` is the only command this rung needs — first run, after editing
48
+ `knowledge/`, or just to bring the server back. It re-embeds only what changed,
49
+ so a rerun on an untouched corpus costs no provider calls. `AGENTS.md` → "Serving to agents" is the
44
50
  full runbook; your coding agent reads it first. `pnpm serve` binds loopback
45
51
  with auth off for local use; a public bind fails closed unless auth is
46
52
  configured. Any other operation is `pnpm exec ksor <verb>`.
@@ -67,8 +73,9 @@ different coding agent's way of finding the same working contract.
67
73
  | `.gemini/settings.json` | points Gemini CLI at `AGENTS.md`; Gemini does not read that filename on its own. |
68
74
  | `.github/workflows/validate.yml` | your CI: runs the same checker on every pull request and push to main. |
69
75
  | `.gitattributes` | markdown is checked out byte-stable on every platform, so the same commit hashes the same everywhere. |
70
- | `.gitignore` | keeps build output, `node_modules/`, and `.env*` out of the record's history. |
71
- | `package.json` | the `pnpm dev` / `pnpm build` / `pnpm check` / `pnpm schema` / `pnpm grant` / `pnpm ingest` / `pnpm serve` commands, the pinned `@panaversity/ksor` tool, and the pnpm version this project pins. |
76
+ | `.env.example` | the variables the served rung needs; copy to `.env` (gitignored) and fill in. |
77
+ | `.gitignore` | keeps build output, `node_modules/`, and `.env` out of the record's history. |
78
+ | `package.json` | the two surface commands — `pnpm dev` (the site) and `pnpm serve` (the agent surface: schema → grant → ingest → serve) — plus `pnpm build` / `pnpm check`, the pinned `@panaversity/ksor` tool, and the pnpm version this project pins. |
72
79
  | `pnpm-workspace.yaml` | where the workspace looks for code (`system/site`, plus reserved `system/gateways/*` and `system/packages/*`), and the supply-chain policy for installs. |
73
80
  | `pnpm-lock.yaml` | the exact dependency versions — the reason two machines build the same site. |
74
81
 
@@ -0,0 +1,23 @@
1
+ # Copy to .env — ksor reads it automatically, and .env is gitignored.
2
+ # A real environment variable always wins over this file, so CI and production
3
+ # keep their own values.
4
+
5
+ # The Postgres store, named by instance.md's database.dsn_env.
6
+ # Needs the pgvector extension: CREATE EXTENSION vector;
7
+ KSOR_DB_URL=postgresql://user:password@host:5432/dbname
8
+
9
+ # The embedding provider key. instance.md defaults to gemini-embedding-001.
10
+ GEMINI_API_KEY=
11
+
12
+ # Local development posture. `ksor serve` REFUSES to boot unauthenticated
13
+ # without this — deliberately, so a server is never open by accident. It binds
14
+ # loopback, where auth off is the intended dev shape.
15
+ #
16
+ # For a PUBLIC deployment, delete this line and configure the SSO door instead:
17
+ # KSOR_SSO_URL=https://your-sso.example.com
18
+ # KSOR_MCP_RESOURCE_URL=https://your-host.example.com/mcp
19
+ # KSOR_JWT_ALLOWED_AUDIENCES=https://your-host.example.com/mcp
20
+ # Serving a public bind with auth off additionally requires
21
+ # KSOR_ALLOW_PUBLIC_UNAUTHENTICATED=1, which serves your whole record to anyone
22
+ # who can reach the port.
23
+ KSOR_AUTH_DISABLED=1
@@ -12,5 +12,6 @@ system/site/.staged-knowledge/
12
12
 
13
13
  # secrets never enter the record — system/ is their future home (serve)
14
14
  .env*
15
+ !.env.example
15
16
 
16
17
  .DS_Store
@@ -7,10 +7,10 @@
7
7
  "dev": "pnpm -C system/site dev",
8
8
  "build": "pnpm -C system/site build",
9
9
  "check": "node .agents/skills/format-checker/check.mjs",
10
+ "serve": "pnpm schema && pnpm grant && pnpm ingest && ksor serve",
10
11
  "schema": "ksor schema --instance instance.md --apply",
11
12
  "grant": "ksor grant --instance instance.md",
12
- "ingest": "ksor ingest --instance instance.md --knowledge knowledge --flip",
13
- "serve": "ksor serve"
13
+ "ingest": "ksor ingest --instance instance.md --knowledge knowledge --flip"
14
14
  },
15
15
  "dependencies": {
16
16
  "@panaversity/ksor": "KSOR-STAMP-VERSION"
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://openapi.vercel.sh/vercel.json",
3
3
  "framework": null,
4
- "installCommand": "pnpm install --frozen-lockfile",
4
+ "installCommand": "pnpm install --no-frozen-lockfile",
5
5
  "buildCommand": "pnpm build",
6
6
  "outputDirectory": "system/site/out",
7
7
  "trailingSlash": true