@knolo/cli 6.1.0 → 6.2.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
@@ -12,13 +12,18 @@ the format is still draft and compressed object/event images require a VQF-aware
12
12
  reader.
13
13
 
14
14
  `knolo compose`, `knolo attest`, and `knolo verify-attestation` cite local
15
- Knowledge Images and write an attestation sidecar. `@knolo/cli` `6.1.0`
16
- depends on `@knolo/core` `^6.1.0`. They do not fetch Hub. Verification
15
+ Knowledge Images and write an attestation sidecar. `@knolo/cli` `6.2.0`
16
+ depends on `@knolo/core` `^6.2.0`. They do not fetch Hub. Verification
17
17
  recomputes `compositionRoot` and checks bundled excerpt bytes. A keyring key
18
18
  must fall inside `notBefore`, `notAfter`, and `revokedAt`. `used` means a
19
19
  query or evaluation ran. It does not mean `SATISFIED`, and it does not mean a
20
20
  claim is true or false. Hub `attested: true` is a listing flag.
21
21
 
22
+ `knolo cortex find`, `knolo cortex ask`, `knolo cortex explain`, and
23
+ `knolo cortex verify` run bounded evidence retrieval. The CLI does not call a
24
+ model and does not take an API key. A verified trace matches the image. It
25
+ does not mean the answer is true.
26
+
22
27
  The official CLI for building `.knolo` knowledge packs.
23
28
 
24
29
  It indexes structured content and produces a deterministic, local-first knowledge bundle for use with `@knolo/core`.
@@ -275,9 +280,10 @@ This ensures:
275
280
  ## 🗺 Roadmap
276
281
 
277
282
  The active roadmap is maintained in [`../../docs/ROADMAP.md`](../../docs/ROADMAP.md).
278
- The current release is `6.1.0`. It preserves the V4 CLI, keeps V5 verification,
283
+ The current release is `6.2.0`. It preserves the V4 CLI, keeps V5 verification,
279
284
  migration, diagnostics, and read-only Studio management, and adds offline
280
- composition and attestation.
285
+ composition, attestation, and `knolo cortex`. Cortex verification does not
286
+ mean an answer is true.
281
287
 
282
288
  ---
283
289
 
package/bin/cortex.mjs ADDED
@@ -0,0 +1,125 @@
1
+ /**
2
+ * knolo cortex
3
+ *
4
+ * Bounded evidence retrieval. The CLI does not call a model and does not
5
+ * take an API key. Verification recomputes roots. It does not mean the
6
+ * answer is true.
7
+ */
8
+ import { existsSync, writeFileSync } from 'node:fs';
9
+ import path from 'node:path';
10
+ import { fileURLToPath, pathToFileURL } from 'node:url';
11
+ import { KAR_CLI_MAX_IMAGE_BYTES, KAR_CLI_MAX_TEXT_BYTES, readBounded, readBoundedJson } from './kar-limits.mjs';
12
+
13
+ const USAGE = `Usage:
14
+ knolo cortex find <image> --question "..." [--top-k 100] [--policy policy.json] --out candidates.json
15
+ knolo cortex ask <image> --question "..." [--graph file] [--plan plan.json] [--mode find_relevant] [--policy policy.json] [--created-at text] --out answer.cortex.json
16
+ knolo cortex explain <answer.cortex.json>
17
+ knolo cortex verify --image <image> --answer answer.cortex.json [--graph file] [--question "..."]
18
+
19
+ The CLI does not load a model. Hosts inject a model through createCortexSession.
20
+ Verification checks the trace against the image. It does not mean the answer is true.
21
+ SATISFIED on a KAR reference means evidence requirements were met. It does not mean a claim is proven.`;
22
+
23
+ export async function runCortexCli(args) {
24
+ const sub = args[0];
25
+ if (sub === 'find') return writeTrace(await runAsk(parseFlags(args.slice(1)), 'find_relevant'));
26
+ if (sub === 'ask') return writeTrace(await runAsk(parseFlags(args.slice(1)), undefined));
27
+ if (sub === 'explain') return explain(parseFlags(args.slice(1)));
28
+ if (sub === 'verify') return verify(parseFlags(args.slice(1)));
29
+ throw new Error(`Unknown cortex command.\n${USAGE}`);
30
+ }
31
+
32
+ async function runAsk(flags, forcedMode) {
33
+ const imagePath = flags._[0];
34
+ if (!imagePath) throw new Error(USAGE);
35
+ const question = required(flags, 'question');
36
+ const api = await loadCortex();
37
+ const policy = flags.policy ? readBoundedJson(flags.policy) : {};
38
+ if (flags['top-k'] !== undefined) {
39
+ const topK = Number(flags['top-k']);
40
+ if (!Number.isInteger(topK) || topK < 1) throw new Error('--top-k must be a positive integer.');
41
+ policy.maxCandidatesPerRound = topK;
42
+ }
43
+ const session = await api.createCortexSession({
44
+ image: new Uint8Array(readBounded(imagePath, KAR_CLI_MAX_IMAGE_BYTES)),
45
+ ...(flags.graph ? { graph: readBoundedJson(flags.graph) } : {}),
46
+ ...(flags.plan ? { plan: readBoundedJson(flags.plan) } : {}),
47
+ policy,
48
+ });
49
+ const mode = forcedMode ?? flags.mode ?? 'evidence_answer';
50
+ const trace = await api.askCortex(session, {
51
+ question,
52
+ mode,
53
+ ...(flags['created-at'] ? { createdAt: flags['created-at'] } : {}),
54
+ });
55
+ return { flags, trace };
56
+ }
57
+
58
+ async function writeTrace(result) {
59
+ const json = `${JSON.stringify(result.trace, null, 2)}\n`;
60
+ if (result.flags.out) {
61
+ const out = path.resolve(process.cwd(), result.flags.out);
62
+ if (existsSync(out)) throw new Error(`Refusing to overwrite ${out}`);
63
+ writeFileSync(out, json);
64
+ console.log(`${result.trace.answer.status} ${result.trace.receipt.traceRoot}`);
65
+ return;
66
+ }
67
+ console.log(json);
68
+ }
69
+
70
+ async function explain(flags) {
71
+ const file = flags._[0] ?? flags.answer;
72
+ if (!file) throw new Error(USAGE);
73
+ const api = await loadCortex();
74
+ const trace = readBoundedJson(file);
75
+ console.log(`${JSON.stringify(api.explainCortexAnswer(trace), null, 2)}\n`);
76
+ }
77
+
78
+ async function verify(flags) {
79
+ const api = await loadCortex();
80
+ const answer = required(flags, 'answer');
81
+ const image = required(flags, 'image');
82
+ const trace = readBoundedJson(answer, KAR_CLI_MAX_TEXT_BYTES);
83
+ const verified = await api.verifyCortexAnswer({
84
+ image: new Uint8Array(readBounded(image, KAR_CLI_MAX_IMAGE_BYTES)),
85
+ question: flags.question ?? trace.question,
86
+ result: trace,
87
+ ...(flags.graph ? { graph: readBoundedJson(flags.graph) } : {}),
88
+ });
89
+ if (!verified.ok) throw new Error(`${verified.code}: ${verified.message}`);
90
+ console.log(`ok ${verified.traceRoot}`);
91
+ }
92
+
93
+ async function loadCortex() {
94
+ const candidate = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../core/dist/cortex/index.js');
95
+ const loaded = await import(pathToFileURL(candidate).href);
96
+ if (!loaded.createCortexSession || !loaded.askCortex || !loaded.verifyCortexAnswer) {
97
+ throw new Error('Could not load @knolo/core/cortex. Build packages/core first.');
98
+ }
99
+ return loaded;
100
+ }
101
+
102
+ function required(flags, name) {
103
+ if (!flags[name]) throw new Error(`Missing --${name}.\n${USAGE}`);
104
+ return flags[name];
105
+ }
106
+
107
+ function parseFlags(args) {
108
+ const flags = { _: [] };
109
+ for (let i = 0; i < args.length; i++) {
110
+ const arg = args[i];
111
+ if (arg === '--question') flags.question = args[++i];
112
+ else if (arg === '--graph') flags.graph = args[++i];
113
+ else if (arg === '--plan') flags.plan = args[++i];
114
+ else if (arg === '--mode') flags.mode = args[++i];
115
+ else if (arg === '--policy') flags.policy = args[++i];
116
+ else if (arg === '--out') flags.out = args[++i];
117
+ else if (arg === '--top-k') flags['top-k'] = args[++i];
118
+ else if (arg === '--created-at') flags['created-at'] = args[++i];
119
+ else if (arg === '--image') flags.image = args[++i];
120
+ else if (arg === '--answer') flags.answer = args[++i];
121
+ else if (arg.startsWith('--')) throw new Error(`Unknown flag ${arg}.\n${USAGE}`);
122
+ else flags._.push(arg);
123
+ }
124
+ return flags;
125
+ }
package/bin/knolo.mjs CHANGED
@@ -76,6 +76,7 @@ const SUBCOMMANDS = new Set([
76
76
  'compose',
77
77
  'attest',
78
78
  'verify-attestation',
79
+ 'cortex',
79
80
  ]);
80
81
  const ICP_SUBCOMMANDS = new Set([
81
82
  'init',
@@ -173,6 +174,7 @@ Commands:
173
174
  kar KAR evidence-set retrieval over a Committed Evidence Graph
174
175
  compose Compose Knowledge Images, packs, and attestations offline
175
176
  attest Create or verify a composition attestation
177
+ cortex Bounded evidence retrieval with a verifiable trace
176
178
  icp Scaffold and operate an ICP-native knowledge canister
177
179
 
178
180
  Global options:
@@ -211,6 +213,7 @@ function printCommandHelp(command) {
211
213
  kar: 'Usage: knolo kar <evaluate|verify|explain|inspect|graph|package|produce|domain|dossier|coverage|benchmark|demo|conformance> [options]\n\nKnolo Adversarial Retrieval. Semantics are frozen at kar-1-research-1.\n\n knolo kar evaluate --image knowledge.knolo --graph knowledge.kar.json --plan plan.json --query "..."\n knolo kar verify --image knowledge.knolo --graph knowledge.kar.json --plan plan.json --query "..." --result result.json\n knolo kar explain --result result.json\n knolo kar inspect --image knowledge.knolo --graph knowledge.kar.json [--plan plan.json]\n knolo kar graph build --image knowledge.knolo --source knowledge.ceg.yaml --out knowledge.kar.json\n knolo kar package --image knowledge.knolo --graph knowledge.kar.json --out dist/my-knowledge-kar/\n knolo kar produce rules --image knowledge.knolo --domain domains/contracts-v4 --out proposals.json\n knolo kar domain validate domains/contracts-v4\n knolo kar dossier build --image knowledge.knolo --graph knowledge.kar.json --plan plan.json --query "..." --out dossier/\n knolo kar dossier verify dossier/\n knolo kar coverage build --image knowledge.knolo --graph knowledge.kar.json --plan plan.json --query "..." --out coverage/\n knolo kar coverage verify coverage/\n knolo kar benchmark annotate --packet packet.json --decisions decisions.json --out labels.json --role human --reviewer-id <id>\n knolo kar demo\n knolo kar conformance\n\n--json prints the machine-readable result. verify exits non-zero when the certificate does not match. Abstention is a result and exits 0. graph and package author a Committed Evidence Graph without changing retrieval. produce proposes CEG Source and does not write committed bytes. dossier packages one evaluation and fails closed when committed fields change. coverage reports met requirements, gaps, and plans that did not decide. A gap is not a false claim. benchmark labels gold without showing producer output. demo and conformance run from a knolo-core checkout.',
212
214
  compose: 'Usage: knolo compose <create|verify|excerpt|query> [options]\n\nOffline composition of Knowledge Images, packs, KAR sidecars, Domain Packs, and bounded excerpts.\n\n knolo compose create --members members.json --out composition/ [--host knowledge.knolo] [--availability material|digest] [--policy policy.json]\n knolo compose verify composition/ [--require-material] [--member alias=path]\n knolo compose excerpt --image knowledge.knolo --object <id> --out excerpt.json\n knolo compose query --composition composition/ --eql "FROM chunk SEARCH \\"valve\\"" [--question "..."] [--json]\n\ncompositionRoot does not change a V5 state root or a KAR root. statement used means a query or evaluation ran. It does not mean SATISFIED, and it does not mean a claim is true or false. Hub attested: true is a listing flag and is not an attestation root. The CLI does not fetch Hub.',
213
215
  attest: 'Usage: knolo attest <create|verify> [options]\n\n knolo attest create --composition composition/composition.json --subject composition --statement mount|used|cited --out attestation.json [--sign-key key.json --principal <id> --key-id <id> --issued-at <ms> --expires-at <ms>]\n knolo attest verify attestation.json [--composition composition.json] [--public-key key.json] [--now <ms>] [--content-only]\n\nstatement used means a query or KAR evaluation ran. It does not mean the claim is true or false. Hub attested: true is unrelated. Signing uses the supplied times. The CLI does not read the clock.',
216
+ cortex: 'Usage: knolo cortex <find|ask|explain|verify> [options]\n\nBounded evidence retrieval. The CLI does not call a model.\n\n knolo cortex find <image> --question "..." [--top-k 100] --out candidates.json\n knolo cortex ask <image> --question "..." [--graph file] [--plan plan.json] [--mode find_relevant] --out answer.cortex.json\n knolo cortex explain answer.cortex.json\n knolo cortex verify --image <image> --answer answer.cortex.json [--graph file]\n\nVerification recomputes the trace roots. It does not mean the answer is true. A KAR status of SATISFIED means evidence requirements were met. It does not mean a claim is proven.',
214
217
  'verify-attestation': 'Usage: knolo verify-attestation <attestation.json> [--composition composition.json] [--public-key key.json] [--keyring keyring.json] [--now <ms>] [--content-only]\n\nSame check as knolo attest verify. A bad signature exits 1 even when the content root matches. --content-only checks the content root and ignores the envelope. statement used is not a truth verdict. Hub attested: true is unrelated.',
215
218
  icp: 'Usage: knolo icp <command> [options]',
216
219
  'semantic:index':
@@ -1865,6 +1868,10 @@ async function main() {
1865
1868
  const { runComposeCli } = await import('./compose.mjs');
1866
1869
  return runComposeCli(command, commandArgs);
1867
1870
  }
1871
+ if (command === 'cortex') {
1872
+ const { runCortexCli } = await import('./cortex.mjs');
1873
+ return runCortexCli(commandArgs);
1874
+ }
1868
1875
  if (command === 'init') return await cmdInit();
1869
1876
  if (command === 'add') return await cmdAdd(commandArgs);
1870
1877
  if (command === 'search') return await runHubSearch(commandArgs);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@knolo/cli",
3
- "version": "6.1.0",
3
+ "version": "6.2.0",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "knolo": "bin/knolo.mjs"
@@ -28,11 +28,11 @@
28
28
  },
29
29
  "scripts": {
30
30
  "build": "node -e \"console.log('cli is executable via bin/knolo.mjs')\"",
31
- "test": "npm run build --prefix ../core && node --test test/cli.test.mjs test/kar.test.mjs test/kar-graph.test.mjs test/kar-produce.test.mjs test/hub.integration.test.mjs test/compose.test.mjs",
31
+ "test": "npm run build --prefix ../core && node --test test/cli.test.mjs test/kar.test.mjs test/kar-graph.test.mjs test/kar-produce.test.mjs test/hub.integration.test.mjs test/compose.test.mjs test/cortex.test.mjs",
32
32
  "test:hub": "node --test test/hub.integration.test.mjs"
33
33
  },
34
34
  "dependencies": {
35
- "@knolo/core": "^6.1.0"
35
+ "@knolo/core": "^6.2.0"
36
36
  },
37
37
  "private": false
38
38
  }