@bunshin-cc/bunshin 0.4.5 → 0.4.7

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 (2) hide show
  1. package/dist/bin.js +109 -31
  2. package/package.json +1 -1
package/dist/bin.js CHANGED
@@ -183,12 +183,13 @@ function renderIndex(lessons) {
183
183
  }
184
184
  return lines.join("\n") + "\n";
185
185
  }
186
- var DEFAULT_CONFIG, EMPTY_SESSION, Store;
186
+ var RETIRED_DEFAULT_MODELS, DEFAULT_CONFIG, EMPTY_SESSION, Store;
187
187
  var init_store = __esm({
188
188
  "src/store.ts"() {
189
189
  "use strict";
190
190
  init_frontmatter();
191
191
  init_util();
192
+ RETIRED_DEFAULT_MODELS = /* @__PURE__ */ new Set(["claude-fable-5-1"]);
192
193
  DEFAULT_CONFIG = {
193
194
  orgId: "local",
194
195
  userId: process.env.USER || "me",
@@ -199,7 +200,7 @@ var init_store = __esm({
199
200
  apiUrl: "",
200
201
  scope: "repo",
201
202
  harness: "claude-code",
202
- model: "claude-fable-5-1",
203
+ model: "claude-opus-5-5",
203
204
  gateModel: "claude-haiku-4-5-20251001",
204
205
  backend: "auto",
205
206
  recall: { enabled: true, maxLessons: 4, hookBudgetMs: 3500, useMothership: true },
@@ -267,6 +268,8 @@ var init_store = __esm({
267
268
  recall: { ...DEFAULT_CONFIG.recall, ...raw.recall ?? {} },
268
269
  reflect: { ...DEFAULT_CONFIG.reflect, ...raw.reflect ?? {} }
269
270
  };
271
+ if (RETIRED_DEFAULT_MODELS.has(merged.model)) merged.model = DEFAULT_CONFIG.model;
272
+ if (process.env.BUNSHIN_MODEL) merged.model = process.env.BUNSHIN_MODEL;
270
273
  const harness = process.env.BUNSHIN_HARNESS;
271
274
  if (harness === "claude-code" || harness === "codex" || harness === "pi" || harness === "other") merged.harness = harness;
272
275
  if (!merged.apiKey && merged.credentialFile) {
@@ -431,9 +434,11 @@ var init_store = __esm({
431
434
  function emptyStats() {
432
435
  return { served: 0, loaded: 0, helped: 0, hurt: 0, byContext: {} };
433
436
  }
437
+ var LEARNING_SIGNALS;
434
438
  var init_types = __esm({
435
439
  "../core/src/v2/types.ts"() {
436
440
  "use strict";
441
+ LEARNING_SIGNALS = ["steering", "environment", "knowledge", "capability", "approach"];
437
442
  }
438
443
  });
439
444
 
@@ -1521,7 +1526,8 @@ function renderSection(s, trigger, opts = {}) {
1521
1526
  const label2 = s.kind === "guardrail" ? "GUARDRAIL" : s.kind.toUpperCase();
1522
1527
  const src = sourceLabel(s);
1523
1528
  const why = trigger ? `_Why you are seeing this: ${trigger}. Id: ${s.id}_` : `_Id: ${s.id}_`;
1524
- const head = [`### ${label2} (${src}): ${s.title}`, why];
1529
+ const trial = s.localTrial?.length ? [`_Includes ${s.localTrial.length === 1 ? "an edit" : `${s.localTrial.length} edits`} learned on this machine and not yet reviewed: treat ${s.localTrial.length === 1 ? "it" : "them"} as provisional._`] : [];
1530
+ const head = [`### ${label2} (${src}): ${s.title}`, why, ...trial];
1525
1531
  const max = opts.maxChars ?? 6e3;
1526
1532
  if (s.skill && opts.deployed) {
1527
1533
  const part2 = opts.anchor ? (s.outline ?? []).find((h) => h.anchor === opts.anchor) : void 0;
@@ -1726,9 +1732,20 @@ Lessons already taken from it. Never propose these again, nor a rewording of the
1726
1732
  ${lessons}
1727
1733
  `;
1728
1734
  }
1735
+ function stampLesson(op, signal, at) {
1736
+ const day = at.slice(0, 10);
1737
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(day)) return op;
1738
+ const recheck = signal === "environment" ? `; recheck after ${new Date(Date.parse(day) + ENVIRONMENT_RECHECK_DAYS * 864e5).toISOString().slice(0, 10)}` : "";
1739
+ const stamp = `_(learned ${day}${signal ? ` from ${SIGNAL_WORDS[signal]}` : ""}${recheck})_`;
1740
+ if (op.op === "append") return /_\(learned \d{4}-/.test(op.text) ? op : { ...op, text: `${op.text.trimEnd()} ${stamp}` };
1741
+ if (op.op === "create") return /_\(learned \d{4}-/.test(op.section.body) ? op : { ...op, section: { ...op.section, body: `${op.section.body.trimEnd()}
1742
+
1743
+ ${stamp}` } };
1744
+ return op;
1745
+ }
1729
1746
  function summarizeOp(op) {
1730
1747
  const clip2 = (s) => {
1731
- const one = s.replace(/\s+/g, " ").trim();
1748
+ const one = s.replace(/\s*_\(learned \d{4}-\d{2}-\d{2}[^)]*\)_/g, "").replace(/\s+/g, " ").trim();
1732
1749
  return one.length > 100 ? `${one.slice(0, 97)}\u2026` : one;
1733
1750
  };
1734
1751
  switch (op.op) {
@@ -1783,8 +1800,14 @@ Capture only what would change how a future agent acts, and is recorded nowhere
1783
1800
  b. the environment corrected the agent (a command, test, API, motion or grasp failed, then
1784
1801
  another approach worked): record the trap AND what worked;
1785
1802
  c. knowledge owned by another team surfaced (a process, policy or manual step);
1786
- d. a capability worked for the first time, or a task was done unusually efficiently.
1803
+ d. a capability worked for the first time, or a task was done unusually efficiently;
1804
+ e. looking back over the whole session, the approach itself was the problem (it took many
1805
+ rounds of fixes, it was over-built, it verified too late) and a different way of working
1806
+ would have avoided it. This has no direct ground truth: say what you would do differently
1807
+ next time, as generally as it truly holds, and where it does not hold.
1787
1808
  "Nothing new" is the expected outcome for most sessions. Never invent a lesson.
1809
+ Label each suggestion with its "signal": a \u2192 "steering", b \u2192 "environment", c \u2192 "knowledge",
1810
+ d \u2192 "capability", e \u2192 "approach".
1788
1811
 
1789
1812
  ## Where it goes
1790
1813
 
@@ -1797,7 +1820,10 @@ ${index}
1797
1820
  Already captured this session (do not repeat): ${input.captured.length ? input.captured.join(", ") : "none"}
1798
1821
 
1799
1822
  Layers you may target: ${input.layers.join(", ")}.
1800
- - Knowledge about one repo that its team owns \u2192 that repo's section (repo:... ids).
1823
+ ${input.profileId ? `- How THIS person works, prefers or wants things done (a steering or approach lesson about them,
1824
+ not about the task) \u2192 op "append" to their profile ${input.profileId}, anchor "how-i-work".
1825
+ A lesson for anyone doing the task belongs in the task's document instead.
1826
+ ` : ""}- Knowledge about one repo that its team owns \u2192 that repo's section (repo:... ids).
1801
1827
  - Cross-repo facts, another team's rules, non-code processes \u2192 a Bunshin section at the right layer.
1802
1828
  - If it only holds in some contexts, use op "append" with a "condition" (e.g. "objects over 50 g").
1803
1829
  ${robot ? `- Values that belong to THIS robot or cell (calibration, pixel columns, colour thresholds under this
@@ -1824,7 +1850,8 @@ At most ${max} suggestions. Schema:
1824
1850
  "paths"?: string[], "entities"?: string[], "skills"?: string[]}}
1825
1851
  | {"op": "param", "key": string, "value": string|number|boolean}
1826
1852
  | {"op": "situation", "text": string},
1827
- "rationale": string, "urgency": "normal"|"urgent"}]}`;
1853
+ "rationale": string, "urgency": "normal"|"urgent",
1854
+ "signal": "steering"|"environment"|"knowledge"|"capability"|"approach"}]}`;
1828
1855
  }
1829
1856
  function extractJson(text2) {
1830
1857
  const fenced = text2.match(/```(?:json)?\s*([\s\S]*?)```/);
@@ -1900,7 +1927,8 @@ function parseReflection(raw, opts = { layers: [] }) {
1900
1927
  target: { ...sectionId ? { sectionId } : {}, ...layer ? { layer } : {} },
1901
1928
  op,
1902
1929
  rationale: clean(it.rationale) || "captured from session",
1903
- urgency: it.urgency === "urgent" ? "urgent" : "normal"
1930
+ urgency: it.urgency === "urgent" ? "urgent" : "normal",
1931
+ ...LEARNING_SIGNALS.includes(it.signal) ? { signal: it.signal } : {}
1904
1932
  });
1905
1933
  if (out.length >= (opts.max ?? 4)) break;
1906
1934
  }
@@ -1913,8 +1941,9 @@ function toSuggestion(p, base) {
1913
1941
  contextId: base.contextId,
1914
1942
  authorUserId: base.authorUserId,
1915
1943
  target: p.target,
1916
- op: p.op,
1944
+ op: stampLesson(p.op, p.signal, base.createdAt),
1917
1945
  rationale: p.rationale,
1946
+ ...p.signal ? { signal: p.signal } : {},
1918
1947
  evidenceEpisodeIds: base.episodeId ? [base.episodeId] : [],
1919
1948
  trial: { uses: 0, successes: 0, failures: 0 },
1920
1949
  origin: base.origin,
@@ -1923,12 +1952,21 @@ function toSuggestion(p, base) {
1923
1952
  createdAt: base.createdAt
1924
1953
  };
1925
1954
  }
1926
- var REFLECT_MARKER, REFLECT_SCHEMA, KINDS, anchorOf, clean, strList;
1955
+ var REFLECT_MARKER, ENVIRONMENT_RECHECK_DAYS, SIGNAL_WORDS, REFLECT_SCHEMA, KINDS, anchorOf, clean, strList;
1927
1956
  var init_reflect = __esm({
1928
1957
  "../core/src/v2/reflect.ts"() {
1929
1958
  "use strict";
1930
1959
  init_redact();
1960
+ init_types();
1931
1961
  REFLECT_MARKER = "BUNSHIN:reflect";
1962
+ ENVIRONMENT_RECHECK_DAYS = 90;
1963
+ SIGNAL_WORDS = {
1964
+ steering: "a person's correction",
1965
+ environment: "the environment",
1966
+ knowledge: "another team's knowledge",
1967
+ capability: "a first success",
1968
+ approach: "reflection on the approach"
1969
+ };
1932
1970
  REFLECT_SCHEMA = {
1933
1971
  type: "object",
1934
1972
  additionalProperties: false,
@@ -1944,7 +1982,8 @@ var init_reflect = __esm({
1944
1982
  target: { type: "object", properties: { sectionId: { type: "string" }, layer: { type: "string" } } },
1945
1983
  op: { type: "object" },
1946
1984
  rationale: { type: "string" },
1947
- urgency: { type: "string", enum: ["normal", "urgent"] }
1985
+ urgency: { type: "string", enum: ["normal", "urgent"] },
1986
+ signal: { type: "string", enum: ["steering", "environment", "knowledge", "capability", "approach"] }
1948
1987
  }
1949
1988
  }
1950
1989
  }
@@ -2003,6 +2042,14 @@ var init_research = __esm({
2003
2042
  }
2004
2043
  });
2005
2044
 
2045
+ // ../core/src/v2/profile.ts
2046
+ var init_profile = __esm({
2047
+ "../core/src/v2/profile.ts"() {
2048
+ "use strict";
2049
+ init_types();
2050
+ }
2051
+ });
2052
+
2006
2053
  // ../core/src/v2/index.ts
2007
2054
  var init_v2 = __esm({
2008
2055
  "../core/src/v2/index.ts"() {
@@ -2019,6 +2066,7 @@ var init_v2 = __esm({
2019
2066
  init_reconcile();
2020
2067
  init_release();
2021
2068
  init_research();
2069
+ init_profile();
2022
2070
  }
2023
2071
  });
2024
2072
 
@@ -2682,7 +2730,7 @@ function planDeploy(bundle, p, harnesses) {
2682
2730
  if (!prev || (LAYER_RANK[layerKind(s.layer)] ?? 0) > (LAYER_RANK[layerKind(prev.layer)] ?? 0)) byName.set(s.skill.name, s);
2683
2731
  }
2684
2732
  const skills = [...byName.values()].sort((a, b) => a.skill.name.localeCompare(b.skill.name));
2685
- const instructions = active.filter((s) => s.delivery === "always" && !s.skill).sort((a, b) => (a.packPath ?? a.title).localeCompare(b.packPath ?? b.title));
2733
+ const instructions = active.filter((s) => s.delivery === "always" && !s.skill && !s.profileOf).sort((a, b) => (a.packPath ?? a.title).localeCompare(b.packPath ?? b.title));
2686
2734
  const plan = { docs: /* @__PURE__ */ new Map(), instructions, skills };
2687
2735
  const roots = [...harnesses.has("claude-code") ? [p.claudeSkills] : [], ...harnesses.has("codex") ? [p.codexSkills] : []];
2688
2736
  for (const s of skills) {
@@ -3197,13 +3245,25 @@ function baselineOf(bundle) {
3197
3245
  }
3198
3246
  function renderInstructions(bundle) {
3199
3247
  const docs = instructionDocs(bundle);
3200
- if (!docs.length) return "";
3201
- return [
3202
- `## Standing instructions (your organization, from Bunshin ${lib(bundle?.libraryVersion)})`,
3203
- "Follow these as you would CLAUDE.md. Bunshin re-delivers them after /clear and compaction, and tells you when they change.",
3204
- "",
3205
- ...docs.map((d) => d.body.trim())
3206
- ].join("\n");
3248
+ const profiles = docs.filter((d) => d.profileOf);
3249
+ const standing = docs.filter((d) => !d.profileOf);
3250
+ const parts = [];
3251
+ if (profiles.length) {
3252
+ parts.push(
3253
+ ["## Who you work for (Bunshin profile)", "Bunshin keeps this current from their sessions and corrections, and tells you when it changes.", "", ...profiles.map((d) => d.body.trim())].join("\n")
3254
+ );
3255
+ }
3256
+ if (standing.length) {
3257
+ parts.push(
3258
+ [
3259
+ `## Standing instructions (your organization, from Bunshin ${lib(bundle?.libraryVersion)})`,
3260
+ "Follow these as you would CLAUDE.md. Bunshin re-delivers them after /clear and compaction, and tells you when they change.",
3261
+ "",
3262
+ ...standing.map((d) => d.body.trim())
3263
+ ].join("\n")
3264
+ );
3265
+ }
3266
+ return parts.join("\n\n");
3207
3267
  }
3208
3268
  function lineDiff(before, after, max = MAX_DIFF_LINES) {
3209
3269
  const a = before.split("\n");
@@ -3237,11 +3297,11 @@ function changesSince(base, bundle, skillPath) {
3237
3297
  const old = base.instructions[d.id];
3238
3298
  if (old === d.body) continue;
3239
3299
  if (old === void 0) {
3240
- lines.push(`New standing instructions ("${d.title}"):`, d.body.trim(), "");
3300
+ lines.push(d.profileOf ? `The profile of the person you work for ("${d.title}"):` : `New standing instructions ("${d.title}"):`, d.body.trim(), "");
3241
3301
  continue;
3242
3302
  }
3243
3303
  const diff = lineDiff(old, d.body);
3244
- if (diff.length) lines.push(`Standing instructions changed ("${d.title}"); follow the new text:`, ...diff, "");
3304
+ if (diff.length) lines.push(d.profileOf ? `The profile of the person you work for changed ("${d.title}"):` : `Standing instructions changed ("${d.title}"); follow the new text:`, ...diff, "");
3245
3305
  }
3246
3306
  for (const id of Object.keys(base.instructions)) if (!now.some((d) => d.id === id)) lines.push(`Standing instructions withdrawn (${id}): stop following them.`, "");
3247
3307
  const skillLines = [];
@@ -3707,13 +3767,13 @@ function applyOverlays(sections, overlays, ctx) {
3707
3767
  if (!ACTIVE_OVERLAY.has(o.status) || o.kind !== "local-section") continue;
3708
3768
  if (o.op.op === "create") {
3709
3769
  const id = o.sectionId || localSectionId(o.suggestionId);
3710
- sections.set(id, draftToSection(id, o.op.section, ctx, ownLayer(ctx), o.appliedAt));
3770
+ sections.set(id, { ...draftToSection(id, o.op.section, ctx, ownLayer(ctx), o.appliedAt), localTrial: [o.suggestionId] });
3711
3771
  continue;
3712
3772
  }
3713
3773
  const base = o.sectionId ? sections.get(o.sectionId) : void 0;
3714
3774
  if (!base) continue;
3715
3775
  const res = applyOpToBody(base.body, o.op);
3716
- if (res.ok && res.text !== base.body) sections.set(base.id, { ...base, body: res.text, updatedAt: o.appliedAt });
3776
+ if (res.ok && res.text !== base.body) sections.set(base.id, { ...base, body: res.text, updatedAt: o.appliedAt, localTrial: [...base.localTrial ?? [], o.suggestionId] });
3717
3777
  }
3718
3778
  }
3719
3779
  function localView(v2, config, cwd, opts = {}) {
@@ -4382,9 +4442,9 @@ function episodeIdFor(contextId, sessionId) {
4382
4442
  return `e_${createHash2("sha256").update(`${contextId}
4383
4443
  ${sessionId}`).digest("hex").slice(0, 16)}`;
4384
4444
  }
4385
- function runClaudeFork(sessionId, prompt, { cwd, timeoutMs = FORK_TIMEOUT_MS } = {}) {
4445
+ function runClaudeFork(sessionId, prompt, { cwd, model, timeoutMs = FORK_TIMEOUT_MS } = {}) {
4386
4446
  const bin = process.env.BUNSHIN_CLAUDE_BIN || "claude";
4387
- const args = ["-p", "--resume", sessionId, "--fork-session", "--output-format", "json", prompt];
4447
+ const args = ["-p", "--resume", sessionId, "--fork-session", "--output-format", "json", ...model ? ["--model", model] : [], prompt];
4388
4448
  return new Promise((resolve) => {
4389
4449
  let out = "";
4390
4450
  let err = "";
@@ -4482,20 +4542,25 @@ async function runReflectionV2(store, opts) {
4482
4542
  const episodeId = episodeIdFor(view.ctx.id, opts.sessionId);
4483
4543
  const prior = session.suggestionIds.map((id) => v2.readSuggestion(id)).filter((s) => !!s);
4484
4544
  const captured = [.../* @__PURE__ */ new Set([...session.captured, ...prior.map((s) => s.target.sectionId ?? localSectionId(s.id))])];
4485
- const index = reflectionIndex(view, [...session.loaded, ...session.pushed, ...session.served], session.task, opts.focus, humanTurnsOf(session.transcriptPath));
4545
+ const profile = view.sections.find((s) => s.profileOf && s.profileOf === config.userId && s.status === "active");
4546
+ const index = [
4547
+ ...profile ? [{ id: profile.id, kind: profile.kind, layer: profile.layer, title: profile.title, when: "how the person you work for works", ...profile.outline?.length ? { outline: profile.outline } : {} }] : [],
4548
+ ...reflectionIndex(view, [...session.loaded, ...session.pushed, ...session.served], session.task, opts.focus, humanTurnsOf(session.transcriptPath)).filter((s) => s.id !== profile?.id)
4549
+ ];
4486
4550
  const harness = session.harness ?? config.harness;
4487
4551
  const facts = readFacts(session.transcriptPath);
4488
4552
  const turns = facts?.turns.length ?? 0;
4489
4553
  const from = reflectCursor(session, turns);
4490
4554
  const lessons = [.../* @__PURE__ */ new Set([...prior.map(lessonLine), ...session.serverLessons ?? []])].slice(0, 40);
4491
4555
  const earlier = from > 0 || lessons.length ? { ...from > 0 && facts ? { from: anchorAt(facts, from) } : {}, lessons } : void 0;
4492
- const promptBase = { contextKind: view.ctx.kind, layers: view.layers, index, captured, ...opts.focus ? { focus: opts.focus } : {}, ...earlier ? { earlier } : {} };
4556
+ const promptBase = { contextKind: view.ctx.kind, layers: view.layers, index, captured, ...opts.focus ? { focus: opts.focus } : {}, ...earlier ? { earlier } : {}, ...profile ? { profileId: profile.id } : {} };
4493
4557
  let raw = null;
4494
4558
  if (harness === "claude-code") {
4495
- const fork = await runClaudeFork(opts.sessionId, buildReflectPrompt(promptBase), { cwd: session.cwd });
4559
+ const fork = await runClaudeFork(opts.sessionId, buildReflectPrompt(promptBase), { cwd: session.cwd, model: config.model });
4496
4560
  v2.log("v2-reflect-fork", {
4497
4561
  session: opts.sessionId,
4498
4562
  ok: fork.ok,
4563
+ model: config.model,
4499
4564
  cache: true,
4500
4565
  cache_read_input_tokens: fork.usage?.cache_read_input_tokens ?? null,
4501
4566
  cache_creation_input_tokens: fork.usage?.cache_creation_input_tokens ?? null,
@@ -4784,6 +4849,13 @@ var init_uicmd = __esm({
4784
4849
  // src/v2/hooks.ts
4785
4850
  import fs14 from "node:fs";
4786
4851
  import path11 from "node:path";
4852
+ function inContext(s) {
4853
+ return s.inContext ?? [...s.loaded, ...s.pushed];
4854
+ }
4855
+ function markInContext(s, ...ids) {
4856
+ s.inContext = s.inContext ?? [...s.loaded, ...s.pushed];
4857
+ addUnique(s.inContext, ...ids);
4858
+ }
4787
4859
  function touch(v2, ctx, patch = () => {
4788
4860
  }) {
4789
4861
  const id = ctx.input.session_id || "no-session";
@@ -4852,6 +4924,7 @@ function liveSummary(text2) {
4852
4924
  for (const m of text2.matchAll(/- `([^`]+)` updated \((v\d+ → v\d+)\)/g)) items.push(`${m[1]} ${m[2]}`);
4853
4925
  for (const m of text2.matchAll(/- `([^`]+)` was retired/g)) items.push(`${m[1]} retired`);
4854
4926
  if (/Standing instructions (changed|withdrawn)|New standing instructions/.test(text2)) items.push("standing instructions changed");
4927
+ if (/The profile of the person you work for/.test(text2)) items.push("your profile");
4855
4928
  return `library updated${range ? ` (${range[2]})` : ""}: ${items.join(", ") || "changes delivered to the agent"}`;
4856
4929
  }
4857
4930
  async function dispatch(event, ctx) {
@@ -4996,7 +5069,7 @@ async function onPrompt(ctx) {
4996
5069
  if (!prompt.trim()) return withLive({});
4997
5070
  const config = ctx.store.config();
4998
5071
  const view = localView(v2, config, ctx.cwd);
4999
- const injected = [...session.loaded, ...session.pushed];
5072
+ const injected = inContext(session);
5000
5073
  const res = auto(view.sections, view.reader, prompt, { exclude: injected });
5001
5074
  const remote = await remoteAuto(ctx, prompt, injected);
5002
5075
  if (remote) {
@@ -5016,6 +5089,7 @@ async function onPrompt(ctx) {
5016
5089
  v2.updateSession(session.sessionId, ctx.cwd, (s) => {
5017
5090
  addUnique(s.served, sec.id);
5018
5091
  addUnique(s.loaded, sec.id);
5092
+ markInContext(s, sec.id);
5019
5093
  });
5020
5094
  v2.log("v2-auto", { session: session.sessionId, section: sec.id, reason: res.reason });
5021
5095
  updateFeed(v2, session.sessionId, (f) => {
@@ -5069,13 +5143,14 @@ function onPreToolUse(ctx) {
5069
5143
  const paths = raw.map((p) => relativeToRoot(view.root, p, ctx.cwd)).filter((p) => !!p);
5070
5144
  const text2 = [session.task, session.lastPrompt].filter(Boolean).join("\n");
5071
5145
  if (!paths.length && !text2) return withLive({});
5072
- const seen = /* @__PURE__ */ new Set([...session.pushed, ...session.loaded]);
5146
+ const seen = new Set(inContext(session));
5073
5147
  const hits = push(view.sections, view.reader, { paths, text: text2 }).filter((h) => !seen.has(h.section.id)).slice(0, MAX_PUSH);
5074
5148
  if (!hits.length) return withLive({});
5075
5149
  const ids = hits.map((h) => h.section.id);
5076
5150
  v2.updateSession(session.sessionId, ctx.cwd, (s) => {
5077
5151
  addUnique(s.pushed, ...ids);
5078
5152
  addUnique(s.loaded, ...ids);
5153
+ markInContext(s, ...ids);
5079
5154
  addUnique(s.served, ...ids);
5080
5155
  });
5081
5156
  v2.log("v2-push", { session: session.sessionId, tool: ctx.input.tool_name, hits: hits.map((h) => `${h.section.id} ${h.trigger}`) });
@@ -5278,7 +5353,10 @@ function onSessionEnd(ctx) {
5278
5353
  function onPreCompact(ctx) {
5279
5354
  const v2 = new V2Store(ctx.store);
5280
5355
  const config = ctx.store.config();
5281
- const session = touch(v2, ctx);
5356
+ const session = touch(v2, ctx, (s) => {
5357
+ s.inContext = [];
5358
+ s.hinted = [];
5359
+ });
5282
5360
  if (!config.reflect.enabled) return {};
5283
5361
  const scan = scanTranscript(ctx.input.transcript_path ?? session.transcriptPath);
5284
5362
  const from = reflectCursor(session, scan.facts.turns.length);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bunshin-cc/bunshin",
3
- "version": "0.4.5",
3
+ "version": "0.4.7",
4
4
  "description": "Shared memory for company AI agents. Connect Codex, Claude Code, or Pi to Bunshin.",
5
5
  "type": "module",
6
6
  "bin": {