sphica 0.6.32 → 0.6.34
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/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/README.md +1 -1
- package/dist/capture.js +271 -61
- package/dist/cli.js +338 -62
- package/dist/deliver.js +200 -62
- package/dist/mcp-record.js +203 -65
- package/package.json +1 -1
package/dist/mcp-record.js
CHANGED
|
@@ -46925,6 +46925,8 @@ var spoolDir = () => path6.join(sphicaHome(), "spool");
|
|
|
46925
46925
|
var stateFile = () => path6.join(sphicaHome(), "capture.json");
|
|
46926
46926
|
var rejectedDir = () => path6.join(spoolDir(), "rejected");
|
|
46927
46927
|
var unregisteredDir = () => path6.join(spoolDir(), "unregistered");
|
|
46928
|
+
var callsDir = () => path6.join(spoolDir(), "calls");
|
|
46929
|
+
var callsRejectedDir = () => path6.join(callsDir(), "rejected");
|
|
46928
46930
|
var HOLD_DAYS = 30;
|
|
46929
46931
|
var HOLD_MAX = 1000;
|
|
46930
46932
|
var baselineDir = () => path6.join(sphicaHome(), "worktree");
|
|
@@ -46973,7 +46975,7 @@ ${z2}`,
|
|
|
46973
46975
|
originalBytes: all
|
|
46974
46976
|
};
|
|
46975
46977
|
}
|
|
46976
|
-
function prune(held) {
|
|
46978
|
+
function prune(held, removed = () => {}) {
|
|
46977
46979
|
let files;
|
|
46978
46980
|
try {
|
|
46979
46981
|
files = fs6.readdirSync(held).filter((f) => f.endsWith(".json") && !f.startsWith(".")).sort();
|
|
@@ -46983,16 +46985,21 @@ function prune(held) {
|
|
|
46983
46985
|
const cutoff = Date.now() - HOLD_DAYS * 24 * 60 * 60 * 1000;
|
|
46984
46986
|
const stale = files.filter((f) => Number(f.split("-")[0]) < cutoff);
|
|
46985
46987
|
const over = files.slice(0, Math.max(0, files.length - HOLD_MAX));
|
|
46986
|
-
for (const f of new Set([...stale, ...over]))
|
|
46988
|
+
for (const f of new Set([...stale, ...over])) {
|
|
46987
46989
|
fs6.rmSync(path6.join(held, f), { force: true });
|
|
46990
|
+
removed();
|
|
46991
|
+
}
|
|
46988
46992
|
}
|
|
46989
|
-
function
|
|
46990
|
-
const dir = spoolDir();
|
|
46993
|
+
function put(dir, record4) {
|
|
46991
46994
|
fs6.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
46992
46995
|
const name = `${Date.now()}-${process.pid}-${Math.random().toString(36).slice(2, 10)}.json`;
|
|
46993
46996
|
const tmp = path6.join(dir, `.${name}`);
|
|
46994
46997
|
fs6.writeFileSync(tmp, JSON.stringify(record4), { mode: 384 });
|
|
46995
46998
|
fs6.renameSync(tmp, path6.join(dir, name));
|
|
46999
|
+
return path6.join(dir, name);
|
|
47000
|
+
}
|
|
47001
|
+
function spool(record4) {
|
|
47002
|
+
put(spoolDir(), record4);
|
|
46996
47003
|
}
|
|
46997
47004
|
var branchOf = (root) => {
|
|
46998
47005
|
try {
|
|
@@ -47047,9 +47054,11 @@ function captureNotice(file2 = dbFile()) {
|
|
|
47047
47054
|
if (!fs6.existsSync(file2))
|
|
47048
47055
|
return panel("sphica: no database, so conversations are not recorded", [file2], "Create it with sphica init");
|
|
47049
47056
|
const s = readState();
|
|
47057
|
+
if (s.unreadable)
|
|
47058
|
+
return panel("sphica: cannot read the recording queue", [spoolDir(), s.unreadable], "Check with sphica doctor");
|
|
47050
47059
|
if (s.stuck)
|
|
47051
47060
|
return panel("sphica: cannot send recordings", [`${s.pending} pending / failed: ${plain2(s.stuck.slice(0, 120))}`], "Check with sphica doctor");
|
|
47052
|
-
if (s.rejected
|
|
47061
|
+
if (s.rejected)
|
|
47053
47062
|
return panel(`sphica: the database rejected ${plural2(s.rejected, "record")}`, [rejectedDir(), `Move them back to ${spoolDir()} to resend`], "Fix them first, then check with sphica doctor");
|
|
47054
47063
|
return null;
|
|
47055
47064
|
}
|
|
@@ -47240,38 +47249,49 @@ function onHook(host, input2) {
|
|
|
47240
47249
|
}
|
|
47241
47250
|
return { flush: false };
|
|
47242
47251
|
}
|
|
47243
|
-
function
|
|
47244
|
-
try {
|
|
47245
|
-
fs6.writeFileSync(stateFile(), JSON.stringify(s));
|
|
47246
|
-
} catch {}
|
|
47247
|
-
}
|
|
47248
|
-
function readState() {
|
|
47249
|
-
const count = (dir) => {
|
|
47250
|
-
try {
|
|
47251
|
-
return fs6.readdirSync(dir).filter((f) => f.endsWith(".json") && !f.startsWith(".")).length;
|
|
47252
|
-
} catch {
|
|
47253
|
-
return 0;
|
|
47254
|
-
}
|
|
47255
|
-
};
|
|
47256
|
-
const counts = {
|
|
47257
|
-
pending: count(spoolDir()),
|
|
47258
|
-
rejected: count(rejectedDir()),
|
|
47259
|
-
unregistered: count(unregisteredDir())
|
|
47260
|
-
};
|
|
47252
|
+
function readStateFile() {
|
|
47261
47253
|
let raw = {};
|
|
47262
47254
|
try {
|
|
47263
47255
|
const parsed = JSON.parse(fs6.readFileSync(stateFile(), "utf8"));
|
|
47264
47256
|
if (parsed && typeof parsed === "object")
|
|
47265
47257
|
raw = parsed;
|
|
47266
47258
|
} catch {}
|
|
47267
|
-
const
|
|
47259
|
+
const p = raw.pruned;
|
|
47268
47260
|
return {
|
|
47269
47261
|
flushedAt: typeof raw.flushedAt === "string" ? raw.flushedAt : undefined,
|
|
47270
|
-
error:
|
|
47262
|
+
error: typeof raw.error === "string" ? raw.error || "unknown failure" : null,
|
|
47271
47263
|
deferred: typeof raw.deferred === "number" ? raw.deferred : undefined,
|
|
47272
|
-
|
|
47273
|
-
|
|
47264
|
+
pruned: p && typeof p.at === "string" && typeof p.count === "number" ? { at: p.at, count: p.count } : undefined
|
|
47265
|
+
};
|
|
47266
|
+
}
|
|
47267
|
+
function writeState(s) {
|
|
47268
|
+
try {
|
|
47269
|
+
fs6.writeFileSync(stateFile(), JSON.stringify({ ...s, pruned: s.pruned ?? readStateFile().pruned }));
|
|
47270
|
+
} catch {}
|
|
47271
|
+
}
|
|
47272
|
+
function readState() {
|
|
47273
|
+
const queue = listQueue(spoolDir());
|
|
47274
|
+
const counts = {
|
|
47275
|
+
pending: queue.files?.length ?? null,
|
|
47276
|
+
rejected: listQueue(rejectedDir()).files?.length ?? null,
|
|
47277
|
+
unregistered: listQueue(unregisteredDir()).files?.length ?? null
|
|
47274
47278
|
};
|
|
47279
|
+
const state = readStateFile();
|
|
47280
|
+
const stuck = state.error && (counts.pending ?? 0) > 0 ? state.error : null;
|
|
47281
|
+
return { ...state, ...counts, unreadable: queue.code, stuck };
|
|
47282
|
+
}
|
|
47283
|
+
function listQueue(dir) {
|
|
47284
|
+
try {
|
|
47285
|
+
const names = fs6.readdirSync(dir);
|
|
47286
|
+
return {
|
|
47287
|
+
files: names.filter((f) => f.endsWith(".json") && !f.startsWith(".")),
|
|
47288
|
+
temp: names.filter((f) => f.endsWith(".json") && f.startsWith(".")),
|
|
47289
|
+
code: null
|
|
47290
|
+
};
|
|
47291
|
+
} catch (e) {
|
|
47292
|
+
const code = e.code ?? "unknown";
|
|
47293
|
+
return code === "ENOENT" ? { files: [], temp: [], code: null } : { files: null, temp: [], code };
|
|
47294
|
+
}
|
|
47275
47295
|
}
|
|
47276
47296
|
function lock() {
|
|
47277
47297
|
const file2 = path6.join(spoolDir(), ".lock");
|
|
@@ -47410,16 +47430,36 @@ var queued = (from) => {
|
|
|
47410
47430
|
return [];
|
|
47411
47431
|
}
|
|
47412
47432
|
};
|
|
47433
|
+
var CODE_NAMES = { 18: "TOOBIG", 19: "CONSTRAINT", 20: "MISMATCH", 25: "RANGE" };
|
|
47434
|
+
function setAside(from, name, dir, reason2) {
|
|
47435
|
+
fs6.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
47436
|
+
try {
|
|
47437
|
+
fs6.renameSync(path6.join(from, name), path6.join(dir, name));
|
|
47438
|
+
} catch (e) {
|
|
47439
|
+
if (e.code === "ENOENT")
|
|
47440
|
+
return;
|
|
47441
|
+
throw e;
|
|
47442
|
+
}
|
|
47443
|
+
try {
|
|
47444
|
+
fs6.writeFileSync(path6.join(dir, `${name}.reason`), reason2, { mode: 384 });
|
|
47445
|
+
} catch {}
|
|
47446
|
+
}
|
|
47413
47447
|
async function sendBatch(db, names) {
|
|
47414
47448
|
const held = unregisteredDir();
|
|
47415
47449
|
const records = [];
|
|
47416
47450
|
for (const { name, from } of names) {
|
|
47451
|
+
let raw;
|
|
47417
47452
|
let r;
|
|
47418
47453
|
try {
|
|
47419
|
-
|
|
47454
|
+
raw = JSON.parse(fs6.readFileSync(path6.join(from, name), "utf8"));
|
|
47455
|
+
} catch {
|
|
47456
|
+
setAside(from, name, rejectedDir(), "unreadable");
|
|
47457
|
+
continue;
|
|
47458
|
+
}
|
|
47459
|
+
try {
|
|
47460
|
+
r = current(raw);
|
|
47420
47461
|
} catch {
|
|
47421
|
-
|
|
47422
|
-
fs6.renameSync(path6.join(from, name), path6.join(rejectedDir(), name));
|
|
47462
|
+
setAside(from, name, rejectedDir(), typeof raw?.project === "string" ? "version" : "no-project");
|
|
47423
47463
|
continue;
|
|
47424
47464
|
}
|
|
47425
47465
|
if (r)
|
|
@@ -47446,21 +47486,12 @@ async function sendBatch(db, names) {
|
|
|
47446
47486
|
} catch (e2) {
|
|
47447
47487
|
if (!rejected(e2))
|
|
47448
47488
|
throw e2;
|
|
47449
|
-
bad.push(x);
|
|
47450
|
-
}
|
|
47451
|
-
}
|
|
47452
|
-
}
|
|
47453
|
-
if (bad.length) {
|
|
47454
|
-
fs6.mkdirSync(rejectedDir(), { recursive: true, mode: 448 });
|
|
47455
|
-
for (const x of bad) {
|
|
47456
|
-
try {
|
|
47457
|
-
fs6.renameSync(path6.join(x.from, x.name), path6.join(rejectedDir(), x.name));
|
|
47458
|
-
} catch (e) {
|
|
47459
|
-
if (e.code !== "ENOENT")
|
|
47460
|
-
throw e;
|
|
47489
|
+
bad.push({ ...x, why: `sqlite:${CODE_NAMES[sqliteCode(e2) ?? -1] ?? "unknown"}` });
|
|
47461
47490
|
}
|
|
47462
47491
|
}
|
|
47463
47492
|
}
|
|
47493
|
+
for (const x of bad)
|
|
47494
|
+
setAside(x.from, x.name, rejectedDir(), x.why);
|
|
47464
47495
|
if (strayed.length) {
|
|
47465
47496
|
fs6.mkdirSync(held, { recursive: true, mode: 448 });
|
|
47466
47497
|
for (const x of strayed) {
|
|
@@ -47480,6 +47511,60 @@ async function sendBatch(db, names) {
|
|
|
47480
47511
|
fs6.rmSync(path6.join(x.from, x.name), { force: true });
|
|
47481
47512
|
return { sent, rejected: bad.length };
|
|
47482
47513
|
}
|
|
47514
|
+
async function sendObservations(db, names) {
|
|
47515
|
+
const aside = (name, why) => {
|
|
47516
|
+
try {
|
|
47517
|
+
setAside(callsDir(), name, callsRejectedDir(), why);
|
|
47518
|
+
} catch {}
|
|
47519
|
+
};
|
|
47520
|
+
const rows = [];
|
|
47521
|
+
for (const { name, from } of names) {
|
|
47522
|
+
let text2;
|
|
47523
|
+
try {
|
|
47524
|
+
text2 = fs6.readFileSync(path6.join(from, name), "utf8");
|
|
47525
|
+
} catch {
|
|
47526
|
+
continue;
|
|
47527
|
+
}
|
|
47528
|
+
let o = null;
|
|
47529
|
+
let why = "unreadable";
|
|
47530
|
+
try {
|
|
47531
|
+
o = observation(JSON.parse(text2));
|
|
47532
|
+
why = "shape";
|
|
47533
|
+
} catch {}
|
|
47534
|
+
if (o)
|
|
47535
|
+
rows.push({ name, o });
|
|
47536
|
+
else
|
|
47537
|
+
aside(name, why);
|
|
47538
|
+
}
|
|
47539
|
+
if (!rows.length)
|
|
47540
|
+
return true;
|
|
47541
|
+
const insert = (part) => db.insertInto("capture_tool_call").values(part.map((x) => observationRow(x.o))).execute();
|
|
47542
|
+
const bad = new Map;
|
|
47543
|
+
try {
|
|
47544
|
+
await insert(rows);
|
|
47545
|
+
} catch (e) {
|
|
47546
|
+
if (!rejected(e))
|
|
47547
|
+
return false;
|
|
47548
|
+
for (const x of rows)
|
|
47549
|
+
try {
|
|
47550
|
+
await insert([x]);
|
|
47551
|
+
} catch (e2) {
|
|
47552
|
+
if (!rejected(e2))
|
|
47553
|
+
return false;
|
|
47554
|
+
bad.set(x.name, `sqlite:${CODE_NAMES[sqliteCode(e2) ?? -1] ?? "unknown"}`);
|
|
47555
|
+
}
|
|
47556
|
+
}
|
|
47557
|
+
for (const x of rows) {
|
|
47558
|
+
const why = bad.get(x.name);
|
|
47559
|
+
if (why)
|
|
47560
|
+
aside(x.name, why);
|
|
47561
|
+
else
|
|
47562
|
+
try {
|
|
47563
|
+
fs6.rmSync(path6.join(callsDir(), x.name), { force: true });
|
|
47564
|
+
} catch {}
|
|
47565
|
+
}
|
|
47566
|
+
return true;
|
|
47567
|
+
}
|
|
47483
47568
|
async function flush(file2 = dbFile(), budgetMs = FLUSH_BUDGET_MS) {
|
|
47484
47569
|
const deadline = Date.now() + budgetMs;
|
|
47485
47570
|
const dir = spoolDir();
|
|
@@ -47487,6 +47572,8 @@ async function flush(file2 = dbFile(), budgetMs = FLUSH_BUDGET_MS) {
|
|
|
47487
47572
|
const total = { sent: 0, deferred: 0, rejected: 0 };
|
|
47488
47573
|
let batches = 0;
|
|
47489
47574
|
let queueBatches = 0;
|
|
47575
|
+
let removed = 0;
|
|
47576
|
+
const pruned = () => removed ? { at: new Date().toISOString(), count: removed } : undefined;
|
|
47490
47577
|
for (let first = true;; first = false) {
|
|
47491
47578
|
let unlock = lock();
|
|
47492
47579
|
while (!unlock && Date.now() < deadline) {
|
|
@@ -47508,12 +47595,20 @@ async function flush(file2 = dbFile(), budgetMs = FLUSH_BUDGET_MS) {
|
|
|
47508
47595
|
const sentBefore = batches;
|
|
47509
47596
|
try {
|
|
47510
47597
|
if (first) {
|
|
47511
|
-
prune(held);
|
|
47598
|
+
prune(held, () => removed++);
|
|
47512
47599
|
for (const part of chunks(queued(held), BATCH)) {
|
|
47513
47600
|
if (late2())
|
|
47514
47601
|
break;
|
|
47515
47602
|
await send(part);
|
|
47516
47603
|
}
|
|
47604
|
+
for (const part of chunks(queued(callsDir()), BATCH)) {
|
|
47605
|
+
if (late2())
|
|
47606
|
+
break;
|
|
47607
|
+
client ??= openWriter("capture", file2);
|
|
47608
|
+
if (!await sendObservations(client, part))
|
|
47609
|
+
break;
|
|
47610
|
+
batches++;
|
|
47611
|
+
}
|
|
47517
47612
|
}
|
|
47518
47613
|
while (queueBatches === 0 || !late2()) {
|
|
47519
47614
|
const part = queued(dir).slice(0, BATCH);
|
|
@@ -47522,12 +47617,19 @@ async function flush(file2 = dbFile(), budgetMs = FLUSH_BUDGET_MS) {
|
|
|
47522
47617
|
await send(part);
|
|
47523
47618
|
queueBatches++;
|
|
47524
47619
|
}
|
|
47525
|
-
prune(held);
|
|
47620
|
+
prune(held, () => removed++);
|
|
47526
47621
|
total.deferred = queued(held).length;
|
|
47527
47622
|
if (batches > sentBefore)
|
|
47528
|
-
writeState({
|
|
47623
|
+
writeState({
|
|
47624
|
+
flushedAt: new Date().toISOString(),
|
|
47625
|
+
error: null,
|
|
47626
|
+
deferred: total.deferred,
|
|
47627
|
+
pruned: pruned()
|
|
47628
|
+
});
|
|
47629
|
+
else if (removed)
|
|
47630
|
+
writeState({ ...readStateFile(), pruned: pruned() });
|
|
47529
47631
|
} catch (e) {
|
|
47530
|
-
writeState({ flushedAt: new Date().toISOString(), error: reason(e).slice(0, 300) });
|
|
47632
|
+
writeState({ flushedAt: new Date().toISOString(), error: reason(e).slice(0, 300), pruned: pruned() });
|
|
47531
47633
|
throw e;
|
|
47532
47634
|
} finally {
|
|
47533
47635
|
await client?.destroy().catch(() => {});
|
|
@@ -47546,27 +47648,63 @@ async function readInput(stream) {
|
|
|
47546
47648
|
}
|
|
47547
47649
|
var RECORD_TOOLS = "mcp__plugin_sphica_record__";
|
|
47548
47650
|
var OBSERVE_WAIT_MS = 5000;
|
|
47549
|
-
var hostId = (v) => v && /^[\x21-\x7e]{1,200}$/.test(v) ? v : null;
|
|
47550
|
-
|
|
47551
|
-
|
|
47552
|
-
|
|
47553
|
-
|
|
47554
|
-
|
|
47651
|
+
var hostId = (v) => typeof v === "string" && /^[\x21-\x7e]{1,200}$/.test(v) ? v : null;
|
|
47652
|
+
var OBSERVATION_KEYS = ["at", "host", "owner", "session", "tool", "toolUse", "turn", "v"].join();
|
|
47653
|
+
function observation(raw) {
|
|
47654
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw))
|
|
47655
|
+
return null;
|
|
47656
|
+
const o = raw;
|
|
47657
|
+
if (Object.keys(o).sort().join() !== OBSERVATION_KEYS)
|
|
47658
|
+
return null;
|
|
47659
|
+
if (o.v !== 1 || o.host !== "claude-code" || !hostId(o.session) || !hostId(o.toolUse))
|
|
47660
|
+
return null;
|
|
47661
|
+
if (!hostId(o.tool)?.startsWith(RECORD_TOOLS))
|
|
47662
|
+
return null;
|
|
47663
|
+
if (o.turn !== null && !hostId(o.turn))
|
|
47664
|
+
return null;
|
|
47665
|
+
if (o.owner !== 0 && o.owner !== 1)
|
|
47666
|
+
return null;
|
|
47667
|
+
const at = typeof o.at === "string" && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/.test(o.at) ? o.at : null;
|
|
47668
|
+
if (!at || Number.isNaN(Date.parse(at)) || new Date(at).toISOString() !== at)
|
|
47669
|
+
return null;
|
|
47670
|
+
return o;
|
|
47671
|
+
}
|
|
47672
|
+
var observationRow = (o) => ({
|
|
47673
|
+
host: o.host,
|
|
47674
|
+
session_external: o.session,
|
|
47675
|
+
turn_id: o.turn,
|
|
47676
|
+
tool_use_id: o.toolUse,
|
|
47677
|
+
tool_name: o.tool,
|
|
47678
|
+
owner_turn: o.owner,
|
|
47679
|
+
observed_at: o.at
|
|
47680
|
+
});
|
|
47681
|
+
async function observeRecordCall(file2, input2, owner, waitMs = OBSERVE_WAIT_MS) {
|
|
47682
|
+
const o = observation({
|
|
47683
|
+
v: 1,
|
|
47684
|
+
host: "claude-code",
|
|
47685
|
+
session: hostId(input2.session_id),
|
|
47686
|
+
turn: hostId(input2.prompt_id),
|
|
47687
|
+
toolUse: hostId(input2.tool_use_id),
|
|
47688
|
+
tool: hostId(input2.tool_name),
|
|
47689
|
+
owner: owner ? 1 : 0,
|
|
47690
|
+
at: iso(Date.now())
|
|
47691
|
+
});
|
|
47692
|
+
if (!o)
|
|
47555
47693
|
return;
|
|
47556
|
-
|
|
47694
|
+
let kept = null;
|
|
47557
47695
|
try {
|
|
47558
|
-
|
|
47559
|
-
|
|
47560
|
-
|
|
47561
|
-
|
|
47562
|
-
|
|
47563
|
-
tool_name: tool,
|
|
47564
|
-
owner_turn: owner ? 1 : 0,
|
|
47565
|
-
observed_at: iso(Date.now())
|
|
47566
|
-
}).execute();
|
|
47696
|
+
kept = put(callsDir(), o);
|
|
47697
|
+
} catch {}
|
|
47698
|
+
const cap = openWriter("capture", file2, waitMs);
|
|
47699
|
+
try {
|
|
47700
|
+
await cap.insertInto("capture_tool_call").values(observationRow(o)).execute();
|
|
47567
47701
|
} finally {
|
|
47568
47702
|
await cap.destroy().catch(() => {});
|
|
47569
47703
|
}
|
|
47704
|
+
if (kept)
|
|
47705
|
+
try {
|
|
47706
|
+
fs6.rmSync(kept, { force: true });
|
|
47707
|
+
} catch {}
|
|
47570
47708
|
}
|
|
47571
47709
|
async function main() {
|
|
47572
47710
|
if (process.argv[2] === "--flush") {
|
|
@@ -48664,8 +48802,8 @@ async function checkRecord(db, target, raw, facts = prepareRecord(target.root, r
|
|
|
48664
48802
|
problems.push(`${key}: commit ${commit.slice(0, 12)} does not hold ${p} in the repository${target.root ? "" : " (no working tree to check)"}; the anchor keeps no commit`);
|
|
48665
48803
|
commit = undefined;
|
|
48666
48804
|
}
|
|
48667
|
-
const
|
|
48668
|
-
const planned = { ...a, symbol: symbol2, commit, path: p, observation };
|
|
48805
|
+
const observation2 = a.role === "evidence" && !commit && target.sessionId ? (await db.selectFrom("edit_observation").select("id").where("session_id", "=", target.sessionId).where("path", "=", p).orderBy("id", "desc").executeTakeFirst())?.id ?? null : null;
|
|
48806
|
+
const planned = { ...a, symbol: symbol2, commit, path: p, observation: observation2 };
|
|
48669
48807
|
if (a.symbol && !symbol2)
|
|
48670
48808
|
fallbacks.add(planned);
|
|
48671
48809
|
anchors.push(planned);
|
|
@@ -48674,7 +48812,7 @@ async function checkRecord(db, target, raw, facts = prepareRecord(target.root, r
|
|
|
48674
48812
|
symbol: symbol2,
|
|
48675
48813
|
role: a.role,
|
|
48676
48814
|
held: Boolean(commit),
|
|
48677
|
-
observed:
|
|
48815
|
+
observed: observation2 !== null
|
|
48678
48816
|
});
|
|
48679
48817
|
if (wrong)
|
|
48680
48818
|
problems.push(`${key}: ${wrong}`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sphica",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.34",
|
|
4
4
|
"description": "Records Claude Code and Codex sessions on your machine and keeps past implementation and decisions, with their sources, for your agent to find.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|