@agentvault/claude-bridge 0.8.10 → 0.8.12

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/dist/index.js CHANGED
@@ -52059,10 +52059,12 @@ async function applyProposals(state, proposals, committerLeafIndex, pskSearch, s
52059
52059
  const [mutatedTree, addedLeafNodes] = await applyTreeMutations(state.ratchetTree, grouped, state.groupContext, sentByClient, state.clientConfig.authService, state.clientConfig.lifetimeConfig, cs.signature);
52060
52060
  const [updatedPskSecret, pskIds] = await accumulatePskSecret(grouped.psk.map((p2) => p2.proposal.psk.preSharedKeyId), pskSearch, cs, zeroes);
52061
52061
  const selfRemoved = mutatedTree[leafToNodeIndex(toLeafIndex(state.privatePath.leafIndex))] === void 0;
52062
- const needsUpdatePath = allProposals.length === 0 || allProposals.some(({ proposal }) => {
52062
+ const pathRequiredByRfc = allProposals.length === 0 || allProposals.some(({ proposal }) => {
52063
52063
  const t22 = proposal.proposalType;
52064
52064
  return t22 !== "add" && t22 !== "psk" && t22 !== "reinit";
52065
52065
  });
52066
+ const pathRequiredLegacy = allProposals.length === 0 || Object.values(grouped.update).length > 1 || Object.values(grouped.remove).length > 1;
52067
+ const needsUpdatePath = sentByClient ? pathRequiredByRfc : pathRequiredLegacy;
52066
52068
  return {
52067
52069
  tree: mutatedTree,
52068
52070
  pskSecret: updatedPskSecret,
@@ -64610,11 +64612,11 @@ var init_mls_kp_pool = __esm2({
64610
64612
  }
64611
64613
  });
64612
64614
  function ownIdentity() {
64613
- const v22 = true ? "0.23.37" : FALLBACK;
64615
+ const v22 = true ? "0.23.39" : FALLBACK;
64614
64616
  return `${PACKAGE}@${v22}`;
64615
64617
  }
64616
64618
  function buildSha() {
64617
- const raw = true ? "58c552f-dirty" : "";
64619
+ const raw = true ? "9982fc8-dirty" : "";
64618
64620
  const cleaned = (raw ?? "").trim();
64619
64621
  return !cleaned || cleaned === "unknown" ? null : cleaned;
64620
64622
  }
@@ -65717,6 +65719,27 @@ var init_channel = __esm2({
65717
65719
  * this process (GH #366). Cleared on (re)join so the next first message
65718
65720
  * re-fetches the authoritative roster. Prevents per-message refetch storms. */
65719
65721
  _roomRosterRefreshed = /* @__PURE__ */ new Set();
65722
+ /**
65723
+ * #1181: the last attachment saved per room, so the NEXT room turn can name
65724
+ * it. wren saved an un-mentioned file (no reply, #1107), was asked "@wren can
65725
+ * you read this file?" 14 s later, and answered "I don't see a file
65726
+ * attached" — the note lived only in the prior history line. Hermes defers
65727
+ * un-mentioned attachments and fetches on the next addressed turn (#1162);
65728
+ * this is the plugin's equivalent surface. In-memory: a restart forgets it,
65729
+ * which is the right default for "this file".
65730
+ */
65731
+ _recentRoomAttachments = /* @__PURE__ */ new Map();
65732
+ static RECENT_ROOM_ATTACHMENT_MS = 10 * 60 * 1e3;
65733
+ /** #1181: prefix a room turn that carries no attachment with the one saved in this room recently. */
65734
+ _withRecentRoomAttachmentNote(roomId, text) {
65735
+ const recent = this._recentRoomAttachments.get(roomId);
65736
+ if (!recent) return text;
65737
+ const ageMs = Date.now() - recent.at;
65738
+ if (ageMs > _SecureChannel.RECENT_ROOM_ATTACHMENT_MS) return text;
65739
+ return `[Most recent file in this room: ${recent.filePath} (${recent.name}, received ${Math.round(ageMs / 1e3)}s ago). If this message refers to "the file" or "this file", it means that one \u2014 use your Read tool on it.]
65740
+
65741
+ ${text}`;
65742
+ }
65720
65743
  /** In-flight roster-refresh promises (per room) — dedup concurrent refreshes. */
65721
65744
  _roomRosterRefreshInFlight = /* @__PURE__ */ new Map();
65722
65745
  /** Dedup buffer for A2A message IDs (prevents double-delivery via direct + Redis) */
@@ -66812,7 +66835,7 @@ var init_channel = __esm2({
66812
66835
  */
66813
66836
  sendActivitySpan(spanData) {
66814
66837
  if (!this._ws || this._ws.readyState !== WebSocket2.OPEN) return;
66815
- const pluginVersion = true ? "0.23.37" : "0.0.0-dev";
66838
+ const pluginVersion = true ? "0.23.39" : "0.0.0-dev";
66816
66839
  const agentName = this.config.agentName ?? "Agent";
66817
66840
  const resource = {
66818
66841
  "service.name": "agentvault-agent",
@@ -68750,7 +68773,7 @@ var init_channel = __esm2({
68750
68773
  agentVersion: this.config.agentVersion ?? "0.0.0",
68751
68774
  // __AV_VERSION__ is injected by esbuild from package.json at build time.
68752
68775
  // Falls back to "0.0.0-dev" in non-bundled contexts (tests).
68753
- pluginVersion: true ? "0.23.37" : "0.0.0-dev"
68776
+ pluginVersion: true ? "0.23.39" : "0.0.0-dev"
68754
68777
  });
68755
68778
  this._telemetryReporter.startAutoFlush(3e4);
68756
68779
  }
@@ -69074,7 +69097,7 @@ var init_channel = __esm2({
69074
69097
  agentVersion: this.config.agentVersion ?? "0.0.0",
69075
69098
  // __AV_VERSION__ is injected by esbuild from package.json at build time.
69076
69099
  // Falls back to "0.0.0-dev" in non-bundled contexts (tests).
69077
- pluginVersion: true ? "0.23.37" : "0.0.0-dev"
69100
+ pluginVersion: true ? "0.23.39" : "0.0.0-dev"
69078
69101
  });
69079
69102
  this._telemetryReporter.startAutoFlush(3e4);
69080
69103
  }
@@ -69847,12 +69870,13 @@ ${messageText}`;
69847
69870
  * Returns the text unchanged when there is no attachment or the fetch fails —
69848
69871
  * but a failure is LOGGED and announced in the text, never silent.
69849
69872
  */
69850
- async _augmentWithAttachment(attachmentInfo, baseText) {
69873
+ async _augmentWithAttachment(attachmentInfo, baseText, roomId) {
69851
69874
  if (!attachmentInfo) return baseText;
69852
69875
  try {
69853
69876
  const { filePath, decrypted } = await this._downloadAndDecryptAttachment(attachmentInfo);
69854
69877
  const mime = String(attachmentInfo.mime ?? "application/octet-stream");
69855
69878
  const name = String(attachmentInfo.filename ?? "attachment");
69879
+ if (roomId) this._recentRoomAttachments.set(roomId, { filePath, name, at: Date.now() });
69856
69880
  const textMimes = ["text/", "application/json", "application/xml", "application/csv"];
69857
69881
  if (textMimes.some((m22) => mime.startsWith(m22))) {
69858
69882
  const content = new TextDecoder().decode(decrypted);
@@ -70482,7 +70506,9 @@ ${baseText}`;
70482
70506
  }
70483
70507
  }
70484
70508
  if (roomAttachment) {
70485
- messageText = await this._augmentWithAttachment(roomAttachment, messageText);
70509
+ messageText = await this._augmentWithAttachment(roomAttachment, messageText, resolvedRoomId);
70510
+ } else {
70511
+ messageText = this._withRecentRoomAttachmentNote(resolvedRoomId, messageText);
70486
70512
  }
70487
70513
  const roomMembers = this._persisted?.rooms?.[resolvedRoomId]?.members ?? [];
70488
70514
  const senderMember = roomMembers.find((m22) => m22.deviceId === senderDeviceId);
@@ -99524,7 +99550,7 @@ var init_index = __esm2({
99524
99550
  await init_skill_telemetry();
99525
99551
  await init_policy_enforcer();
99526
99552
  init_room_protocol();
99527
- VERSION = true ? "0.23.37" : "0.0.0-dev";
99553
+ VERSION = true ? "0.23.39" : "0.0.0-dev";
99528
99554
  }
99529
99555
  });
99530
99556
  await init_index();
@@ -134886,6 +134912,11 @@ var PersistentClaudeSession = class {
134886
134912
  * which also prevents agent↔agent reply loops. Tool access is unaffected: it
134887
134913
  * stays gated on currentAutoReply (owner DM) OR currentArmedGetter (armed room). */
134888
134914
  currentReplyExpected = false;
134915
+ /** #1180: whether the say tool may deliver on this turn. Rooms pass an explicit
134916
+ * `replyExpected` (#1107: no @ → nobody replies) and that decides it; a 1:1 DM
134917
+ * passes none and say stays allowed there. Distinct from `currentReplyExpected`,
134918
+ * which drives only the #416 prose fallback and is false for un-attested DMs. */
134919
+ currentSayAllowed = true;
134889
134920
  saidThisTurn = false;
134890
134921
  turnText = "";
134891
134922
  /** #630 drop-probe: did a `result` event arrive for the CURRENT turn? Reset per
@@ -134958,6 +134989,12 @@ var PersistentClaudeSession = class {
134958
134989
  * back to opts.onSay. This is what closes the DM→room leak: the destination is
134959
134990
  * the one captured for the message being answered, not a live global target. */
134960
134991
  async deliver(text) {
134992
+ if (!this.currentSayAllowed) {
134993
+ logLine(
134994
+ `[session] say during a turn this agent does not owe (no @mention) \u2014 SUPPRESSED: ${JSON.stringify(text.slice(0, 80))}`
134995
+ );
134996
+ return;
134997
+ }
134961
134998
  this.saidThisTurn = true;
134962
134999
  if (this.activeReply) await this.activeReply(text);
134963
135000
  else if (this.opts.onSay) await this.opts.onSay(text);
@@ -134999,6 +135036,7 @@ var PersistentClaudeSession = class {
134999
135036
  this.activeReply = item.reply;
135000
135037
  this.currentAutoReply = item.autoReplyOnText ?? false;
135001
135038
  this.currentReplyExpected = item.replyExpected ?? item.autoReplyOnText ?? false;
135039
+ this.currentSayAllowed = item.replyExpected ?? true;
135002
135040
  this.currentArmedGetter = item.armed ?? (() => false);
135003
135041
  this.saidThisTurn = false;
135004
135042
  this.turnText = "";
@@ -135928,7 +135966,7 @@ async function main() {
135928
135966
  "[bridge] warning: passing the invite token on the command line is visible to other local users via 'ps'. Prefer: AV_INVITE_TOKEN=\u2026 npx @agentvault/claude-bridge"
135929
135967
  );
135930
135968
  }
135931
- logLine(`[bridge] version: ${true ? "0.8.10" : "dev"}`);
135969
+ logLine(`[bridge] version: ${true ? "0.8.12" : "dev"}`);
135932
135970
  logLine(`[bridge] data dir: ${cfg.dataDir} (${cfg.dataDirSource})`);
135933
135971
  logLine(`[bridge] workspace: ${cfg.workspaceDir} \xB7 permissions: ${cfg.permissionMode} \xB7 enclave dir fenced`);
135934
135972
  if (cfg.armRoom) {
@@ -135960,7 +135998,7 @@ async function main() {
135960
135998
  // its default would render "@agentvault/agentvault@0.7.x" — the wrong
135961
135999
  // package name attached to the bridge's version number, which is worse
135962
136000
  // than either alone.
135963
- clientVersion: `@agentvault/claude-bridge@${true ? "0.8.10" : "dev"}`
136001
+ clientVersion: `@agentvault/claude-bridge@${true ? "0.8.12" : "dev"}`
135964
136002
  });
135965
136003
  const agentSystemPrompt = cfg.systemPrompt ?? `You are ${cfg.agentName}, an AI agent on AgentVault. You talk with your owner in 1:1 direct messages and collaborate with other agents in shared rooms. That is your identity \u2014 introduce yourself by that name and do not claim to be any other agent. To speak, call the say tool. In a 1:1 with your owner, reply to what they say. In a room you see every message \u2014 call say only when you have something worth adding, and otherwise stay silent. Keep messages concise.`;
135966
136004
  const deviceJwt = () => {
package/dist/session.d.ts CHANGED
@@ -135,6 +135,11 @@ export declare class PersistentClaudeSession {
135
135
  * which also prevents agent↔agent reply loops. Tool access is unaffected: it
136
136
  * stays gated on currentAutoReply (owner DM) OR currentArmedGetter (armed room). */
137
137
  private currentReplyExpected;
138
+ /** #1180: whether the say tool may deliver on this turn. Rooms pass an explicit
139
+ * `replyExpected` (#1107: no @ → nobody replies) and that decides it; a 1:1 DM
140
+ * passes none and say stays allowed there. Distinct from `currentReplyExpected`,
141
+ * which drives only the #416 prose fallback and is false for un-attested DMs. */
142
+ private currentSayAllowed;
138
143
  private saidThisTurn;
139
144
  private turnText;
140
145
  /** #630 drop-probe: did a `result` event arrive for the CURRENT turn? Reset per
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentvault/claude-bridge",
3
- "version": "0.8.10",
3
+ "version": "0.8.12",
4
4
  "type": "module",
5
5
  "description": "AgentVault Claude Bridge — daemon for bridging a Claude agent into secure E2E-encrypted AgentVault 1:1 direct messages and rooms.",
6
6
  "main": "dist/index.js",