@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.
- package/CHANGELOG.md +34 -0
- package/docs/README.md +2 -0
- package/docs/agent-memory-protocol.md +146 -0
- package/docs/agents.md +7 -0
- package/docs/learning/CURRICULUM-MAP.md +3 -2
- package/docs/learning/FEATURE-GUIDE.md +7 -1
- package/docs/learning/lessons/L00-why-cairnkeep.md +1 -1
- package/docs/learning/lessons/L01-safe-trial.md +1 -1
- package/docs/learning/lessons/L02-installation.md +1 -1
- package/docs/learning/lessons/L03-first-project.md +1 -1
- package/docs/learning/lessons/L16-evaluation.md +23 -0
- package/docs/learning/lessons/L19-validated-skill-improvement.md +1 -1
- package/docs/learning/lessons/L20-mcp-tool-profiles.md +1 -1
- package/docs/learning/lessons/L21-context-packs.md +1 -1
- package/docs/learning/lessons/L22-native-windows.md +1 -1
- package/docs/learning/lessons/L23-guided-setup.md +1 -1
- package/docs/learning/lessons/L24-okf-exchange.md +1 -1
- package/docs/learning/lessons/L25-playbooks.md +1 -1
- package/docs/learning/lessons/L26-context-intelligence.md +1 -1
- package/docs/learning/lessons/L27-ecosystem-evidence.md +1 -1
- package/docs/learning/lessons/L28-security-assurance.md +2 -2
- package/docs/learning/tracks/evidence-and-evaluation.md +4 -0
- package/docs/learning/video-scripts/V16-memory-protocol-audit.md +32 -0
- package/docs/operating.md +12 -0
- package/docs/privacy-and-data-flow.md +9 -0
- package/docs/releasing.md +7 -0
- package/docs/research/release-ledger-task.md +36 -0
- package/docs/security-assurance.md +8 -0
- package/examples/eval/README.md +13 -0
- package/examples/eval/bundled-fake.json +1 -1
- package/examples/eval/protocol-codex.jsonl +5 -0
- package/examples/eval/protocol-narration.json +12 -0
- package/examples/eval/protocol-project-search.json +13 -0
- package/mcp-memory-server/dist/eval-cli.js +25 -2
- package/mcp-memory-server/dist/eval-codex.js +163 -0
- package/mcp-memory-server/dist/eval-protocol.js +200 -0
- package/mcp-memory-server/package.json +3 -1
- package/package.json +1 -1
- package/scripts/cairn-cli.mjs +1 -1
- package/scripts/completion.sh +9 -4
- package/scripts/doctor.sh +20 -4
- package/scripts/spikes/verify-release-ledger.mjs +46 -0
- package/scripts/test-ci-sharding.sh +33 -0
- package/scripts/test-completion.sh +7 -3
- package/scripts/test-doctor.sh +29 -11
- package/scripts/test-ecosystem-spikes.sh +1 -0
- package/scripts/test-package-install.sh +24 -1
- 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
|
package/scripts/test-doctor.sh
CHANGED
|
@@ -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
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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
|
-
: {
|
|
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-
|
|
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=
|
|
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
|
|
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
|
|
135
|
-
fail "expected a
|
|
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
|
|
@@ -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
|
-
$
|
|
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
|
}
|