@cairnkeep/cli 2.21.0 → 2.22.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.
Files changed (48) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/docs/README.md +2 -0
  3. package/docs/agent-memory-protocol.md +146 -0
  4. package/docs/agents.md +7 -0
  5. package/docs/learning/CURRICULUM-MAP.md +3 -2
  6. package/docs/learning/FEATURE-GUIDE.md +7 -1
  7. package/docs/learning/lessons/L00-why-cairnkeep.md +1 -1
  8. package/docs/learning/lessons/L01-safe-trial.md +1 -1
  9. package/docs/learning/lessons/L02-installation.md +1 -1
  10. package/docs/learning/lessons/L03-first-project.md +1 -1
  11. package/docs/learning/lessons/L16-evaluation.md +23 -0
  12. package/docs/learning/lessons/L19-validated-skill-improvement.md +1 -1
  13. package/docs/learning/lessons/L20-mcp-tool-profiles.md +1 -1
  14. package/docs/learning/lessons/L21-context-packs.md +1 -1
  15. package/docs/learning/lessons/L22-native-windows.md +1 -1
  16. package/docs/learning/lessons/L23-guided-setup.md +1 -1
  17. package/docs/learning/lessons/L24-okf-exchange.md +1 -1
  18. package/docs/learning/lessons/L25-playbooks.md +1 -1
  19. package/docs/learning/lessons/L26-context-intelligence.md +1 -1
  20. package/docs/learning/lessons/L27-ecosystem-evidence.md +1 -1
  21. package/docs/learning/lessons/L28-security-assurance.md +2 -2
  22. package/docs/learning/tracks/evidence-and-evaluation.md +4 -0
  23. package/docs/learning/video-scripts/V16-memory-protocol-audit.md +32 -0
  24. package/docs/operating.md +12 -0
  25. package/docs/privacy-and-data-flow.md +9 -0
  26. package/docs/releasing.md +7 -0
  27. package/docs/research/release-ledger-task.md +36 -0
  28. package/docs/security-assurance.md +8 -0
  29. package/examples/eval/README.md +13 -0
  30. package/examples/eval/bundled-fake.json +1 -1
  31. package/examples/eval/protocol-codex.jsonl +5 -0
  32. package/examples/eval/protocol-narration.json +12 -0
  33. package/examples/eval/protocol-project-search.json +13 -0
  34. package/mcp-memory-server/dist/eval-cli.js +25 -2
  35. package/mcp-memory-server/dist/eval-codex.js +163 -0
  36. package/mcp-memory-server/dist/eval-protocol.js +200 -0
  37. package/mcp-memory-server/package.json +3 -1
  38. package/package.json +1 -1
  39. package/scripts/cairn-cli.mjs +1 -1
  40. package/scripts/completion.sh +9 -4
  41. package/scripts/doctor.sh +20 -4
  42. package/scripts/spikes/verify-release-ledger.mjs +46 -0
  43. package/scripts/test-ci-sharding.sh +33 -0
  44. package/scripts/test-completion.sh +7 -3
  45. package/scripts/test-doctor.sh +29 -11
  46. package/scripts/test-ecosystem-spikes.sh +1 -0
  47. package/scripts/test-package-install.sh +24 -1
  48. package/scripts/windows-platform.mjs +2 -1
@@ -0,0 +1,33 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
5
+ node --input-type=module - "$ROOT" <<'NODE'
6
+ import assert from "node:assert/strict";
7
+ import { readFileSync } from "node:fs";
8
+ import { join } from "node:path";
9
+ import { pathToFileURL } from "node:url";
10
+
11
+ const root = process.argv[2];
12
+ const { buildShardPlan, smokeChecks } = await import(
13
+ pathToFileURL(join(root, "mcp-memory-server", "scripts", "run-smoke-suite.mjs")));
14
+ const plan = buildShardPlan(3);
15
+ const flattened = plan.flatMap(({ checks }) => checks.map(({ name }) => name));
16
+ assert.equal(flattened.length, smokeChecks.length);
17
+ assert.equal(new Set(flattened).size, smokeChecks.length);
18
+ assert.deepEqual([...flattened].sort(), smokeChecks.map(({ name }) => name).sort());
19
+ assert.ok(plan.every(({ checks }) => checks.length > 0));
20
+ const weights = plan.map(({ weight }) => weight);
21
+ assert.ok(Math.max(...weights) - Math.min(...weights) <= 5,
22
+ `native Windows shards are imbalanced: ${weights.join(", ")}`);
23
+
24
+ const pkg = JSON.parse(readFileSync(join(root, "mcp-memory-server", "package.json"), "utf8"));
25
+ assert.match(pkg.scripts["test:smoke"], /run-smoke-suite\.mjs/);
26
+ const workflow = readFileSync(join(root, ".github", "workflows", "ci.yml"), "utf8");
27
+ assert.match(workflow, /name: native-windows \(\$\{\{ matrix\.node \}\}\)/);
28
+ for (const shard of ["1/3", "2/3", "3/3"]) assert.ok(workflow.includes(`shard: "${shard}"`));
29
+ assert.match(workflow, /run-smoke-suite\.mjs --shard \$\{\{ matrix\.shard \}\}/);
30
+ assert.doesNotMatch(workflow.match(/ native-windows:[\s\S]*?\n repository:/)?.[0] ?? "",
31
+ /npm --prefix mcp-memory-server test/);
32
+ console.log(`PASS: ${smokeChecks.length} memory-server contracts split once across balanced Windows shards (${weights.join(", ")})`);
33
+ NODE
@@ -12,8 +12,8 @@ for shell in bash zsh fish; do
12
12
  done
13
13
 
14
14
  capability_ids='memory.write memory.search notes.distill wiki graph security.audit route.check context.explore'
15
- eval_commands='validate run ablate report prune delete'
16
- eval_flags='--task-set --adapter --output --repetitions --seed --json --yes --disable --experiment --older-than-days --dry-run'
15
+ eval_commands='validate run ablate report prune delete protocol'
16
+ eval_flags='--task-set --adapter --output --repetitions --seed --json --yes --disable --experiment --older-than-days --dry-run --trajectory --codex-jsonl --capture-authorized'
17
17
  skill_commands='harvest list show review propose evaluate apply rollback'
18
18
  for shell in bash zsh fish; do
19
19
  grep -q 'capabilities' "$tmp/$shell"
@@ -70,7 +70,7 @@ for shell in bash zsh fish; do
70
70
  done
71
71
 
72
72
  "$ROOT/bin/cairn" help >"$tmp/root-help"
73
- grep -qF 'cairn eval <validate|run|ablate|report|prune|delete>' "$tmp/root-help"
73
+ grep -qF 'cairn eval <validate|run|ablate|report|prune|delete|protocol>' "$tmp/root-help"
74
74
  node "$ROOT/mcp-memory-server/dist/eval-cli.js" --help >"$tmp/eval-help"
75
75
  for command in $eval_commands; do
76
76
  grep -q "cairn eval $command" "$tmp/eval-help"
@@ -152,6 +152,10 @@ for shell in bash zsh fish; do
152
152
  fi
153
153
  done
154
154
  "$ROOT/bin/cairn" completion powershell >"$tmp/powershell"
155
+ grep -qF "'protocol'" "$tmp/powershell"
156
+ grep -qF "'--trajectory'" "$tmp/powershell"
157
+ grep -qF "'--codex-jsonl'" "$tmp/powershell"
158
+ grep -qF "'--capture-authorized'" "$tmp/powershell"
155
159
  if ! grep -qE "['\"]setup['\"]" "$tmp/powershell"; then
156
160
  completion_surface_complete=false
157
161
  fi
@@ -92,13 +92,15 @@ const server = createServer((request, response) => {
92
92
  request.on("end", () => {
93
93
  let body = {};
94
94
  try { body = JSON.parse(Buffer.concat(chunks).toString("utf8")); } catch {}
95
- const valid = request.url === "/v1/embeddings"
96
- && request.headers.authorization === "Bearer test-key"
97
- && body.model === "test-embedding";
98
- response.writeHead(valid ? 200 : 404, { "Content-Type": "application/json" });
99
- response.end(JSON.stringify(valid
95
+ const routeValid = request.url === "/v1/embeddings";
96
+ const keyValid = request.headers.authorization === "Bearer test-key";
97
+ const modelValid = body.model === "test-embedding";
98
+ const unauthorized = body.model === "unauthorized-model";
99
+ const status = routeValid && keyValid && modelValid ? 200 : unauthorized ? 403 : 404;
100
+ response.writeHead(status, { "Content-Type": "application/json" });
101
+ response.end(JSON.stringify(status === 200
100
102
  ? { data: [{ index: 0, embedding: [1, 0] }] }
101
- : { error: "invalid request" }));
103
+ : { message: "private upstream detail must not leak", request_id: "private-request-id" }));
102
104
  });
103
105
  });
104
106
  server.listen(0, "127.0.0.1", () => {
@@ -122,17 +124,33 @@ EOF
122
124
  grep -q "\[PASS\] embedding endpoint accepted model test-embedding" "$tmp/out3" ||
123
125
  fail "expected a functional embedding PASS line"
124
126
 
125
- proj4="$tmp/embedding-wrong-model"; mkdir -p "$proj4/.ai"
127
+ proj4="$tmp/embedding-unauthorized"; mkdir -p "$proj4/.ai"
126
128
  cat > "$proj4/.ai/.env" <<EOF
127
129
  CAIRN_MEMORY_EMBEDDING_URL=http://127.0.0.1:$port/v1
128
- CAIRN_MEMORY_EMBEDDING_MODEL=missing-model
130
+ CAIRN_MEMORY_EMBEDDING_MODEL=unauthorized-model
129
131
  CAIRN_LLM_API_KEY=test-key
130
132
  EOF
131
133
  if ( cd "$proj4" && "$doctor" ) >"$tmp/out4" 2>&1; then
132
- fail "doctor should reject a missing embedding model:\n$(cat "$tmp/out4")"
134
+ fail "doctor should reject an unauthorized embedding request:\n$(cat "$tmp/out4")"
135
+ fi
136
+ grep -q "\[FAIL\] embedding authorization rejected (HTTP 403) for model unauthorized-model" "$tmp/out4" ||
137
+ fail "expected a sanitized embedding authorization diagnostic"
138
+ ! grep -q "private upstream detail\|private-request-id" "$tmp/out4" ||
139
+ fail "embedding diagnostic leaked an upstream body or request id"
140
+
141
+ proj5="$tmp/embedding-missing-model"; mkdir -p "$proj5/.ai"
142
+ cat > "$proj5/.ai/.env" <<EOF
143
+ CAIRN_MEMORY_EMBEDDING_URL=http://127.0.0.1:$port/v1
144
+ CAIRN_MEMORY_EMBEDDING_MODEL=missing-model
145
+ CAIRN_LLM_API_KEY=test-key
146
+ EOF
147
+ if ( cd "$proj5" && "$doctor" ) >"$tmp/out5" 2>&1; then
148
+ fail "doctor should reject a missing embedding model:\n$(cat "$tmp/out5")"
133
149
  fi
134
- grep -q "\[FAIL\] embedding request failed for model missing-model" "$tmp/out4" ||
135
- fail "expected a functional embedding FAIL line"
150
+ grep -q "\[FAIL\] embedding endpoint or model not found (HTTP 404) for model missing-model" "$tmp/out5" ||
151
+ fail "expected a sanitized missing embedding model diagnostic"
152
+ ! grep -q "private upstream detail\|private-request-id" "$tmp/out5" ||
153
+ fail "missing-model diagnostic leaked an upstream body or request id"
136
154
  fi
137
155
 
138
156
  # 5. Artifact doctor passes a valid store, repairs only derived state, and
@@ -6,6 +6,7 @@ ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
6
6
  for script in \
7
7
  run-memfork-evaluation.mjs \
8
8
  run-clm-pi-evaluation.mjs \
9
+ verify-release-ledger.mjs \
9
10
  run-ollaya-decision-evaluation.mjs
10
11
  do
11
12
  node --check "$ROOT/scripts/spikes/$script"
@@ -76,6 +76,21 @@ cmp -s "$tmp/source-eval-disabled.json" "$tmp/installed-eval-disabled.json" || \
76
76
  fail "source and installed disabled eval identity differ"
77
77
  [[ ! -e "$tmp/installed-unwritten-output" ]] || fail "installed disabled eval created output"
78
78
 
79
+ CAIRN_EVAL=1 cairn eval protocol \
80
+ --trajectory "$installed_root/examples/eval/protocol-project-search.json" --json >"$tmp/protocol-pass.json"
81
+ node -e 'const v=require(process.argv[1]);if(v.status!=="pass"||v.evidence_scope!=="local-trajectory-observation")process.exit(1)' "$tmp/protocol-pass.json" \
82
+ || fail "installed memory protocol positive control failed"
83
+ set +e
84
+ CAIRN_EVAL=1 cairn eval protocol \
85
+ --trajectory "$installed_root/examples/eval/protocol-narration.json" --json >"$tmp/protocol-fail.json"
86
+ protocol_status=$?
87
+ set -e
88
+ [[ "$protocol_status" -eq 1 ]] || fail "installed memory protocol counted narration as evidence"
89
+ CAIRN_EVAL=1 cairn eval protocol \
90
+ --codex-jsonl "$installed_root/examples/eval/protocol-codex.jsonl" --json >"$tmp/protocol-codex.json"
91
+ node -e 'const v=require(process.argv[1]);if(v.status!=="pass"||v.harness!=="codex")process.exit(1)' "$tmp/protocol-codex.json" \
92
+ || fail "installed native Codex protocol control failed"
93
+
79
94
  for required in \
80
95
  schemas/capability-contract.schema.json \
81
96
  schemas/capability-callback.schema.json \
@@ -159,11 +174,16 @@ for required in \
159
174
  mcp-memory-server/dist/eval-report.js \
160
175
  mcp-memory-server/dist/eval-runner.js \
161
176
  mcp-memory-server/dist/eval-cli.js \
177
+ mcp-memory-server/dist/eval-protocol.js \
178
+ mcp-memory-server/dist/eval-codex.js \
162
179
  scripts/fake-eval-adapter.mjs \
163
180
  examples/eval/task-set.json \
164
181
  examples/eval/bundled-fake.json \
165
182
  examples/eval/adapter.json \
166
183
  examples/eval/ecosystem-decisions.json \
184
+ examples/eval/protocol-project-search.json \
185
+ examples/eval/protocol-narration.json \
186
+ examples/eval/protocol-codex.jsonl \
167
187
  examples/eval/README.md
168
188
  do
169
189
  [[ -f "$installed_root/$required" ]] || fail "npm tarball omitted $required"
@@ -183,13 +203,16 @@ for asset in \
183
203
  examples/eval/bundled-fake.json \
184
204
  examples/eval/adapter.json \
185
205
  examples/eval/ecosystem-decisions.json \
206
+ examples/eval/protocol-project-search.json \
207
+ examples/eval/protocol-narration.json \
208
+ examples/eval/protocol-codex.jsonl \
186
209
  examples/eval/README.md
187
210
  do
188
211
  [[ ! -x "$installed_root/$asset" ]] || \
189
212
  fail "installed public eval data asset is unexpectedly executable: $asset"
190
213
  done
191
214
  eval_assets=$(cd "$installed_root/examples/eval" && find . -type f -print | LC_ALL=C sort)
192
- [[ "$eval_assets" == $'./README.md\n./adapter.json\n./bundled-fake.json\n./ecosystem-decisions.json\n./task-set.json' ]] || \
215
+ [[ "$eval_assets" == $'./README.md\n./adapter.json\n./bundled-fake.json\n./ecosystem-decisions.json\n./protocol-codex.jsonl\n./protocol-narration.json\n./protocol-project-search.json\n./task-set.json' ]] || \
193
216
  fail "installed eval examples contain an unexpected path"
194
217
 
195
218
  node - "$installed_root/examples/eval/adapter.json" <<'NODE'
@@ -736,7 +736,8 @@ export function powershellCompletion() {
736
736
  $pack = 'init','lock','validate','install','import-okf','validate-okf','export-okf','list','show','remove','enable','disable','update','skills','approve-skill','revoke-skill','doctor','--repair','--project','--project-id','--json'
737
737
  $proposals = 'create','list','show','apply','doctor','--session','--scope','--model','--category','--project','--json'
738
738
  $security = 'doctor','--project','--json'
739
- $candidates = if ($commandAst.ToString() -match '^\\s*cairn\\s+setup(?:\\s|$)') { $setup } elseif ($commandAst.ToString() -match '^\\s*cairn\\s+playbook(?:\\s|$)') { $playbook } elseif ($commandAst.ToString() -match '^\\s*cairn\\s+pack(?:\\s|$)') { $pack } elseif ($commandAst.ToString() -match '^\\s*cairn\\s+proposals(?:\\s|$)') { $proposals } elseif ($commandAst.ToString() -match '^\\s*cairn\\s+security(?:\\s|$)') { $security } else { $commands }
739
+ $evaluation = 'validate','run','ablate','report','prune','delete','protocol','--task-set','--adapter','--output','--repetitions','--seed','--yes','--json','--disable','--experiment','--older-than-days','--dry-run','--trajectory','--codex-jsonl','--capture-authorized'
740
+ $candidates = if ($commandAst.ToString() -match '^\\s*cairn\\s+setup(?:\\s|$)') { $setup } elseif ($commandAst.ToString() -match '^\\s*cairn\\s+playbook(?:\\s|$)') { $playbook } elseif ($commandAst.ToString() -match '^\\s*cairn\\s+pack(?:\\s|$)') { $pack } elseif ($commandAst.ToString() -match '^\\s*cairn\\s+proposals(?:\\s|$)') { $proposals } elseif ($commandAst.ToString() -match '^\\s*cairn\\s+security(?:\\s|$)') { $security } elseif ($commandAst.ToString() -match '^\\s*cairn\\s+eval(?:\\s|$)') { $evaluation } else { $commands }
740
741
  $candidates | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
741
742
  [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
742
743
  }