@bivy/bivy 0.18.11-staging.5 → 0.18.11-staging.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.
@@ -1089,6 +1089,10 @@ class ClaudeSession {
1089
1089
  if (!images.length)
1090
1090
  return;
1091
1091
  const toolUseId = String(block.tool_use_id ?? "");
1092
+ // A file read returns an image that already exists on disk — most often
1093
+ // one the user just uploaded — so surfacing it would repeat it in chat.
1094
+ if (this.toolDetailsByUseId.get(toolUseId)?.kind === "read")
1095
+ return;
1092
1096
  const toolName = this.toolNamesByUseId.get(toolUseId) ?? "tool";
1093
1097
  for (const image of images) {
1094
1098
  const byteLength = Buffer.byteLength(image.data, "base64");
@@ -72,7 +72,7 @@ export function createForkCommands(deps) {
72
72
  // When the client has already picked a target agent, pass it so the
73
73
  // bundle omits the native payload for a cross-runtime fork (it could
74
74
  // never be replayed there — see buildForkBundle). Unset => keep it.
75
- const bundle = buildForkBundle({ runtime: deps.getRuntime(rec.runtimeId), sessionFile: rec.sessionFile, record: forkRecord, dirtyPatch, workspaceSnapshot, targetRuntimeId: deps.agentFrom(msg), liveMessages: deps.forkMessages(rec), state: deps.forkInFlightState(rec) });
75
+ const bundle = buildForkBundle({ runtime: deps.getRuntime(rec.runtimeId), sessionFile: rec.sessionFile, record: forkRecord, dirtyPatch, workspaceSnapshot, targetRuntimeId: deps.agentFrom(msg), liveMessages: deps.forkMessages(rec), display: deps.forkDisplay(rec), state: deps.forkInFlightState(rec) });
76
76
  ctx.reply({ type: "session.fork.bundle", requestId, bundle });
77
77
  }
78
78
  catch (error) {
@@ -150,7 +150,7 @@ export function createForkCommands(deps) {
150
150
  ? captureWorkspaceSnapshot(sourceCwd, { maxBytes: deps.forkWorkspaceMaxBytes() })
151
151
  : undefined;
152
152
  // Same runtime → the bundle carries the native payload → full fidelity.
153
- const bundle = buildForkBundle({ runtime, sessionFile: rec.sessionFile, record: forkRecord, dirtyPatch, workspaceSnapshot, targetRuntimeId: rec.runtimeId, liveMessages: deps.forkMessages(rec), state: deps.forkInFlightState(rec) });
153
+ const bundle = buildForkBundle({ runtime, sessionFile: rec.sessionFile, record: forkRecord, dirtyPatch, workspaceSnapshot, targetRuntimeId: rec.runtimeId, liveMessages: deps.forkMessages(rec), display: deps.forkDisplay(rec), state: deps.forkInFlightState(rec) });
154
154
  // Cut a fresh fork branch (the source still holds its own); skip prereq
155
155
  // detection (same node + same runtime ⇒ agent and repo are present).
156
156
  const outcome = await deps.standUpFork({
package/dist/server.js CHANGED
@@ -2105,6 +2105,7 @@ const RELAY_COMMANDS = {
2105
2105
  ...createForkCommands({
2106
2106
  broadcast,
2107
2107
  forkMessages: (rec) => transcripts.forkMessages(rec),
2108
+ forkDisplay: (rec) => transcripts.conversationMessages(rec),
2108
2109
  resolveSession: (sessionId) => resolveSession(sessionId),
2109
2110
  getRuntime: (runtimeId) => getRuntime(runtimeId),
2110
2111
  forkRecordFor,
@@ -8919,6 +8920,7 @@ const forkStandUp = createForkStandUp({
8919
8920
  createSession,
8920
8921
  broadcast,
8921
8922
  persistSessionMetadata,
8923
+ recordForkDisplay: (record, messages, forkedAt) => eventLog.appendForkDisplay(record.id, messages, forkedAt),
8922
8924
  scheduleAdvertise,
8923
8925
  bivySessionEnvelope,
8924
8926
  applyRequestedModel,
@@ -209,6 +209,12 @@ function isBase(value) {
209
209
  const record = value;
210
210
  return record.bivyKind === "base" && Array.isArray(record.messages) && typeof record.reset === "boolean";
211
211
  }
212
+ function isForkDisplay(value) {
213
+ if (!value || typeof value !== "object")
214
+ return false;
215
+ const record = value;
216
+ return record.bivyKind === "fork-display" && Array.isArray(record.messages) && typeof record.forkedAt === "number";
217
+ }
212
218
  function isAttachment(value) {
213
219
  if (!value || typeof value !== "object")
214
220
  return false;
@@ -255,7 +261,7 @@ function isAppReviewEntry(value) {
255
261
  return entry.bivyKind === "app-review" && typeof entry.id === "string" && typeof entry.createdAt === "number" && typeof entry.afterMessageCount === "number" && isAppReview(entry.review);
256
262
  }
257
263
  function isRecord(value) {
258
- return isOverlay(value) || isBase(value) || isAttachment(value) || isOutboundAttachment(value) || isInlineImage(value) || isAppPublication(value) || isAppReviewEntry(value) || isSuggestionEntry(value);
264
+ return isOverlay(value) || isBase(value) || isForkDisplay(value) || isAttachment(value) || isOutboundAttachment(value) || isInlineImage(value) || isAppPublication(value) || isAppReviewEntry(value) || isSuggestionEntry(value);
259
265
  }
260
266
  /**
261
267
  * Fold attachment records into a text→refs list: last write wins per text (a
@@ -743,7 +749,23 @@ export class EventLog {
743
749
  * the display overlays — what a fork carries to another agent. */
744
750
  deriveBase(id, runtimeBase) {
745
751
  const logged = this.readBase(id);
746
- return runtimeBase && runtimeBase.length ? mergeBases(logged, runtimeBase) : logged;
752
+ const base = runtimeBase && runtimeBase.length ? mergeBases(logged, runtimeBase) : logged;
753
+ const display = this.entries(id).find(isForkDisplay);
754
+ if (!display)
755
+ return base;
756
+ const replayed = (m) => {
757
+ const ts = m.timestamp;
758
+ return typeof ts === "number" && ts <= display.forkedAt;
759
+ };
760
+ return [...display.messages, ...base.filter((m) => !replayed(m))];
761
+ }
762
+ /** Record a fork's source transcript for display (see ForkDisplayLogEntry). */
763
+ appendForkDisplay(id, messages, forkedAt) {
764
+ if (!messages.length)
765
+ return;
766
+ this.load(id);
767
+ const record = { bivyKind: "fork-display", createdAt: Date.now(), forkedAt, messages: JSON.parse(JSON.stringify(messages)) };
768
+ this.enqueue(id, this.syntheticKey(id), record);
747
769
  }
748
770
  /** Full ordered record list (already-flushed followed by pending). */
749
771
  entries(id) {
@@ -217,6 +217,11 @@ export function createForkStandUp(deps) {
217
217
  const record = plan.kind === "resume"
218
218
  ? await deps.createSession(cwd, plan.sessionFile, { runtimeId: targetRuntimeId, source: bundle.record.source, sandbox: forkSandbox, makeActive: false, newSession: true })
219
219
  : await deps.createSession(cwd, undefined, { runtimeId: targetRuntimeId, source: bundle.record.source, sandbox: forkSandbox, makeActive: false, newSession: true });
220
+ // A replayed or seeded destination resumes from a text rendering of the
221
+ // conversation. Show the source's own transcript (tool cards, sub-agent
222
+ // nesting) in its place; everything the fork does from now on follows it.
223
+ if (plan.fidelity !== "full" && bundle.display?.length)
224
+ deps.recordForkDisplay(record, bundle.display, Date.now());
220
225
  // Mark the new session as a fork of its source, so the run card can show
221
226
  // "Forked from …" and the lineage survives a reload (persisted below).
222
227
  record.forkedFrom = bundle.record.sourceSessionId;
@@ -1,4 +1,7 @@
1
1
  import { normalizeMessages, buildSeedPrompt, buildForkHistory, } from "./transcript-normal.js";
2
+ /** Cap on the display transcript a bundle carries; larger sessions fall back to
3
+ * showing the destination agent's own (text) replay. */
4
+ export const FORK_DISPLAY_MAX_BYTES = 2 * 1024 * 1024;
2
5
  /**
3
6
  * Capture a fork bundle on the source node. Always includes the normalized
4
7
  * transcript (the cross-runtime seed, and the fallback when a same-runtime
@@ -49,7 +52,8 @@ export function buildForkBundle(opts) {
49
52
  // transcript below still supports replay or a seeded continuation.
50
53
  }
51
54
  }
52
- return { record, normalized, ...(native ? { native } : {}), ...(opts.dirtyPatch ? { dirtyPatch: opts.dirtyPatch } : {}), ...(opts.workspaceSnapshot ? { workspaceSnapshot: opts.workspaceSnapshot } : {}), ...(opts.state ? { state: opts.state } : {}) };
55
+ const display = opts.display?.length && JSON.stringify(opts.display).length <= FORK_DISPLAY_MAX_BYTES ? [...opts.display] : undefined;
56
+ return { record, normalized, ...(native ? { native } : {}), ...(display ? { display } : {}), ...(opts.dirtyPatch ? { dirtyPatch: opts.dirtyPatch } : {}), ...(opts.workspaceSnapshot ? { workspaceSnapshot: opts.workspaceSnapshot } : {}), ...(opts.state ? { state: opts.state } : {}) };
53
57
  }
54
58
  /**
55
59
  * Decide the best fidelity a fork of `bundle` into `targetRuntime` can achieve:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bivy/bivy",
3
- "version": "0.18.11-staging.5",
3
+ "version": "0.18.11-staging.7",
4
4
  "type": "module",
5
5
  "license": "AGPL-3.0-only",
6
6
  "description": "Run coding agents on machines you own. Open-source, self-hostable agent workspace.",