@cairnkeep/cli 2.19.1 → 2.20.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 +21 -1
- package/docs/README.md +3 -0
- package/docs/ecosystem.md +33 -0
- package/docs/harness-compatibility.md +1 -1
- package/docs/learning/CURRICULUM-MAP.md +4 -2
- package/docs/learning/FEATURE-GUIDE.md +15 -1
- package/docs/learning/README.md +1 -0
- 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/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/L27-ecosystem-evidence.md +93 -0
- package/docs/learning/video-scripts/V27-ecosystem-evidence.md +80 -0
- package/docs/operating.md +10 -4
- package/docs/privacy-and-data-flow.md +13 -2
- package/docs/research/ecosystem-evidence-2026-10.md +113 -0
- package/examples/eval/README.md +7 -0
- package/examples/eval/bundled-fake.json +1 -1
- package/examples/eval/ecosystem-decisions.json +42 -0
- package/mcp-memory-server/dist/compaction-normalize.js +37 -6
- package/mcp-memory-server/scripts/fixtures/compaction-opencode-2.0.24.json +40 -0
- package/opencode/plugins/memory-capture.ts +354 -120
- package/opencode/plugins/memory-recall.ts +176 -57
- package/opencode/plugins/memory-wakeup.ts +147 -73
- package/package.json +1 -1
- package/scripts/lib/opencode-v2-plugin-harness.mjs +156 -0
- package/scripts/lib/trajectory-opencode-plugin-harness.mjs +6 -2
- package/scripts/spikes/run-clm-pi-evaluation.mjs +212 -0
- package/scripts/spikes/run-memfork-evaluation.mjs +153 -0
- package/scripts/spikes/run-ollaya-decision-evaluation.mjs +108 -0
- package/scripts/test-compaction-hooks.sh +6 -2
- package/scripts/test-ecosystem-spikes.sh +36 -0
- package/scripts/test-opencode-v2-plugins.sh +76 -0
- package/scripts/test-package-install.sh +9 -2
|
@@ -259,7 +259,10 @@ const client = { session: {
|
|
|
259
259
|
messages: async (input) => { calls.push(["messages", input]); return { data: envelope.messages } },
|
|
260
260
|
} }
|
|
261
261
|
const capture = await import(`${pathToFileURL(capturePath).href}?capture=${Date.now()}`)
|
|
262
|
-
|
|
262
|
+
// Dual entrypoint (2026-09-08): named export (legacy) or default.server (V1/V2
|
|
263
|
+
// dual module); both resolve to the same V1 implementation this harness drives.
|
|
264
|
+
const captureFactory = capture.MemoryCapturePlugin ?? capture.default?.server
|
|
265
|
+
const plugin = await captureFactory({ client, directory: repo })
|
|
263
266
|
await plugin.event({ event })
|
|
264
267
|
if (mode === "disabled") {
|
|
265
268
|
assert.deepEqual(calls, [])
|
|
@@ -282,7 +285,8 @@ const dollar = (...args) => {
|
|
|
282
285
|
return { quiet() { return this }, nothrow: async () => ({ stdout: "" }) }
|
|
283
286
|
}
|
|
284
287
|
const wakeup = await import(`${pathToFileURL(wakeupPath).href}?wakeup=${Date.now()}`)
|
|
285
|
-
const
|
|
288
|
+
const wakeupFactory = wakeup.MemoryWakeupPlugin ?? wakeup.default?.server
|
|
289
|
+
const wakeupPlugin = await wakeupFactory({ $: dollar, directory: repo })
|
|
286
290
|
const output = { system: [] }
|
|
287
291
|
await wakeupPlugin["experimental.chat.system.transform"]({ sessionID: envelope.session.id }, output)
|
|
288
292
|
if (mode === "disabled") {
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
|
|
5
|
+
|
|
6
|
+
for script in \
|
|
7
|
+
run-memfork-evaluation.mjs \
|
|
8
|
+
run-clm-pi-evaluation.mjs \
|
|
9
|
+
run-ollaya-decision-evaluation.mjs
|
|
10
|
+
do
|
|
11
|
+
node --check "$ROOT/scripts/spikes/$script"
|
|
12
|
+
done
|
|
13
|
+
|
|
14
|
+
node - "$ROOT/examples/eval/ecosystem-decisions.json" <<'NODE'
|
|
15
|
+
const fs = require("node:fs")
|
|
16
|
+
const data = JSON.parse(fs.readFileSync(process.argv[2], "utf8"))
|
|
17
|
+
if (data.schema_version !== 1 || data.id !== "cairnkeep-policy-decisions-v1") process.exit(1)
|
|
18
|
+
if (JSON.stringify(data.labels) !== JSON.stringify(["observe", "approval", "deny", "none"])) process.exit(1)
|
|
19
|
+
if (!Array.isArray(data.cases) || data.cases.length !== 32) process.exit(1)
|
|
20
|
+
if (new Set(data.cases.map(({ id }) => id)).size !== data.cases.length) process.exit(1)
|
|
21
|
+
const counts = Object.fromEntries(data.labels.map((label) => [label, 0]))
|
|
22
|
+
for (const item of data.cases) {
|
|
23
|
+
const { relevant_context, read_only, mutates_state, forbidden } = item.signals ?? {}
|
|
24
|
+
if (![relevant_context, read_only, mutates_state, forbidden].every((value) => typeof value === "boolean")) process.exit(1)
|
|
25
|
+
const expected = forbidden ? "deny" : mutates_state ? "approval" : relevant_context && read_only ? "observe" : "none"
|
|
26
|
+
if (item.expected !== expected) process.exit(1)
|
|
27
|
+
counts[item.expected] += 1
|
|
28
|
+
}
|
|
29
|
+
if (!Object.values(counts).every((count) => count === 8)) process.exit(1)
|
|
30
|
+
NODE
|
|
31
|
+
|
|
32
|
+
grep -qF 'MemFork | release `v0.3.0`' "$ROOT/docs/research/ecosystem-evidence-2026-10.md"
|
|
33
|
+
grep -qF '| Deterministic policy | 32/32 | 100% |' "$ROOT/docs/research/ecosystem-evidence-2026-10.md"
|
|
34
|
+
grep -qF '| Cairnkeep + pi-clm | 15/15 | 51,530 | $0.0543623 |' "$ROOT/docs/research/ecosystem-evidence-2026-10.md"
|
|
35
|
+
|
|
36
|
+
echo "PASS: ecosystem spike runners, frozen decision data, and evidence contract"
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
|
|
5
|
+
tmp=$(mktemp -d)
|
|
6
|
+
trap 'rm -rf "$tmp"' EXIT
|
|
7
|
+
|
|
8
|
+
render() {
|
|
9
|
+
sed "s|@@INFRA_ROOT@@|$ROOT|g" "$1" > "$2"
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
mkdir -p "$tmp/project" "$tmp/rendered"
|
|
13
|
+
render "$ROOT/opencode/plugins/memory-capture.ts" "$tmp/rendered/memory-capture.ts"
|
|
14
|
+
render "$ROOT/opencode/plugins/memory-recall.ts" "$tmp/rendered/memory-recall.ts"
|
|
15
|
+
render "$ROOT/opencode/plugins/memory-wakeup.ts" "$tmp/rendered/memory-wakeup.ts"
|
|
16
|
+
|
|
17
|
+
CAIRN_TRAJECTORY_CAPTURE=1 CAIRN_COMPACTION_CAPTURE=1 node --experimental-strip-types \
|
|
18
|
+
"$ROOT/scripts/lib/opencode-v2-plugin-harness.mjs" \
|
|
19
|
+
"$tmp/rendered/memory-capture.ts" \
|
|
20
|
+
"$tmp/rendered/memory-recall.ts" \
|
|
21
|
+
"$tmp/rendered/memory-wakeup.ts" \
|
|
22
|
+
"$tmp/project" \
|
|
23
|
+
"$ROOT/mcp-memory-server/dist/trajectory-cli.js" \
|
|
24
|
+
"$ROOT/mcp-memory-server/dist/artifact-cli.js"
|
|
25
|
+
|
|
26
|
+
# When a V2 binary is available, also exercise the real loader in a sterile
|
|
27
|
+
# config/data tree. CI remains deterministic without installing an unrelated
|
|
28
|
+
# harness package; release workstations run this second gate automatically.
|
|
29
|
+
if ! command -v opencode >/dev/null 2>&1; then
|
|
30
|
+
echo "SKIP: live OpenCode V2 loader (opencode not installed)"
|
|
31
|
+
exit 0
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
version=$(opencode --version 2>/dev/null || true)
|
|
35
|
+
major=$(printf '%s\n' "$version" | sed -nE 's/.*v([0-9]+).*/\1/p')
|
|
36
|
+
if [[ -z "$major" || "$major" -lt 2 ]]; then
|
|
37
|
+
echo "SKIP: live OpenCode V2 loader ($version)"
|
|
38
|
+
exit 0
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
git -C "$tmp/project" init -q
|
|
42
|
+
"$ROOT/scripts/sync-opencode-plugin-assets.sh" --apply --live-root "$tmp/config" >/dev/null
|
|
43
|
+
|
|
44
|
+
set +e
|
|
45
|
+
(
|
|
46
|
+
cd "$tmp/project"
|
|
47
|
+
XDG_DATA_HOME="$tmp/data" \
|
|
48
|
+
XDG_CACHE_HOME="$tmp/cache" \
|
|
49
|
+
XDG_STATE_HOME="$tmp/state" \
|
|
50
|
+
OPENCODE_CONFIG_DIR="$tmp/config" \
|
|
51
|
+
TERM=dumb \
|
|
52
|
+
timeout 5 opencode --standalone --print-logs --log-level debug
|
|
53
|
+
) >"$tmp/live.out" 2>"$tmp/live.err"
|
|
54
|
+
live_status=$?
|
|
55
|
+
set -e
|
|
56
|
+
|
|
57
|
+
if [[ "$live_status" -ne 0 && "$live_status" -ne 124 ]]; then
|
|
58
|
+
echo "FAIL: live OpenCode V2 loader exited with $live_status" >&2
|
|
59
|
+
sed -n '1,160p' "$tmp/live.err" >&2
|
|
60
|
+
exit 1
|
|
61
|
+
fi
|
|
62
|
+
|
|
63
|
+
for plugin in memory-capture.ts memory-recall.ts memory-wakeup.ts; do
|
|
64
|
+
grep -q "loading plugin.*$plugin" "$tmp/live.err" || {
|
|
65
|
+
echo "FAIL: live OpenCode V2 did not load $plugin" >&2
|
|
66
|
+
exit 1
|
|
67
|
+
}
|
|
68
|
+
done
|
|
69
|
+
|
|
70
|
+
if grep -Eq 'SchemaError|Missing key at \["default"\]|failed to load plugin' "$tmp/live.err"; then
|
|
71
|
+
echo "FAIL: live OpenCode V2 rejected a Cairnkeep plugin" >&2
|
|
72
|
+
sed -n '1,200p' "$tmp/live.err" >&2
|
|
73
|
+
exit 1
|
|
74
|
+
fi
|
|
75
|
+
|
|
76
|
+
echo "PASS: live OpenCode $version loaded all Cairnkeep V2 plugins"
|
|
@@ -163,12 +163,17 @@ for required in \
|
|
|
163
163
|
examples/eval/task-set.json \
|
|
164
164
|
examples/eval/bundled-fake.json \
|
|
165
165
|
examples/eval/adapter.json \
|
|
166
|
+
examples/eval/ecosystem-decisions.json \
|
|
166
167
|
examples/eval/README.md
|
|
167
168
|
do
|
|
168
169
|
[[ -f "$installed_root/$required" ]] || fail "npm tarball omitted $required"
|
|
169
170
|
done
|
|
170
171
|
[[ -x "$installed_root/scripts/fake-eval-adapter.mjs" ]] || \
|
|
171
172
|
fail "installed fake adapter is not executable"
|
|
173
|
+
for runner in run-memfork-evaluation.mjs run-clm-pi-evaluation.mjs run-ollaya-decision-evaluation.mjs; do
|
|
174
|
+
[[ -x "$installed_root/scripts/spikes/$runner" ]] || \
|
|
175
|
+
fail "installed ecosystem runner is missing or not executable: $runner"
|
|
176
|
+
done
|
|
172
177
|
for asset in \
|
|
173
178
|
schemas/eval-task-set.schema.json \
|
|
174
179
|
schemas/eval-adapter.schema.json \
|
|
@@ -177,13 +182,14 @@ for asset in \
|
|
|
177
182
|
examples/eval/task-set.json \
|
|
178
183
|
examples/eval/bundled-fake.json \
|
|
179
184
|
examples/eval/adapter.json \
|
|
185
|
+
examples/eval/ecosystem-decisions.json \
|
|
180
186
|
examples/eval/README.md
|
|
181
187
|
do
|
|
182
188
|
[[ ! -x "$installed_root/$asset" ]] || \
|
|
183
189
|
fail "installed public eval data asset is unexpectedly executable: $asset"
|
|
184
190
|
done
|
|
185
191
|
eval_assets=$(cd "$installed_root/examples/eval" && find . -type f -print | LC_ALL=C sort)
|
|
186
|
-
[[ "$eval_assets" == $'./README.md\n./adapter.json\n./bundled-fake.json\n./task-set.json' ]] || \
|
|
192
|
+
[[ "$eval_assets" == $'./README.md\n./adapter.json\n./bundled-fake.json\n./ecosystem-decisions.json\n./task-set.json' ]] || \
|
|
187
193
|
fail "installed eval examples contain an unexpected path"
|
|
188
194
|
|
|
189
195
|
node - "$installed_root/examples/eval/adapter.json" <<'NODE'
|
|
@@ -336,7 +342,8 @@ for fixture in \
|
|
|
336
342
|
compaction-claude-code-2.1.219.json \
|
|
337
343
|
compaction-claude-code-2.1.220.json \
|
|
338
344
|
compaction-opencode-1.17.20-event.json \
|
|
339
|
-
compaction-opencode-1.17.20-messages.json
|
|
345
|
+
compaction-opencode-1.17.20-messages.json \
|
|
346
|
+
compaction-opencode-2.0.24.json
|
|
340
347
|
do
|
|
341
348
|
[[ -f "$installed_root/mcp-memory-server/scripts/fixtures/$fixture" ]] || \
|
|
342
349
|
fail "npm tarball omitted versioned fixture $fixture"
|