@agentvault/claude-bridge 0.8.11 → 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
@@ -64612,11 +64612,11 @@ var init_mls_kp_pool = __esm2({
64612
64612
  }
64613
64613
  });
64614
64614
  function ownIdentity() {
64615
- const v22 = true ? "0.23.38" : FALLBACK;
64615
+ const v22 = true ? "0.23.39" : FALLBACK;
64616
64616
  return `${PACKAGE}@${v22}`;
64617
64617
  }
64618
64618
  function buildSha() {
64619
- const raw = true ? "a6ce360-dirty" : "";
64619
+ const raw = true ? "9982fc8-dirty" : "";
64620
64620
  const cleaned = (raw ?? "").trim();
64621
64621
  return !cleaned || cleaned === "unknown" ? null : cleaned;
64622
64622
  }
@@ -65719,6 +65719,27 @@ var init_channel = __esm2({
65719
65719
  * this process (GH #366). Cleared on (re)join so the next first message
65720
65720
  * re-fetches the authoritative roster. Prevents per-message refetch storms. */
65721
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
+ }
65722
65743
  /** In-flight roster-refresh promises (per room) — dedup concurrent refreshes. */
65723
65744
  _roomRosterRefreshInFlight = /* @__PURE__ */ new Map();
65724
65745
  /** Dedup buffer for A2A message IDs (prevents double-delivery via direct + Redis) */
@@ -66814,7 +66835,7 @@ var init_channel = __esm2({
66814
66835
  */
66815
66836
  sendActivitySpan(spanData) {
66816
66837
  if (!this._ws || this._ws.readyState !== WebSocket2.OPEN) return;
66817
- const pluginVersion = true ? "0.23.38" : "0.0.0-dev";
66838
+ const pluginVersion = true ? "0.23.39" : "0.0.0-dev";
66818
66839
  const agentName = this.config.agentName ?? "Agent";
66819
66840
  const resource = {
66820
66841
  "service.name": "agentvault-agent",
@@ -68752,7 +68773,7 @@ var init_channel = __esm2({
68752
68773
  agentVersion: this.config.agentVersion ?? "0.0.0",
68753
68774
  // __AV_VERSION__ is injected by esbuild from package.json at build time.
68754
68775
  // Falls back to "0.0.0-dev" in non-bundled contexts (tests).
68755
- pluginVersion: true ? "0.23.38" : "0.0.0-dev"
68776
+ pluginVersion: true ? "0.23.39" : "0.0.0-dev"
68756
68777
  });
68757
68778
  this._telemetryReporter.startAutoFlush(3e4);
68758
68779
  }
@@ -69076,7 +69097,7 @@ var init_channel = __esm2({
69076
69097
  agentVersion: this.config.agentVersion ?? "0.0.0",
69077
69098
  // __AV_VERSION__ is injected by esbuild from package.json at build time.
69078
69099
  // Falls back to "0.0.0-dev" in non-bundled contexts (tests).
69079
- pluginVersion: true ? "0.23.38" : "0.0.0-dev"
69100
+ pluginVersion: true ? "0.23.39" : "0.0.0-dev"
69080
69101
  });
69081
69102
  this._telemetryReporter.startAutoFlush(3e4);
69082
69103
  }
@@ -69849,12 +69870,13 @@ ${messageText}`;
69849
69870
  * Returns the text unchanged when there is no attachment or the fetch fails —
69850
69871
  * but a failure is LOGGED and announced in the text, never silent.
69851
69872
  */
69852
- async _augmentWithAttachment(attachmentInfo, baseText) {
69873
+ async _augmentWithAttachment(attachmentInfo, baseText, roomId) {
69853
69874
  if (!attachmentInfo) return baseText;
69854
69875
  try {
69855
69876
  const { filePath, decrypted } = await this._downloadAndDecryptAttachment(attachmentInfo);
69856
69877
  const mime = String(attachmentInfo.mime ?? "application/octet-stream");
69857
69878
  const name = String(attachmentInfo.filename ?? "attachment");
69879
+ if (roomId) this._recentRoomAttachments.set(roomId, { filePath, name, at: Date.now() });
69858
69880
  const textMimes = ["text/", "application/json", "application/xml", "application/csv"];
69859
69881
  if (textMimes.some((m22) => mime.startsWith(m22))) {
69860
69882
  const content = new TextDecoder().decode(decrypted);
@@ -70484,7 +70506,9 @@ ${baseText}`;
70484
70506
  }
70485
70507
  }
70486
70508
  if (roomAttachment) {
70487
- messageText = await this._augmentWithAttachment(roomAttachment, messageText);
70509
+ messageText = await this._augmentWithAttachment(roomAttachment, messageText, resolvedRoomId);
70510
+ } else {
70511
+ messageText = this._withRecentRoomAttachmentNote(resolvedRoomId, messageText);
70488
70512
  }
70489
70513
  const roomMembers = this._persisted?.rooms?.[resolvedRoomId]?.members ?? [];
70490
70514
  const senderMember = roomMembers.find((m22) => m22.deviceId === senderDeviceId);
@@ -99526,7 +99550,7 @@ var init_index = __esm2({
99526
99550
  await init_skill_telemetry();
99527
99551
  await init_policy_enforcer();
99528
99552
  init_room_protocol();
99529
- VERSION = true ? "0.23.38" : "0.0.0-dev";
99553
+ VERSION = true ? "0.23.39" : "0.0.0-dev";
99530
99554
  }
99531
99555
  });
99532
99556
  await init_index();
@@ -134888,6 +134912,11 @@ var PersistentClaudeSession = class {
134888
134912
  * which also prevents agent↔agent reply loops. Tool access is unaffected: it
134889
134913
  * stays gated on currentAutoReply (owner DM) OR currentArmedGetter (armed room). */
134890
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;
134891
134920
  saidThisTurn = false;
134892
134921
  turnText = "";
134893
134922
  /** #630 drop-probe: did a `result` event arrive for the CURRENT turn? Reset per
@@ -134960,6 +134989,12 @@ var PersistentClaudeSession = class {
134960
134989
  * back to opts.onSay. This is what closes the DM→room leak: the destination is
134961
134990
  * the one captured for the message being answered, not a live global target. */
134962
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
+ }
134963
134998
  this.saidThisTurn = true;
134964
134999
  if (this.activeReply) await this.activeReply(text);
134965
135000
  else if (this.opts.onSay) await this.opts.onSay(text);
@@ -135001,6 +135036,7 @@ var PersistentClaudeSession = class {
135001
135036
  this.activeReply = item.reply;
135002
135037
  this.currentAutoReply = item.autoReplyOnText ?? false;
135003
135038
  this.currentReplyExpected = item.replyExpected ?? item.autoReplyOnText ?? false;
135039
+ this.currentSayAllowed = item.replyExpected ?? true;
135004
135040
  this.currentArmedGetter = item.armed ?? (() => false);
135005
135041
  this.saidThisTurn = false;
135006
135042
  this.turnText = "";
@@ -135930,7 +135966,7 @@ async function main() {
135930
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"
135931
135967
  );
135932
135968
  }
135933
- logLine(`[bridge] version: ${true ? "0.8.11" : "dev"}`);
135969
+ logLine(`[bridge] version: ${true ? "0.8.12" : "dev"}`);
135934
135970
  logLine(`[bridge] data dir: ${cfg.dataDir} (${cfg.dataDirSource})`);
135935
135971
  logLine(`[bridge] workspace: ${cfg.workspaceDir} \xB7 permissions: ${cfg.permissionMode} \xB7 enclave dir fenced`);
135936
135972
  if (cfg.armRoom) {
@@ -135962,7 +135998,7 @@ async function main() {
135962
135998
  // its default would render "@agentvault/agentvault@0.7.x" — the wrong
135963
135999
  // package name attached to the bridge's version number, which is worse
135964
136000
  // than either alone.
135965
- clientVersion: `@agentvault/claude-bridge@${true ? "0.8.11" : "dev"}`
136001
+ clientVersion: `@agentvault/claude-bridge@${true ? "0.8.12" : "dev"}`
135966
136002
  });
135967
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.`;
135968
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.11",
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",