sphica 0.6.16 → 0.6.17

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://anthropic.com/claude-code/plugin.schema.json",
3
3
  "name": "sphica",
4
- "version": "0.6.16",
4
+ "version": "0.6.17",
5
5
  "description": "Records Claude Code and Codex sessions on your machine and keeps past implementation and decisions, with their sources, for your agent to find.",
6
6
  "author": {
7
7
  "name": "iroha924",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sphica",
3
- "version": "0.6.16",
3
+ "version": "0.6.17",
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
  "author": {
6
6
  "name": "iroha924",
@@ -0,0 +1,20 @@
1
+ -- Revision 5 → 6 of generation 2: the subagent a delivery ran in (existing rows keep null, the main conversation), and a capture view
2
+ -- that writes it while capture_delivery keeps its columns. `sphica init` runs this in one transaction with foreign keys off, then checks
3
+ -- foreign_key_check. Every statement matches db/schema.sql at revision 6; server/test/migrate.test.ts compares the two.
4
+
5
+ alter table delivery add column agent_id text check (length(agent_id) between 1 and 200);
6
+
7
+ create view capture_delivery_scoped as
8
+ select session_id, agent_id, event, outcome, reason, path, eligible, omitted, chars, at, null as units from delivery;
9
+ create trigger capture_delivery_scoped_insert instead of insert on capture_delivery_scoped begin
10
+ select raise(abort, 'the unit and the delivered session belong to different projects')
11
+ where exists (select 1 from json_each(coalesce(new.units, '[]')) j
12
+ where (select project_id from unit where id = j.value) is not (select project_id from session where id = new.session_id));
13
+ insert into delivery (session_id, agent_id, event, outcome, reason, path, eligible, omitted, chars, at)
14
+ values (new.session_id, new.agent_id, new.event, new.outcome, new.reason, new.path, coalesce(new.eligible, 0),
15
+ coalesce(new.omitted, 0), coalesce(new.chars, 0), new.at);
16
+ insert into delivery_unit (delivery_id, unit_id)
17
+ select last_insert_rowid(), j.value from json_each(coalesce(new.units, '[]')) j where true on conflict do nothing;
18
+ end;
19
+
20
+ pragma user_version = 6;
package/db/schema.sql CHANGED
@@ -934,8 +934,9 @@ create table work (
934
934
  create index work_open on work (project_id, updated_at desc) where status in ('active', 'blocked', 'paused');
935
935
  create index work_run on work (run_id) where run_id is not null;
936
936
 
937
- -- What a delivery hook emitted or suppressed, and how many eligible units it left out. No source text is copied here.
938
- -- chars is the delivered length without the omission note (Sphica's own text), since the read budget adds it up.
937
+ -- What a delivery hook emitted or suppressed, and how many eligible units it left out (no source text). chars leaves out the omission note,
938
+ -- since the read budget adds it up. A session_start's reason is the start source or 'subagent'; agent_id is the subagent (null: main),
939
+ -- written the way SQLite stores a column ALTER TABLE added, so a migrated database has the same definition.
939
940
  create table delivery (
940
941
  id integer primary key autoincrement not null,
941
942
  session_id text references session (id) on delete cascade,
@@ -947,7 +948,7 @@ create table delivery (
947
948
  omitted integer not null default 0 check (omitted >= 0 and omitted <= eligible),
948
949
  chars integer not null default 0 check (chars >= 0),
949
950
  at text not null check (strftime('%Y-%m-%dT%H:%M:%fZ', at) is at)
950
- ) strict;
951
+ , agent_id text check (length(agent_id) between 1 and 200)) strict;
951
952
  create index delivery_session on delivery (session_id, at);
952
953
  create table delivery_unit (
953
954
  delivery_id integer not null references delivery (id) on delete cascade,
@@ -1024,5 +1025,19 @@ create trigger capture_delivery_insert instead of insert on capture_delivery beg
1024
1025
  insert into delivery_unit (delivery_id, unit_id)
1025
1026
  select last_insert_rowid(), j.value from json_each(coalesce(new.units, '[]')) j where true on conflict do nothing;
1026
1027
  end;
1028
+ -- The same as capture_delivery with the subagent the delivery ran in. capture_delivery keeps its columns, since an older plugin's
1029
+ -- capture writes through it into a database of this revision
1030
+ create view capture_delivery_scoped as
1031
+ select session_id, agent_id, event, outcome, reason, path, eligible, omitted, chars, at, null as units from delivery;
1032
+ create trigger capture_delivery_scoped_insert instead of insert on capture_delivery_scoped begin
1033
+ select raise(abort, 'the unit and the delivered session belong to different projects')
1034
+ where exists (select 1 from json_each(coalesce(new.units, '[]')) j
1035
+ where (select project_id from unit where id = j.value) is not (select project_id from session where id = new.session_id));
1036
+ insert into delivery (session_id, agent_id, event, outcome, reason, path, eligible, omitted, chars, at)
1037
+ values (new.session_id, new.agent_id, new.event, new.outcome, new.reason, new.path, coalesce(new.eligible, 0),
1038
+ coalesce(new.omitted, 0), coalesce(new.chars, 0), new.at);
1039
+ insert into delivery_unit (delivery_id, unit_id)
1040
+ select last_insert_rowid(), j.value from json_each(coalesce(new.units, '[]')) j where true on conflict do nothing;
1041
+ end;
1027
1042
 
1028
- pragma user_version = 5;
1043
+ pragma user_version = 6;
package/dist/capture.js CHANGED
@@ -11109,7 +11109,7 @@ var HOST_MARKS = new Set([".orphaned_at", ".in_use"]);
11109
11109
 
11110
11110
  // server/src/sqlite.ts
11111
11111
  var SCHEMA_GENERATION = 2;
11112
- var SCHEMA_REVISION = 5;
11112
+ var SCHEMA_REVISION = 6;
11113
11113
  var sphicaHome = () => process.env.SPHICA_HOME || path2.join(os.homedir(), ".sphica");
11114
11114
  var dbFile = () => process.env.SPHICA_DB || path2.join(sphicaHome(), "sphica.db");
11115
11115
  function requireRuntime() {
@@ -11245,16 +11245,24 @@ var DDL = () => ddl ??= new Set([
11245
11245
  return code;
11246
11246
  }));
11247
11247
  var readsDataVersion = (p1, p2) => p1 === "data_version" && p2 === null;
11248
- var CAPTURE_VIEWS = new Set(["capture_session", "capture_message", "capture_edit", "capture_delivery"]);
11248
+ var CAPTURE_VIEWS = new Set([
11249
+ "capture_session",
11250
+ "capture_message",
11251
+ "capture_edit",
11252
+ "capture_delivery",
11253
+ "capture_delivery_scoped"
11254
+ ]);
11249
11255
  var TRIGGER_WRITES = {
11250
11256
  capture_session_insert: new Set(["session"]),
11251
11257
  capture_message_insert: new Set(["source"]),
11252
11258
  capture_edit_insert: new Set(["edit_observation"]),
11253
11259
  capture_delivery_insert: new Set(["delivery", "delivery_unit"]),
11260
+ capture_delivery_scoped_insert: new Set(["delivery", "delivery_unit"]),
11254
11261
  source_fts_ai: new Set(["source_fts"])
11255
11262
  };
11256
11263
  var TRIGGER_FUNCTIONS = {
11257
- capture_delivery_insert: new Set(["coalesce", "json_each", "last_insert_rowid"])
11264
+ capture_delivery_insert: new Set(["coalesce", "json_each", "last_insert_rowid"]),
11265
+ capture_delivery_scoped_insert: new Set(["coalesce", "json_each", "last_insert_rowid"])
11258
11266
  };
11259
11267
  var CAPTURE_READS = {
11260
11268
  project: new Set(["id", "key", "name"]),
package/dist/cli.js CHANGED
@@ -3625,7 +3625,7 @@ ${indent}${caution(note)}` : ""}`);
3625
3625
 
3626
3626
  // server/src/sqlite.ts
3627
3627
  var SCHEMA_GENERATION = 2;
3628
- var SCHEMA_REVISION = 5;
3628
+ var SCHEMA_REVISION = 6;
3629
3629
  var sphicaHome = () => process.env.SPHICA_HOME || path3.join(os2.homedir(), ".sphica");
3630
3630
  var dbFile = () => process.env.SPHICA_DB || path3.join(sphicaHome(), "sphica.db");
3631
3631
  function requireRuntime() {
@@ -14712,16 +14712,24 @@ var DDL = () => ddl ??= new Set([
14712
14712
  return code;
14713
14713
  }));
14714
14714
  var readsDataVersion = (p1, p2) => p1 === "data_version" && p2 === null;
14715
- var CAPTURE_VIEWS = new Set(["capture_session", "capture_message", "capture_edit", "capture_delivery"]);
14715
+ var CAPTURE_VIEWS = new Set([
14716
+ "capture_session",
14717
+ "capture_message",
14718
+ "capture_edit",
14719
+ "capture_delivery",
14720
+ "capture_delivery_scoped"
14721
+ ]);
14716
14722
  var TRIGGER_WRITES = {
14717
14723
  capture_session_insert: new Set(["session"]),
14718
14724
  capture_message_insert: new Set(["source"]),
14719
14725
  capture_edit_insert: new Set(["edit_observation"]),
14720
14726
  capture_delivery_insert: new Set(["delivery", "delivery_unit"]),
14727
+ capture_delivery_scoped_insert: new Set(["delivery", "delivery_unit"]),
14721
14728
  source_fts_ai: new Set(["source_fts"])
14722
14729
  };
14723
14730
  var TRIGGER_FUNCTIONS = {
14724
- capture_delivery_insert: new Set(["coalesce", "json_each", "last_insert_rowid"])
14731
+ capture_delivery_insert: new Set(["coalesce", "json_each", "last_insert_rowid"]),
14732
+ capture_delivery_scoped_insert: new Set(["coalesce", "json_each", "last_insert_rowid"])
14725
14733
  };
14726
14734
  var CAPTURE_READS = {
14727
14735
  project: new Set(["id", "key", "name"]),
package/dist/deliver.js CHANGED
@@ -11134,7 +11134,7 @@ var HOST_MARKS = new Set([".orphaned_at", ".in_use"]);
11134
11134
 
11135
11135
  // server/src/sqlite.ts
11136
11136
  var SCHEMA_GENERATION = 2;
11137
- var SCHEMA_REVISION = 5;
11137
+ var SCHEMA_REVISION = 6;
11138
11138
  var sphicaHome = () => process.env.SPHICA_HOME || path3.join(os.homedir(), ".sphica");
11139
11139
  var dbFile = () => process.env.SPHICA_DB || path3.join(sphicaHome(), "sphica.db");
11140
11140
  function requireRuntime() {
@@ -11294,16 +11294,24 @@ var DDL = () => ddl ??= new Set([
11294
11294
  return code;
11295
11295
  }));
11296
11296
  var readsDataVersion = (p1, p2) => p1 === "data_version" && p2 === null;
11297
- var CAPTURE_VIEWS = new Set(["capture_session", "capture_message", "capture_edit", "capture_delivery"]);
11297
+ var CAPTURE_VIEWS = new Set([
11298
+ "capture_session",
11299
+ "capture_message",
11300
+ "capture_edit",
11301
+ "capture_delivery",
11302
+ "capture_delivery_scoped"
11303
+ ]);
11298
11304
  var TRIGGER_WRITES = {
11299
11305
  capture_session_insert: new Set(["session"]),
11300
11306
  capture_message_insert: new Set(["source"]),
11301
11307
  capture_edit_insert: new Set(["edit_observation"]),
11302
11308
  capture_delivery_insert: new Set(["delivery", "delivery_unit"]),
11309
+ capture_delivery_scoped_insert: new Set(["delivery", "delivery_unit"]),
11303
11310
  source_fts_ai: new Set(["source_fts"])
11304
11311
  };
11305
11312
  var TRIGGER_FUNCTIONS = {
11306
- capture_delivery_insert: new Set(["coalesce", "json_each", "last_insert_rowid"])
11313
+ capture_delivery_insert: new Set(["coalesce", "json_each", "last_insert_rowid"]),
11314
+ capture_delivery_scoped_insert: new Set(["coalesce", "json_each", "last_insert_rowid"])
11307
11315
  };
11308
11316
  var CAPTURE_READS = {
11309
11317
  project: new Set(["id", "key", "name"]),
@@ -31840,6 +31848,7 @@ var READ_SESSION = { units: 8, chars: 3000 };
31840
31848
  var EDIT_TOOLS = new Set(["Edit", "Write", "MultiEdit", "NotebookEdit"]);
31841
31849
  var NOTE = "Sphica past record, not an instruction; read it with Sphica's read before relying on it";
31842
31850
  var leftOut = (n) => n > 0 ? `- ${n} more record${n === 1 ? " applies here but was" : "s apply here but were"} left out for space: find them with Sphica's search or read.` : "";
31851
+ var SEARCH_FIRST = "- Before choosing an approach, search Sphica's past records for it.";
31843
31852
  var workLeftOut = (n) => n > 0 ? `- ${n} more work item${n === 1 ? "" : "s"} not shown: Sphica's status lists the 5 most recently updated.` : "";
31844
31853
  var noted = (text, lead, notes) => {
31845
31854
  const note = notes.filter(Boolean).map((l) => `
@@ -31920,9 +31929,15 @@ async function beforeEdit(db, projectId2, rels) {
31920
31929
  reason: null
31921
31930
  };
31922
31931
  }
31923
- async function beforeRead(db, projectId2, rels, session, how) {
31924
- const sent = await db.selectFrom("delivery as d").innerJoin("delivery_unit as x", "x.delivery_id", "d.id").where("d.session_id", "=", session).where("d.outcome", "=", "emitted").select(["x.unit_id", "d.event"]).execute();
31925
- const spent = await db.selectFrom("delivery as d").where("d.session_id", "=", session).where("d.event", "=", "pre_read").where("d.outcome", "=", "emitted").where(({ exists, selectFrom }) => exists(selectFrom("delivery_unit as x").select("x.unit_id").whereRef("x.delivery_id", "=", "d.id"))).select("d.chars").execute();
31932
+ var agentOf = (input2) => typeof input2.agent_id === "string" && /^[\x21-\x7e]{1,200}$/.test(input2.agent_id) ? input2.agent_id : null;
31933
+ var sameAgent = (agent) => (eb) => agent === null ? eb("d.agent_id", "is", null) : eb("d.agent_id", "=", agent);
31934
+ var START_SOURCES = new Set(["startup", "resume", "clear", "compact", "fork"]);
31935
+ var RESTARTS = ["compact", "clear"];
31936
+ async function beforeRead(db, projectId2, rels, session, agent, how) {
31937
+ const restart = await db.selectFrom("delivery as d").where("d.session_id", "=", session).where(sameAgent(agent)).where("d.event", "=", "session_start").where("d.reason", "in", RESTARTS).select((eb) => eb.fn.max("d.id").as("id")).executeTakeFirst();
31938
+ const since = restart?.id ?? 0;
31939
+ const sent = await db.selectFrom("delivery as d").innerJoin("delivery_unit as x", "x.delivery_id", "d.id").where("d.session_id", "=", session).where(sameAgent(agent)).where("d.id", ">", since).where("d.outcome", "=", "emitted").select(["x.unit_id", "d.event"]).execute();
31940
+ const spent = await db.selectFrom("delivery as d").where("d.session_id", "=", session).where(sameAgent(agent)).where("d.id", ">", since).where("d.event", "=", "pre_read").where("d.outcome", "=", "emitted").where(({ exists, selectFrom }) => exists(selectFrom("delivery_unit as x").select("x.unit_id").whereRef("x.delivery_id", "=", "d.id"))).select("d.chars").execute();
31926
31941
  const seen = new Set(sent.map((r) => r.unit_id));
31927
31942
  const readUnits = sent.filter((r) => r.event === "pre_read").length;
31928
31943
  const rows = (await anchoredTo(db, projectId2, rels).where("u.kind", "in", ["decision", "constraint"]).execute()).filter((u) => !seen.has(u.id));
@@ -32052,7 +32067,12 @@ async function atStart(db, projectId2, branch, place) {
32052
32067
  const workLeft = Math.max((workTotal ?? 0) - f.kept.filter((i) => i < work.length).length, 0);
32053
32068
  const broadLeft = Math.max((broadTotal ?? 0) - shownUnits.length, 0);
32054
32069
  return {
32055
- ...noted(f.text, lead, [leftOut(broadLeft), workLeftOut(workLeft), await waiting(db, projectId2, place)]),
32070
+ ...noted(f.text, lead, [
32071
+ leftOut(broadLeft),
32072
+ workLeftOut(workLeft),
32073
+ await waiting(db, projectId2, place),
32074
+ place.subagent ? SEARCH_FIRST : ""
32075
+ ]),
32056
32076
  units: shownUnits,
32057
32077
  eligible: (workTotal ?? 0) + (broadTotal ?? 0),
32058
32078
  omitted: workLeft + broadLeft,
@@ -32120,34 +32140,62 @@ function markOnce(kind, key) {
32120
32140
  function onceUnavailable(session) {
32121
32141
  return markOnce("unavailable", session);
32122
32142
  }
32123
- async function log(file2, projectId2, host, external2, event, plan, outcome, branch) {
32143
+ async function write2(trx, e, plan) {
32144
+ const id = sessionId(e.projectId, e.host, e.external);
32145
+ const now = iso(Date.now());
32146
+ await trx.insertInto("capture_session").values({
32147
+ id,
32148
+ project_id: e.projectId,
32149
+ host: e.host,
32150
+ external_id: e.external,
32151
+ branch: e.branch,
32152
+ started_at: now
32153
+ }).execute();
32154
+ await trx.insertInto("capture_delivery_scoped").values({
32155
+ session_id: id,
32156
+ agent_id: e.agent,
32157
+ event: e.event,
32158
+ outcome: plan.text ? "emitted" : "nothing",
32159
+ reason: plan.reason,
32160
+ path: plan.path,
32161
+ eligible: plan.eligible,
32162
+ omitted: plan.omitted,
32163
+ chars: plan.text.length - plan.note.length,
32164
+ at: now,
32165
+ units: JSON.stringify(plan.units)
32166
+ }).execute();
32167
+ }
32168
+ async function log(file2, e, plan) {
32124
32169
  const cap = openWriter("capture", file2, LOG_WAIT_MS);
32125
32170
  try {
32126
- const id = sessionId(projectId2, host, external2);
32127
- const now = iso(Date.now());
32171
+ await inTransaction(cap, (trx) => write2(trx, e, plan));
32172
+ } finally {
32173
+ await cap.destroy().catch(() => {});
32174
+ }
32175
+ }
32176
+ async function lockedPlan(file2, e, make, keep) {
32177
+ const cap = openWriter("capture", file2, LOG_WAIT_MS);
32178
+ let held = false;
32179
+ let plan = null;
32180
+ try {
32128
32181
  await inTransaction(cap, async (trx) => {
32129
- await trx.insertInto("capture_session").values({ id, project_id: projectId2, host, external_id: external2, branch, started_at: now }).execute();
32130
- await trx.insertInto("capture_delivery").values({
32131
- session_id: id,
32132
- event,
32133
- outcome,
32134
- reason: plan.reason,
32135
- path: plan.path,
32136
- eligible: plan.eligible,
32137
- omitted: plan.omitted,
32138
- chars: plan.text.length - plan.note.length,
32139
- at: now,
32140
- units: JSON.stringify(plan.units)
32141
- }).execute();
32182
+ held = true;
32183
+ plan = await make();
32184
+ if (keep(plan))
32185
+ await write2(trx, e, plan);
32142
32186
  });
32187
+ } catch (err) {
32188
+ if (held && !plan)
32189
+ throw err;
32143
32190
  } finally {
32144
32191
  await cap.destroy().catch(() => {});
32145
32192
  }
32193
+ return plan ?? make();
32146
32194
  }
32147
32195
  async function deliver(input2, host = "claude-code", file2 = dbFile()) {
32148
32196
  const name = input2.hook_event_name;
32149
32197
  const call = reviewCall(input2);
32150
- const event = call ? "review" : name === "SessionStart" ? "session_start" : name === "UserPromptSubmit" ? "prompt" : name === "PreToolUse" ? input2.tool_name === "Read" || input2.tool_name === "Bash" && !(host === "codex" && shellPatch(input2)) ? "pre_read" : "pre_edit" : null;
32198
+ const event = call ? "review" : name === "SessionStart" || name === "SubagentStart" ? "session_start" : name === "UserPromptSubmit" ? "prompt" : name === "PreToolUse" ? input2.tool_name === "Read" || input2.tool_name === "Bash" && !(host === "codex" && shellPatch(input2)) ? "pre_read" : "pre_edit" : null;
32151
32199
  if (!event || !input2.session_id)
32152
32200
  return "";
32153
32201
  if (event === "prompt" && !isOwnerTurn(input2, undefined, undefined, host === "codex" ? process.env.CODEX_THREAD_ID : undefined))
@@ -32181,21 +32229,38 @@ async function deliver(input2, host = "claude-code", file2 = dbFile()) {
32181
32229
  return "";
32182
32230
  }
32183
32231
  if (event === "session_start" && input2.source === "resume") {
32184
- const said = await db.selectFrom("delivery").select("id").where("session_id", "=", sessionId(pid, host, input2.session_id)).where("event", "=", "session_start").where("outcome", "=", "emitted").executeTakeFirst();
32232
+ const said = await db.selectFrom("delivery").select("id").where("session_id", "=", sessionId(pid, host, input2.session_id)).where("agent_id", "is", null).where("event", "=", "session_start").where((eb) => eb.or([eb("reason", "is", null), eb("reason", "!=", "subagent")])).where("outcome", "=", "emitted").executeTakeFirst();
32185
32233
  if (said)
32186
32234
  return "";
32187
32235
  }
32188
- const plan = event === "pre_edit" ? await beforeEdit(db, pid, rels) : event === "pre_read" ? await beforeRead(db, pid, rels, sessionId(pid, host, input2.session_id), shell ? "named" : "reading") : event === "prompt" ? await onPrompt(db, pid, place.root, input2.prompt ?? "") : call ? await beforeReview(db, pid, place.root, call) : await atStart(db, pid, branchOf(place.root), {
32189
- file: file2,
32190
- key: place.key,
32236
+ const reader = db;
32237
+ const session = input2.session_id;
32238
+ const make = async () => event === "pre_edit" ? await beforeEdit(reader, pid, rels) : event === "pre_read" ? await beforeRead(reader, pid, rels, sessionId(pid, host, session), agentOf(input2), shell ? "named" : "reading") : event === "prompt" ? await onPrompt(reader, pid, place.root, input2.prompt ?? "") : call ? await beforeReview(reader, pid, place.root, call) : {
32239
+ ...await atStart(reader, pid, branchOf(place.root), {
32240
+ file: file2,
32241
+ key: place.key,
32242
+ host,
32243
+ subagent: name === "SubagentStart",
32244
+ owner: name !== "SubagentStart" && isOwnerTurn(input2, undefined, undefined, host === "codex" ? process.env.CODEX_THREAD_ID : undefined)
32245
+ }),
32246
+ reason: name === "SubagentStart" ? "subagent" : input2.source && START_SOURCES.has(input2.source) ? input2.source : null
32247
+ };
32248
+ const entry = {
32249
+ projectId: pid,
32191
32250
  host,
32192
- owner: isOwnerTurn(input2, undefined, undefined, host === "codex" ? process.env.CODEX_THREAD_ID : undefined)
32193
- });
32251
+ external: session,
32252
+ agent: agentOf(input2),
32253
+ event,
32254
+ branch: branchOf(place.root)
32255
+ };
32256
+ const keep = (p) => !(onPath && !p.text);
32257
+ if (event === "pre_read" || event === "session_start")
32258
+ return (await lockedPlan(file2, entry, make, keep)).text;
32259
+ const plan = await make();
32194
32260
  if (call && plan.text && toldBefore(`${host}\x00${input2.session_id}`, plan.once ?? plan.text))
32195
32261
  return "";
32196
- if (onPath && !plan.text)
32197
- return "";
32198
- await log(file2, pid, host, input2.session_id, event, plan, plan.text ? "emitted" : "nothing", branchOf(place.root)).catch(() => {});
32262
+ if (keep(plan))
32263
+ await log(file2, entry, plan).catch(() => {});
32199
32264
  return plan.text;
32200
32265
  } catch (e) {
32201
32266
  if (e instanceof RevisionMismatch)
@@ -45869,7 +45869,7 @@ var HOST_MARKS = new Set([".orphaned_at", ".in_use"]);
45869
45869
 
45870
45870
  // server/src/sqlite.ts
45871
45871
  var SCHEMA_GENERATION = 2;
45872
- var SCHEMA_REVISION = 5;
45872
+ var SCHEMA_REVISION = 6;
45873
45873
  var sphicaHome = () => process.env.SPHICA_HOME || path2.join(os.homedir(), ".sphica");
45874
45874
  var dbFile = () => process.env.SPHICA_DB || path2.join(sphicaHome(), "sphica.db");
45875
45875
  function requireRuntime() {
@@ -46005,16 +46005,24 @@ var DDL = () => ddl ??= new Set([
46005
46005
  return code;
46006
46006
  }));
46007
46007
  var readsDataVersion = (p1, p2) => p1 === "data_version" && p2 === null;
46008
- var CAPTURE_VIEWS = new Set(["capture_session", "capture_message", "capture_edit", "capture_delivery"]);
46008
+ var CAPTURE_VIEWS = new Set([
46009
+ "capture_session",
46010
+ "capture_message",
46011
+ "capture_edit",
46012
+ "capture_delivery",
46013
+ "capture_delivery_scoped"
46014
+ ]);
46009
46015
  var TRIGGER_WRITES = {
46010
46016
  capture_session_insert: new Set(["session"]),
46011
46017
  capture_message_insert: new Set(["source"]),
46012
46018
  capture_edit_insert: new Set(["edit_observation"]),
46013
46019
  capture_delivery_insert: new Set(["delivery", "delivery_unit"]),
46020
+ capture_delivery_scoped_insert: new Set(["delivery", "delivery_unit"]),
46014
46021
  source_fts_ai: new Set(["source_fts"])
46015
46022
  };
46016
46023
  var TRIGGER_FUNCTIONS = {
46017
- capture_delivery_insert: new Set(["coalesce", "json_each", "last_insert_rowid"])
46024
+ capture_delivery_insert: new Set(["coalesce", "json_each", "last_insert_rowid"]),
46025
+ capture_delivery_scoped_insert: new Set(["coalesce", "json_each", "last_insert_rowid"])
46018
46026
  };
46019
46027
  var CAPTURE_READS = {
46020
46028
  project: new Set(["id", "key", "name"]),
package/dist/mcp.js CHANGED
@@ -46030,7 +46030,7 @@ var HOST_MARKS = new Set([".orphaned_at", ".in_use"]);
46030
46030
 
46031
46031
  // server/src/sqlite.ts
46032
46032
  var SCHEMA_GENERATION = 2;
46033
- var SCHEMA_REVISION = 5;
46033
+ var SCHEMA_REVISION = 6;
46034
46034
  var sphicaHome = () => process.env.SPHICA_HOME || path2.join(os.homedir(), ".sphica");
46035
46035
  var dbFile = () => process.env.SPHICA_DB || path2.join(sphicaHome(), "sphica.db");
46036
46036
  function requireRuntime() {
package/hooks/codex.json CHANGED
@@ -18,6 +18,18 @@
18
18
  ]
19
19
  }
20
20
  ],
21
+ "SubagentStart": [
22
+ {
23
+ "hooks": [
24
+ {
25
+ "type": "command",
26
+ "command": "node \"${PLUGIN_ROOT}/dist/deliver.js\" codex",
27
+ "commandWindows": "powershell.exe -NoProfile -NonInteractive -Command node $env:PLUGIN_ROOT/dist/deliver.js codex",
28
+ "timeout": 10
29
+ }
30
+ ]
31
+ }
32
+ ],
21
33
  "UserPromptSubmit": [
22
34
  {
23
35
  "hooks": [
package/hooks/hooks.json CHANGED
@@ -20,6 +20,17 @@
20
20
  ]
21
21
  }
22
22
  ],
23
+ "SubagentStart": [
24
+ {
25
+ "hooks": [
26
+ {
27
+ "type": "command",
28
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/dist/deliver.js\"",
29
+ "timeout": 5
30
+ }
31
+ ]
32
+ }
33
+ ],
23
34
  "UserPromptSubmit": [
24
35
  {
25
36
  "hooks": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sphica",
3
- "version": "0.6.16",
3
+ "version": "0.6.17",
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",