@agentvault/claude-bridge 0.7.6 → 0.7.8

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
@@ -453,12 +453,12 @@ import { join as join11 } from "node:path";
453
453
  // ../plugin/dist/index.js
454
454
  import * as nc from "node:crypto";
455
455
  import { chmod, mkdir, writeFile } from "node:fs/promises";
456
- import { readFile, rm } from "node:fs/promises";
456
+ import { readdir, readFile, rename, rm } from "node:fs/promises";
457
457
  import { join } from "node:path";
458
- import { readFile as readFile2, rename, rm as rm2 } from "node:fs/promises";
458
+ import { readFile as readFile2, rename as rename2, rm as rm2 } from "node:fs/promises";
459
459
  import { join as join2 } from "node:path";
460
460
  import { Readable } from "node:stream";
461
- import { readdir, readFile as readFile3, writeFile as writeFile2, rename as rename2, stat, unlink, mkdir as mkdir2 } from "node:fs/promises";
461
+ import { readdir as readdir2, readFile as readFile3, writeFile as writeFile2, rename as rename3, stat, unlink, mkdir as mkdir2 } from "node:fs/promises";
462
462
  import { join as join3 } from "node:path";
463
463
  import { randomUUID } from "node:crypto";
464
464
  import { EventEmitter } from "node:events";
@@ -53073,8 +53073,8 @@ var init_dhkem = __esm2({
53073
53073
  async deserializePrivateKey(key) {
53074
53074
  return await this._prim.deserializePrivateKey(toArrayBuffer(key));
53075
53075
  }
53076
- async importKey(format, key, isPublic = true) {
53077
- return await this._prim.importKey(format, key, isPublic);
53076
+ async importKey(format2, key, isPublic = true) {
53077
+ return await this._prim.importKey(format2, key, isPublic);
53078
53078
  }
53079
53079
  async generateKeyPair() {
53080
53080
  return await this._prim.generateKeyPair();
@@ -53717,10 +53717,10 @@ var init_ec = __esm2({
53717
53717
  throw new DeserializeError(e7);
53718
53718
  }
53719
53719
  }
53720
- async importKey(format, key, isPublic) {
53720
+ async importKey(format2, key, isPublic) {
53721
53721
  await this._setup();
53722
53722
  try {
53723
- if (format === "raw") {
53723
+ if (format2 === "raw") {
53724
53724
  return await this._importRawKey(key, isPublic);
53725
53725
  }
53726
53726
  if (key instanceof ArrayBuffer) {
@@ -55107,10 +55107,10 @@ var init_x25519 = __esm2({
55107
55107
  throw new DeserializeError(e7);
55108
55108
  }
55109
55109
  }
55110
- async importKey(format, key, isPublic) {
55110
+ async importKey(format2, key, isPublic) {
55111
55111
  await this._setup();
55112
55112
  try {
55113
- if (format === "raw") {
55113
+ if (format2 === "raw") {
55114
55114
  return await this._importRawKey(key, isPublic);
55115
55115
  }
55116
55116
  if (key instanceof ArrayBuffer) {
@@ -60045,10 +60045,10 @@ function _splitEndoScalar(k2, basis, n22) {
60045
60045
  }
60046
60046
  return { k1neg, k1: k12, k2neg, k2: k22 };
60047
60047
  }
60048
- function validateSigFormat(format) {
60049
- if (!["compact", "recovered", "der"].includes(format))
60048
+ function validateSigFormat(format2) {
60049
+ if (!["compact", "recovered", "der"].includes(format2))
60050
60050
  throw new Error('Signature format must be "compact", "recovered", or "der"');
60051
- return format;
60051
+ return format2;
60052
60052
  }
60053
60053
  function validateSigOpts(opts, def) {
60054
60054
  const optsn = {};
@@ -60736,11 +60736,11 @@ function ecdsa(Point, hash22, ecdsaOpts = {}) {
60736
60736
  throw new Error(`invalid signature ${title}: out of range 1..Point.Fn.ORDER`);
60737
60737
  return num;
60738
60738
  }
60739
- function validateSigLength(bytes, format) {
60740
- validateSigFormat(format);
60739
+ function validateSigLength(bytes, format2) {
60740
+ validateSigFormat(format2);
60741
60741
  const size = lengths.signature;
60742
- const sizer = format === "compact" ? size : format === "recovered" ? size + 1 : void 0;
60743
- return _abytes2(bytes, sizer, `${format} signature`);
60742
+ const sizer = format2 === "compact" ? size : format2 === "recovered" ? size + 1 : void 0;
60743
+ return _abytes2(bytes, sizer, `${format2} signature`);
60744
60744
  }
60745
60745
  class Signature {
60746
60746
  constructor(r22, s22, recovery) {
@@ -60750,16 +60750,16 @@ function ecdsa(Point, hash22, ecdsaOpts = {}) {
60750
60750
  this.recovery = recovery;
60751
60751
  Object.freeze(this);
60752
60752
  }
60753
- static fromBytes(bytes, format = defaultSigOpts_format) {
60754
- validateSigLength(bytes, format);
60753
+ static fromBytes(bytes, format2 = defaultSigOpts_format) {
60754
+ validateSigLength(bytes, format2);
60755
60755
  let recid;
60756
- if (format === "der") {
60756
+ if (format2 === "der") {
60757
60757
  const { r: r32, s: s32 } = DER.toSig(_abytes2(bytes));
60758
60758
  return new Signature(r32, s32);
60759
60759
  }
60760
- if (format === "recovered") {
60760
+ if (format2 === "recovered") {
60761
60761
  recid = bytes[0];
60762
- format = "compact";
60762
+ format2 = "compact";
60763
60763
  bytes = bytes.subarray(1);
60764
60764
  }
60765
60765
  const L22 = Fn3.BYTES;
@@ -60767,8 +60767,8 @@ function ecdsa(Point, hash22, ecdsaOpts = {}) {
60767
60767
  const s22 = bytes.subarray(L22, L22 * 2);
60768
60768
  return new Signature(Fn3.fromBytes(r22), Fn3.fromBytes(s22), recid);
60769
60769
  }
60770
- static fromHex(hex32, format) {
60771
- return this.fromBytes(hexToBytes4(hex32), format);
60770
+ static fromHex(hex32, format2) {
60771
+ return this.fromBytes(hexToBytes4(hex32), format2);
60772
60772
  }
60773
60773
  addRecoveryBit(recovery) {
60774
60774
  return new Signature(this.r, this.s, recovery);
@@ -60800,21 +60800,21 @@ function ecdsa(Point, hash22, ecdsaOpts = {}) {
60800
60800
  hasHighS() {
60801
60801
  return isBiggerThanHalfOrder(this.s);
60802
60802
  }
60803
- toBytes(format = defaultSigOpts_format) {
60804
- validateSigFormat(format);
60805
- if (format === "der")
60803
+ toBytes(format2 = defaultSigOpts_format) {
60804
+ validateSigFormat(format2);
60805
+ if (format2 === "der")
60806
60806
  return hexToBytes4(DER.hexFromSig(this));
60807
60807
  const r22 = Fn3.toBytes(this.r);
60808
60808
  const s22 = Fn3.toBytes(this.s);
60809
- if (format === "recovered") {
60809
+ if (format2 === "recovered") {
60810
60810
  if (this.recovery == null)
60811
60811
  throw new Error("recovery bit must be present");
60812
60812
  return concatBytes2(Uint8Array.of(this.recovery), r22, s22);
60813
60813
  }
60814
60814
  return concatBytes2(r22, s22);
60815
60815
  }
60816
- toHex(format) {
60817
- return bytesToHex4(this.toBytes(format));
60816
+ toHex(format2) {
60817
+ return bytesToHex4(this.toBytes(format2));
60818
60818
  }
60819
60819
  // TODO: remove
60820
60820
  assertValidity() {
@@ -60931,12 +60931,12 @@ function ecdsa(Point, hash22, ecdsaOpts = {}) {
60931
60931
  return sig;
60932
60932
  }
60933
60933
  function verify(signature, message, publicKey, opts = {}) {
60934
- const { lowS, prehash, format } = validateSigOpts(opts, defaultSigOpts);
60934
+ const { lowS, prehash, format: format2 } = validateSigOpts(opts, defaultSigOpts);
60935
60935
  publicKey = ensureBytes("publicKey", publicKey);
60936
60936
  message = validateMsgAndHash(ensureBytes("message", message), prehash);
60937
60937
  if ("strict" in opts)
60938
60938
  throw new Error("options.strict was renamed to lowS");
60939
- const sig = format === void 0 ? tryParsingSig(signature) : Signature.fromBytes(ensureBytes("sig", signature), format);
60939
+ const sig = format2 === void 0 ? tryParsingSig(signature) : Signature.fromBytes(ensureBytes("sig", signature), format2);
60940
60940
  if (sig === false)
60941
60941
  return false;
60942
60942
  try {
@@ -63991,7 +63991,7 @@ async function backupState(dataDir) {
63991
63991
  try {
63992
63992
  const data = await readFile2(src, "utf-8");
63993
63993
  await writeSecureFile(tmp, data);
63994
- await rename(tmp, bak);
63994
+ await rename2(tmp, bak);
63995
63995
  } catch {
63996
63996
  }
63997
63997
  }
@@ -64126,19 +64126,30 @@ var init_transport2 = __esm2({
64126
64126
  });
64127
64127
  var openclaw_compat_exports = {};
64128
64128
  __export2(openclaw_compat_exports, {
64129
+ AGENT_EVENT_CANDIDATES: () => AGENT_EVENT_CANDIDATES,
64130
+ HEARTBEAT_CANDIDATES: () => HEARTBEAT_CANDIDATES,
64131
+ TRANSCRIPT_CANDIDATES: () => TRANSCRIPT_CANDIDATES,
64132
+ _resetCompatCacheForTest: () => _resetCompatCacheForTest,
64129
64133
  onAgentEvent: () => onAgentEvent,
64130
64134
  onSessionTranscriptUpdate: () => onSessionTranscriptUpdate,
64131
- requestHeartbeatNow: () => requestHeartbeatNow
64135
+ requestHeartbeatNow: () => requestHeartbeatNow,
64136
+ resolveSdkFn: () => resolveSdkFn
64132
64137
  });
64133
- async function requestHeartbeatNow(opts) {
64134
- if (_heartbeatFn === null) {
64138
+ async function resolveSdkFn(candidates, importer = defaultImporter) {
64139
+ for (const { specifier, symbol: symbol22 } of candidates) {
64135
64140
  try {
64136
- const mod4 = await import("openclaw/dist/plugin-sdk/infra/heartbeat-wake.js");
64137
- _heartbeatFn = mod4.requestHeartbeatNow ?? mod4.default?.requestHeartbeatNow ?? false;
64141
+ const mod4 = await importer(specifier);
64142
+ const found = mod4?.[symbol22] ?? mod4?.default?.[symbol22];
64143
+ if (typeof found === "function") return found;
64138
64144
  } catch {
64139
- _heartbeatFn = false;
64140
64145
  }
64141
64146
  }
64147
+ return false;
64148
+ }
64149
+ async function requestHeartbeatNow(opts) {
64150
+ if (_heartbeatFn === null) {
64151
+ _heartbeatFn = await resolveSdkFn(HEARTBEAT_CANDIDATES);
64152
+ }
64142
64153
  if (typeof _heartbeatFn === "function") {
64143
64154
  try {
64144
64155
  await _heartbeatFn(opts);
@@ -64151,12 +64162,7 @@ async function requestHeartbeatNow(opts) {
64151
64162
  }
64152
64163
  async function onAgentEvent(callback) {
64153
64164
  if (_agentEventFn === null) {
64154
- try {
64155
- const mod4 = await import("openclaw/dist/plugin-sdk/infra/agent-events.js");
64156
- _agentEventFn = mod4.onAgentEvent ?? mod4.default?.onAgentEvent ?? false;
64157
- } catch {
64158
- _agentEventFn = false;
64159
- }
64165
+ _agentEventFn = await resolveSdkFn(AGENT_EVENT_CANDIDATES);
64160
64166
  }
64161
64167
  if (typeof _agentEventFn === "function") {
64162
64168
  try {
@@ -64171,12 +64177,7 @@ async function onAgentEvent(callback) {
64171
64177
  }
64172
64178
  async function onSessionTranscriptUpdate(callback) {
64173
64179
  if (_transcriptFn === null) {
64174
- try {
64175
- const mod4 = await import("openclaw/dist/plugin-sdk/sessions/transcript-events.js");
64176
- _transcriptFn = mod4.onSessionTranscriptUpdate ?? mod4.default?.onSessionTranscriptUpdate ?? false;
64177
- } catch {
64178
- _transcriptFn = false;
64179
- }
64180
+ _transcriptFn = await resolveSdkFn(TRANSCRIPT_CANDIDATES);
64180
64181
  }
64181
64182
  if (typeof _transcriptFn === "function") {
64182
64183
  try {
@@ -64189,12 +64190,45 @@ async function onSessionTranscriptUpdate(callback) {
64189
64190
  return () => {
64190
64191
  };
64191
64192
  }
64193
+ function _resetCompatCacheForTest() {
64194
+ _heartbeatFn = null;
64195
+ _agentEventFn = null;
64196
+ _transcriptFn = null;
64197
+ }
64198
+ var defaultImporter;
64199
+ var HEARTBEAT_CANDIDATES;
64200
+ var AGENT_EVENT_CANDIDATES;
64201
+ var TRANSCRIPT_CANDIDATES;
64192
64202
  var _heartbeatFn;
64193
64203
  var _agentEventFn;
64194
64204
  var _transcriptFn;
64195
64205
  var init_openclaw_compat = __esm2({
64196
64206
  "src/openclaw-compat.ts"() {
64197
64207
  "use strict";
64208
+ defaultImporter = (specifier) => import(
64209
+ /* @vite-ignore */
64210
+ specifier
64211
+ );
64212
+ HEARTBEAT_CANDIDATES = [
64213
+ // 2026.7.x — exported subpath, and the symbol lost its `Now` suffix.
64214
+ { specifier: "openclaw/plugin-sdk/heartbeat-runtime", symbol: "requestHeartbeat" },
64215
+ // <=2026.6.x — deep dist path, original name.
64216
+ { specifier: "openclaw/dist/plugin-sdk/infra/heartbeat-wake.js", symbol: "requestHeartbeatNow" }
64217
+ ];
64218
+ AGENT_EVENT_CANDIDATES = [
64219
+ { specifier: "openclaw/plugin-sdk/agent-harness", symbol: "onAgentEvent" },
64220
+ { specifier: "openclaw/dist/plugin-sdk/infra/agent-events.js", symbol: "onAgentEvent" }
64221
+ ];
64222
+ TRANSCRIPT_CANDIDATES = [
64223
+ {
64224
+ specifier: "openclaw/plugin-sdk/memory-core-host-engine-foundation",
64225
+ symbol: "onSessionTranscriptUpdate"
64226
+ },
64227
+ {
64228
+ specifier: "openclaw/dist/plugin-sdk/sessions/transcript-events.js",
64229
+ symbol: "onSessionTranscriptUpdate"
64230
+ }
64231
+ ];
64198
64232
  _heartbeatFn = null;
64199
64233
  _agentEventFn = null;
64200
64234
  _transcriptFn = null;
@@ -64509,7 +64543,7 @@ async function handleWorkspaceUpload(data, workspaceDir) {
64509
64543
  const tempPath = join3(workspaceDir, `.tmp_${randomUUID()}_${data.filename}`);
64510
64544
  try {
64511
64545
  await writeFile2(tempPath, data.content, "utf-8");
64512
- await rename2(tempPath, targetPath);
64546
+ await rename3(tempPath, targetPath);
64513
64547
  } catch (err) {
64514
64548
  try {
64515
64549
  await unlink(tempPath);
@@ -64526,7 +64560,7 @@ async function handleWorkspaceUpload(data, workspaceDir) {
64526
64560
  }
64527
64561
  async function handleWorkspaceList(workspaceDir) {
64528
64562
  try {
64529
- const entries = await readdir(workspaceDir);
64563
+ const entries = await readdir2(workspaceDir);
64530
64564
  const files = [];
64531
64565
  for (const entry of entries) {
64532
64566
  if (!entry.endsWith(".md")) continue;
@@ -65687,7 +65721,7 @@ var init_channel = __esm2({
65687
65721
  */
65688
65722
  sendActivitySpan(spanData) {
65689
65723
  if (!this._ws || this._ws.readyState !== WebSocket2.OPEN) return;
65690
- const pluginVersion = true ? "0.23.18" : "0.0.0-dev";
65724
+ const pluginVersion = true ? "0.23.19" : "0.0.0-dev";
65691
65725
  const agentName = this.config.agentName ?? "Agent";
65692
65726
  const resource = {
65693
65727
  "service.name": "agentvault-agent",
@@ -67552,7 +67586,7 @@ var init_channel = __esm2({
67552
67586
  agentVersion: this.config.agentVersion ?? "0.0.0",
67553
67587
  // __AV_VERSION__ is injected by esbuild from package.json at build time.
67554
67588
  // Falls back to "0.0.0-dev" in non-bundled contexts (tests).
67555
- pluginVersion: true ? "0.23.18" : "0.0.0-dev"
67589
+ pluginVersion: true ? "0.23.19" : "0.0.0-dev"
67556
67590
  });
67557
67591
  this._telemetryReporter.startAutoFlush(3e4);
67558
67592
  }
@@ -67875,7 +67909,7 @@ var init_channel = __esm2({
67875
67909
  agentVersion: this.config.agentVersion ?? "0.0.0",
67876
67910
  // __AV_VERSION__ is injected by esbuild from package.json at build time.
67877
67911
  // Falls back to "0.0.0-dev" in non-bundled contexts (tests).
67878
- pluginVersion: true ? "0.23.18" : "0.0.0-dev"
67912
+ pluginVersion: true ? "0.23.19" : "0.0.0-dev"
67879
67913
  });
67880
67914
  this._telemetryReporter.startAutoFlush(3e4);
67881
67915
  }
@@ -71697,6 +71731,11 @@ var init_tool_audit = __esm2({
71697
71731
  "use strict";
71698
71732
  }
71699
71733
  });
71734
+ var init_harness_compat = __esm2({
71735
+ "src/harness-compat.ts"() {
71736
+ "use strict";
71737
+ }
71738
+ });
71700
71739
  var isUsingManagedRoutes;
71701
71740
  var init_openclaw_entry = __esm2({
71702
71741
  "src/openclaw-entry.ts"() {
@@ -71707,6 +71746,7 @@ var init_openclaw_entry = __esm2({
71707
71746
  init_openclaw_compat();
71708
71747
  init_lifecycle();
71709
71748
  init_tool_audit();
71749
+ init_harness_compat();
71710
71750
  init_types();
71711
71751
  isUsingManagedRoutes = false;
71712
71752
  }
@@ -80824,13 +80864,13 @@ function _stringbool(Classes, _params) {
80824
80864
  return codec22;
80825
80865
  }
80826
80866
  // @__NO_SIDE_EFFECTS__
80827
- function _stringFormat(Class22, format, fnOrRegex, _params = {}) {
80867
+ function _stringFormat(Class22, format2, fnOrRegex, _params = {}) {
80828
80868
  const params = normalizeParams(_params);
80829
80869
  const def = {
80830
80870
  ...normalizeParams(_params),
80831
80871
  check: "string_format",
80832
80872
  type: "string",
80833
- format,
80873
+ format: format2,
80834
80874
  fn: typeof fnOrRegex === "function" ? fnOrRegex : (val) => fnOrRegex.test(val),
80835
80875
  ...params
80836
80876
  };
@@ -81295,16 +81335,16 @@ var init_json_schema_processors = __esm2({
81295
81335
  stringProcessor = (schema, ctx, _json, _params) => {
81296
81336
  const json22 = _json;
81297
81337
  json22.type = "string";
81298
- const { minimum, maximum, format, patterns, contentEncoding } = schema._zod.bag;
81338
+ const { minimum, maximum, format: format2, patterns, contentEncoding } = schema._zod.bag;
81299
81339
  if (typeof minimum === "number")
81300
81340
  json22.minLength = minimum;
81301
81341
  if (typeof maximum === "number")
81302
81342
  json22.maxLength = maximum;
81303
- if (format) {
81304
- json22.format = formatMap[format] ?? format;
81343
+ if (format2) {
81344
+ json22.format = formatMap[format2] ?? format2;
81305
81345
  if (json22.format === "")
81306
81346
  delete json22.format;
81307
- if (format === "time") {
81347
+ if (format2 === "time") {
81308
81348
  delete json22.format;
81309
81349
  }
81310
81350
  }
@@ -81326,8 +81366,8 @@ var init_json_schema_processors = __esm2({
81326
81366
  };
81327
81367
  numberProcessor = (schema, ctx, _json, _params) => {
81328
81368
  const json22 = _json;
81329
- const { minimum, maximum, format, multipleOf, exclusiveMaximum, exclusiveMinimum } = schema._zod.bag;
81330
- if (typeof format === "string" && format.includes("int"))
81369
+ const { minimum, maximum, format: format2, multipleOf, exclusiveMaximum, exclusiveMinimum } = schema._zod.bag;
81370
+ if (typeof format2 === "string" && format2.includes("int"))
81331
81371
  json22.type = "integer";
81332
81372
  else
81333
81373
  json22.type = "number";
@@ -82482,8 +82522,8 @@ function e1642(params) {
82482
82522
  function jwt(params) {
82483
82523
  return /* @__PURE__ */ _jwt(ZodJWT, params);
82484
82524
  }
82485
- function stringFormat(format, fnOrRegex, _params = {}) {
82486
- return /* @__PURE__ */ _stringFormat(ZodCustomStringFormat, format, fnOrRegex, _params);
82525
+ function stringFormat(format2, fnOrRegex, _params = {}) {
82526
+ return /* @__PURE__ */ _stringFormat(ZodCustomStringFormat, format2, fnOrRegex, _params);
82487
82527
  }
82488
82528
  function hostname2(_params) {
82489
82529
  return /* @__PURE__ */ _stringFormat(ZodCustomStringFormat, "hostname", regexes_exports.hostname, _params);
@@ -82493,11 +82533,11 @@ function hex2(_params) {
82493
82533
  }
82494
82534
  function hash(alg, params) {
82495
82535
  const enc = params?.enc ?? "hex";
82496
- const format = `${alg}_${enc}`;
82497
- const regex = regexes_exports[format];
82536
+ const format2 = `${alg}_${enc}`;
82537
+ const regex = regexes_exports[format2];
82498
82538
  if (!regex)
82499
- throw new Error(`Unrecognized hash format: ${format}`);
82500
- return /* @__PURE__ */ _stringFormat(ZodCustomStringFormat, format, regex, params);
82539
+ throw new Error(`Unrecognized hash format: ${format2}`);
82540
+ return /* @__PURE__ */ _stringFormat(ZodCustomStringFormat, format2, regex, params);
82501
82541
  }
82502
82542
  function number2(params) {
82503
82543
  return /* @__PURE__ */ _number(ZodNumber2, params);
@@ -91980,10 +92020,10 @@ var require_core = __commonJS({
91980
92020
  return this;
91981
92021
  }
91982
92022
  // Add format
91983
- addFormat(name, format) {
91984
- if (typeof format == "string")
91985
- format = new RegExp(format);
91986
- this.formats[name] = format;
92023
+ addFormat(name, format2) {
92024
+ if (typeof format2 == "string")
92025
+ format2 = new RegExp(format2);
92026
+ this.formats[name] = format2;
91987
92027
  return this;
91988
92028
  }
91989
92029
  errorsText(errors = this.errors, { separator = ", ", dataVar = "data" } = {}) {
@@ -92101,9 +92141,9 @@ var require_core = __commonJS({
92101
92141
  }
92102
92142
  function addInitialFormats() {
92103
92143
  for (const name in this.opts.formats) {
92104
- const format = this.opts.formats[name];
92105
- if (format)
92106
- this.addFormat(name, format);
92144
+ const format2 = this.opts.formats[name];
92145
+ if (format2)
92146
+ this.addFormat(name, format2);
92107
92147
  }
92108
92148
  }
92109
92149
  function addInitialKeywords(defs) {
@@ -93718,18 +93758,18 @@ var require_format = __commonJS({
93718
93758
  });
93719
93759
  const fDef = gen.const("fDef", (0, codegen_1._)`${fmts}[${schemaCode}]`);
93720
93760
  const fType = gen.let("fType");
93721
- const format = gen.let("format");
93722
- gen.if((0, codegen_1._)`typeof ${fDef} == "object" && !(${fDef} instanceof RegExp)`, () => gen.assign(fType, (0, codegen_1._)`${fDef}.type || "string"`).assign(format, (0, codegen_1._)`${fDef}.validate`), () => gen.assign(fType, (0, codegen_1._)`"string"`).assign(format, fDef));
93761
+ const format2 = gen.let("format");
93762
+ gen.if((0, codegen_1._)`typeof ${fDef} == "object" && !(${fDef} instanceof RegExp)`, () => gen.assign(fType, (0, codegen_1._)`${fDef}.type || "string"`).assign(format2, (0, codegen_1._)`${fDef}.validate`), () => gen.assign(fType, (0, codegen_1._)`"string"`).assign(format2, fDef));
93723
93763
  cxt.fail$data((0, codegen_1.or)(unknownFmt(), invalidFmt()));
93724
93764
  function unknownFmt() {
93725
93765
  if (opts.strictSchema === false)
93726
93766
  return codegen_1.nil;
93727
- return (0, codegen_1._)`${schemaCode} && !${format}`;
93767
+ return (0, codegen_1._)`${schemaCode} && !${format2}`;
93728
93768
  }
93729
93769
  function invalidFmt() {
93730
- const callFormat = schemaEnv.$async ? (0, codegen_1._)`(${fDef}.async ? await ${format}(${data}) : ${format}(${data}))` : (0, codegen_1._)`${format}(${data})`;
93731
- const validData = (0, codegen_1._)`(typeof ${format} == "function" ? ${callFormat} : ${format}.test(${data}))`;
93732
- return (0, codegen_1._)`${format} && ${format} !== true && ${fType} === ${ruleType} && !${validData}`;
93770
+ const callFormat = schemaEnv.$async ? (0, codegen_1._)`(${fDef}.async ? await ${format2}(${data}) : ${format2}(${data}))` : (0, codegen_1._)`${format2}(${data})`;
93771
+ const validData = (0, codegen_1._)`(typeof ${format2} == "function" ? ${callFormat} : ${format2}.test(${data}))`;
93772
+ return (0, codegen_1._)`${format2} && ${format2} !== true && ${fType} === ${ruleType} && !${validData}`;
93733
93773
  }
93734
93774
  }
93735
93775
  function validateFormat() {
@@ -93740,7 +93780,7 @@ var require_format = __commonJS({
93740
93780
  }
93741
93781
  if (formatDef === true)
93742
93782
  return;
93743
- const [fmtType, format, fmtRef] = getFormat(formatDef);
93783
+ const [fmtType, format2, fmtRef] = getFormat(formatDef);
93744
93784
  if (fmtType === ruleType)
93745
93785
  cxt.pass(validCondition());
93746
93786
  function unknownFormat() {
@@ -93767,7 +93807,7 @@ var require_format = __commonJS({
93767
93807
  throw new Error("async format in sync schema");
93768
93808
  return (0, codegen_1._)`await ${fmtRef}(${data})`;
93769
93809
  }
93770
- return typeof format == "function" ? (0, codegen_1._)`${fmtRef}(${data})` : (0, codegen_1._)`${fmtRef}.test(${data})`;
93810
+ return typeof format2 == "function" ? (0, codegen_1._)`${fmtRef}(${data})` : (0, codegen_1._)`${fmtRef}.test(${data})`;
93771
93811
  }
93772
93812
  }
93773
93813
  }
@@ -93780,8 +93820,8 @@ var require_format2 = __commonJS({
93780
93820
  "use strict";
93781
93821
  Object.defineProperty(exports, "__esModule", { value: true });
93782
93822
  var format_1 = require_format();
93783
- var format = [format_1.default];
93784
- exports.default = format;
93823
+ var format2 = [format_1.default];
93824
+ exports.default = format2;
93785
93825
  }
93786
93826
  });
93787
93827
  var require_metadata = __commonJS({
@@ -94407,17 +94447,17 @@ var require_limit = __commonJS({
94407
94447
  cxt.fail$data((0, codegen_1.or)((0, codegen_1._)`typeof ${fmt} != "object"`, (0, codegen_1._)`${fmt} instanceof RegExp`, (0, codegen_1._)`typeof ${fmt}.compare != "function"`, compareCode(fmt)));
94408
94448
  }
94409
94449
  function validateFormat() {
94410
- const format = fCxt.schema;
94411
- const fmtDef = self2.formats[format];
94450
+ const format2 = fCxt.schema;
94451
+ const fmtDef = self2.formats[format2];
94412
94452
  if (!fmtDef || fmtDef === true)
94413
94453
  return;
94414
94454
  if (typeof fmtDef != "object" || fmtDef instanceof RegExp || typeof fmtDef.compare != "function") {
94415
- throw new Error(`"${keyword}": format "${format}" does not define "compare" function`);
94455
+ throw new Error(`"${keyword}": format "${format2}" does not define "compare" function`);
94416
94456
  }
94417
94457
  const fmt = gen.scopeValue("formats", {
94418
- key: format,
94458
+ key: format2,
94419
94459
  ref: fmtDef,
94420
- code: opts.code.formats ? (0, codegen_1._)`${opts.code.formats}${(0, codegen_1.getProperty)(format)}` : void 0
94460
+ code: opts.code.formats ? (0, codegen_1._)`${opts.code.formats}${(0, codegen_1.getProperty)(format2)}` : void 0
94421
94461
  });
94422
94462
  cxt.fail$data(compareCode(fmt));
94423
94463
  }
@@ -97758,7 +97798,7 @@ var init_index = __esm2({
97758
97798
  init_skill_invoker();
97759
97799
  await init_skill_telemetry();
97760
97800
  await init_policy_enforcer();
97761
- VERSION = true ? "0.23.18" : "0.0.0-dev";
97801
+ VERSION = true ? "0.23.19" : "0.0.0-dev";
97762
97802
  }
97763
97803
  });
97764
97804
  await init_index();
@@ -119140,6 +119180,16 @@ function readDeviceId(dataDir) {
119140
119180
  );
119141
119181
  }
119142
119182
 
119183
+ // src/log.ts
119184
+ import { format } from "node:util";
119185
+ function stamp(message) {
119186
+ const ts2 = (/* @__PURE__ */ new Date()).toISOString();
119187
+ return message.split("\n").map((line) => `${ts2} ${line}`).join("\n");
119188
+ }
119189
+ function logLine(...args) {
119190
+ console.error(stamp(format(...args)));
119191
+ }
119192
+
119143
119193
  // src/worker-permission.ts
119144
119194
  var PATH_FIELDS = ["file_path", "path", "notebook_path"];
119145
119195
  function canonical(p2) {
@@ -119246,7 +119296,7 @@ function makeWorkerPreToolUseHook(opts) {
119246
119296
  }
119247
119297
  }
119248
119298
  if (verdict.deny) {
119249
- console.error(`[worker-gate] DENY ${toolName} \u2014 ${verdict.reason}`);
119299
+ logLine(`[worker-gate] DENY ${toolName} \u2014 ${verdict.reason}`);
119250
119300
  return {
119251
119301
  hookSpecificOutput: {
119252
119302
  hookEventName: "PreToolUse",
@@ -119264,7 +119314,7 @@ function makeWorkerPermission(opts) {
119264
119314
  return async (toolName, input) => {
119265
119315
  const verdict = gateDecision(toolName, input, opts);
119266
119316
  if (verdict.deny) {
119267
- console.error(`[worker-gate] DENY ${toolName} \u2014 ${verdict.reason}`);
119317
+ logLine(`[worker-gate] DENY ${toolName} \u2014 ${verdict.reason}`);
119268
119318
  return { behavior: "deny", message: verdict.reason };
119269
119319
  }
119270
119320
  return { behavior: "allow", updatedInput: input };
@@ -130162,13 +130212,13 @@ function _stringbool2(Classes, _params) {
130162
130212
  return codec3;
130163
130213
  }
130164
130214
  // @__NO_SIDE_EFFECTS__
130165
- function _stringFormat2(Class3, format, fnOrRegex, _params = {}) {
130215
+ function _stringFormat2(Class3, format2, fnOrRegex, _params = {}) {
130166
130216
  const params = normalizeParams2(_params);
130167
130217
  const def = {
130168
130218
  ...normalizeParams2(_params),
130169
130219
  check: "string_format",
130170
130220
  type: "string",
130171
- format,
130221
+ format: format2,
130172
130222
  fn: typeof fnOrRegex === "function" ? fnOrRegex : (val) => fnOrRegex.test(val),
130173
130223
  ...params
130174
130224
  };
@@ -130543,16 +130593,16 @@ var formatMap2 = {
130543
130593
  var stringProcessor2 = (schema, ctx, _json, _params) => {
130544
130594
  const json3 = _json;
130545
130595
  json3.type = "string";
130546
- const { minimum, maximum, format, patterns, contentEncoding } = schema._zod.bag;
130596
+ const { minimum, maximum, format: format2, patterns, contentEncoding } = schema._zod.bag;
130547
130597
  if (typeof minimum === "number")
130548
130598
  json3.minLength = minimum;
130549
130599
  if (typeof maximum === "number")
130550
130600
  json3.maxLength = maximum;
130551
- if (format) {
130552
- json3.format = formatMap2[format] ?? format;
130601
+ if (format2) {
130602
+ json3.format = formatMap2[format2] ?? format2;
130553
130603
  if (json3.format === "")
130554
130604
  delete json3.format;
130555
- if (format === "time") {
130605
+ if (format2 === "time") {
130556
130606
  delete json3.format;
130557
130607
  }
130558
130608
  }
@@ -130574,8 +130624,8 @@ var stringProcessor2 = (schema, ctx, _json, _params) => {
130574
130624
  };
130575
130625
  var numberProcessor2 = (schema, ctx, _json, _params) => {
130576
130626
  const json3 = _json;
130577
- const { minimum, maximum, format, multipleOf, exclusiveMaximum, exclusiveMinimum } = schema._zod.bag;
130578
- if (typeof format === "string" && format.includes("int"))
130627
+ const { minimum, maximum, format: format2, multipleOf, exclusiveMaximum, exclusiveMinimum } = schema._zod.bag;
130628
+ if (typeof format2 === "string" && format2.includes("int"))
130579
130629
  json3.type = "integer";
130580
130630
  else
130581
130631
  json3.type = "number";
@@ -131764,8 +131814,8 @@ var ZodCustomStringFormat2 = /* @__PURE__ */ $constructor2("ZodCustomStringForma
131764
131814
  $ZodCustomStringFormat2.init(inst, def);
131765
131815
  ZodStringFormat2.init(inst, def);
131766
131816
  });
131767
- function stringFormat2(format, fnOrRegex, _params = {}) {
131768
- return _stringFormat2(ZodCustomStringFormat2, format, fnOrRegex, _params);
131817
+ function stringFormat2(format2, fnOrRegex, _params = {}) {
131818
+ return _stringFormat2(ZodCustomStringFormat2, format2, fnOrRegex, _params);
131769
131819
  }
131770
131820
  function hostname5(_params) {
131771
131821
  return _stringFormat2(ZodCustomStringFormat2, "hostname", regexes_exports2.hostname, _params);
@@ -131775,11 +131825,11 @@ function hex4(_params) {
131775
131825
  }
131776
131826
  function hash2(alg, params) {
131777
131827
  const enc = params?.enc ?? "hex";
131778
- const format = `${alg}_${enc}`;
131779
- const regex = regexes_exports2[format];
131828
+ const format2 = `${alg}_${enc}`;
131829
+ const regex = regexes_exports2[format2];
131780
131830
  if (!regex)
131781
- throw new Error(`Unrecognized hash format: ${format}`);
131782
- return _stringFormat2(ZodCustomStringFormat2, format, regex, params);
131831
+ throw new Error(`Unrecognized hash format: ${format2}`);
131832
+ return _stringFormat2(ZodCustomStringFormat2, format2, regex, params);
131783
131833
  }
131784
131834
  var ZodNumber3 = /* @__PURE__ */ $constructor2("ZodNumber", (inst, def) => {
131785
131835
  $ZodNumber2.init(inst, def);
@@ -132755,52 +132805,52 @@ function convertBaseSchema(schema, ctx) {
132755
132805
  case "string": {
132756
132806
  let stringSchema = z4.string();
132757
132807
  if (schema.format) {
132758
- const format = schema.format;
132759
- if (format === "email") {
132808
+ const format2 = schema.format;
132809
+ if (format2 === "email") {
132760
132810
  stringSchema = stringSchema.check(z4.email());
132761
- } else if (format === "uri" || format === "uri-reference") {
132811
+ } else if (format2 === "uri" || format2 === "uri-reference") {
132762
132812
  stringSchema = stringSchema.check(z4.url());
132763
- } else if (format === "uuid" || format === "guid") {
132813
+ } else if (format2 === "uuid" || format2 === "guid") {
132764
132814
  stringSchema = stringSchema.check(z4.uuid());
132765
- } else if (format === "date-time") {
132815
+ } else if (format2 === "date-time") {
132766
132816
  stringSchema = stringSchema.check(z4.iso.datetime());
132767
- } else if (format === "date") {
132817
+ } else if (format2 === "date") {
132768
132818
  stringSchema = stringSchema.check(z4.iso.date());
132769
- } else if (format === "time") {
132819
+ } else if (format2 === "time") {
132770
132820
  stringSchema = stringSchema.check(z4.iso.time());
132771
- } else if (format === "duration") {
132821
+ } else if (format2 === "duration") {
132772
132822
  stringSchema = stringSchema.check(z4.iso.duration());
132773
- } else if (format === "ipv4") {
132823
+ } else if (format2 === "ipv4") {
132774
132824
  stringSchema = stringSchema.check(z4.ipv4());
132775
- } else if (format === "ipv6") {
132825
+ } else if (format2 === "ipv6") {
132776
132826
  stringSchema = stringSchema.check(z4.ipv6());
132777
- } else if (format === "mac") {
132827
+ } else if (format2 === "mac") {
132778
132828
  stringSchema = stringSchema.check(z4.mac());
132779
- } else if (format === "cidr") {
132829
+ } else if (format2 === "cidr") {
132780
132830
  stringSchema = stringSchema.check(z4.cidrv4());
132781
- } else if (format === "cidr-v6") {
132831
+ } else if (format2 === "cidr-v6") {
132782
132832
  stringSchema = stringSchema.check(z4.cidrv6());
132783
- } else if (format === "base64") {
132833
+ } else if (format2 === "base64") {
132784
132834
  stringSchema = stringSchema.check(z4.base64());
132785
- } else if (format === "base64url") {
132835
+ } else if (format2 === "base64url") {
132786
132836
  stringSchema = stringSchema.check(z4.base64url());
132787
- } else if (format === "e164") {
132837
+ } else if (format2 === "e164") {
132788
132838
  stringSchema = stringSchema.check(z4.e164());
132789
- } else if (format === "jwt") {
132839
+ } else if (format2 === "jwt") {
132790
132840
  stringSchema = stringSchema.check(z4.jwt());
132791
- } else if (format === "emoji") {
132841
+ } else if (format2 === "emoji") {
132792
132842
  stringSchema = stringSchema.check(z4.emoji());
132793
- } else if (format === "nanoid") {
132843
+ } else if (format2 === "nanoid") {
132794
132844
  stringSchema = stringSchema.check(z4.nanoid());
132795
- } else if (format === "cuid") {
132845
+ } else if (format2 === "cuid") {
132796
132846
  stringSchema = stringSchema.check(z4.cuid());
132797
- } else if (format === "cuid2") {
132847
+ } else if (format2 === "cuid2") {
132798
132848
  stringSchema = stringSchema.check(z4.cuid2());
132799
- } else if (format === "ulid") {
132849
+ } else if (format2 === "ulid") {
132800
132850
  stringSchema = stringSchema.check(z4.ulid());
132801
- } else if (format === "xid") {
132851
+ } else if (format2 === "xid") {
132802
132852
  stringSchema = stringSchema.check(z4.xid());
132803
- } else if (format === "ksuid") {
132853
+ } else if (format2 === "ksuid") {
132804
132854
  stringSchema = stringSchema.check(z4.ksuid());
132805
132855
  }
132806
132856
  }
@@ -133279,7 +133329,7 @@ var PersistentClaudeSession = class {
133279
133329
  });
133280
133330
  }
133281
133331
  };
133282
- console.error(
133332
+ logLine(
133283
133333
  `[worker-gate] active \u2014 1:1 owner DM: FULL access, no allowlist, no workspace fence. Armed room: FULL access whenever "Remove all gates" is on (live, owner-controlled, off by default); otherwise allowlist, workspace=${this.opts.workspaceDir ?? "(none: file tools disabled)"}, bash=${this.opts.osIsolated ? "enabled (OS-isolated)" : "disabled"}`
133284
133334
  );
133285
133335
  return {
@@ -133320,7 +133370,19 @@ var PersistentClaudeSession = class {
133320
133370
  this.roomServer = _s({
133321
133371
  name: "room",
133322
133372
  version: "0.2.0",
133323
- tools: [makeRoomSayTool((text) => this.deliver(text))]
133373
+ tools: [makeRoomSayTool((text) => this.deliver(text))],
133374
+ // LOAD-BEARING for turn latency. The SDK defers MCP tools behind tool
133375
+ // search by default, so the WORKER (which gets the full built-in toolset
133376
+ // for owner DMs) could not see its own voice without first spending a
133377
+ // model round-trip on `ToolSearch {"query":"select:mcp__room__say"}` —
133378
+ // measured on 175 of 175 worker sessions, ~2-5s of a ~20s turn, for a tool
133379
+ // already in `allowedTools`. The listener escaped it only by accident, via
133380
+ // `tools: []`. Set on BOTH lanes so the listener stays correct if its
133381
+ // toolset ever widens.
133382
+ //
133383
+ // NOT a permission change: room_say is already allowed on every session.
133384
+ // This stops HIDING it, it does not grant it.
133385
+ alwaysLoad: true
133324
133386
  });
133325
133387
  const sdkOptions = this.buildSdkOptions();
133326
133388
  const q10 = this.opts.queryImpl ? this.opts.queryImpl({
@@ -133346,7 +133408,7 @@ var PersistentClaudeSession = class {
133346
133408
  if (reply) {
133347
133409
  void reply(this.turnText);
133348
133410
  } else {
133349
- console.error(
133411
+ logLine(
133350
133412
  `[drop-probe] BLOCKED at result: composed ${this.turnText.length} chars, reply expected but activeReply unset \u2014 reply DROPPED`
133351
133413
  );
133352
133414
  }
@@ -133355,7 +133417,7 @@ var PersistentClaudeSession = class {
133355
133417
  }
133356
133418
  } finally {
133357
133419
  if (this.currentReplyExpected && !this.saidThisTurn && !this.sawResultThisTurn && this.turnText.trim()) {
133358
- console.error(
133420
+ logLine(
133359
133421
  `[drop-probe] STREAM ENDED before result: composed ${this.turnText.length} chars, reply expected, said=false \u2014 reply DROPPED (no result event)`
133360
133422
  );
133361
133423
  }
@@ -133398,6 +133460,16 @@ var WorkerQueue = class {
133398
133460
  this.running = false;
133399
133461
  }
133400
133462
  }
133463
+ /**
133464
+ * What the worker had produced, when the session can report it. Read on BOTH the
133465
+ * clean and the failing path: on a clean turn `said: false` with a non-zero
133466
+ * `composedChars` is the "composed but could never ship it" signature (agent-
133467
+ * authored room traffic, where `replyExpected` is false), which is otherwise
133468
+ * indistinguishable from a turn that did nothing.
133469
+ */
133470
+ snapshotOf(session) {
133471
+ return session?.snapshot?.();
133472
+ }
133401
133473
  /** Best-effort trap emit. A broken sink must never break the queue. */
133402
133474
  emitIncident(rec) {
133403
133475
  try {
@@ -133429,10 +133501,23 @@ var WorkerQueue = class {
133429
133501
  }, this.deps.timeoutMs);
133430
133502
  });
133431
133503
  await Promise.race([session.start(), timeout]);
133504
+ const okSnap = this.snapshotOf(session);
133505
+ this.emitIncident({
133506
+ at: new Date(startedAt).toISOString(),
133507
+ outcome: "ok",
133508
+ waitedMs,
133509
+ ranMs: Date.now() - startedAt,
133510
+ timeoutMs: this.deps.timeoutMs,
133511
+ queueDepthAtEnqueue: entry.queueDepthAtEnqueue,
133512
+ queueDepthAtStart,
133513
+ instructionChars: task.instruction.length,
133514
+ replyExpected: task.replyExpected,
133515
+ ...okSnap ? { session: okSnap } : {}
133516
+ });
133432
133517
  } catch (e7) {
133433
133518
  const err = e7;
133434
133519
  this.deps.log(`[worker-queue] task failed: ${err.message}`);
133435
- const snap = session?.snapshot?.();
133520
+ const snap = this.snapshotOf(session);
133436
133521
  this.emitIncident({
133437
133522
  at: new Date(startedAt).toISOString(),
133438
133523
  outcome: timedOut ? "timeout" : "error",
@@ -133443,6 +133528,7 @@ var WorkerQueue = class {
133443
133528
  queueDepthAtEnqueue: entry.queueDepthAtEnqueue,
133444
133529
  queueDepthAtStart,
133445
133530
  instructionChars: task.instruction.length,
133531
+ replyExpected: task.replyExpected,
133446
133532
  ...snap ? { session: snap } : {}
133447
133533
  });
133448
133534
  session?.abort();
@@ -133464,7 +133550,7 @@ function makeRouter(deps) {
133464
133550
  const replySink = reply ?? (() => {
133465
133551
  });
133466
133552
  const isOwnerDm = opts?.autoReplyOnText === true && opts?.armed === void 0 && deps.workAllowed();
133467
- const isArmedRoom = opts?.armed?.() === true && deps.workAllowed();
133553
+ const isArmedRoom = opts?.armed?.() === true && deps.workAllowed() && opts?.replyExpected !== false;
133468
133554
  if (isOwnerDm) {
133469
133555
  deps.queue.enqueue({
133470
133556
  instruction: text,
@@ -134023,22 +134109,22 @@ async function main() {
134023
134109
  const cfg = loadConfig(process.env, process.argv.slice(2));
134024
134110
  const firstArg = process.argv.slice(2)[0];
134025
134111
  if (firstArg && !firstArg.startsWith("-") && firstArg === cfg.inviteToken) {
134026
- console.error(
134112
+ logLine(
134027
134113
  "[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"
134028
134114
  );
134029
134115
  }
134030
- console.error(`[bridge] version: ${true ? "0.7.6" : "dev"}`);
134031
- console.error(`[bridge] data dir: ${cfg.dataDir} (${cfg.dataDirSource})`);
134032
- console.error(`[bridge] workspace: ${cfg.workspaceDir} \xB7 permissions: ${cfg.permissionMode} \xB7 enclave dir fenced`);
134116
+ logLine(`[bridge] version: ${true ? "0.7.8" : "dev"}`);
134117
+ logLine(`[bridge] data dir: ${cfg.dataDir} (${cfg.dataDirSource})`);
134118
+ logLine(`[bridge] workspace: ${cfg.workspaceDir} \xB7 permissions: ${cfg.permissionMode} \xB7 enclave dir fenced`);
134033
134119
  if (cfg.armRoom) {
134034
- console.error(
134120
+ logLine(
134035
134121
  `[bridge] ARMED ROOM ${cfg.roomFilter} \u2014 worker tools are enabled on this room's turns. The enclave key dir stays fenced; the workspace dir is ADVISORY (the SDK does not confine file ops to it \u2014 they root in $HOME). Blast radius = whatever this host/environment exposes.`
134036
134122
  );
134037
- console.error(
134123
+ logLine(
134038
134124
  "[bridge] ARMED ROOM WARNING: this room contains agents you do not control. A room peer's message can drive your agent's tools. The directory is NOT a sandbox \u2014 run this bridge in an owner-provisioned confined environment (dedicated droplet / container / OS-user) with no connected accounts or ambient credentials. See the Slice 2 hardening guide."
134039
134125
  );
134040
134126
  } else {
134041
- console.error(
134127
+ logLine(
134042
134128
  '[bridge] 1:1 owner DMs run with FULL tool access when "Allow tools" is on for this agent (dashboard switch, default off): any command, any file this user account can reach, network, skills and subagents \u2014 equivalent to a terminal on this machine, with no per-action prompt. Rooms are NOT affected: they stay say-only unless armed (AV_ARM_ROOM=1 with a pinned AV_ROOM_ID), and an armed room keeps the confined allowlist. Each tool-enabled turn runs in a fresh session seeded with only that one message, so room text cannot leak into a DM turn.'
134043
134129
  );
134044
134130
  }
@@ -134069,7 +134155,7 @@ async function main() {
134069
134155
  // per-message binding is what prevents a private 1:1 reply from leaking into a
134070
134156
  // room when room traffic arrives mid-compose.
134071
134157
  // Assistant reasoning that wasn't sent — log a short trace only.
134072
- onObserve: (text) => console.error(`[bridge] observed (${text.length} chars, not sent)`),
134158
+ onObserve: (text) => logLine(`[bridge] observed (${text.length} chars, not sent)`),
134073
134159
  worker: false
134074
134160
  });
134075
134161
  const WORKER_MAX_TURNS = 20;
@@ -134078,7 +134164,7 @@ async function main() {
134078
134164
  makeSession: (task) => new PersistentClaudeSession({
134079
134165
  model: cfg.model,
134080
134166
  systemPrompt: agentSystemPrompt,
134081
- onObserve: (text) => console.error(`[worker] observed (${text.length} chars, not sent)`),
134167
+ onObserve: (text) => logLine(`[worker] observed (${text.length} chars, not sent)`),
134082
134168
  worker: true,
134083
134169
  // Task 4: the hard backstop — no tool runs on any worker turn (owner DM or
134084
134170
  // armed room) unless Work is on. Read live per tool decision.
@@ -134101,7 +134187,7 @@ async function main() {
134101
134187
  deviceJwt
134102
134188
  }),
134103
134189
  timeoutMs: WORKER_TIMEOUT_MS,
134104
- log: (m6) => console.error(m6),
134190
+ log: (m6) => logLine(m6),
134105
134191
  // TRAP (2026-07-25). The 07-24 five-minute silence was diagnosable only by
134106
134192
  // luck — the drop-trap happened to record a reply landing 300.016s after the
134107
134193
  // ack, and 300s is WORKER_TIMEOUT_MS exactly. Nothing recorded WHY the worker
@@ -134109,15 +134195,15 @@ async function main() {
134109
134195
  // Append-only JSONL beside the logs, plus one stamped console line so the
134110
134196
  // incident is visible in bridge.error.log at the moment it happens.
134111
134197
  onIncident: (rec) => {
134112
- console.error(
134113
- `[worker-trap] ${rec.at} ${rec.outcome} after ${rec.ranMs}ms (waited ${rec.waitedMs}ms behind ${rec.queueDepthAtEnqueue}, ${rec.queueDepthAtStart} still queued)` + (rec.session ? ` composed=${rec.session.composedChars} result=${rec.session.sawResult} said=${rec.session.said}` : "")
134198
+ logLine(
134199
+ `[worker-trap] ${rec.at} ${rec.outcome} after ${rec.ranMs}ms (waited ${rec.waitedMs}ms behind ${rec.queueDepthAtEnqueue}, ${rec.queueDepthAtStart} still queued) replyExpected=${rec.replyExpected}` + (rec.session ? ` composed=${rec.session.composedChars} result=${rec.session.sawResult} said=${rec.session.said}` : "")
134114
134200
  );
134115
134201
  try {
134116
134202
  const dir = join11(cfg.dataDir, "logs");
134117
134203
  mkdirSync6(dir, { recursive: true });
134118
134204
  appendFileSync2(join11(dir, "worker-incidents.jsonl"), JSON.stringify(rec) + "\n");
134119
134205
  } catch (err) {
134120
- console.error(`[worker-trap] could not persist incident: ${err.message}`);
134206
+ logLine(`[worker-trap] could not persist incident: ${err.message}`);
134121
134207
  }
134122
134208
  }
134123
134209
  });
@@ -134129,7 +134215,7 @@ async function main() {
134129
134215
  {
134130
134216
  roomFilter: cfg.roomFilter,
134131
134217
  armRoom: cfg.armRoom,
134132
- log: (m6) => console.error("[bridge] " + m6),
134218
+ log: (m6) => logLine("[bridge] " + m6),
134133
134219
  // Slice 2 Plan C (T11): live arm/disarm + local-approval poll + heartbeat.
134134
134220
  workspaceDir: cfg.workspaceDir,
134135
134221
  dataDir: cfg.dataDir,
@@ -134145,21 +134231,21 @@ async function main() {
134145
134231
  }
134146
134232
  );
134147
134233
  attachLifecycle2(channel, {
134148
- log: (m6) => console.error("[bridge] " + m6)
134234
+ log: (m6) => logLine("[bridge] " + m6)
134149
134235
  });
134150
- channel.on("state", (s10) => console.error(`[bridge] channel state: ${JSON.stringify(s10)}`));
134236
+ channel.on("state", (s10) => logLine(`[bridge] channel state: ${JSON.stringify(s10)}`));
134151
134237
  channel.on(
134152
134238
  "room_joined",
134153
- (e7) => console.error(`[bridge] joined room ${e7.name} (${e7.roomId})`)
134239
+ (e7) => logLine(`[bridge] joined room ${e7.name} (${e7.roomId})`)
134154
134240
  );
134155
134241
  listener.start().catch((err) => {
134156
- console.error("[bridge] fatal:", err);
134242
+ logLine("[bridge] fatal:", err);
134157
134243
  process.exit(1);
134158
134244
  });
134159
134245
  await channel.start();
134160
134246
  }
134161
134247
  main().catch((err) => {
134162
- console.error("[bridge] fatal:", err);
134248
+ logLine("[bridge] fatal:", err);
134163
134249
  process.exit(1);
134164
134250
  });
134165
134251
  /*! Bundled license information:
package/dist/log.d.ts ADDED
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Prefix every line of `message` with an ISO-8601 UTC timestamp.
3
+ *
4
+ * Per-LINE, not per-call, on purpose: `logLine("[bridge] fatal:", err)` renders a
5
+ * multi-line stack trace, and a first-line-only stamp would leave every frame
6
+ * unanchored — the case where the clock matters most. The acceptance criterion
7
+ * on #790 is "every line in the file", so this reads it literally.
8
+ */
9
+ export declare function stamp(message: string): string;
10
+ /**
11
+ * Write one stamped record to stderr.
12
+ *
13
+ * stderr (not stdout) because the launchd/systemd unit routes both, and the
14
+ * bridge's existing sinks were already `console.error` — keeping the stream the
15
+ * same means this change alters the CONTENT of the logs and nothing else.
16
+ *
17
+ * Multi-arg is supported via `util.format`, which is what `console.error` uses
18
+ * internally, so `logLine("[bridge] fatal:", err)` renders exactly as before —
19
+ * just anchored to a clock.
20
+ */
21
+ export declare function logLine(...args: unknown[]): void;
22
+ //# sourceMappingURL=log.d.ts.map
@@ -17,19 +17,27 @@ export type WorkerTask = {
17
17
  armed?: () => boolean;
18
18
  };
19
19
  /**
20
- * Forensic record of a task that did NOT complete cleanly.
20
+ * Forensic record of EVERY task the queue runs clean or not.
21
21
  *
22
22
  * Exists because the 2026-07-24 "loopita went quiet for 5 minutes" incident was
23
23
  * diagnosable only by coincidence: the drop-trap happened to capture that the
24
24
  * reply landed 300.016s after the ack, and 300s is `WORKER_TIMEOUT_MS` exactly.
25
25
  * Nothing in the bridge recorded WHY the worker hung, and the logs carry no
26
26
  * timestamps at all, so the evidence was gone by the time we looked.
27
+ *
28
+ * 2026-08-06: widened from failures-only to every turn. Recording only failures
29
+ * made the SLOW case unmeasurable, and slow is the common case: loopita answered
30
+ * Ballroom2 in 21s when the room was quiet and 81s when a second agent was
31
+ * talking, and nothing said whether the extra 60s was `waitedMs` or `ranMs`.
32
+ * Those are different bugs with identical symptoms. A trap that only reports
33
+ * when it breaks cannot tell you it is the reason things are slow.
27
34
  */
28
35
  export type WorkerIncident = {
29
36
  /** ISO timestamp — the bridge's own logs are unstamped, so this is the anchor. */
30
37
  at: string;
31
- outcome: "timeout" | "error";
32
- error: string;
38
+ outcome: "ok" | "timeout" | "error";
39
+ /** Absent on a clean turn — its presence is what marks a real incident. */
40
+ error?: string;
33
41
  /**
34
42
  * enqueue -> start. THE load-bearing field: it separates "this task hung" from
35
43
  * "this task was stuck behind one that hung". Both look identical to the owner
@@ -44,6 +52,15 @@ export type WorkerIncident = {
44
52
  /** Depth still waiting when this task began — the blast radius of this hang. */
45
53
  queueDepthAtStart: number;
46
54
  instructionChars: number;
55
+ /**
56
+ * Whether a reply was expected of this turn — mirrors `WorkerTask.replyExpected`,
57
+ * i.e. `senderIsAgent === false` upstream. Without it a silent turn
58
+ * (composed=0, said=false) is unattributable: it could be agent-authored traffic
59
+ * that was never going to answer, or a turn that waited so long the previous one
60
+ * already answered. Ballroom2 on 2026-08-06 produced both readings and the record
61
+ * could not separate them.
62
+ */
63
+ replyExpected?: boolean;
47
64
  /** What the worker had produced when it died, when the session can report it. */
48
65
  session?: {
49
66
  composedChars: number;
@@ -91,6 +108,14 @@ export declare class WorkerQueue {
91
108
  /** Resolves when the queue has processed everything enqueued so far. */
92
109
  whenDrained(): Promise<void>;
93
110
  private drain;
111
+ /**
112
+ * What the worker had produced, when the session can report it. Read on BOTH the
113
+ * clean and the failing path: on a clean turn `said: false` with a non-zero
114
+ * `composedChars` is the "composed but could never ship it" signature (agent-
115
+ * authored room traffic, where `replyExpected` is false), which is otherwise
116
+ * indistinguishable from a turn that did nothing.
117
+ */
118
+ private snapshotOf;
94
119
  /** Best-effort trap emit. A broken sink must never break the queue. */
95
120
  private emitIncident;
96
121
  private runOne;
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@agentvault/claude-bridge",
3
- "version": "0.7.6",
3
+ "version": "0.7.8",
4
4
  "type": "module",
5
- "description": "AgentVault Claude Bridge daemon for bridging a Claude agent into secure E2E-encrypted AgentVault 1:1 direct messages and rooms.",
5
+ "description": "AgentVault Claude Bridge \u2014 daemon for bridging a Claude agent into secure E2E-encrypted AgentVault 1:1 direct messages and rooms.",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
8
8
  "bin": {