@securecoms/agent-bridge 0.33.1 → 0.35.0

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/bin.js CHANGED
@@ -7416,37 +7416,37 @@ __export(layout_exports, {
7416
7416
  presentWithSize: () => presentWithSize,
7417
7417
  wrongTypeAt: () => wrongTypeAt
7418
7418
  });
7419
- import { chmodSync, lstatSync as lstatSync2, mkdirSync, renameSync, rmSync } from "node:fs";
7419
+ import { chmodSync as chmodSync2, lstatSync as lstatSync3, mkdirSync as mkdirSync2, renameSync, rmSync } from "node:fs";
7420
7420
  import { homedir as homedir2 } from "node:os";
7421
- import { basename as basename2, dirname as dirname4, join as join9 } from "node:path";
7421
+ import { basename as basename3, dirname as dirname5, join as join10 } from "node:path";
7422
7422
  function kind(p) {
7423
7423
  try {
7424
- return lstatSync2(p).isDirectory() ? "dir" : "other";
7424
+ return lstatSync3(p).isDirectory() ? "dir" : "other";
7425
7425
  } catch (e) {
7426
7426
  if (e.code === "ENOENT") return "absent";
7427
7427
  throw e;
7428
7428
  }
7429
7429
  }
7430
7430
  function plainDirChain(root3, parts, opts) {
7431
- const modelsDir = dirname4(root3);
7431
+ const modelsDir = dirname5(root3);
7432
7432
  for (const part of parts) if (!part || part === "." || part === ".." || part.includes("/") || part.includes("\\")) throw new LayoutRefused();
7433
7433
  const k = kind(modelsDir);
7434
7434
  if (k === "other") throw new LayoutRefused();
7435
7435
  if (k === "absent") {
7436
7436
  if (!opts.create) return null;
7437
- mkdirSync(modelsDir, { recursive: true, mode: 448 });
7437
+ mkdirSync2(modelsDir, { recursive: true, mode: 448 });
7438
7438
  if (kind(modelsDir) !== "dir") throw new LayoutRefused();
7439
7439
  }
7440
- const chain = opts.fromModelsDir ? parts : [basename2(root3), ...parts];
7440
+ const chain = opts.fromModelsDir ? parts : [basename3(root3), ...parts];
7441
7441
  let p = modelsDir;
7442
7442
  for (let i = 0; i < chain.length; i++) {
7443
- p = join9(p, chain[i]);
7443
+ p = join10(p, chain[i]);
7444
7444
  const c = kind(p);
7445
7445
  if (c === "other") throw new LayoutRefused();
7446
7446
  if (c === "absent") {
7447
7447
  if (!opts.create) return null;
7448
7448
  try {
7449
- mkdirSync(p, { mode: i === chain.length - 1 && opts.mode !== void 0 ? opts.mode : 493 });
7449
+ mkdirSync2(p, { mode: i === chain.length - 1 && opts.mode !== void 0 ? opts.mode : 493 });
7450
7450
  } catch (e) {
7451
7451
  if (e.code !== "EEXIST") throw e;
7452
7452
  }
@@ -7457,7 +7457,7 @@ function plainDirChain(root3, parts, opts) {
7457
7457
  }
7458
7458
  function presentWithSize(p, size) {
7459
7459
  try {
7460
- const s = lstatSync2(p);
7460
+ const s = lstatSync3(p);
7461
7461
  return s.isFile() && s.size === size;
7462
7462
  } catch {
7463
7463
  return false;
@@ -7465,7 +7465,7 @@ function presentWithSize(p, size) {
7465
7465
  }
7466
7466
  function wrongTypeAt(p) {
7467
7467
  try {
7468
- const s = lstatSync2(p);
7468
+ const s = lstatSync3(p);
7469
7469
  return !s.isFile() && !s.isSymbolicLink();
7470
7470
  } catch {
7471
7471
  return false;
@@ -7478,16 +7478,16 @@ function installVerified(root3, modelId, f, partial, opts) {
7478
7478
  dir = plainDirChain(root3, [modelId, f.sha256], { create: true });
7479
7479
  if (!dir) throw new LayoutRefused();
7480
7480
  if (!opts.replace && presentWithSize(dest, f.size)) return;
7481
- chmodSync(dir, 493);
7481
+ chmodSync2(dir, 493);
7482
7482
  try {
7483
7483
  renameSync(partial, dest);
7484
7484
  } catch (e) {
7485
7485
  if (!presentWithSize(dest, f.size)) throw e;
7486
7486
  return;
7487
7487
  }
7488
- chmodSync(dest, 292);
7488
+ chmodSync2(dest, 292);
7489
7489
  } finally {
7490
- if (dir) chmodSync(dir, 365);
7490
+ if (dir) chmodSync2(dir, 365);
7491
7491
  rmSync(partial, { force: true });
7492
7492
  }
7493
7493
  }
@@ -7495,9 +7495,9 @@ var defaultModelsRoot, partialDir, filePath, LayoutRefused;
7495
7495
  var init_layout = __esm({
7496
7496
  "src/memory/embedder/layout.ts"() {
7497
7497
  "use strict";
7498
- defaultModelsRoot = (home = homedir2()) => join9(home, ".securecoms", "models", "embeddings");
7499
- partialDir = (root3) => join9(dirname4(root3), ".partial");
7500
- filePath = (root3, modelId, f) => join9(root3, modelId, f.sha256, f.name);
7498
+ defaultModelsRoot = (home = homedir2()) => join10(home, ".securecoms", "models", "embeddings");
7499
+ partialDir = (root3) => join10(dirname5(root3), ".partial");
7500
+ filePath = (root3, modelId, f) => join10(root3, modelId, f.sha256, f.name);
7501
7501
  LayoutRefused = class extends Error {
7502
7502
  constructor() {
7503
7503
  super("the model directory is not a plain directory");
@@ -7557,11 +7557,11 @@ __export(import_bundle_exports, {
7557
7557
  });
7558
7558
  import { createHash as createHash6, randomBytes as randomBytes12 } from "node:crypto";
7559
7559
  import { readFileSync as readFileSync16, statSync as statSync7, writeFileSync as writeFileSync4 } from "node:fs";
7560
- import { join as join24 } from "node:path";
7560
+ import { join as join25 } from "node:path";
7561
7561
  async function importModelBundle(srcDir, root3, m) {
7562
7562
  const verified = [];
7563
7563
  for (const f of m.files) {
7564
- const src = join24(srcDir, f.name);
7564
+ const src = join25(srcDir, f.name);
7565
7565
  let size;
7566
7566
  try {
7567
7567
  const s = statSync7(src);
@@ -7585,7 +7585,7 @@ async function importModelBundle(srcDir, root3, m) {
7585
7585
  for (const { f, bytes } of verified) {
7586
7586
  try {
7587
7587
  const pdir = plainDirChain(root3, [".partial"], { create: true, fromModelsDir: true, mode: 448 });
7588
- const partial = join24(pdir, randomBytes12(12).toString("hex"));
7588
+ const partial = join25(pdir, randomBytes12(12).toString("hex"));
7589
7589
  writeFileSync4(partial, bytes, { mode: 384, flag: "wx" });
7590
7590
  installVerified(root3, m.modelId, f, partial, { replace: true });
7591
7591
  } catch (e) {
@@ -19331,6 +19331,8 @@ function isEpochConflict(e) {
19331
19331
  var LABEL = "agentvault-connect-v1";
19332
19332
  var DEFAULT_STUCK_THRESHOLD = 2;
19333
19333
  var DEFAULT_QUARANTINE_THRESHOLD = 8;
19334
+ var KEPT_WELCOME_COMMIT_DEFER_MS = 24 * 60 * 60 * 1e3;
19335
+ var KEPT_WELCOME_COMMIT_DEFER_MAX_ROWS = 32;
19334
19336
  var DELIVERY_PULL_PAGE = 100;
19335
19337
  var DELIVERY_MAX_RETRIES = 5;
19336
19338
  var DrainPageFullError = class extends Error {
@@ -19535,6 +19537,15 @@ var MlsGroupKeyProvider = class _MlsGroupKeyProvider {
19535
19537
  * {@link errorStats}; mutated only through {@link recordDecryptError} /
19536
19538
  * {@link recordDecryptSuccess}.
19537
19539
  */
19540
+ /** {@link KEPT_WELCOME_COMMIT_DEFER_MS}: channel -> when its first commit was
19541
+ * deferred behind the kept Welcome `welcomeId`. Restarted for a different
19542
+ * Welcome; cleared when the channel decrypts, or when a pass that saw the
19543
+ * whole queue kept no Welcome for it (fix round 1, L2: the Welcome
19544
+ * resolved, so a later re-Welcome gets a fresh, bounded window). An entry
19545
+ * past its bound stays (expired) until then, so the bound cannot restart
19546
+ * itself pass after pass. In memory: a reload restarts it, and the
19547
+ * per-pass row cap is what bounds the page cost across reloads. */
19548
+ keptWelcomeDeferSince = /* @__PURE__ */ new Map();
19538
19549
  errors = /* @__PURE__ */ new Map();
19539
19550
  stuckThreshold;
19540
19551
  /** Consecutive-error threshold at which a wedged channel's row is quarantined
@@ -19753,8 +19764,33 @@ var MlsGroupKeyProvider = class _MlsGroupKeyProvider {
19753
19764
  * channel resumes normal processing and can quarantine again later if it
19754
19765
  * re-wedges (the onWedged guard is reset so the hook can fire again).
19755
19766
  */
19767
+ /** See {@link MlsGroupKeyProviderOpts.deferCommitsWhileWelcomePending}. The
19768
+ * caller has already established that this pass KEPT a no-state Welcome
19769
+ * for `channelId`. True: defer the commit row. Never throws. */
19770
+ deferKeptWelcomeCommit(channelId, welcomeId) {
19771
+ const pred = this.o.deferCommitsWhileWelcomePending;
19772
+ if (!pred)
19773
+ return false;
19774
+ let boundMs = 0;
19775
+ try {
19776
+ const v = pred(channelId);
19777
+ boundMs = v === true ? KEPT_WELCOME_COMMIT_DEFER_MS : typeof v === "number" && Number.isFinite(v) && v > 0 ? Math.min(v, KEPT_WELCOME_COMMIT_DEFER_MS) : 0;
19778
+ } catch {
19779
+ boundMs = 0;
19780
+ }
19781
+ if (boundMs <= 0)
19782
+ return false;
19783
+ const now = this.nowMs();
19784
+ const entry = this.keptWelcomeDeferSince.get(channelId);
19785
+ if (entry === void 0 || entry.welcomeId !== welcomeId) {
19786
+ this.keptWelcomeDeferSince.set(channelId, { since: now, welcomeId });
19787
+ return true;
19788
+ }
19789
+ return now - entry.since < boundMs;
19790
+ }
19756
19791
  recordDecryptSuccess(channelId) {
19757
19792
  this.lastDecryptAt.set(channelId, Date.now());
19793
+ this.keptWelcomeDeferSince.delete(channelId);
19758
19794
  const e = this.errors.get(channelId);
19759
19795
  if (!e)
19760
19796
  return;
@@ -20150,6 +20186,12 @@ var MlsGroupKeyProvider = class _MlsGroupKeyProvider {
20150
20186
  * server's REJOIN_THRESHOLD, there was nothing to apply, or a commit would
20151
20187
  * not apply (which means the state is genuinely FORKED, not merely behind,
20152
20188
  * and replay cannot fix a fork).
20189
+ *
20190
+ * THROWS (retryable, never `false`, #1290): CustodyPersistError when the
20191
+ * channel is under the custody hold (nothing applied), and the C2 classes
20192
+ * (PendingCommitUnresolvedError from the guarded MAIN write) after evicting
20193
+ * the advanced cache, so the persisted state is unchanged. A /sync request
20194
+ * that fails also throws, before anything is applied.
20153
20195
  */
20154
20196
  async resyncByReplay(channelId, groupId) {
20155
20197
  if (!await this.hasPersistedChannelState(channelId))
@@ -20169,12 +20211,26 @@ var MlsGroupKeyProvider = class _MlsGroupKeyProvider {
20169
20211
  let applied = 0;
20170
20212
  try {
20171
20213
  await this.locks.run(channelId, async () => {
20214
+ if (await this.custodyHeld(channelId))
20215
+ throw new CustodyPersistError(channelId);
20172
20216
  const mgr = await this.manager(channelId);
20173
20217
  try {
20174
20218
  for (const c of commits) {
20175
20219
  if (c.epoch <= Number(mgr.epoch))
20176
20220
  continue;
20177
- await mgr.decrypt(hexToBytes22(c.payload));
20221
+ const commitBytes = hexToBytes22(c.payload);
20222
+ try {
20223
+ await mgr.decrypt(commitBytes);
20224
+ } catch (e) {
20225
+ if (await probeOwnRemoval(mgr, commitBytes) !== null) {
20226
+ this.noteOwnLeafRemoved(channelId, {
20227
+ group_id: groupId,
20228
+ epoch: c.epoch,
20229
+ message_id: c.id
20230
+ });
20231
+ }
20232
+ throw e;
20233
+ }
20178
20234
  applied++;
20179
20235
  }
20180
20236
  if (applied > 0)
@@ -20186,6 +20242,10 @@ var MlsGroupKeyProvider = class _MlsGroupKeyProvider {
20186
20242
  }
20187
20243
  });
20188
20244
  } catch (e) {
20245
+ if (isSdkError(e, "CustodyPersistError") || e instanceof CustodyPersistError || e instanceof PendingCommitUnresolvedError || e instanceof GroupSupersededError || e instanceof AttestedOrphanRefusedError) {
20246
+ writeServiceLog2("warn", "resync.replay_held", { applied }, e);
20247
+ throw e;
20248
+ }
20189
20249
  writeServiceLog2("warn", "resync.replay_commit_failed", { applied }, e);
20190
20250
  return false;
20191
20251
  }
@@ -22886,6 +22946,8 @@ var MlsGroupKeyProvider = class _MlsGroupKeyProvider {
22886
22946
  out.push({
22887
22947
  queueId: `replay-${m.id}`,
22888
22948
  messageId: envelope.message_id,
22949
+ // #1246 C-1: the group-messages row id is the same mls_messages key.
22950
+ ...typeof m.id === "string" && m.id ? { serverMessageId: m.id } : {},
22889
22951
  channelId,
22890
22952
  plaintext,
22891
22953
  envelope,
@@ -23786,6 +23848,8 @@ var MlsGroupKeyProvider = class _MlsGroupKeyProvider {
23786
23848
  const heldBundleRefs = () => heldBundleRefsMemo ??= this.readHeldBundleRefs();
23787
23849
  const w2Acked = /* @__PURE__ */ new Map();
23788
23850
  const w2Kept = /* @__PURE__ */ new Set();
23851
+ const keptWelcomeId = /* @__PURE__ */ new Map();
23852
+ const deferredThisPass = /* @__PURE__ */ new Map();
23789
23853
  const unpinnedRefusals = /* @__PURE__ */ new Map();
23790
23854
  const welcomes = rows.filter((r) => r.message_type === "welcome");
23791
23855
  const nonWelcomes = rows.filter((r) => r.message_type !== "welcome");
@@ -23944,6 +24008,8 @@ var MlsGroupKeyProvider = class _MlsGroupKeyProvider {
23944
24008
  writeServiceLog2("warn", "welcome.unopenable");
23945
24009
  } else {
23946
24010
  w2Kept.add(wcid);
24011
+ if (!keptWelcomeId.has(wcid))
24012
+ keptWelcomeId.set(wcid, row.queue_id);
23947
24013
  if (typeof process !== "undefined" && process.env?.SECURECOMS_TRACE_DELIVERY) {
23948
24014
  writeServiceLog2("debug", "delivery.welcome_no_local_state");
23949
24015
  }
@@ -23954,6 +24020,10 @@ var MlsGroupKeyProvider = class _MlsGroupKeyProvider {
23954
24020
  const cid = row.connect_channel_id;
23955
24021
  if (!cid)
23956
24022
  continue;
24023
+ if (w2Kept.has(cid) && (deferredThisPass.get(cid) ?? 0) < KEPT_WELCOME_COMMIT_DEFER_MAX_ROWS && this.deferKeptWelcomeCommit(cid, keptWelcomeId.get(cid) ?? "")) {
24024
+ deferredThisPass.set(cid, (deferredThisPass.get(cid) ?? 0) + 1);
24025
+ continue;
24026
+ }
23957
24027
  let advancedEpoch;
23958
24028
  let custodyHeld = false;
23959
24029
  await this.locks.run(cid, async () => {
@@ -24110,6 +24180,12 @@ var MlsGroupKeyProvider = class _MlsGroupKeyProvider {
24110
24180
  this.leftStreak.set(cid, { pulls: 1, since: pullNow });
24111
24181
  this.scheduleAutoResolve(cid);
24112
24182
  }
24183
+ if (rows.length < DELIVERY_PULL_PAGE) {
24184
+ for (const cid of [...this.keptWelcomeDeferSince.keys()]) {
24185
+ if (!w2Kept.has(cid))
24186
+ this.keptWelcomeDeferSince.delete(cid);
24187
+ }
24188
+ }
24113
24189
  for (const [cid, gid] of w2Acked) {
24114
24190
  if (w2Kept.has(cid))
24115
24191
  continue;
@@ -24389,7 +24465,9 @@ var MlsGroupKeyProvider = class _MlsGroupKeyProvider {
24389
24465
  voiceDefaultResponder: row.voice_default_responder ?? void 0,
24390
24466
  ...typeof row.identity_signature === "string" && row.identity_signature ? { identitySignature: row.identity_signature } : {},
24391
24467
  payloadHex: row.payload,
24392
- openedAt
24468
+ openedAt,
24469
+ // #1246 C-1: the server row id, beside the sender-chosen envelope id.
24470
+ ...typeof row.message_id === "string" && row.message_id ? { serverMessageId: row.message_id } : {}
24393
24471
  };
24394
24472
  const custodiedAt = this.nowMs();
24395
24473
  this.custody.set(row.queue_id, entry);
@@ -25396,8 +25474,8 @@ function openDeliveryPings(o) {
25396
25474
  return Promise.resolve({ value: void 0, done: true });
25397
25475
  if (queue.length)
25398
25476
  return Promise.resolve({ value: queue.shift(), done: false });
25399
- return new Promise((resolve4) => {
25400
- waiter = resolve4;
25477
+ return new Promise((resolve5) => {
25478
+ waiter = resolve5;
25401
25479
  });
25402
25480
  }
25403
25481
  };
@@ -25717,8 +25795,8 @@ var PingSubscriber = class {
25717
25795
  return Promise.resolve({ value: void 0, done: true });
25718
25796
  if (this.queue.length)
25719
25797
  return Promise.resolve({ value: this.queue.shift(), done: false });
25720
- return new Promise((resolve4) => {
25721
- this.waiter = resolve4;
25798
+ return new Promise((resolve5) => {
25799
+ this.waiter = resolve5;
25722
25800
  });
25723
25801
  }
25724
25802
  };
@@ -26911,6 +26989,11 @@ var FakeConnectBackend = class _FakeConnectBackend {
26911
26989
  // Owner-signed tool grants (spec §7.0): like Plan 0's backend, each row carries
26912
26990
  // the envelope's `identity_proof.signature` as `identity_signature` beside the
26913
26991
  // unsigned canonical payload. `omitSignature` models a pre-Plan-0 row.
26992
+ /** `rowMessageId` (#1246 C-1): the server row id to deliver, when it is NOT
26993
+ * the envelope's id. The REST send path keys the row by the envelope id
26994
+ * (connect_messages.py, `MLSMessage(id=message_id)`); the WS `message_mls`
26995
+ * path stores through `store_mls_message`, whose row id is server-generated,
26996
+ * so there the two differ and the envelope id is the sender's free choice. */
26914
26997
  async ingestApplication(envelope, opts) {
26915
26998
  const channelId = envelope.channel_id;
26916
26999
  const canonical = await canonicalEnvelopeBytes(envelope);
@@ -26922,7 +27005,7 @@ var FakeConnectBackend = class _FakeConnectBackend {
26922
27005
  continue;
26923
27006
  this.q(dev).push({
26924
27007
  queue_id: "q-" + this._seq++,
26925
- message_id: envelope.message_id,
27008
+ message_id: opts?.rowMessageId ?? envelope.message_id,
26926
27009
  group_id: groupId,
26927
27010
  message_type: "application",
26928
27011
  sender_device_id: senderDeviceId ?? "",
@@ -27306,6 +27389,7 @@ function openSchema(db) {
27306
27389
  ensureRuntimeInboxDeliveryAttempts(db);
27307
27390
  ensureRuntimeReplyIntent(db);
27308
27391
  ensureRuntimeReplyIntentEnvelopeGroup(db);
27392
+ ensureRuntimeReplyIntentCredit(db);
27309
27393
  ensureGroupLink(db);
27310
27394
  ensureMlsCustody(db);
27311
27395
  ensureToolGrantTables(db);
@@ -27386,6 +27470,19 @@ function ensureRuntimeReplyIntentEnvelopeGroup(db) {
27386
27470
  throw e;
27387
27471
  }
27388
27472
  }
27473
+ function ensureRuntimeReplyIntentCredit(db) {
27474
+ const hasColumn = () => db.prepare("PRAGMA table_info(runtime_reply_intent)").all().some((c) => c.name === "reply_credit");
27475
+ const cols = db.prepare("PRAGMA table_info(runtime_reply_intent)").all();
27476
+ if (cols.length === 0 || cols.some((c) => c.name === "reply_credit"))
27477
+ return;
27478
+ try {
27479
+ db.exec("ALTER TABLE runtime_reply_intent ADD COLUMN reply_credit TEXT");
27480
+ } catch (e) {
27481
+ if (hasColumn())
27482
+ return;
27483
+ throw e;
27484
+ }
27485
+ }
27389
27486
  var RECALL_STATE_COLUMNS = [
27390
27487
  ["recall_enabled", "INTEGER NOT NULL DEFAULT 0"],
27391
27488
  ["recall_mode_epoch", "INTEGER NOT NULL DEFAULT 0"],
@@ -28437,8 +28534,8 @@ var SqliteMlsStateStore = class _SqliteMlsStateStore {
28437
28534
  this.db.prepare("DELETE FROM runtime_reply_intent WHERE identity_id=? AND updated_at < ?").run(this.id, now - RUNTIME_REPLY_INTENT_RETENTION_MS);
28438
28535
  const info = this.db.prepare(`INSERT OR IGNORE INTO runtime_reply_intent
28439
28536
  (identity_id, scope, part, in_reply_to, channel_id, reply_message_id, body_mac,
28440
- sealed_body, sealed_envelope, state, created_at, updated_at)
28441
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, NULL, 'pending', ?, ?)`).run(this.id, input.scope, input.part, input.inReplyTo, input.channelId, input.replyMessageId, input.bodyMac, sealed, now, now);
28537
+ sealed_body, sealed_envelope, state, created_at, updated_at, reply_credit)
28538
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, NULL, 'pending', ?, ?, ?)`).run(this.id, input.scope, input.part, input.inReplyTo, input.channelId, input.replyMessageId, input.bodyMac, sealed, now, now, input.replyCredit ?? null);
28442
28539
  const count = this.db.prepare("SELECT COUNT(*) AS n FROM runtime_reply_intent WHERE identity_id=?").get(this.id);
28443
28540
  if (count.n > RUNTIME_REPLY_INTENT_MAX) {
28444
28541
  this.db.prepare(`DELETE FROM runtime_reply_intent
@@ -28448,7 +28545,7 @@ var SqliteMlsStateStore = class _SqliteMlsStateStore {
28448
28545
  )`).run(this.id, this.id, RUNTIME_REPLY_INTENT_MAX);
28449
28546
  }
28450
28547
  const row = this.db.prepare(`SELECT scope, part, in_reply_to, channel_id, reply_message_id, body_mac,
28451
- sealed_body, sealed_envelope, sealed_envelope_group, state, created_at
28548
+ sealed_body, sealed_envelope, sealed_envelope_group, state, created_at, reply_credit
28452
28549
  FROM runtime_reply_intent WHERE identity_id=? AND scope=? AND part=?`).get(this.id, input.scope, input.part);
28453
28550
  if (!row)
28454
28551
  throw new Error("reply intent vanished after claim");
@@ -28457,7 +28554,7 @@ var SqliteMlsStateStore = class _SqliteMlsStateStore {
28457
28554
  /** SEC-06 #18: every intent answering `inReplyTo`, in claim order. */
28458
28555
  async listReplyIntents(inReplyTo) {
28459
28556
  const rows = this.db.prepare(`SELECT scope, part, in_reply_to, channel_id, reply_message_id, body_mac,
28460
- sealed_body, sealed_envelope, sealed_envelope_group, state, created_at
28557
+ sealed_body, sealed_envelope, sealed_envelope_group, state, created_at, reply_credit
28461
28558
  FROM runtime_reply_intent WHERE identity_id=? AND in_reply_to=?
28462
28559
  ORDER BY created_at ASC, rowid ASC`).all(this.id, inReplyTo);
28463
28560
  const out = [];
@@ -28468,7 +28565,7 @@ var SqliteMlsStateStore = class _SqliteMlsStateStore {
28468
28565
  /** SEC-06 #18: re-read one intent by its key (null when absent). */
28469
28566
  async getReplyIntent(scope, part) {
28470
28567
  const row = this.db.prepare(`SELECT scope, part, in_reply_to, channel_id, reply_message_id, body_mac,
28471
- sealed_body, sealed_envelope, sealed_envelope_group, state, created_at
28568
+ sealed_body, sealed_envelope, sealed_envelope_group, state, created_at, reply_credit
28472
28569
  FROM runtime_reply_intent WHERE identity_id=? AND scope=? AND part=?`).get(this.id, scope, part);
28473
28570
  return row ? this.openReplyIntent(row) : null;
28474
28571
  }
@@ -28542,7 +28639,8 @@ var SqliteMlsStateStore = class _SqliteMlsStateStore {
28542
28639
  envelope,
28543
28640
  envelopeGroupId: envelopeGroupFor(envelope, await openOrNull(row.sealed_envelope_group)),
28544
28641
  state: row.state === "sent" ? "sent" : row.state === "skipped" ? "skipped" : "pending",
28545
- createdAt: Number(row.created_at)
28642
+ createdAt: Number(row.created_at),
28643
+ replyCredit: typeof row.reply_credit === "string" ? row.reply_credit : null
28546
28644
  };
28547
28645
  }
28548
28646
  async recordMemory(input) {
@@ -30312,8 +30410,8 @@ function decodeReplyBody(bytes) {
30312
30410
  if (b?.kind === "a2a" && b.a2a && typeof b.a2a === "object") return b;
30313
30411
  throw new Error("reply intent body is malformed");
30314
30412
  }
30315
- function replyBodyMac(key, replyId, channelId, body) {
30316
- return createHmac("sha256", key).update(JSON.stringify(["reply-body", replyId, channelId])).update("\0").update(body).digest("hex");
30413
+ function replyBodyMac(key, replyId, channelId, body, credit) {
30414
+ return createHmac("sha256", key).update(JSON.stringify(credit === void 0 ? ["reply-body", replyId, channelId] : ["reply-body", replyId, channelId, credit])).update("\0").update(body).digest("hex");
30317
30415
  }
30318
30416
  function partOrdinal(part) {
30319
30417
  const m = /^p:(\d+)$/.exec(part);
@@ -30395,8 +30493,96 @@ function loadPackageMeta(importMetaUrl) {
30395
30493
  }
30396
30494
 
30397
30495
  // src/bridge-agent.ts
30398
- import { existsSync as existsSync5, promises as fsp, readFileSync as readFileSync11, statSync as statSync5 } from "node:fs";
30399
- import { join as join19, dirname as dirname7 } from "node:path";
30496
+ import { existsSync as existsSync5, promises as fsp2, readFileSync as readFileSync11, statSync as statSync5 } from "node:fs";
30497
+
30498
+ // src/outbox.ts
30499
+ import { constants, mkdirSync, lstatSync as lstatSync2, chmodSync, realpathSync, promises as fsp } from "node:fs";
30500
+ import { basename, dirname as dirname4, join as join4, resolve } from "node:path";
30501
+ var VAULT_OUTBOX_DIRNAME = "vault-outbox";
30502
+ var PathOutsideOutboxError = class extends Error {
30503
+ constructor() {
30504
+ super("path_outside_outbox");
30505
+ this.name = "PathOutsideOutboxError";
30506
+ }
30507
+ };
30508
+ var POSIX = process.platform !== "win32";
30509
+ function ownedPrivateDir(st) {
30510
+ if (st.isSymbolicLink() || !st.isDirectory()) return false;
30511
+ if (!POSIX) return true;
30512
+ const uid = typeof process.getuid === "function" ? process.getuid() : void 0;
30513
+ if (uid !== void 0 && st.uid !== uid) return false;
30514
+ return (st.mode & 511) === 448;
30515
+ }
30516
+ function ensureOutbox(dir) {
30517
+ mkdirSync(dir, { recursive: true, mode: 448 });
30518
+ let st = lstatSync2(dir);
30519
+ if (st.isSymbolicLink() || !st.isDirectory()) throw new PathOutsideOutboxError();
30520
+ const uid = POSIX && typeof process.getuid === "function" ? process.getuid() : void 0;
30521
+ if (uid !== void 0 && st.uid !== uid) throw new PathOutsideOutboxError();
30522
+ if (POSIX && (st.mode & 511) !== 448) {
30523
+ chmodSync(dir, 448);
30524
+ st = lstatSync2(dir);
30525
+ }
30526
+ if (!ownedPrivateDir(st)) throw new PathOutsideOutboxError();
30527
+ return { dir, real: realpathSync(dir), dev: st.dev, ino: st.ino };
30528
+ }
30529
+ var NOFOLLOW = constants.O_NOFOLLOW ?? 0;
30530
+ var NONBLOCK = constants.O_NONBLOCK ?? 0;
30531
+ async function readOutboxFile(pin, callerPath) {
30532
+ if (!pin) throw new PathOutsideOutboxError();
30533
+ try {
30534
+ const st = await fsp.lstat(pin.dir);
30535
+ if (!ownedPrivateDir(st) || st.dev !== pin.dev || st.ino !== pin.ino) throw new PathOutsideOutboxError();
30536
+ } catch {
30537
+ throw new PathOutsideOutboxError();
30538
+ }
30539
+ const requested = resolve(pin.dir, callerPath);
30540
+ const name = basename(requested);
30541
+ let realParent;
30542
+ try {
30543
+ realParent = await fsp.realpath(dirname4(requested));
30544
+ } catch {
30545
+ throw new PathOutsideOutboxError();
30546
+ }
30547
+ if (realParent !== pin.real) throw new PathOutsideOutboxError();
30548
+ const target = join4(pin.real, name);
30549
+ const pre = await fsp.lstat(target);
30550
+ if (pre.isSymbolicLink() || !pre.isFile()) throw new PathOutsideOutboxError();
30551
+ let handle;
30552
+ try {
30553
+ handle = await fsp.open(target, constants.O_RDONLY | NOFOLLOW | NONBLOCK);
30554
+ } catch (e) {
30555
+ if (e.code === "ENOENT") throw e;
30556
+ throw new PathOutsideOutboxError();
30557
+ }
30558
+ try {
30559
+ const st = await handle.stat();
30560
+ if (!st.isFile() || st.dev !== pre.dev || st.ino !== pre.ino || st.nlink !== 1) {
30561
+ throw new PathOutsideOutboxError();
30562
+ }
30563
+ return { bytes: new Uint8Array(await handle.readFile()), path: target, dev: st.dev, ino: st.ino };
30564
+ } finally {
30565
+ await handle.close().catch(() => {
30566
+ });
30567
+ }
30568
+ }
30569
+ async function consumeOutboxFile(pin, read) {
30570
+ try {
30571
+ const dir = await fsp.lstat(pin.dir);
30572
+ if (!ownedPrivateDir(dir) || dir.dev !== pin.dev || dir.ino !== pin.ino) return false;
30573
+ if (await fsp.realpath(pin.dir) !== pin.real) return false;
30574
+ const target = join4(pin.real, basename(read.path));
30575
+ const st = await fsp.lstat(target);
30576
+ if (st.isSymbolicLink() || st.dev !== read.dev || st.ino !== read.ino) return false;
30577
+ await fsp.unlink(target);
30578
+ return true;
30579
+ } catch {
30580
+ return false;
30581
+ }
30582
+ }
30583
+
30584
+ // src/bridge-agent.ts
30585
+ import { join as join20, dirname as dirname8 } from "node:path";
30400
30586
  import { fileURLToPath as fileURLToPath2 } from "node:url";
30401
30587
 
30402
30588
  // src/latency.ts
@@ -30844,11 +31030,21 @@ function pinCoverage(pinStripped, ownerMessage, coverTexts) {
30844
31030
  }
30845
31031
 
30846
31032
  // src/attachments.ts
30847
- import { basename } from "node:path";
31033
+ import { basename as basename2 } from "node:path";
30848
31034
  var VAULT_REF_MAX_SIZE_HINT = 100 * 1024 * 1024 - 16;
30849
31035
  var VAULT_REF_MAX_NAME = 255;
30850
31036
  var VAULT_REF_MAX_MIME = 127;
30851
31037
  var CANONICAL_UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
31038
+ var InvalidBlobIdError = class extends Error {
31039
+ constructor() {
31040
+ super("invalid_blob_id");
31041
+ this.name = "InvalidBlobIdError";
31042
+ }
31043
+ };
31044
+ function assertBlobId(blobId) {
31045
+ if (typeof blobId !== "string" || !CANONICAL_UUID.test(blobId)) throw new InvalidBlobIdError();
31046
+ return blobId;
31047
+ }
30852
31048
  var VAULT_PIN = /^blake2b:[0-9a-f]{64}$/;
30853
31049
  var HINT_FORBIDDEN = /[\u0000-\u001f\u007f-\u009f\u202a-\u202e\u2066-\u2069]/;
30854
31050
  var MIME_TYPE = /^[A-Za-z0-9][A-Za-z0-9!#$&^_.+-]*\/[A-Za-z0-9][A-Za-z0-9!#$&^_.+-]*$/;
@@ -30887,7 +31083,7 @@ function toVaultRefEntry(body) {
30887
31083
  return entry;
30888
31084
  }
30889
31085
  function sanitizeFilename(name, fallback) {
30890
- const base = basename(name ?? "");
31086
+ const base = basename2(name ?? "");
30891
31087
  const cleaned = base.replace(/[/\\]/g, "").replace(/^\.+$/, "");
30892
31088
  return cleaned.length > 0 ? cleaned : fallback;
30893
31089
  }
@@ -30917,7 +31113,7 @@ function mimeForName(name) {
30917
31113
  async function prepareAttachment(filePath2, caption, channelId, deps) {
30918
31114
  await deps.ready();
30919
31115
  const bytes = await deps.readFile(filePath2);
30920
- const name = basename(filePath2);
31116
+ const name = basename2(filePath2);
30921
31117
  const { encryptedData, fileKey, fileNonce, digest } = await deps.encryptFile(bytes);
30922
31118
  const blobId = await deps.uploadBlob(encryptedData, channelId);
30923
31119
  const body = {
@@ -30935,8 +31131,9 @@ async function prepareAttachment(filePath2, caption, channelId, deps) {
30935
31131
  return body;
30936
31132
  }
30937
31133
  async function materializeAttachment(body, ctx, deps) {
31134
+ const blobId = assertBlobId(body.blobId);
30938
31135
  await deps.ready();
30939
- const ciphertext = await deps.downloadBlob(body.blobId);
31136
+ const ciphertext = await deps.downloadBlob(blobId);
30940
31137
  const sha25612 = await deps.computeFileDigest(ciphertext);
30941
31138
  if (sha25612 !== body.digest) {
30942
31139
  throw new Error(`attachment digest mismatch for blob ${body.blobId}`);
@@ -30962,13 +31159,13 @@ function ensureSodiumReady2() {
30962
31159
  // src/memory/hermes-session-map.ts
30963
31160
  import { readFileSync as readFileSync3 } from "node:fs";
30964
31161
  import { homedir } from "node:os";
30965
- import { join as join4 } from "node:path";
31162
+ import { join as join5 } from "node:path";
30966
31163
  function expandHome(p) {
30967
- return p === "~" || p.startsWith("~/") ? join4(homedir(), p.slice(1)) : p;
31164
+ return p === "~" || p.startsWith("~/") ? join5(homedir(), p.slice(1)) : p;
30968
31165
  }
30969
31166
  function resolveHermesSession(baseDir, conversationId) {
30970
31167
  try {
30971
- const path = join4(expandHome(baseDir), "sessions", "sessions.json");
31168
+ const path = join5(expandHome(baseDir), "sessions", "sessions.json");
30972
31169
  const map = JSON.parse(readFileSync3(path, "utf8"));
30973
31170
  const entry = map[`agent:main:securecoms:dm:${conversationId}`];
30974
31171
  const sid = entry?.session_id;
@@ -30980,11 +31177,11 @@ function resolveHermesSession(baseDir, conversationId) {
30980
31177
 
30981
31178
  // src/memory/hermes-store.ts
30982
31179
  import { DatabaseSync as DatabaseSync2 } from "node:sqlite";
30983
- import { join as join5 } from "node:path";
31180
+ import { join as join6 } from "node:path";
30984
31181
  var HermesStore = class {
30985
31182
  dbPath;
30986
31183
  constructor(baseDir) {
30987
- this.dbPath = join5(expandHome(baseDir), "state.db");
31184
+ this.dbPath = join6(expandHome(baseDir), "state.db");
30988
31185
  }
30989
31186
  /** Recent-K conversational turns for a session, chronological. Fail-safe → []. */
30990
31187
  readRecent(sessionId, limit) {
@@ -31126,10 +31323,10 @@ var HermesMemoryProvider = class {
31126
31323
 
31127
31324
  // src/memory/openclaw-session-map.ts
31128
31325
  import { readFileSync as readFileSync4 } from "node:fs";
31129
- import { join as join6 } from "node:path";
31326
+ import { join as join7 } from "node:path";
31130
31327
  function resolveOwnerDmSession(baseDir, agent) {
31131
31328
  try {
31132
- const path = join6(expandHome(baseDir), "agents", agent, "sessions", "sessions.json");
31329
+ const path = join7(expandHome(baseDir), "agents", agent, "sessions", "sessions.json");
31133
31330
  const map = JSON.parse(readFileSync4(path, "utf8"));
31134
31331
  let best = null;
31135
31332
  for (const [key, entry] of Object.entries(map)) {
@@ -31147,11 +31344,11 @@ function resolveOwnerDmSession(baseDir, agent) {
31147
31344
 
31148
31345
  // src/memory/openclaw-session-log.ts
31149
31346
  import { readFileSync as readFileSync5 } from "node:fs";
31150
- import { join as join7 } from "node:path";
31347
+ import { join as join8 } from "node:path";
31151
31348
  function readRecentTurns(baseDir, agent, sessionId, k) {
31152
31349
  let raw;
31153
31350
  try {
31154
- raw = readFileSync5(join7(expandHome(baseDir), "agents", agent, "sessions", `${sessionId}.jsonl`), "utf8");
31351
+ raw = readFileSync5(join8(expandHome(baseDir), "agents", agent, "sessions", `${sessionId}.jsonl`), "utf8");
31155
31352
  } catch {
31156
31353
  return [];
31157
31354
  }
@@ -31182,7 +31379,7 @@ function readRecentTurns(baseDir, agent, sessionId, k) {
31182
31379
 
31183
31380
  // src/memory/openclaw-workspace-facts.ts
31184
31381
  import { readdirSync, readFileSync as readFileSync6, statSync as statSync2 } from "node:fs";
31185
- import { join as join8 } from "node:path";
31382
+ import { join as join9 } from "node:path";
31186
31383
  var DEFAULT_FACTS_BUDGET = 8e3;
31187
31384
  var MEMORY_MD_CAP = 6e3;
31188
31385
  var MIN_FRAGMENT = 200;
@@ -31193,7 +31390,7 @@ function resolveWorkspaceDir(baseDir, agent, override) {
31193
31390
  const base = expandHome(baseDir);
31194
31391
  const isMain2 = agent === "" || agent === "main";
31195
31392
  try {
31196
- const cfg = JSON.parse(readFileSync6(join8(base, "openclaw.json"), "utf8"));
31393
+ const cfg = JSON.parse(readFileSync6(join9(base, "openclaw.json"), "utf8"));
31197
31394
  const agents = cfg?.agents;
31198
31395
  if (agents && typeof agents === "object") {
31199
31396
  const list = Array.isArray(agents.list) ? agents.list : [];
@@ -31206,7 +31403,7 @@ function resolveWorkspaceDir(baseDir, agent, override) {
31206
31403
  }
31207
31404
  } catch {
31208
31405
  }
31209
- return isMain2 ? join8(base, "workspace") : join8(base, `workspace-${agent}`);
31406
+ return isMain2 ? join9(base, "workspace") : join9(base, `workspace-${agent}`);
31210
31407
  }
31211
31408
  var TRUNC = "\u2026[truncated]";
31212
31409
  function headSlice(s, n) {
@@ -31236,7 +31433,7 @@ function readWorkspaceFacts(baseDir, agent, opts) {
31236
31433
  let truncated = false;
31237
31434
  let remaining = budget;
31238
31435
  try {
31239
- const r = readBounded(join8(dir, "MEMORY.md"));
31436
+ const r = readBounded(join9(dir, "MEMORY.md"));
31240
31437
  if (r.oversize) truncated = true;
31241
31438
  else if (r.text && r.text.trim()) {
31242
31439
  const { text, clipped } = headSlice(r.text, Math.min(MEMORY_MD_CAP, budget));
@@ -31249,7 +31446,7 @@ ${TRUNC}` : ""}`);
31249
31446
  } catch {
31250
31447
  }
31251
31448
  try {
31252
- const dailies = readdirSync(join8(dir, "memory")).filter((f) => DAILY_RE.test(f)).sort().reverse().slice(0, 2);
31449
+ const dailies = readdirSync(join9(dir, "memory")).filter((f) => DAILY_RE.test(f)).sort().reverse().slice(0, 2);
31253
31450
  for (const f of dailies) {
31254
31451
  if (remaining < MIN_FRAGMENT) {
31255
31452
  truncated = true;
@@ -31257,7 +31454,7 @@ ${TRUNC}` : ""}`);
31257
31454
  }
31258
31455
  let r;
31259
31456
  try {
31260
- r = readBounded(join8(dir, "memory", f));
31457
+ r = readBounded(join9(dir, "memory", f));
31261
31458
  } catch {
31262
31459
  continue;
31263
31460
  }
@@ -31996,14 +32193,14 @@ function parsePayloadMemory(raw) {
31996
32193
 
31997
32194
  // src/memory/embedder/client.ts
31998
32195
  import { connect } from "node:net";
31999
- import { existsSync as existsSync3, mkdirSync as mkdirSync4, readFileSync as readFileSync9, rmSync as rmSync6, statSync as statSync3 } from "node:fs";
32196
+ import { existsSync as existsSync3, mkdirSync as mkdirSync5, readFileSync as readFileSync9, rmSync as rmSync6, statSync as statSync3 } from "node:fs";
32000
32197
  import { fileURLToPath } from "node:url";
32001
32198
 
32002
32199
  // src/memory/embedder/download.ts
32003
32200
  import { createHash as createHash2, randomBytes as randomBytes6 } from "node:crypto";
32004
- import { chmodSync as chmodSync2, rmSync as rmSync2 } from "node:fs";
32201
+ import { chmodSync as chmodSync3, rmSync as rmSync2 } from "node:fs";
32005
32202
  import { open } from "node:fs/promises";
32006
- import { join as join10 } from "node:path";
32203
+ import { join as join11 } from "node:path";
32007
32204
  init_layout();
32008
32205
  var WRONG_TYPE_STRIKES = 3;
32009
32206
  var wrongTypeStrikes = /* @__PURE__ */ new Map();
@@ -32019,8 +32216,8 @@ function removeModelFiles(root3, m) {
32019
32216
  for (const f of m.files) {
32020
32217
  const dir = plainDirChain(root3, [m.modelId, f.sha256], { create: false });
32021
32218
  if (!dir) continue;
32022
- chmodSync2(dir, 493);
32023
- rmSync2(join10(dir, f.name), { force: true });
32219
+ chmodSync3(dir, 493);
32220
+ rmSync2(join11(dir, f.name), { force: true });
32024
32221
  }
32025
32222
  }
32026
32223
  var VerifyFailed = class extends Error {
@@ -32067,7 +32264,7 @@ async function fetchSameOrigin(url, origin, fetchImpl, signal, stallMs, abort) {
32067
32264
  async function downloadOne(f, o) {
32068
32265
  if (wrongTypeAt(filePath(o.root, o.modelId, f))) throw new WrongType("wrong type at model path");
32069
32266
  const pdir = plainDirChain(o.root, [".partial"], { create: true, fromModelsDir: true, mode: 448 });
32070
- const partial = join10(pdir, randomBytes6(12).toString("hex"));
32267
+ const partial = join11(pdir, randomBytes6(12).toString("hex"));
32071
32268
  const fh = await open(partial, "wx", 384);
32072
32269
  const ac = new AbortController();
32073
32270
  const abort = () => ac.abort();
@@ -32144,17 +32341,17 @@ async function ensureModelFiles(o) {
32144
32341
 
32145
32342
  // src/memory/embedder/gc.ts
32146
32343
  init_layout();
32147
- import { chmodSync as chmodSync3, closeSync, constants, fstatSync, futimesSync, lstatSync as lstatSync3, openSync, readdirSync as readdirSync2, rmSync as rmSync3 } from "node:fs";
32148
- import { join as join11 } from "node:path";
32344
+ import { chmodSync as chmodSync4, closeSync, constants as constants2, fstatSync, futimesSync, lstatSync as lstatSync4, openSync, readdirSync as readdirSync2, rmSync as rmSync3 } from "node:fs";
32345
+ import { join as join12 } from "node:path";
32149
32346
  var THIRTY_DAYS = 30 * 864e5;
32150
32347
  var SAFE_ID = /^[A-Za-z0-9_-]{1,128}$/;
32151
32348
  function touchUserMarker(root3, modelId, identityId) {
32152
32349
  if (!SAFE_ID.test(identityId)) throw new Error("invalid identity id for the model marker");
32153
32350
  const dir = plainDirChain(root3, [modelId, ".users"], { create: true });
32154
- const p = join11(dir, identityId);
32351
+ const p = join12(dir, identityId);
32155
32352
  let fd;
32156
32353
  try {
32157
- fd = openSync(p, constants.O_WRONLY | constants.O_CREAT | constants.O_NOFOLLOW | constants.O_NONBLOCK, 420);
32354
+ fd = openSync(p, constants2.O_WRONLY | constants2.O_CREAT | constants2.O_NOFOLLOW | constants2.O_NONBLOCK, 420);
32158
32355
  } catch {
32159
32356
  throw new Error("the model marker is not a regular file");
32160
32357
  }
@@ -32168,22 +32365,22 @@ function touchUserMarker(root3, modelId, identityId) {
32168
32365
  }
32169
32366
  function isRealDir(p) {
32170
32367
  try {
32171
- return lstatSync3(p).isDirectory();
32368
+ return lstatSync4(p).isDirectory();
32172
32369
  } catch {
32173
32370
  return false;
32174
32371
  }
32175
32372
  }
32176
32373
  function makeWritable(p) {
32177
32374
  if (!isRealDir(p)) return;
32178
- chmodSync3(p, 493);
32179
- for (const e of readdirSync2(p)) makeWritable(join11(p, e));
32375
+ chmodSync4(p, 493);
32376
+ for (const e of readdirSync2(p)) makeWritable(join12(p, e));
32180
32377
  }
32181
32378
  function newestMarkerMs(users) {
32182
32379
  if (!isRealDir(users)) return void 0;
32183
32380
  let newest;
32184
32381
  for (const u of readdirSync2(users)) {
32185
32382
  try {
32186
- const s = lstatSync3(join11(users, u));
32383
+ const s = lstatSync4(join12(users, u));
32187
32384
  if (!s.isFile()) continue;
32188
32385
  newest = newest === void 0 ? s.mtimeMs : Math.max(newest, s.mtimeMs);
32189
32386
  } catch {
@@ -32196,9 +32393,9 @@ function gcModels(root3, keepModelIds, now, maxAgeMs = THIRTY_DAYS) {
32196
32393
  const removed = [];
32197
32394
  for (const modelId of readdirSync2(root3).sort()) {
32198
32395
  if (modelId.startsWith(".") || keepModelIds.includes(modelId)) continue;
32199
- const tree = join11(root3, modelId);
32396
+ const tree = join12(root3, modelId);
32200
32397
  if (!isRealDir(tree)) continue;
32201
- const lastUse = newestMarkerMs(join11(tree, ".users")) ?? lstatSync3(tree).mtimeMs;
32398
+ const lastUse = newestMarkerMs(join12(tree, ".users")) ?? lstatSync4(tree).mtimeMs;
32202
32399
  if (now - lastUse <= maxAgeMs) continue;
32203
32400
  makeWritable(tree);
32204
32401
  rmSync3(tree, { recursive: true, force: true });
@@ -32304,44 +32501,44 @@ var SealedChannel = class {
32304
32501
  // src/memory/embedder/embedder-paths.ts
32305
32502
  import { createHash as createHash3 } from "node:crypto";
32306
32503
  import { homedir as homedir3 } from "node:os";
32307
- import { join as join12 } from "node:path";
32504
+ import { join as join13 } from "node:path";
32308
32505
  function embedderKey(m) {
32309
32506
  const model = m.files.find((f) => f.name === "model.onnx");
32310
32507
  return createHash3("sha256").update(`${EMBEDDER_PROTOCOL}\0${m.modelId}\0${model.sha256}\0${m.wasmSha256}`).digest("hex").slice(0, 16);
32311
32508
  }
32312
- var embedderRunDir = (home = homedir3()) => join12(home, ".securecoms", "run", "embedder");
32509
+ var embedderRunDir = (home = homedir3()) => join13(home, ".securecoms", "run", "embedder");
32313
32510
  var embedderPaths = (dir, key) => ({
32314
- sock: join12(dir, `${key}.sock`),
32315
- token: join12(dir, `${key}.token`),
32316
- lock: join12(dir, `${key}.spawn.lock`),
32317
- status: join12(dir, `${key}.status`),
32318
- pid: join12(dir, `${key}.pid`)
32511
+ sock: join13(dir, `${key}.sock`),
32512
+ token: join13(dir, `${key}.token`),
32513
+ lock: join13(dir, `${key}.spawn.lock`),
32514
+ status: join13(dir, `${key}.status`),
32515
+ pid: join13(dir, `${key}.pid`)
32319
32516
  // E1 delta H1: the instance's lifetime lock
32320
32517
  });
32321
32518
  var SOCKET_PATH_MAX = 103;
32322
32519
  var windowsRunDir = (env = process.env) => {
32323
32520
  if (!env.LOCALAPPDATA) throw new Error("LOCALAPPDATA unset");
32324
- return join12(env.LOCALAPPDATA, "SecureComs", "run", "embedder");
32521
+ return join13(env.LOCALAPPDATA, "SecureComs", "run", "embedder");
32325
32522
  };
32326
32523
 
32327
32524
  // src/memory/embedder/spawn.ts
32328
32525
  import { spawn } from "node:child_process";
32329
- import { existsSync as existsSync2, mkdirSync as mkdirSync3, realpathSync } from "node:fs";
32526
+ import { existsSync as existsSync2, mkdirSync as mkdirSync4, realpathSync as realpathSync2 } from "node:fs";
32330
32527
 
32331
32528
  // src/memory/embedder/run-dir.ts
32332
- import { existsSync, lstatSync as lstatSync4, mkdirSync as mkdirSync2 } from "node:fs";
32333
- import { dirname as dirname5 } from "node:path";
32529
+ import { existsSync, lstatSync as lstatSync5, mkdirSync as mkdirSync3 } from "node:fs";
32530
+ import { dirname as dirname6 } from "node:path";
32334
32531
  var RunDirRefused = class extends Error {
32335
32532
  };
32336
- function ensureRunDir(dir, uid = process.getuid?.() ?? -1, lstat = lstatSync4) {
32337
- const parent3 = dirname5(dir);
32338
- if (!existsSync(parent3)) mkdirSync2(parent3, { recursive: true, mode: 448 });
32533
+ function ensureRunDir(dir, uid = process.getuid?.() ?? -1, lstat = lstatSync5) {
32534
+ const parent3 = dirname6(dir);
32535
+ if (!existsSync(parent3)) mkdirSync3(parent3, { recursive: true, mode: 448 });
32339
32536
  const ps = lstat(parent3);
32340
32537
  if (ps.isSymbolicLink() || !ps.isDirectory()) throw new RunDirRefused("run directory parent is not a real directory");
32341
32538
  if (ps.uid !== uid) throw new RunDirRefused("run directory parent is not owned by this user");
32342
32539
  if ((ps.mode & 18) !== 0) throw new RunDirRefused("run directory parent is group or world writable");
32343
32540
  try {
32344
- mkdirSync2(dir, { mode: 448 });
32541
+ mkdirSync3(dir, { mode: 448 });
32345
32542
  } catch (e) {
32346
32543
  if (e.code !== "EEXIST") throw e;
32347
32544
  }
@@ -32352,11 +32549,11 @@ function ensureRunDir(dir, uid = process.getuid?.() ?? -1, lstat = lstatSync4) {
32352
32549
  }
32353
32550
 
32354
32551
  // src/memory/embedder/spawn.ts
32355
- import { basename as basename3, dirname as dirname6, join as join13 } from "node:path";
32552
+ import { basename as basename4, dirname as dirname7, join as join14 } from "node:path";
32356
32553
  init_layout();
32357
32554
 
32358
32555
  // src/memory/embedder/locks.ts
32359
- import { closeSync as closeSync2, constants as constants2, fstatSync as fstatSync2, linkSync, openSync as openSync2, readFileSync as readFileSync7, renameSync as renameSync2, rmSync as rmSync4, writeSync } from "node:fs";
32556
+ import { closeSync as closeSync2, constants as constants3, fstatSync as fstatSync2, linkSync, openSync as openSync2, readFileSync as readFileSync7, renameSync as renameSync2, rmSync as rmSync4, writeSync } from "node:fs";
32360
32557
  import { randomBytes as randomBytes9 } from "node:crypto";
32361
32558
  import { uptime } from "node:os";
32362
32559
  var LOCK_STALE_MS = 1e4;
@@ -32378,7 +32575,7 @@ var PROCESS_LOCK_ENV = {
32378
32575
  function inspect(path) {
32379
32576
  let fd;
32380
32577
  try {
32381
- fd = openSync2(path, constants2.O_RDONLY | constants2.O_NOFOLLOW);
32578
+ fd = openSync2(path, constants3.O_RDONLY | constants3.O_NOFOLLOW);
32382
32579
  } catch {
32383
32580
  return null;
32384
32581
  }
@@ -32402,7 +32599,7 @@ function takeLock(path, now = Date.now, env = PROCESS_LOCK_ENV) {
32402
32599
  const id = `${env.pid} ${env.bootTime()} ${env.startTime()} ${randomBytes9(8).toString("hex")}`;
32403
32600
  for (let attempt = 0; attempt < 3; attempt++) {
32404
32601
  try {
32405
- const fd = openSync2(path, constants2.O_CREAT | constants2.O_EXCL | constants2.O_WRONLY, 384);
32602
+ const fd = openSync2(path, constants3.O_CREAT | constants3.O_EXCL | constants3.O_WRONLY, 384);
32406
32603
  try {
32407
32604
  writeSync(fd, id);
32408
32605
  } finally {
@@ -32463,7 +32660,7 @@ function embedderEnv(env = process.env, platform = process.platform) {
32463
32660
  }
32464
32661
  var withReal = (p) => {
32465
32662
  try {
32466
- const r = realpathSync(p);
32663
+ const r = realpathSync2(p);
32467
32664
  return r === p ? [p] : [p, r];
32468
32665
  } catch {
32469
32666
  return [p];
@@ -32471,10 +32668,10 @@ var withReal = (p) => {
32471
32668
  };
32472
32669
  var realish = (p) => {
32473
32670
  try {
32474
- return realpathSync(p);
32671
+ return realpathSync2(p);
32475
32672
  } catch {
32476
- const up = dirname6(p);
32477
- return up === p ? p : join13(realish(up), basename3(p));
32673
+ const up = dirname7(p);
32674
+ return up === p ? p : join14(realish(up), basename4(p));
32478
32675
  }
32479
32676
  };
32480
32677
  var both = (p) => {
@@ -32486,7 +32683,7 @@ function spawnEmbedder(o) {
32486
32683
  const run = o.runDir ?? embedderRunDir(o.home);
32487
32684
  const win = (o.platform ?? process.platform) === "win32";
32488
32685
  if (o.createRunDir !== false) {
32489
- if (win) mkdirSync3(run, { recursive: true, mode: 448 });
32686
+ if (win) mkdirSync4(run, { recursive: true, mode: 448 });
32490
32687
  else ensureRunDir(run);
32491
32688
  }
32492
32689
  const perm = permissionFlags();
@@ -32495,13 +32692,13 @@ function spawnEmbedder(o) {
32495
32692
  o.onNoPermissionModel?.();
32496
32693
  }
32497
32694
  const reads = perm.length === 0 ? [] : [
32498
- ...withReal(dirname6(o.distDir)),
32695
+ ...withReal(dirname7(o.distDir)),
32499
32696
  ...existsSync2(defaultModelsRoot(o.home)) ? withReal(defaultModelsRoot(o.home)) : [defaultModelsRoot(o.home)],
32500
- ...both(dirname6(run)),
32501
- ...win ? both(join13(o.home, ".securecoms", "run")) : []
32697
+ ...both(dirname7(run)),
32698
+ ...win ? both(join14(o.home, ".securecoms", "run")) : []
32502
32699
  // the voice-call heartbeats (the run dir lives elsewhere)
32503
32700
  ];
32504
- const script = withReal(join13(o.distDir, "embedder.js")).at(-1);
32701
+ const script = withReal(join14(o.distDir, "embedder.js")).at(-1);
32505
32702
  const net = (o.nodeFlags ?? process.allowedNodeEnvironmentFlags).has("--allow-net") ? ["--allow-net"] : [];
32506
32703
  const flags = perm.length === 0 ? [] : [...perm, ...reads.map((r) => `--allow-fs-read=${r}`), ...both(run).map((w) => `--allow-fs-write=${w}`), "--allow-worker", ...net];
32507
32704
  const child = (o.spawnFn ?? spawn)(
@@ -32510,7 +32707,7 @@ function spawnEmbedder(o) {
32510
32707
  // 11c review LOW 3, fixed by the Task 12 Mini gate: cwd is the run directory's PARENT, which the bridge created (0700)
32511
32708
  // before spawning; the run directory itself is created by the embedder, so on a first start it does not exist yet and
32512
32709
  // a cwd there fails the spawn with ENOENT. Never the bridge's working directory.
32513
- { detached: true, stdio: "ignore", windowsHide: true, cwd: dirname6(run), env: win ? { ...embedderEnv(process.env, "win32"), USERPROFILE: o.home, LOCALAPPDATA: dirname6(dirname6(dirname6(run))) } : { ...embedderEnv(), HOME: o.home } }
32710
+ { detached: true, stdio: "ignore", windowsHide: true, cwd: dirname7(run), env: win ? { ...embedderEnv(process.env, "win32"), USERPROFILE: o.home, LOCALAPPDATA: dirname7(dirname7(dirname7(run))) } : { ...embedderEnv(), HOME: o.home } }
32514
32711
  );
32515
32712
  child.unref();
32516
32713
  child.on("error", () => {
@@ -32522,8 +32719,8 @@ function spawnEmbedder(o) {
32522
32719
  import { execFile } from "node:child_process";
32523
32720
  import { promisify } from "node:util";
32524
32721
  import { randomBytes as randomBytes10 } from "node:crypto";
32525
- import { lstatSync as lstatSync5, readFileSync as readFileSync8, rmSync as rmSync5 } from "node:fs";
32526
- import { join as join14, win32 } from "node:path";
32722
+ import { lstatSync as lstatSync6, readFileSync as readFileSync8, rmSync as rmSync5 } from "node:fs";
32723
+ import { join as join15, win32 } from "node:path";
32527
32724
  var WINDOWS_SEMANTIC_ENABLED = false;
32528
32725
  var windowsSemanticOn = (platform, enabled = WINDOWS_SEMANTIC_ENABLED) => platform !== "win32" || enabled;
32529
32726
  function parseSaveSddl(saveFile) {
@@ -32551,7 +32748,7 @@ function aclAllows(sddl, ownerSid) {
32551
32748
  if (aces.length === 0 || rest !== "" && !rest.startsWith("S:")) return false;
32552
32749
  return aces.every((a) => a[0] === "A" && allowed.has(a[5] ?? "") || a[0] === "D");
32553
32750
  }
32554
- function hasReparsePoint(path, lstat = lstatSync5) {
32751
+ function hasReparsePoint(path, lstat = lstatSync6) {
32555
32752
  const root3 = win32.parse(path).root;
32556
32753
  let cur = root3;
32557
32754
  for (const part of path.slice(root3.length).split(/[\\/]/).filter(Boolean)) {
@@ -32570,7 +32767,7 @@ async function currentUserSid(run = defaultRunner) {
32570
32767
  }
32571
32768
  async function verifyRunDirAcl(path, deps = {}, sid) {
32572
32769
  const run = deps.run ?? defaultRunner;
32573
- const out = join14(deps.tmp ?? process.env.TEMP ?? process.env.TMP ?? ".", `sc-acl-${randomBytes10(8).toString("hex")}.txt`);
32770
+ const out = join15(deps.tmp ?? process.env.TEMP ?? process.env.TMP ?? ".", `sc-acl-${randomBytes10(8).toString("hex")}.txt`);
32574
32771
  try {
32575
32772
  if (hasReparsePoint(path, deps.lstat)) return false;
32576
32773
  const user = await (sid ? sid() : currentUserSid(run));
@@ -32651,12 +32848,12 @@ var EmbedderClient = class {
32651
32848
  ready(ms) {
32652
32849
  if (this.st.state === "ready") return Promise.resolve(true);
32653
32850
  if (this.st.state !== "preparing" || !(ms > 0)) return Promise.resolve(false);
32654
- return new Promise((resolve4) => {
32851
+ return new Promise((resolve5) => {
32655
32852
  let t;
32656
32853
  const w = (ok) => {
32657
32854
  clearTimeout(t);
32658
32855
  this.readyWaiters.delete(w);
32659
- resolve4(ok);
32856
+ resolve5(ok);
32660
32857
  };
32661
32858
  this.readyWaiters.add(w);
32662
32859
  t = setTimeout(() => w(false), ms);
@@ -32765,7 +32962,7 @@ var EmbedderClient = class {
32765
32962
  const p = this.paths();
32766
32963
  if (!this.tcp() && Buffer.byteLength(`${p.sock}.00000000`) > SOCKET_PATH_MAX) return this.fail("run_dir_refused");
32767
32964
  if (this.tcp()) {
32768
- mkdirSync4(this.runDir(), { recursive: true, mode: 448 });
32965
+ mkdirSync5(this.runDir(), { recursive: true, mode: 448 });
32769
32966
  if (!await this.aclOk(true)) return this.fail("acl_unverified");
32770
32967
  }
32771
32968
  if (!this.tcp()) {
@@ -32901,7 +33098,7 @@ var EmbedderClient = class {
32901
33098
  }
32902
33099
  if (this.closed) return false;
32903
33100
  }
32904
- return new Promise((resolve4) => {
33101
+ return new Promise((resolve5) => {
32905
33102
  const s = this.tcp() ? connect({ host: "127.0.0.1", port: pub.port }) : connect(p.sock);
32906
33103
  const nc = nonce();
32907
33104
  let ns = "";
@@ -32914,7 +33111,7 @@ var EmbedderClient = class {
32914
33111
  if (!settled) {
32915
33112
  settled = true;
32916
33113
  clearTimeout(t);
32917
- resolve4(ok);
33114
+ resolve5(ok);
32918
33115
  }
32919
33116
  }
32920
33117
  const refuse3 = (reason) => {
@@ -33483,12 +33680,12 @@ var ReembedQueue = class {
33483
33680
  };
33484
33681
 
33485
33682
  // src/memory/recall/call-heartbeat.ts
33486
- import { closeSync as closeSync3, constants as constants3, fstatSync as fstatSync3, futimesSync as futimesSync2, openSync as openSync3, rmSync as rmSync7 } from "node:fs";
33487
- import { join as join16 } from "node:path";
33683
+ import { closeSync as closeSync3, constants as constants4, fstatSync as fstatSync3, futimesSync as futimesSync2, openSync as openSync3, rmSync as rmSync7 } from "node:fs";
33684
+ import { join as join17 } from "node:path";
33488
33685
 
33489
33686
  // src/memory/embedder/heartbeats.ts
33490
- import { lstatSync as lstatSync6, readdirSync as readdirSync3 } from "node:fs";
33491
- import { join as join15 } from "node:path";
33687
+ import { lstatSync as lstatSync7, readdirSync as readdirSync3 } from "node:fs";
33688
+ import { join as join16 } from "node:path";
33492
33689
  var HEARTBEAT_FUTURE_SKEW_MS = 6e4;
33493
33690
  var HEARTBEAT_STALE_MS = 3e4;
33494
33691
  function heartbeatAgeFresh(ageMs, staleMs = HEARTBEAT_STALE_MS) {
@@ -33503,7 +33700,7 @@ function anyHeartbeatFresh(dir, now, staleMs = HEARTBEAT_STALE_MS) {
33503
33700
  }
33504
33701
  return names.some((n) => {
33505
33702
  try {
33506
- const st = lstatSync6(join15(dir, n));
33703
+ const st = lstatSync7(join16(dir, n));
33507
33704
  return st.isFile() && heartbeatAgeFresh(now - st.mtimeMs, staleMs);
33508
33705
  } catch {
33509
33706
  return false;
@@ -33569,7 +33766,7 @@ var CallHeartbeat = class {
33569
33766
  if (dir === null) return;
33570
33767
  let fd;
33571
33768
  try {
33572
- fd = openSync3(join16(dir, this.identityId), constants3.O_WRONLY | constants3.O_CREAT | constants3.O_NOFOLLOW | constants3.O_NONBLOCK, 384);
33769
+ fd = openSync3(join17(dir, this.identityId), constants4.O_WRONLY | constants4.O_CREAT | constants4.O_NOFOLLOW | constants4.O_NONBLOCK, 384);
33573
33770
  if (!fstatSync3(fd).isFile()) return;
33574
33771
  const t = new Date(this.now());
33575
33772
  futimesSync2(fd, t, t);
@@ -33584,7 +33781,7 @@ var CallHeartbeat = class {
33584
33781
  const dir = SAFE_ID2.test(this.identityId) ? this.dirOk() : null;
33585
33782
  if (dir === null) return;
33586
33783
  try {
33587
- rmSync7(join16(dir, this.identityId), { force: true });
33784
+ rmSync7(join17(dir, this.identityId), { force: true });
33588
33785
  this.wrote = false;
33589
33786
  } catch (e) {
33590
33787
  this.log("warn", "memory.call_heartbeat_failed", {}, e);
@@ -33647,9 +33844,9 @@ function queryTokenMacs(key, text, max = QUERY_MACS_MAX) {
33647
33844
 
33648
33845
  // src/memory/unified/ranking-client.ts
33649
33846
  import { Worker } from "node:worker_threads";
33650
- import { join as join17 } from "node:path";
33847
+ import { join as join18 } from "node:path";
33651
33848
  function workerTransport(init, distDir) {
33652
- const w = new Worker(join17(distDir, "memory-worker.js"), { workerData: init });
33849
+ const w = new Worker(join18(distDir, "memory-worker.js"), { workerData: init });
33653
33850
  return {
33654
33851
  post: (m) => w.postMessage(m),
33655
33852
  onMessage: (cb) => {
@@ -33709,15 +33906,15 @@ var RankingClient = class {
33709
33906
  ask(build, deadlineAt) {
33710
33907
  const id = ++this.seq;
33711
33908
  if (this.closed) return Promise.resolve("failed");
33712
- return new Promise((resolve4) => {
33909
+ return new Promise((resolve5) => {
33713
33910
  const timer = setTimeout(() => {
33714
33911
  this.waiting.delete(id);
33715
33912
  this.misses++;
33716
- resolve4("late");
33913
+ resolve5("late");
33717
33914
  }, Math.max(0, deadlineAt - Date.now()));
33718
33915
  this.waiting.set(id, { resolve: (m) => {
33719
33916
  clearTimeout(timer);
33720
- resolve4(m);
33917
+ resolve5(m);
33721
33918
  } });
33722
33919
  try {
33723
33920
  this.transport().post(build(id));
@@ -33726,7 +33923,7 @@ var RankingClient = class {
33726
33923
  this.waiting.delete(id);
33727
33924
  this.misses++;
33728
33925
  (this.d.log ?? writeServiceLog)("warn", "memory.ranking_worker_spawn_failed", void 0, e);
33729
- resolve4("failed");
33926
+ resolve5("failed");
33730
33927
  }
33731
33928
  });
33732
33929
  }
@@ -35054,17 +35251,17 @@ async function runSummarize(opts) {
35054
35251
  // src/service/runtime-config-dir.ts
35055
35252
  import { createHash as createHash5, randomUUID } from "node:crypto";
35056
35253
  import {
35057
- chmodSync as chmodSync4,
35254
+ chmodSync as chmodSync5,
35058
35255
  existsSync as existsSync4,
35059
- lstatSync as lstatSync7,
35060
- mkdirSync as mkdirSync5,
35256
+ lstatSync as lstatSync8,
35257
+ mkdirSync as mkdirSync6,
35061
35258
  readFileSync as readFileSync10,
35062
35259
  renameSync as renameSync3,
35063
35260
  statSync as statSync4,
35064
35261
  unlinkSync,
35065
35262
  writeFileSync
35066
35263
  } from "node:fs";
35067
- import { basename as basename4, isAbsolute, join as join18 } from "node:path";
35264
+ import { basename as basename5, isAbsolute, join as join19 } from "node:path";
35068
35265
 
35069
35266
  // src/grants/search-providers.ts
35070
35267
  var SEARCH_PROVIDERS = /* @__PURE__ */ new Set(["brave", "tavily", "parallel", "exa", "firecrawl"]);
@@ -35076,18 +35273,18 @@ function isSearchProvider(value) {
35076
35273
  // src/service/runtime-config-dir.ts
35077
35274
  var MEMORY_FILE = "MEMORY.md";
35078
35275
  var REAL_FS = {
35079
- mkdir: (p, mode) => mkdirSync5(p, { recursive: true, mode }),
35080
- chmod: (p, mode) => chmodSync4(p, mode),
35276
+ mkdir: (p, mode) => mkdirSync6(p, { recursive: true, mode }),
35277
+ chmod: (p, mode) => chmodSync5(p, mode),
35081
35278
  writeFile: (p, data, mode) => writeFileSync(p, data, { mode }),
35082
35279
  rename: (a, b) => renameSync3(a, b),
35083
35280
  readFile: (p) => readFileSync10(p, "utf8"),
35084
35281
  unlink: (p) => unlinkSync(p)
35085
35282
  };
35086
35283
  function runtimeConfigDir(home, identityId) {
35087
- return join18(home, ".securecoms", "runtime-config", identityId);
35284
+ return join19(home, ".securecoms", "runtime-config", identityId);
35088
35285
  }
35089
35286
  function assertManagedName(name) {
35090
- if (!name || name.includes("/") || name.includes("\\") || name === "." || name === ".." || name !== basename4(name)) {
35287
+ if (!name || name.includes("/") || name.includes("\\") || name === "." || name === ".." || name !== basename5(name)) {
35091
35288
  throw new Error(`refusing managed config file name ${JSON.stringify(name)}: not a plain filename`);
35092
35289
  }
35093
35290
  if (isLocalOnlyName(name)) {
@@ -35101,19 +35298,19 @@ function isLocalOnlyName(name) {
35101
35298
  var MANIFEST_POLICY_FILE = "manifest-policy.json";
35102
35299
  function removeManagedLlmSource(home, identityId, deps = REAL_FS) {
35103
35300
  try {
35104
- deps.unlink(join18(runtimeConfigDir(home, identityId), "llm-source.json"));
35301
+ deps.unlink(join19(runtimeConfigDir(home, identityId), "llm-source.json"));
35105
35302
  } catch (e) {
35106
35303
  if (e?.code !== "ENOENT") throw e;
35107
35304
  }
35108
35305
  }
35109
35306
  function writeManifestPolicy(home, identityId, policy, deps = REAL_FS) {
35110
- const parent3 = join18(home, ".securecoms", "runtime-config");
35307
+ const parent3 = join19(home, ".securecoms", "runtime-config");
35111
35308
  const dir = runtimeConfigDir(home, identityId);
35112
- for (const d of [join18(home, ".securecoms"), parent3, dir]) {
35309
+ for (const d of [join19(home, ".securecoms"), parent3, dir]) {
35113
35310
  deps.mkdir(d, 448);
35114
35311
  deps.chmod(d, 448);
35115
35312
  }
35116
- atomicWrite(join18(dir, MANIFEST_POLICY_FILE), JSON.stringify(policy) + "\n", deps);
35313
+ atomicWrite(join19(dir, MANIFEST_POLICY_FILE), JSON.stringify(policy) + "\n", deps);
35117
35314
  }
35118
35315
  function atomicWrite(path, body, deps) {
35119
35316
  const tmp = `${path}.${process.pid}.${randomUUID()}.tmp`;
@@ -35132,13 +35329,13 @@ function atomicWrite(path, body, deps) {
35132
35329
  function writeManagedFiles(home, identityId, files, deps = REAL_FS) {
35133
35330
  const entries = Object.entries(files);
35134
35331
  for (const [name] of entries) assertManagedName(name);
35135
- const parent3 = join18(home, ".securecoms", "runtime-config");
35332
+ const parent3 = join19(home, ".securecoms", "runtime-config");
35136
35333
  const dir = runtimeConfigDir(home, identityId);
35137
- for (const d of [join18(home, ".securecoms"), parent3, dir]) {
35334
+ for (const d of [join19(home, ".securecoms"), parent3, dir]) {
35138
35335
  deps.mkdir(d, 448);
35139
35336
  deps.chmod(d, 448);
35140
35337
  }
35141
- for (const [name, body] of entries) atomicWrite(join18(dir, name), body, deps);
35338
+ for (const [name, body] of entries) atomicWrite(join19(dir, name), body, deps);
35142
35339
  }
35143
35340
  function checkConfiguredPaths(skillsJson, statFn = (p) => {
35144
35341
  const s = statSync4(p);
@@ -35154,6 +35351,8 @@ function checkConfiguredPaths(skillsJson, statFn = (p) => {
35154
35351
  }
35155
35352
  const search = skillsJson.search;
35156
35353
  if (search?.keyFile) paths.push(search.keyFile);
35354
+ const weather = skillsJson.weather;
35355
+ if (weather?.keyFile) paths.push(weather.keyFile);
35157
35356
  return paths.map((path) => {
35158
35357
  let st;
35159
35358
  try {
@@ -35179,7 +35378,7 @@ function parseSecretPathsReport(raw) {
35179
35378
  if (!raw || typeof raw !== "object" || Array.isArray(raw)) return void 0;
35180
35379
  const r = raw;
35181
35380
  const f = r.outside_by_field;
35182
- if (r.mode !== "report" && r.mode !== "enforce" || !isCount(r.inside) || !isCount(r.allowlisted) || !isCount(r.outside) || !f || typeof f !== "object" || Array.isArray(f) || !isCount(f.headers_file) || !isCount(f.postgres_url_file) || !isCount(f.search_key_file) || f.sqlite !== void 0 && !isCount(f.sqlite)) return void 0;
35381
+ if (r.mode !== "report" && r.mode !== "enforce" || !isCount(r.inside) || !isCount(r.allowlisted) || !isCount(r.outside) || !f || typeof f !== "object" || Array.isArray(f) || !isCount(f.headers_file) || !isCount(f.postgres_url_file) || !isCount(f.search_key_file) || f.sqlite !== void 0 && !isCount(f.sqlite) || f.weather_key_file !== void 0 && !isCount(f.weather_key_file)) return void 0;
35183
35382
  return {
35184
35383
  mode: r.mode,
35185
35384
  inside: r.inside,
@@ -35189,7 +35388,8 @@ function parseSecretPathsReport(raw) {
35189
35388
  headers_file: f.headers_file,
35190
35389
  postgres_url_file: f.postgres_url_file,
35191
35390
  search_key_file: f.search_key_file,
35192
- ...f.sqlite !== void 0 ? { sqlite: f.sqlite } : {}
35391
+ ...f.sqlite !== void 0 ? { sqlite: f.sqlite } : {},
35392
+ ...f.weather_key_file !== void 0 ? { weather_key_file: f.weather_key_file } : {}
35193
35393
  }
35194
35394
  };
35195
35395
  }
@@ -35214,7 +35414,7 @@ function localLegacySources(doc) {
35214
35414
  const channelId = source.channelId;
35215
35415
  if (kind2 !== "global-memory" && kind2 !== "conversation-history" && kind2 !== "captured-memory" || typeof path !== "string" || !isAbsolute(path) || channelId !== void 0 && typeof channelId !== "string") continue;
35216
35416
  try {
35217
- const st = lstatSync7(path);
35417
+ const st = lstatSync8(path);
35218
35418
  if (!st.isFile() || st.isSymbolicLink()) continue;
35219
35419
  if (st.size <= LEGACY_MEMORY_SOURCE_MAX_BYTES && st.size > contentBudget) continue;
35220
35420
  const oversize = st.size > LEGACY_MEMORY_SOURCE_MAX_BYTES;
@@ -35242,7 +35442,7 @@ function localLegacySources(doc) {
35242
35442
  return out;
35243
35443
  }
35244
35444
  function readLocalReport(home, identityId) {
35245
- const p = join18(runtimeConfigDir(home, identityId), LOCAL_REPORT_FILE);
35445
+ const p = join19(runtimeConfigDir(home, identityId), LOCAL_REPORT_FILE);
35246
35446
  if (!existsSync4(p)) return null;
35247
35447
  let doc;
35248
35448
  try {
@@ -35269,6 +35469,7 @@ function readLocalReport(home, identityId) {
35269
35469
  has_search_key: hasSearchKey,
35270
35470
  has_llm_key: hasLlmKey,
35271
35471
  has_extract_provider: doc.has_extract_provider === true,
35472
+ has_weather_key: doc.has_weather_key === true,
35272
35473
  ...ownerDmCap !== void 0 ? { owner_dm_search_cap_effective: ownerDmCap } : {},
35273
35474
  legacy_memory: localLegacySources(doc).map(({ path: _path, content: _content, ...source }) => source),
35274
35475
  secret_paths: parseSecretPathsReport(doc.secret_paths)
@@ -35276,7 +35477,7 @@ function readLocalReport(home, identityId) {
35276
35477
  }
35277
35478
  function deleteLegacyMemorySources(home, identityId, ids) {
35278
35479
  if (ids.length === 0) return 0;
35279
- const p = join18(runtimeConfigDir(home, identityId), LOCAL_REPORT_FILE);
35480
+ const p = join19(runtimeConfigDir(home, identityId), LOCAL_REPORT_FILE);
35280
35481
  if (!existsSync4(p)) return 0;
35281
35482
  let doc;
35282
35483
  try {
@@ -35319,7 +35520,7 @@ async function applyLegacyMemoryFactDecisions(home, identityId, rawDecisions, re
35319
35520
  group.set(decision.fact_hash, decision);
35320
35521
  bySource.set(decision.source_id, group);
35321
35522
  }
35322
- const reportPath = join18(runtimeConfigDir(home, identityId), LOCAL_REPORT_FILE);
35523
+ const reportPath = join19(runtimeConfigDir(home, identityId), LOCAL_REPORT_FILE);
35323
35524
  if (!existsSync4(reportPath)) return { sourcesDeleted: 0, factsAssigned: 0 };
35324
35525
  let doc;
35325
35526
  try {
@@ -35357,7 +35558,7 @@ async function applyLegacyMemoryFactDecisions(home, identityId, rawDecisions, re
35357
35558
  }
35358
35559
  function pruneManagedMemory(home, identityId, hashes, deps = REAL_FS) {
35359
35560
  const dir = runtimeConfigDir(home, identityId);
35360
- const path = join18(dir, MEMORY_FILE);
35561
+ const path = join19(dir, MEMORY_FILE);
35361
35562
  if (!existsSync4(path)) return 0;
35362
35563
  const drop = new Set(hashes);
35363
35564
  const before = deps.readFile(path).split("\n");
@@ -35370,6 +35571,19 @@ function pruneManagedMemory(home, identityId, hashes, deps = REAL_FS) {
35370
35571
  return removed;
35371
35572
  }
35372
35573
 
35574
+ // src/reply-credit.ts
35575
+ var REPLY_CREDIT_FEATURE = "reply_credit_v1";
35576
+ var WEATHER_CREDIT_LINE = "Weather data: WeatherAPI.com. For general information only.";
35577
+ var WEATHER_CREDIT_SUFFIX = `
35578
+
35579
+ ${WEATHER_CREDIT_LINE}`;
35580
+ function isReplyCredit(v) {
35581
+ return v === "weatherapi";
35582
+ }
35583
+ function stripCreditSuffix(text) {
35584
+ return text.endsWith(WEATHER_CREDIT_SUFFIX) ? text.slice(0, text.length - WEATHER_CREDIT_SUFFIX.length) : text;
35585
+ }
35586
+
35373
35587
  // src/grants/binding.ts
35374
35588
  import { sha256 as sha25611 } from "@noble/hashes/sha256";
35375
35589
 
@@ -35377,9 +35591,10 @@ import { sha256 as sha25611 } from "@noble/hashes/sha256";
35377
35591
  import { ed25519 as ed255195 } from "@noble/curves/ed25519";
35378
35592
  var TOOL_GRANTS_DOMAIN = "SECURECOMS-TOOL-GRANTS-V1:";
35379
35593
  var MANIFEST_KNOWN_TOOLS = Object.freeze({
35380
- owner_dm: Object.freeze(["web_search", "web_extract", "fetch_url", "http_request", "sql_query"]),
35594
+ owner_dm: Object.freeze(["web_search", "web_extract", "fetch_url", "http_request", "sql_query", "weather_forecast", "currency_convert"]),
35381
35595
  // web_extract: owner DM only, never team_room (spec §5.4, D5)
35382
- team_room: Object.freeze(["web_search"])
35596
+ // Weather/currency spec §6.3 (U5): both utility tools in both audiences, each held behind utility_tools_v1 (TOOL_FEATURE).
35597
+ team_room: Object.freeze(["web_search", "weather_forecast", "currency_convert"])
35383
35598
  });
35384
35599
  function manifestSignedBytes(manifest) {
35385
35600
  const domain = new TextEncoder().encode(TOOL_GRANTS_DOMAIN);
@@ -35703,20 +35918,30 @@ function bindTargets(raw, signed, equals) {
35703
35918
  }
35704
35919
  return { written, mismatches };
35705
35920
  }
35706
- function signedManifestTools(manifest, audience, searchProvidersCapable) {
35707
- return MANIFEST_KNOWN_TOOLS[audience].filter((t) => (manifest.grants[audience] ?? []).includes(t) && (t !== "web_extract" || searchProvidersCapable));
35921
+ var NO_FEATURES = Object.freeze(/* @__PURE__ */ new Set());
35922
+ var TOOL_FEATURE = Object.freeze({
35923
+ web_extract: "search_providers_v2",
35924
+ weather_forecast: "utility_tools_v1",
35925
+ currency_convert: "utility_tools_v1"
35926
+ });
35927
+ function toolAllowedByFeatures(tool, features) {
35928
+ if (!Object.hasOwn(TOOL_FEATURE, tool)) return true;
35929
+ return features.has(TOOL_FEATURE[tool]);
35708
35930
  }
35709
- function manifestPolicyFor(manifest, switches, searchProvidersCapable) {
35931
+ function signedManifestTools(manifest, audience, features) {
35932
+ return MANIFEST_KNOWN_TOOLS[audience].filter((t) => (manifest.grants[audience] ?? []).includes(t) && toolAllowedByFeatures(t, features));
35933
+ }
35934
+ function manifestPolicyFor(manifest, switches, features) {
35710
35935
  return {
35711
35936
  v: 1,
35712
35937
  manifest_tools: [.../* @__PURE__ */ new Set([
35713
- ...switches.ownerDm ? signedManifestTools(manifest, "owner_dm", searchProvidersCapable) : [],
35714
- ...switches.room ? signedManifestTools(manifest, "team_room", searchProvidersCapable) : []
35938
+ ...switches.ownerDm ? signedManifestTools(manifest, "owner_dm", features) : [],
35939
+ ...switches.room ? signedManifestTools(manifest, "team_room", features) : []
35715
35940
  ])].sort(),
35716
35941
  secret_path_mode: "enforce"
35717
35942
  };
35718
35943
  }
35719
- function bindRuntimeConfig(manifest, payload, prior, switches = { ownerDm: true, room: true }, searchProvidersCapable = false) {
35944
+ function bindRuntimeConfig(manifest, payload, prior, switches = { ownerDm: true, room: true }, features = NO_FEATURES) {
35720
35945
  const mismatches = /* @__PURE__ */ new Set();
35721
35946
  const cfg = manifest.config;
35722
35947
  const out = {};
@@ -35745,7 +35970,8 @@ function bindRuntimeConfig(manifest, payload, prior, switches = { ownerDm: true,
35745
35970
  for (const name of Object.keys(files)) {
35746
35971
  if (name !== LLM_SOURCE && !PERSONA_FILES.includes(name)) mismatches.add("files");
35747
35972
  }
35748
- const ownerDmTools = signedManifestTools(manifest, "owner_dm", searchProvidersCapable);
35973
+ const ownerDmTools = signedManifestTools(manifest, "owner_dm", features);
35974
+ const ownerDmSearchTools = ownerDmTools.filter((t) => t === "web_search" || t === "web_extract");
35749
35975
  const sj = isObj2(payload.skills_json) ? payload.skills_json : void 0;
35750
35976
  const eps = bindTargets(sj?.endpoints, manifest.endpoints, endpointEquals);
35751
35977
  const dbs = bindTargets(sj?.databases, manifest.databases, databaseEquals);
@@ -35766,17 +35992,20 @@ function bindRuntimeConfig(manifest, payload, prior, switches = { ownerDm: true,
35766
35992
  const usablePrior = cfg.search !== void 0 && prior.search?.provider === cfg.search.provider ? prior.search : void 0;
35767
35993
  searchBlock = cfg.search === void 0 ? void 0 : usablePrior ?? Object.freeze({ provider: cfg.search.provider });
35768
35994
  }
35995
+ const pw = isObj2(sj?.weather) ? sj.weather : void 0;
35996
+ const weatherBlock = typeof pw?.keyFile === "string" && pw.keyFile !== "" ? { keyFile: pw.keyFile } : void 0;
35769
35997
  const skillsDoc = {
35770
35998
  endpoints: eps.written,
35771
35999
  databases: dbs.written,
35772
36000
  flags: [...cfg.flags],
35773
36001
  ...deny !== void 0 || cfg.tools_allow !== void 0 ? { tools: { ...deny !== void 0 ? { deny } : {}, ...cfg.tools_allow !== void 0 ? { allow: [...cfg.tools_allow] } : {} } } : {},
35774
36002
  ...searchBlock ? { search: searchBlock } : {},
36003
+ ...weatherBlock ? { weather: weatherBlock } : {},
35775
36004
  // D31: the marker the runtime keys D24 (iii) on; written under every manifest, true when config.search is signed.
35776
36005
  search_signed: cfg.search !== void 0,
35777
36006
  // D36, D39: the signed owner-DM cap, only when the owner-DM switch is on and the manifest grants an owner-DM tool;
35778
36007
  // otherwise none, so the runtime clamps a signed raise to the default. Never read from the payload.
35779
- ...cfg.search?.owner_dm_daily_cap !== void 0 && switches.ownerDm && ownerDmTools.length > 0 ? { owner_dm_search_cap: cfg.search.owner_dm_daily_cap } : {}
36008
+ ...cfg.search?.owner_dm_daily_cap !== void 0 && switches.ownerDm && ownerDmSearchTools.length > 0 ? { owner_dm_search_cap: cfg.search.owner_dm_daily_cap } : {}
35780
36009
  };
35781
36010
  out["skills.json"] = JSON.stringify(skillsDoc, null, 2) + "\n";
35782
36011
  const signedMemory = { mode: cfg.memory.mode, ...cfg.memory.recall_days !== void 0 ? { recall_days: cfg.memory.recall_days } : {} };
@@ -35807,7 +36036,7 @@ function bindRuntimeConfig(manifest, payload, prior, switches = { ownerDm: true,
35807
36036
  mismatches.add("legacy_assign");
35808
36037
  legacyDecisions = decisions.filter((d) => isObj2(d) && d.action === "delete");
35809
36038
  }
35810
- const policy = manifestPolicyFor(manifest, switches, searchProvidersCapable);
36039
+ const policy = manifestPolicyFor(manifest, switches, features);
35811
36040
  return {
35812
36041
  mismatches: CONFIG_BINDING_FIELDS.filter((f) => mismatches.has(f)).sort(),
35813
36042
  targetMismatches: { endpoint: eps.mismatches, database: dbs.mismatches },
@@ -35950,7 +36179,11 @@ var TOOL_ROOM_CLASSES = Object.freeze({
35950
36179
  // D5: spends the owner's key and sends URLs out
35951
36180
  fetch_url: Object.freeze(["egress"]),
35952
36181
  sql_query: Object.freeze(["owner_credential"]),
35953
- http_request: Object.freeze(["owner_credential", "egress", "side_effect"])
36182
+ http_request: Object.freeze(["owner_credential", "egress", "side_effect"]),
36183
+ // Weather/currency (plan audit r1; supersedes spec §8's values): both send room words to an outside host, and weather
36184
+ // spends the owner's key.
36185
+ weather_forecast: Object.freeze(["owner_credential", "egress"]),
36186
+ currency_convert: Object.freeze(["egress"])
35954
36187
  });
35955
36188
  var LIVE_MEMBER_STATUSES = /* @__PURE__ */ new Set(["active", "add_pending"]);
35956
36189
  function isLiveRow(r) {
@@ -36367,8 +36600,8 @@ function voiceWindowEligible(t) {
36367
36600
  }
36368
36601
  function waitMs(ms) {
36369
36602
  if (ms <= 0) return Promise.resolve();
36370
- return new Promise((resolve4) => {
36371
- const t = setTimeout(resolve4, ms);
36603
+ return new Promise((resolve5) => {
36604
+ const t = setTimeout(resolve5, ms);
36372
36605
  t.unref?.();
36373
36606
  });
36374
36607
  }
@@ -36505,7 +36738,7 @@ var FileOwnerAuthorityStore = class {
36505
36738
  }
36506
36739
  async readAll() {
36507
36740
  try {
36508
- return JSON.parse(await fsp.readFile(this.path, "utf8"));
36741
+ return JSON.parse(await fsp2.readFile(this.path, "utf8"));
36509
36742
  } catch (e) {
36510
36743
  if (e.code === "ENOENT") return {};
36511
36744
  throw e;
@@ -36518,8 +36751,8 @@ var FileOwnerAuthorityStore = class {
36518
36751
  async save(enclaveId, pin) {
36519
36752
  const all = await this.readAll();
36520
36753
  all[enclaveId] = pin;
36521
- await fsp.mkdir(dirname7(this.path), { recursive: true });
36522
- await fsp.writeFile(this.path, JSON.stringify(all), "utf8");
36754
+ await fsp2.mkdir(dirname8(this.path), { recursive: true });
36755
+ await fsp2.writeFile(this.path, JSON.stringify(all), "utf8");
36523
36756
  }
36524
36757
  };
36525
36758
  var AgentPausedSendError = class extends Error {
@@ -36726,7 +36959,7 @@ var BridgeAgent = class _BridgeAgent {
36726
36959
  threshold: EMBEDDING_MANIFEST.similarityThreshold,
36727
36960
  warm: windowsSemanticOn(this.platform)
36728
36961
  };
36729
- return deps.rankingTransport ? deps.rankingTransport(init) : workerTransport(init, dirname7(fileURLToPath2(import.meta.url)));
36962
+ return deps.rankingTransport ? deps.rankingTransport(init) : workerTransport(init, dirname8(fileURLToPath2(import.meta.url)));
36730
36963
  }
36731
36964
  });
36732
36965
  this.embedder = deps.embedder ?? (deps.hostMachine === true && deps.home === void 0 ? new EmbedderClient({ identityId: cfg.identityId, home: homedir4(), source: cfg.modelSource, fetchImpl: this.fetchImpl, platform: this.platform }) : INERT_EMBEDDER);
@@ -36737,7 +36970,7 @@ var BridgeAgent = class _BridgeAgent {
36737
36970
  modelId: EMBEDDING_MANIFEST.modelId,
36738
36971
  threshold: EMBEDDING_MANIFEST.similarityThreshold
36739
36972
  });
36740
- this.heartbeat = new CallHeartbeat(this.machinePaths ? join19(this.home, ".securecoms", "run", "voice-call") : null, cfg.identityId, VOICE_LIVE_WINDOW_MS);
36973
+ this.heartbeat = new CallHeartbeat(this.machinePaths ? join20(this.home, ".securecoms", "run", "voice-call") : null, cfg.identityId, VOICE_LIVE_WINDOW_MS);
36741
36974
  this.reembed = new ReembedQueue({
36742
36975
  store: this.store,
36743
36976
  embedder: this.embedder,
@@ -36796,15 +37029,15 @@ var BridgeAgent = class _BridgeAgent {
36796
37029
  this.bootDrainWaitMs = deps.bootDrainWaitMs ?? BOOT_DRAIN_WAIT_MS_DEFAULT;
36797
37030
  this.searchCapabilityRefetchDebounceMs = deps.searchCapabilityRefetchDebounceMs ?? SEARCH_CAPABILITY_REFETCH_DEBOUNCE_MS;
36798
37031
  this.searchCapabilityRefetchMaxWaitMs = deps.searchCapabilityRefetchMaxWaitMs ?? SEARCH_CAPABILITY_REFETCH_MAX_WAIT_MS;
36799
- this.replySleep = deps.replySleep ?? ((ms) => new Promise((resolve4) => {
36800
- const t = setTimeout(resolve4, ms);
37032
+ this.replySleep = deps.replySleep ?? ((ms) => new Promise((resolve5) => {
37033
+ const t = setTimeout(resolve5, ms);
36801
37034
  t.unref?.();
36802
37035
  }));
36803
37036
  this.actionRateFloor = new TokenBucketFloor({ capacity: 1, refillPerMinute: 30, now: this.actionClock });
36804
37037
  this.globalActionRateFloor = new TokenBucketFloor({ capacity: 1, refillPerMinute: 30, now: this.actionClock });
36805
37038
  const self = this;
36806
37039
  this.downloadBlobImpl = deps.downloadBlob ?? async function(blobId) {
36807
- const url = `${cfg.apiBase}/api/v1/attachments/${encodeURIComponent(blobId)}?caller_identity_id=${encodeURIComponent(cfg.identityId)}`;
37040
+ const url = `${cfg.apiBase}/api/v1/attachments/${encodeURIComponent(assertBlobId(blobId))}?caller_identity_id=${encodeURIComponent(cfg.identityId)}`;
36808
37041
  const res = await fetchWithDeadline(self.fetchImpl, url, { headers: { authorization: `Bearer ${cfg.apiKey}` } }, {
36809
37042
  op: "attachment_download",
36810
37043
  timeoutMs: FETCH_DEADLINE_MS.blobBase,
@@ -36824,8 +37057,9 @@ var BridgeAgent = class _BridgeAgent {
36824
37057
  const j = await res.json();
36825
37058
  return String(j.attachment_id);
36826
37059
  };
36827
- this.attachmentsDirImpl = deps.attachmentsDir ?? join19(dirname7(cfg.storePath), "attachments");
36828
- this.vaultOwnerStoreImpl = deps.vaultOwnerStore ?? new FileOwnerAuthorityStore(join19(dirname7(cfg.storePath), "vault-owner-pins.json"));
37060
+ this.attachmentsDirImpl = deps.attachmentsDir ?? join20(dirname8(cfg.storePath), "attachments");
37061
+ this.vaultOutboxDirImpl = deps.vaultOutboxDir !== void 0 ? deps.vaultOutboxDir : cfg.storePath === ":memory:" ? null : join20(dirname8(cfg.storePath), VAULT_OUTBOX_DIRNAME);
37062
+ this.vaultOwnerStoreImpl = deps.vaultOwnerStore ?? new FileOwnerAuthorityStore(join20(dirname8(cfg.storePath), "vault-owner-pins.json"));
36829
37063
  this.action = {
36830
37064
  begin: (channelId) => this.actionBegin(channelId),
36831
37065
  start: (opts) => this.actionStart(opts),
@@ -37556,6 +37790,9 @@ var BridgeAgent = class _BridgeAgent {
37556
37790
  uploadBlobImpl;
37557
37791
  /** Track 5: directory where received attachments are saved. */
37558
37792
  attachmentsDirImpl;
37793
+ vaultOutboxDirImpl;
37794
+ /** Audit 2026-10-30 HIGH 2: the outbox pinned at start(); null = every upload refused. */
37795
+ outboxPin = null;
37559
37796
  /** B7: persistence for the vault kekset's owner-pin TOFU state. */
37560
37797
  vaultOwnerStoreImpl;
37561
37798
  /** B7: last-seen kekset generation per enclave, enforced monotonic across
@@ -37764,7 +38001,7 @@ var BridgeAgent = class _BridgeAgent {
37764
38001
  }
37765
38002
  let bound;
37766
38003
  try {
37767
- bound = this.grantWriters.bindRuntimeConfig(manifest, payload, { search: prior }, { ownerDm: this.ownerDmManifestGrants, room: this.roomManifestGrants }, this.searchProvidersCapable());
38004
+ bound = this.grantWriters.bindRuntimeConfig(manifest, payload, { search: prior }, { ownerDm: this.ownerDmManifestGrants, room: this.roomManifestGrants }, this.capabilityFeatures());
37768
38005
  } catch (e) {
37769
38006
  bound = null;
37770
38007
  return this.bindFailed(manifest, payload.memory, e);
@@ -37785,7 +38022,7 @@ var BridgeAgent = class _BridgeAgent {
37785
38022
  try {
37786
38023
  this.grantWriters.writeManagedFiles(this.home, this.cfg.identityId, bound.files);
37787
38024
  if (manifest.config.llm === void 0) this.grantWriters.removeManagedLlmSource(this.home, this.cfg.identityId);
37788
- this.grantWriters.writeManifestPolicy(this.home, this.cfg.identityId, manifestPolicyFor(manifest, { ownerDm: this.ownerDmManifestGrants, room: this.roomManifestGrants }, this.searchProvidersCapable()));
38025
+ this.grantWriters.writeManifestPolicy(this.home, this.cfg.identityId, manifestPolicyFor(manifest, { ownerDm: this.ownerDmManifestGrants, room: this.roomManifestGrants }, this.capabilityFeatures()));
37789
38026
  } catch (e) {
37790
38027
  this.manifestBinding = null;
37791
38028
  throw e;
@@ -37832,13 +38069,14 @@ var BridgeAgent = class _BridgeAgent {
37832
38069
  }
37833
38070
  /** No manifest in force: a manifest-policy.json left by an earlier one is replaced by an empty tool list. */
37834
38071
  overwriteStalePolicy() {
37835
- if (!existsSync5(join19(runtimeConfigDir(this.home, this.cfg.identityId), "manifest-policy.json"))) return;
38072
+ if (!existsSync5(join20(runtimeConfigDir(this.home, this.cfg.identityId), "manifest-policy.json"))) return;
37836
38073
  this.grantWriters.writeManifestPolicy(this.home, this.cfg.identityId, { v: 1, manifest_tools: [], secret_path_mode: "enforce" });
37837
38074
  }
37838
38075
  /** The report half of refetchRuntimeConfigOnce (split out for the plan 4 Task 11 bind_failed path). */
37839
38076
  async postRuntimeConfigReport(base, version, skillsJson) {
37840
38077
  const local = readLocalReport(this.home, this.cfg.identityId);
37841
38078
  const reportedSearchProviders = this.searchProvidersCapable();
38079
+ const reportedUtilityTools = this.utilityToolsCapable();
37842
38080
  const report = {
37843
38081
  applied_version: version,
37844
38082
  paths: checkConfiguredPaths(skillsJson),
@@ -37856,6 +38094,9 @@ var BridgeAgent = class _BridgeAgent {
37856
38094
  // Final review I-2 (controller ruling): AND the current search_providers_v2, so the web never shows extract while
37857
38095
  // the bridge withholds web_extract from the runtime.
37858
38096
  has_extract_provider: (local?.has_extract_provider ?? false) && reportedSearchProviders,
38097
+ // Weather/currency L-1, §8 item 5: AND the current utility_tools_v1, so the web never shows a usable weather key
38098
+ // while the bridge withholds weather_forecast from the runtime.
38099
+ has_weather_key: (local?.has_weather_key ?? false) && reportedUtilityTools,
37859
38100
  // D39: the owner-DM daily cap the runtime enforces, passed through when it reported a valid one; never the signed value.
37860
38101
  ...local?.owner_dm_search_cap_effective !== void 0 ? { owner_dm_search_cap_effective: local.owner_dm_search_cap_effective } : {},
37861
38102
  // Same idea, for the BYOK LLM key file: only the runtime can see
@@ -37903,7 +38144,9 @@ var BridgeAgent = class _BridgeAgent {
37903
38144
  // D28 (§11.1 step 3): a separate key, never a value in the capability enum. False with no runtime attached.
37904
38145
  search_providers_v2: reportedSearchProviders,
37905
38146
  // D19 item 4: content-free; the signed provider is one the attached runtime(s) cannot honour.
37906
- search_provider_unsupported: this.searchProviderUnsupported(reportedSearchProviders)
38147
+ search_provider_unsupported: this.searchProviderUnsupported(reportedSearchProviders),
38148
+ // Weather/currency §8 item 5: a separate key, false with no runtime attached.
38149
+ utility_tools_v1: reportedUtilityTools
37907
38150
  }
37908
38151
  };
37909
38152
  const reportedVerifyFailures = this.ownerVerifyFailures;
@@ -37925,6 +38168,7 @@ var BridgeAgent = class _BridgeAgent {
37925
38168
  }
37926
38169
  this.ownerVerifyFailures = Math.max(0, this.ownerVerifyFailures - reportedVerifyFailures);
37927
38170
  this.lastReportedSearchProviders = reportedSearchProviders;
38171
+ this.lastReportedUtilityTools = reportedUtilityTools;
37928
38172
  if (version) writeServiceLog("info", "runtime_config.applied", { version });
37929
38173
  else writeServiceLog("info", "runtime_config.legacy_memory_reported");
37930
38174
  }
@@ -37992,9 +38236,16 @@ var BridgeAgent = class _BridgeAgent {
37992
38236
  }
37993
38237
  async startBody() {
37994
38238
  this.startedAt = Date.now();
38239
+ if (this.vaultOutboxDirImpl) {
38240
+ try {
38241
+ this.outboxPin = ensureOutbox(this.vaultOutboxDirImpl);
38242
+ } catch (e) {
38243
+ writeServiceLog("warn", "vault_outbox.unavailable", {}, e);
38244
+ }
38245
+ }
37995
38246
  await this.restoreRuntimeInbox();
37996
38247
  await this.loadToolGrantState();
37997
- this.recomputeManifestPolicy(false);
38248
+ this.recomputeManifestPolicy(NO_FEATURES);
37998
38249
  try {
37999
38250
  this.store.pruneRoomSearchCap(utcDayOf(Date.now()));
38000
38251
  } catch (e) {
@@ -38017,8 +38268,8 @@ var BridgeAgent = class _BridgeAgent {
38017
38268
  const drain = this.ingestOnce().then(() => true, () => true);
38018
38269
  const drainedInTime = await Promise.race([
38019
38270
  drain,
38020
- new Promise((resolve4) => {
38021
- const t = setTimeout(() => resolve4(false), this.bootDrainWaitMs);
38271
+ new Promise((resolve5) => {
38272
+ const t = setTimeout(() => resolve5(false), this.bootDrainWaitMs);
38022
38273
  t.unref?.();
38023
38274
  void drain.then(() => clearTimeout(t));
38024
38275
  })
@@ -38028,7 +38279,7 @@ var BridgeAgent = class _BridgeAgent {
38028
38279
  await this.bootReconcileKeyPackage();
38029
38280
  } else {
38030
38281
  writeServiceLog("warn", "boot.drain_slow", { wait_ms: this.bootDrainWaitMs });
38031
- const settled = new Promise((resolve4) => this.ingestSettledWaiters.push(resolve4));
38282
+ const settled = new Promise((resolve5) => this.ingestSettledWaiters.push(resolve5));
38032
38283
  this.deferredBootPublish = settled.then(async () => {
38033
38284
  this.bootDrainPending = false;
38034
38285
  if (this.closingDown) return;
@@ -38977,13 +39228,13 @@ var BridgeAgent = class _BridgeAgent {
38977
39228
  * absent, because the id is derived.
38978
39229
  */
38979
39230
  async send(channelId, text, messageId, opts = {}) {
38980
- const body = { kind: "text", text };
39231
+ const body = { kind: "text", text: opts.credit !== void 0 ? text + WEATHER_CREDIT_SUFFIX : text };
38981
39232
  if (this.isIdempotentSend(messageId, opts)) return this.trackReplyWork(this.sendIdempotent(channelId, body, opts));
38982
39233
  const id = resolveMessageId(messageId);
38983
39234
  const purgeEpochBeforePublish = this.store.conversationPurgeEpoch(channelId);
38984
39235
  const replyCapture = this.unifiedReplyCapture(channelId, opts.inReplyTo);
38985
39236
  await this.publishBody(channelId, body, id);
38986
- this.afterPublish(channelId, body, id, opts, purgeEpochBeforePublish, replyCapture);
39237
+ this.afterPublish(channelId, body, id, opts, purgeEpochBeforePublish, replyCapture, text);
38987
39238
  return { message_id: id, status: "sent" };
38988
39239
  }
38989
39240
  /** PR #716 review MEDIUM-5: register reply work so close() can wait for it. */
@@ -39002,7 +39253,7 @@ var BridgeAgent = class _BridgeAgent {
39002
39253
  const purgeEpochBeforePublish = this.store.conversationPurgeEpoch(channelId);
39003
39254
  const replyCapture = this.unifiedReplyCapture(channelId, opts.inReplyTo);
39004
39255
  await this.publishBody(channelId, body, id);
39005
- this.afterPublish(channelId, body, id, opts, purgeEpochBeforePublish, replyCapture);
39256
+ this.afterPublish(channelId, body, id, opts, purgeEpochBeforePublish, replyCapture, null);
39006
39257
  return { message_id: id, status: "sent" };
39007
39258
  }
39008
39259
  /**
@@ -39135,18 +39386,24 @@ var BridgeAgent = class _BridgeAgent {
39135
39386
  const meta = this.freshMeta(capChannel);
39136
39387
  if (meta && !meta.is_dm) this.enforceRateFloor("room", capChannel);
39137
39388
  }
39138
- /** Bookkeeping after a CONFIRMED publish of `body` under `id`. */
39139
- afterPublish(channelId, body, id, opts, purgeEpoch, replyCapture) {
39389
+ /**
39390
+ * Bookkeeping after a CONFIRMED publish of `body` under `id`. `recordText` (weather/currency Task 17) is what a text
39391
+ * reply is RECORDED as: the published text minus any credit line. Every record site below reads `recorded`, never
39392
+ * `body.text`. Every production caller passes it (pinned by reply-credit.test.ts: text, or null for an a2a body);
39393
+ * undefined is left only for direct test calls of an uncredited body, where the published and record texts are equal.
39394
+ */
39395
+ afterPublish(channelId, body, id, opts, purgeEpoch, replyCapture, recordText) {
39396
+ const recorded = recordText ?? (recordText === void 0 && body.kind === "text" ? body.text : "");
39140
39397
  if (opts.agentTurn !== false) this.agentHops.noteOwnSend(channelId);
39141
39398
  const modality = this.spokenReplyIds.has(id) ? "voice" : "text";
39142
- const bridgeLine = this.unifiedOn && body.kind === "text" && isBridgeMemoryLine(body.text);
39399
+ const bridgeLine = this.unifiedOn && body.kind === "text" && isBridgeMemoryLine(recorded);
39143
39400
  if (opts.inReplyTo !== void 0 && this.isOwnerVerifyFailed(opts.inReplyTo)) return;
39144
- if (body.kind === "text" && opts.callBufferReply === true && opts.inReplyTo !== void 0 && !this.spokenReplyIds.has(id) && !isBridgeMemoryLine(body.text) && this.callBuffer.isVoiceScopedId(opts.inReplyTo)) {
39145
- this.callBuffer.appendAgent(channelId, body.text, "reply");
39401
+ if (body.kind === "text" && opts.callBufferReply === true && opts.inReplyTo !== void 0 && !this.spokenReplyIds.has(id) && !isBridgeMemoryLine(recorded) && this.callBuffer.isVoiceScopedId(opts.inReplyTo)) {
39402
+ this.callBuffer.appendAgent(channelId, recorded, "reply");
39146
39403
  }
39147
39404
  const origin = this.spokenReplyIds.has(id) ? "fast_path" : !this.taintScope(channelId) ? "reply" : opts.inReplyTo !== void 0 && this.cleanTurns.has(opts.inReplyTo) && !this.taintedTurns.has(opts.inReplyTo) ? "reply" : "untrusted";
39148
- if (body.kind === "text" && !bridgeLine) void recordOutboundMemory(this.memory, channelId, body.text, id, modality, purgeEpoch, origin);
39149
- if (this.unifiedOn && body.kind === "text" && !bridgeLine) this.spawnUnifiedReply(channelId, body.text, id, replyCapture ?? null, origin);
39405
+ if (body.kind === "text" && !bridgeLine) void recordOutboundMemory(this.memory, channelId, recorded, id, modality, purgeEpoch, origin);
39406
+ if (this.unifiedOn && body.kind === "text" && !bridgeLine) this.spawnUnifiedReply(channelId, recorded, id, replyCapture ?? null, origin);
39150
39407
  }
39151
39408
  /**
39152
39409
  * Final review bridge I-1 (ledger T25): the agent reply's U11 capture, taken BEFORE the publish await. The clear generation
@@ -39224,14 +39481,14 @@ var BridgeAgent = class _BridgeAgent {
39224
39481
  const k = replyIntentKey({ inReplyTo: opts.inReplyTo, idempotencyKey: opts.idempotencyKey, ordinal: () => 0 });
39225
39482
  if (!k) throw new Error("idempotent send without a key");
39226
39483
  const id = replyMessageId(this.replyIdKey, this.cfg.identityId, channelId, k);
39227
- const mac2 = replyBodyMac(this.replyIdKey, id, channelId, bytes);
39228
- const { intent } = await this.store.claimReplyIntent({ ...k, channelId, replyMessageId: id, bodyMac: mac2, body: bytes });
39484
+ const mac2 = replyBodyMac(this.replyIdKey, id, channelId, bytes, opts.credit);
39485
+ const { intent } = await this.store.claimReplyIntent({ ...k, channelId, replyMessageId: id, bodyMac: mac2, body: bytes, replyCredit: opts.credit ?? null });
39229
39486
  if (intent.bodyMac !== mac2 || intent.channelId !== channelId || intent.state === "skipped") {
39230
39487
  throw new IdempotencyConflictError();
39231
39488
  }
39232
39489
  return k;
39233
39490
  }
39234
- return this.claimUnkeyedPart(opts.inReplyTo, channelId, bytes, true);
39491
+ return this.claimUnkeyedPart(opts.inReplyTo, channelId, bytes, true, opts.credit ?? null);
39235
39492
  });
39236
39493
  return this.publishIntent(key, opts);
39237
39494
  }
@@ -39240,22 +39497,23 @@ var BridgeAgent = class _BridgeAgent {
39240
39497
  * the reply to `inReplyTo`. Caller holds the scope lock. `matchRetry` false
39241
39498
  * after a slot collision: the body must go to a NEW part.
39242
39499
  */
39243
- async claimUnkeyedPart(inReplyTo, channelId, bytes, matchRetry) {
39500
+ async claimUnkeyedPart(inReplyTo, channelId, bytes, matchRetry, replyCredit) {
39501
+ const credit = replyCredit ?? void 0;
39244
39502
  const scope = `reply:${inReplyTo}`;
39245
39503
  let latest = null;
39246
39504
  for (const intent of await this.store.listReplyIntents(inReplyTo)) {
39247
39505
  const n = intent.scope === scope ? partOrdinal(intent.part) : null;
39248
39506
  if (n !== null && (latest === null || n > latest.ordinal)) latest = { ordinal: n, intent };
39249
39507
  }
39250
- if (matchRetry && latest && latest.intent.state !== "skipped" && latest.intent.channelId === channelId && latest.intent.bodyMac === replyBodyMac(this.replyIdKey, latest.intent.replyMessageId, channelId, bytes)) {
39508
+ if (matchRetry && latest && latest.intent.state !== "skipped" && latest.intent.channelId === channelId && latest.intent.bodyMac === replyBodyMac(this.replyIdKey, latest.intent.replyMessageId, channelId, bytes, credit)) {
39251
39509
  return { scope, part: latest.intent.part, inReplyTo };
39252
39510
  }
39253
39511
  let ordinal = latest ? latest.ordinal + 1 : 0;
39254
39512
  for (let i = 0; i < REPLY_MAX_PART_ADVANCES; i++, ordinal++) {
39255
39513
  const k = { scope, part: `p:${ordinal}`, inReplyTo };
39256
39514
  const id = replyMessageId(this.replyIdKey, this.cfg.identityId, channelId, k);
39257
- const mac2 = replyBodyMac(this.replyIdKey, id, channelId, bytes);
39258
- const { created, intent } = await this.store.claimReplyIntent({ ...k, channelId, replyMessageId: id, bodyMac: mac2, body: bytes });
39515
+ const mac2 = replyBodyMac(this.replyIdKey, id, channelId, bytes, credit);
39516
+ const { created, intent } = await this.store.claimReplyIntent({ ...k, channelId, replyMessageId: id, bodyMac: mac2, body: bytes, replyCredit });
39259
39517
  if (created) return k;
39260
39518
  if (matchRetry && intent.state !== "skipped" && intent.bodyMac === mac2 && intent.channelId === channelId) return k;
39261
39519
  writeServiceLog("info", "reply.part_slot_taken", { attempt: i + 1 });
@@ -39300,6 +39558,12 @@ var BridgeAgent = class _BridgeAgent {
39300
39558
  }
39301
39559
  const bytes = intent.body;
39302
39560
  const body = decodeReplyBody(bytes);
39561
+ const recordText = this.recordTextOf(intent.replyCredit, body);
39562
+ if (recordText === void 0) {
39563
+ writeServiceLog("error", "reply.intent_integrity_refused");
39564
+ this.store.deletePendingReplyIntent(key.scope, key.part);
39565
+ throw new ReplyIntentIntegrityError();
39566
+ }
39303
39567
  const purgeEpochBeforePublish = this.store.conversationPurgeEpoch(intent.channelId);
39304
39568
  const replyCapture = this.unifiedReplyCapture(intent.channelId, intent.inReplyTo ?? void 0);
39305
39569
  try {
@@ -39328,14 +39592,17 @@ var BridgeAgent = class _BridgeAgent {
39328
39592
  const inReplyTo = intent.inReplyTo;
39329
39593
  const next = await this.withReplyLock(
39330
39594
  JSON.stringify(["scope", key.scope]),
39331
- () => this.claimUnkeyedPart(inReplyTo, intent.channelId, bytes, false)
39595
+ () => (
39596
+ // r2 BLOCKER 1: the colliding row's OWN credit (verified above), never `opts`.
39597
+ this.claimUnkeyedPart(inReplyTo, intent.channelId, bytes, false, isReplyCredit(intent.replyCredit) ? intent.replyCredit : null)
39598
+ )
39332
39599
  );
39333
39600
  return this.publishIntent(next, opts, advances + 1);
39334
39601
  }
39335
39602
  throw e;
39336
39603
  }
39337
39604
  this.store.markReplyIntentSent(key.scope, key.part);
39338
- this.afterPublish(intent.channelId, body, intent.replyMessageId, opts, purgeEpochBeforePublish, replyCapture);
39605
+ this.afterPublish(intent.channelId, body, intent.replyMessageId, opts, purgeEpochBeforePublish, replyCapture, recordText);
39339
39606
  return { message_id: intent.replyMessageId, status: "sent" };
39340
39607
  });
39341
39608
  }
@@ -39345,9 +39612,21 @@ var BridgeAgent = class _BridgeAgent {
39345
39612
  */
39346
39613
  intentVerifies(intent) {
39347
39614
  if (!intent.body) return false;
39615
+ const credit = intent.replyCredit ?? null;
39616
+ if (credit !== null && !isReplyCredit(credit)) return false;
39348
39617
  const id = replyMessageId(this.replyIdKey, this.cfg.identityId, intent.channelId, intent);
39349
39618
  if (id !== intent.replyMessageId) return false;
39350
- return replyBodyMac(this.replyIdKey, intent.replyMessageId, intent.channelId, intent.body) === intent.bodyMac;
39619
+ return replyBodyMac(this.replyIdKey, intent.replyMessageId, intent.channelId, intent.body, credit ?? void 0) === intent.bodyMac;
39620
+ }
39621
+ /**
39622
+ * Weather/currency Task 17: what a verified row's reply is RECORDED as. Null credit: the decoded text unchanged (null
39623
+ * for an a2a body). "weatherapi": the text minus the exact credit suffix. Undefined: the row is refused (an unknown
39624
+ * credit, a credited a2a body, or a credited text without the exact suffix).
39625
+ */
39626
+ recordTextOf(credit, body) {
39627
+ if (credit === null) return body.kind === "text" ? body.text : null;
39628
+ if (!isReplyCredit(credit) || body.kind !== "text" || !body.text.endsWith(WEATHER_CREDIT_SUFFIX)) return void 0;
39629
+ return body.text.slice(0, body.text.length - WEATHER_CREDIT_SUFFIX.length);
39351
39630
  }
39352
39631
  /**
39353
39632
  * SEC-06 #18 item 3: a rate-limited reply retries the PUBLISH in-process
@@ -39507,7 +39786,8 @@ var BridgeAgent = class _BridgeAgent {
39507
39786
  uploadBlob: (ciphertext, channelId) => this.uploadBlobImpl(ciphertext, channelId),
39508
39787
  encryptFile,
39509
39788
  bytesToBase64: bytesToBase642,
39510
- readFile: (p) => fsp.readFile(p).then((b) => new Uint8Array(b))
39789
+ // Security fix 2026-10-30: attachments upload only from the outbox.
39790
+ readFile: (p) => readOutboxFile(this.outboxPin, p).then((r) => r.bytes)
39511
39791
  };
39512
39792
  }
39513
39793
  receiveDeps() {
@@ -39517,8 +39797,8 @@ var BridgeAgent = class _BridgeAgent {
39517
39797
  decryptFile,
39518
39798
  computeFileDigest,
39519
39799
  base64ToBytes: base64ToBytes2,
39520
- mkdirp: (d) => fsp.mkdir(d, { recursive: true }).then(() => void 0),
39521
- writeFile: (p, data) => fsp.writeFile(p, data)
39800
+ mkdirp: (d) => fsp2.mkdir(d, { recursive: true }).then(() => void 0),
39801
+ writeFile: (p, data) => fsp2.writeFile(p, data)
39522
39802
  };
39523
39803
  }
39524
39804
  async sendAttachment(channelId, filePath2, caption) {
@@ -39526,8 +39806,11 @@ var BridgeAgent = class _BridgeAgent {
39526
39806
  const joined = await this.reconcileJoin(channelId);
39527
39807
  if (!joined) throw new ChannelPendingError(channelId);
39528
39808
  let body;
39809
+ let read;
39529
39810
  try {
39530
- body = await prepareAttachment(filePath2, caption, channelId, this.sendDeps());
39811
+ read = await readOutboxFile(this.outboxPin, filePath2);
39812
+ const bytes = read.bytes;
39813
+ body = await prepareAttachment(filePath2, caption, channelId, { ...this.sendDeps(), readFile: async () => bytes });
39531
39814
  } catch (e) {
39532
39815
  if (e.code === "ENOENT" || e.code === "EACCES" || e.code === "ENOTDIR" || e.code === "EISDIR") {
39533
39816
  throw new InvalidPathError(filePath2, e);
@@ -39568,6 +39851,7 @@ var BridgeAgent = class _BridgeAgent {
39568
39851
  );
39569
39852
  this.latency.record("send.total", performance.now() - sendStart);
39570
39853
  this.agentHops.noteOwnSend(channelId);
39854
+ await this.consumeUploaded(read);
39571
39855
  return { message_id: id, status: "sent" };
39572
39856
  }
39573
39857
  // ── B7: FileSafe vault commands (loopback boundary) ──────────────────────
@@ -40093,25 +40377,26 @@ var BridgeAgent = class _BridgeAgent {
40093
40377
  }
40094
40378
  };
40095
40379
  }
40096
- /** Read `path` off local disk, encrypt + wrap its DEK under the vault's
40380
+ /** Read `path` from the vault outbox (security fix 2026-10-30; see
40381
+ * ./outbox.ts), encrypt + wrap its DEK under the vault's
40097
40382
  * current KEK generation, and create+upload+commit it as a new vault
40098
40383
  * file node named `name` (which may differ from `path`'s basename). */
40099
40384
  async vaultWrite(enclaveId, vaultChannelId, path, name) {
40100
- const session = await this.getVaultSession(enclaveId, await this.resolveVaultChannelId(enclaveId, vaultChannelId));
40101
- let bytes;
40385
+ let read;
40102
40386
  try {
40103
- bytes = new Uint8Array(await fsp.readFile(path));
40387
+ read = await readOutboxFile(this.outboxPin, path);
40104
40388
  } catch (e) {
40105
40389
  const code = e.code;
40106
40390
  if (code === "ENOENT" || code === "EACCES" || code === "ENOTDIR" || code === "EISDIR") throw new InvalidPathError(path, e);
40107
40391
  throw e;
40108
40392
  }
40393
+ const session = await this.getVaultSession(enclaveId, await this.resolveVaultChannelId(enclaveId, vaultChannelId));
40109
40394
  const nodeId = crypto.randomUUID();
40110
- return vaultWriteImpl({
40395
+ const written = await vaultWriteImpl({
40111
40396
  enclaveId,
40112
40397
  nodeId,
40113
40398
  name,
40114
- bytes,
40399
+ bytes: read.bytes,
40115
40400
  deps: {
40116
40401
  exporterKekForGeneration: session.kekForGeneration,
40117
40402
  currentGeneration: session.currentGeneration,
@@ -40120,6 +40405,16 @@ var BridgeAgent = class _BridgeAgent {
40120
40405
  commitNode: (committedNodeId) => this.vaultCommitNode(enclaveId, committedNodeId)
40121
40406
  }
40122
40407
  });
40408
+ await this.consumeUploaded(read);
40409
+ return written;
40410
+ }
40411
+ /** Audit 2026-10-30 MED 5: an uploaded file leaves the outbox (no plaintext lingers). Content-free log. */
40412
+ async consumeUploaded(read) {
40413
+ if (!this.outboxPin || !await consumeOutboxFile(this.outboxPin, read)) writeServiceLog("warn", "vault_outbox.consume_failed");
40414
+ }
40415
+ /** Audit 2026-10-30 MED 3: the pinned outbox path, served only on the authenticated GET /v1/vault-outbox. */
40416
+ vaultOutboxPath() {
40417
+ return this.outboxPin?.dir ?? null;
40123
40418
  }
40124
40419
  /** Download + verify + decrypt a vault file node. Returns the plaintext
40125
40420
  * base64-encoded (the loopback boundary is JSON-only, mirroring how
@@ -42552,13 +42847,13 @@ var BridgeAgent = class _BridgeAgent {
42552
42847
  throw new Error("summarize_no_runtime");
42553
42848
  }
42554
42849
  const messages = [{ role: "user", content: prompt }];
42555
- return new Promise((resolve4, reject) => {
42850
+ return new Promise((resolve5, reject) => {
42556
42851
  const onAbort = () => reject(new Error("aborted"));
42557
42852
  signal.addEventListener("abort", onAbort);
42558
42853
  this.fastLlm.complete(messages).then(
42559
42854
  (text) => {
42560
42855
  signal.removeEventListener("abort", onAbort);
42561
- resolve4(text);
42856
+ resolve5(text);
42562
42857
  },
42563
42858
  (err) => {
42564
42859
  signal.removeEventListener("abort", onAbort);
@@ -43471,7 +43766,7 @@ var BridgeAgent = class _BridgeAgent {
43471
43766
  this.memory,
43472
43767
  m.channelId,
43473
43768
  m.envelope.sender_id,
43474
- parsed.text,
43769
+ m.envelope.sender_id === this.cfg.identityId ? stripCreditSuffix(parsed.text) : parsed.text,
43475
43770
  m.messageId,
43476
43771
  m.voiceMode ? "voice" : "text",
43477
43772
  purgeEpochAtTap
@@ -43506,7 +43801,7 @@ var BridgeAgent = class _BridgeAgent {
43506
43801
  return got;
43507
43802
  }
43508
43803
  const cap = Math.min(waitSeconds, 50) * 1e3;
43509
- await new Promise((resolve4) => {
43804
+ await new Promise((resolve5) => {
43510
43805
  let fired = false;
43511
43806
  let timer;
43512
43807
  const wake = () => {
@@ -43515,7 +43810,7 @@ var BridgeAgent = class _BridgeAgent {
43515
43810
  clearTimeout(timer);
43516
43811
  const i = this.waiters.indexOf(wake);
43517
43812
  if (i >= 0) this.waiters.splice(i, 1);
43518
- resolve4();
43813
+ resolve5();
43519
43814
  };
43520
43815
  timer = setTimeout(wake, cap);
43521
43816
  this.waiters.push(wake);
@@ -43558,13 +43853,13 @@ var BridgeAgent = class _BridgeAgent {
43558
43853
  const bound = b !== null && b.state === "bound" && b.counter === this.toolGrantCounter;
43559
43854
  const switchOn = audience === "owner_dm" ? this.ownerDmManifestGrants : audience === "team_room" ? this.roomManifestGrants : false;
43560
43855
  const signedNow = new Set(audience && manifest ? manifest.grants[audience] ?? [] : []);
43561
- const constants4 = new Set(audience === "owner_dm" ? OWNER_DM_DEFAULT_READ_GRANTS.map((g) => g.tool) : audience === "team_room" ? ROOM_DEFAULT_READ_GRANTS.map((g) => g.tool) : []);
43856
+ const constants5 = new Set(audience === "owner_dm" ? OWNER_DM_DEFAULT_READ_GRANTS.map((g) => g.tool) : audience === "team_room" ? ROOM_DEFAULT_READ_GRANTS.map((g) => g.tool) : []);
43562
43857
  let changed = false;
43563
43858
  const out = a.tool_grants.flatMap((g) => {
43564
43859
  if (!(g.mode === "read" && g.source === "manifest")) return [g];
43565
43860
  if (switchOn && bound && signedNow.has(g.tool)) return [g];
43566
43861
  changed = true;
43567
- return constants4.has(g.tool) && signedNow.has(g.tool) ? [Object.freeze({ tool: g.tool, mode: "read" })] : [];
43862
+ return constants5.has(g.tool) && signedNow.has(g.tool) ? [Object.freeze({ tool: g.tool, mode: "read" })] : [];
43568
43863
  });
43569
43864
  if (!changed) return m;
43570
43865
  return { ...m, authorization: Object.freeze({ ...a, tool_grants: Object.freeze(out) }) };
@@ -44269,21 +44564,49 @@ ${channelId}`;
44269
44564
  */
44270
44565
  searchProvidersSockets = /* @__PURE__ */ new Map();
44271
44566
  /**
44272
- * The D28 value the files were last recomputed for; null until the first socket observation, so that observation
44273
- * always recomputes (final review I-1), even when it is non-capable.
44567
+ * Weather/currency U6 (H-1): the same AND-over-sockets state for utility_tools_v1, kept separately per socket, so a
44568
+ * runtime that declares search_providers_v2 but not utility_tools_v1 (0.14.0, 0.14.1) keeps web_extract and never
44569
+ * sees a utility name.
44570
+ */
44571
+ utilityToolsSockets = /* @__PURE__ */ new Map();
44572
+ /**
44573
+ * The (search, utility) tuple the files were last recomputed for; null until the first socket observation, so that
44574
+ * observation always recomputes (final review I-1), even when it is non-capable. One trigger keyed on BOTH values.
44274
44575
  */
44275
- searchProvidersState = null;
44576
+ capabilityState = null;
44276
44577
  searchCapabilityRefetchTimer = null;
44277
44578
  /** When the current burst's first change happened (valid while the timer is set). */
44278
44579
  searchCapabilityBurstStart = 0;
44279
44580
  /** The D28 value in the last report posted (null until one is); a change needs a refetch to reach the web. */
44280
44581
  lastReportedSearchProviders = null;
44582
+ /** U6: the utility_tools_v1 value in the last report posted (null until one is). */
44583
+ lastReportedUtilityTools = null;
44281
44584
  noteSearchProvidersSocket(token, declared) {
44282
44585
  this.searchProvidersSockets.set(token, declared);
44283
- this.onSearchProvidersMaybeChanged();
44586
+ this.onCapabilityMaybeChanged();
44284
44587
  }
44285
44588
  clearSearchProvidersSocket(token) {
44286
- if (this.searchProvidersSockets.delete(token)) this.onSearchProvidersMaybeChanged();
44589
+ if (this.searchProvidersSockets.delete(token)) this.onCapabilityMaybeChanged();
44590
+ }
44591
+ noteUtilityToolsSocket(token, declared) {
44592
+ this.utilityToolsSockets.set(token, declared);
44593
+ this.onCapabilityMaybeChanged();
44594
+ }
44595
+ clearUtilityToolsSocket(token) {
44596
+ if (this.utilityToolsSockets.delete(token)) this.onCapabilityMaybeChanged();
44597
+ }
44598
+ /** U6: every attached runtime socket declared utility_tools_v1; no socket attached means absent (false). */
44599
+ utilityToolsCapable() {
44600
+ if (this.utilityToolsSockets.size === 0) return false;
44601
+ for (const v of this.utilityToolsSockets.values()) if (!v) return false;
44602
+ return true;
44603
+ }
44604
+ /** U6: the features every attached runtime socket declared, for the policy functions. */
44605
+ capabilityFeatures() {
44606
+ const out = /* @__PURE__ */ new Set();
44607
+ if (this.searchProvidersCapable()) out.add("search_providers_v2");
44608
+ if (this.utilityToolsCapable()) out.add("utility_tools_v1");
44609
+ return out;
44287
44610
  }
44288
44611
  /** D28: every attached runtime socket declared search_providers_v2; no socket attached means absent (false). */
44289
44612
  searchProvidersCapable() {
@@ -44310,14 +44633,14 @@ ${channelId}`;
44310
44633
  * D35 (P-4): on a capability change, rewrite manifest-policy.json at once from the stored manifest, the kill
44311
44634
  * switches and the new capability, then a debounced refetch (one per burst) only when needed.
44312
44635
  */
44313
- onSearchProvidersMaybeChanged() {
44314
- const capable = this.searchProvidersCapable();
44315
- const prev = this.searchProvidersState;
44316
- if (capable === prev) return;
44317
- this.searchProvidersState = capable;
44318
- this.recomputeManifestPolicy(capable);
44636
+ onCapabilityMaybeChanged() {
44637
+ const next = { search: this.searchProvidersCapable(), utility: this.utilityToolsCapable() };
44638
+ const prev = this.capabilityState;
44639
+ if (prev !== null && prev.search === next.search && prev.utility === next.utility) return;
44640
+ this.capabilityState = next;
44641
+ this.recomputeManifestPolicy(this.capabilityFeatures());
44319
44642
  if (this.closingDown) return;
44320
- if (prev === null && !capable) return;
44643
+ if (prev === null && !next.search && !next.utility) return;
44321
44644
  const now = Date.now();
44322
44645
  if (this.searchCapabilityRefetchTimer !== null) clearTimeout(this.searchCapabilityRefetchTimer);
44323
44646
  else this.searchCapabilityBurstStart = now;
@@ -44335,7 +44658,7 @@ ${channelId}`;
44335
44658
  searchCapabilityRefetchNeeded() {
44336
44659
  if (!this.bootConfigSettled || this.closingDown) return false;
44337
44660
  const inForce = (this.cfg.ownerGrantPin?.pin ?? null) !== null && this.toolGrantManifest !== null;
44338
- return inForce || this.lastReportedSearchProviders !== this.searchProvidersCapable();
44661
+ return inForce || this.lastReportedSearchProviders !== this.searchProvidersCapable() || this.lastReportedUtilityTools !== this.utilityToolsCapable();
44339
44662
  }
44340
44663
  /**
44341
44664
  * D35: the local recompute. Widening (web_extract back in) only while the binding for the in-force manifest stands,
@@ -44343,16 +44666,17 @@ ${channelId}`;
44343
44666
  * lists web_extract loses it at once, so an older runtime never reads it as unreadable (which drops every grant).
44344
44667
  * Content-free; a write failure is logged and the debounced refetch tries again.
44345
44668
  */
44346
- recomputeManifestPolicy(capable) {
44669
+ recomputeManifestPolicy(features) {
44347
44670
  const pin = this.cfg.ownerGrantPin?.pin ?? null;
44348
44671
  const manifest = pin !== null ? this.toolGrantManifest : null;
44349
44672
  try {
44350
44673
  if (manifest !== null && this.manifestBinding !== null && this.manifestBinding.counter === manifest.counter) {
44351
- this.grantWriters.writeManifestPolicy(this.home, this.cfg.identityId, manifestPolicyFor(manifest, { ownerDm: this.ownerDmManifestGrants, room: this.roomManifestGrants }, capable));
44674
+ this.grantWriters.writeManifestPolicy(this.home, this.cfg.identityId, manifestPolicyFor(manifest, { ownerDm: this.ownerDmManifestGrants, room: this.roomManifestGrants }, features));
44352
44675
  return;
44353
44676
  }
44354
- if (capable) return;
44355
- const path = join19(runtimeConfigDir(this.home, this.cfg.identityId), MANIFEST_POLICY_FILE);
44677
+ const withheld = new Set(Object.keys(TOOL_FEATURE).filter((t) => !toolAllowedByFeatures(t, features)));
44678
+ if (withheld.size === 0) return;
44679
+ const path = join20(runtimeConfigDir(this.home, this.cfg.identityId), MANIFEST_POLICY_FILE);
44356
44680
  if (!existsSync5(path)) return;
44357
44681
  let doc;
44358
44682
  try {
@@ -44361,10 +44685,10 @@ ${channelId}`;
44361
44685
  return;
44362
44686
  }
44363
44687
  const tools = doc?.manifest_tools;
44364
- if (!Array.isArray(tools) || !tools.includes("web_extract")) return;
44688
+ if (!Array.isArray(tools) || !tools.some((t) => typeof t === "string" && withheld.has(t))) return;
44365
44689
  this.grantWriters.writeManifestPolicy(this.home, this.cfg.identityId, {
44366
44690
  v: 1,
44367
- manifest_tools: tools.filter((t) => typeof t === "string" && t !== "web_extract"),
44691
+ manifest_tools: tools.filter((t) => typeof t === "string" && !withheld.has(t)),
44368
44692
  secret_path_mode: "enforce"
44369
44693
  });
44370
44694
  } catch (e) {
@@ -44577,8 +44901,8 @@ ${channelId}`;
44577
44901
  let timer;
44578
44902
  await Promise.race([
44579
44903
  ingest.released,
44580
- new Promise((resolve4) => {
44581
- timer = setTimeout(resolve4, _BridgeAgent.GRACEFUL_DRAIN_MS);
44904
+ new Promise((resolve5) => {
44905
+ timer = setTimeout(resolve5, _BridgeAgent.GRACEFUL_DRAIN_MS);
44582
44906
  })
44583
44907
  ]);
44584
44908
  if (timer) clearTimeout(timer);
@@ -44588,8 +44912,8 @@ ${channelId}`;
44588
44912
  let timer;
44589
44913
  await Promise.race([
44590
44914
  starting,
44591
- new Promise((resolve4) => {
44592
- timer = setTimeout(resolve4, _BridgeAgent.GRACEFUL_DRAIN_MS);
44915
+ new Promise((resolve5) => {
44916
+ timer = setTimeout(resolve5, _BridgeAgent.GRACEFUL_DRAIN_MS);
44593
44917
  })
44594
44918
  ]);
44595
44919
  if (timer) clearTimeout(timer);
@@ -44598,8 +44922,8 @@ ${channelId}`;
44598
44922
  let timer;
44599
44923
  await Promise.race([
44600
44924
  this.membershipSweepWork,
44601
- new Promise((resolve4) => {
44602
- timer = setTimeout(resolve4, _BridgeAgent.GRACEFUL_DRAIN_MS);
44925
+ new Promise((resolve5) => {
44926
+ timer = setTimeout(resolve5, _BridgeAgent.GRACEFUL_DRAIN_MS);
44603
44927
  })
44604
44928
  ]);
44605
44929
  if (timer) clearTimeout(timer);
@@ -44609,8 +44933,8 @@ ${channelId}`;
44609
44933
  let timer;
44610
44934
  await Promise.race([
44611
44935
  this.recallPruneWork,
44612
- new Promise((resolve4) => {
44613
- timer = setTimeout(resolve4, _BridgeAgent.GRACEFUL_DRAIN_MS);
44936
+ new Promise((resolve5) => {
44937
+ timer = setTimeout(resolve5, _BridgeAgent.GRACEFUL_DRAIN_MS);
44614
44938
  })
44615
44939
  ]);
44616
44940
  if (timer) clearTimeout(timer);
@@ -44620,8 +44944,8 @@ ${channelId}`;
44620
44944
  let timer;
44621
44945
  await Promise.race([
44622
44946
  this.recallMaintenanceWork,
44623
- new Promise((resolve4) => {
44624
- timer = setTimeout(resolve4, _BridgeAgent.GRACEFUL_DRAIN_MS);
44947
+ new Promise((resolve5) => {
44948
+ timer = setTimeout(resolve5, _BridgeAgent.GRACEFUL_DRAIN_MS);
44625
44949
  })
44626
44950
  ]);
44627
44951
  if (timer) clearTimeout(timer);
@@ -44633,8 +44957,8 @@ ${channelId}`;
44633
44957
  let timer;
44634
44958
  await Promise.race([
44635
44959
  Promise.allSettled([...this.recallTapWork]),
44636
- new Promise((resolve4) => {
44637
- timer = setTimeout(resolve4, _BridgeAgent.GRACEFUL_DRAIN_MS);
44960
+ new Promise((resolve5) => {
44961
+ timer = setTimeout(resolve5, _BridgeAgent.GRACEFUL_DRAIN_MS);
44638
44962
  })
44639
44963
  ]);
44640
44964
  if (timer) clearTimeout(timer);
@@ -44643,8 +44967,8 @@ ${channelId}`;
44643
44967
  let timer;
44644
44968
  await Promise.race([
44645
44969
  Promise.allSettled([...this.unifiedWork]),
44646
- new Promise((resolve4) => {
44647
- timer = setTimeout(resolve4, _BridgeAgent.GRACEFUL_DRAIN_MS);
44970
+ new Promise((resolve5) => {
44971
+ timer = setTimeout(resolve5, _BridgeAgent.GRACEFUL_DRAIN_MS);
44648
44972
  })
44649
44973
  ]);
44650
44974
  if (timer) clearTimeout(timer);
@@ -44653,8 +44977,8 @@ ${channelId}`;
44653
44977
  let timer;
44654
44978
  await Promise.race([
44655
44979
  this.recallPrewarmWork,
44656
- new Promise((resolve4) => {
44657
- timer = setTimeout(resolve4, _BridgeAgent.GRACEFUL_DRAIN_MS);
44980
+ new Promise((resolve5) => {
44981
+ timer = setTimeout(resolve5, _BridgeAgent.GRACEFUL_DRAIN_MS);
44658
44982
  })
44659
44983
  ]);
44660
44984
  if (timer) clearTimeout(timer);
@@ -44664,8 +44988,8 @@ ${channelId}`;
44664
44988
  let timer;
44665
44989
  await Promise.race([
44666
44990
  Promise.allSettled([...this.replyWork]),
44667
- new Promise((resolve4) => {
44668
- timer = setTimeout(resolve4, _BridgeAgent.GRACEFUL_DRAIN_MS);
44991
+ new Promise((resolve5) => {
44992
+ timer = setTimeout(resolve5, _BridgeAgent.GRACEFUL_DRAIN_MS);
44669
44993
  })
44670
44994
  ]);
44671
44995
  if (timer) clearTimeout(timer);
@@ -44755,6 +45079,13 @@ var BRIDGE_ERRORS = {
44755
45079
  // C9 (spec §4.1.6 step 1): another copy of this agent took its SecureComs
44756
45080
  // session (WS 4409), so this copy stopped. Every local route but /healthz.
44757
45081
  session_superseded: { http: 503, message: "another copy of this agent took its session; this copy stopped" },
45082
+ // Weather/currency Task 17 (OQ-P1): a send frame's credit is not the closed value, or rides an a2a frame. TERMINAL:
45083
+ // the inbound is retired, so the model (and the weather call) never runs again for it.
45084
+ reply_credit_unknown: { http: 400, message: "reply credit not recognised; not sent" },
45085
+ // Security fix 2026-10-30: an upload route was given a path that is not a
45086
+ // regular file directly inside the bridge's vault outbox (`vault_outbox` on
45087
+ // /healthz). Fixed and content-free: the path is never echoed or logged.
45088
+ path_outside_outbox: { http: 400, message: "uploads must come from the bridge vault outbox; copy the file there first" },
44758
45089
  internal: { http: 500, message: "internal error" }
44759
45090
  };
44760
45091
  function errorBody(code, detail) {
@@ -44784,6 +45115,7 @@ function withConsumerGrants(m, perToolConsumer) {
44784
45115
  }
44785
45116
  var MANIFEST_GRANTS_FEATURE = "manifest_grants_v1";
44786
45117
  var SEARCH_PROVIDERS_FEATURE = "search_providers_v2";
45118
+ var UTILITY_TOOLS_FEATURE = "utility_tools_v1";
44787
45119
  var CONSTANT_TOOL_NAMES = Object.freeze({
44788
45120
  owner_dm: Object.freeze(["current_time", "web_search"]),
44789
45121
  team_room: Object.freeze(["current_time"])
@@ -44793,10 +45125,10 @@ function withManifestCapability(m, manifestConsumer) {
44793
45125
  if (manifestConsumer) return m;
44794
45126
  const a = m.authorization;
44795
45127
  if (!a || !a.tool_grants.some(isManifestGrant)) return m;
44796
- const constants4 = new Set(a.audience === "owner_dm" || a.audience === "team_room" ? CONSTANT_TOOL_NAMES[a.audience] : []);
45128
+ const constants5 = new Set(a.audience === "owner_dm" || a.audience === "team_room" ? CONSTANT_TOOL_NAMES[a.audience] : []);
44797
45129
  const narrowed = a.tool_grants.flatMap((g) => {
44798
45130
  if (!isManifestGrant(g)) return [g];
44799
- return constants4.has(g.tool) ? [Object.freeze({ tool: g.tool, mode: "read" })] : [];
45131
+ return constants5.has(g.tool) ? [Object.freeze({ tool: g.tool, mode: "read" })] : [];
44800
45132
  });
44801
45133
  return { ...m, authorization: Object.freeze({ ...a, tool_grants: Object.freeze(narrowed) }) };
44802
45134
  }
@@ -44815,6 +45147,15 @@ function withSearchCapability(m, consumerHasFeature, signedProvider) {
44815
45147
  if (kept.length === a.tool_grants.length) return m;
44816
45148
  return { ...m, authorization: Object.freeze({ ...a, tool_grants: kept.length === 0 ? NO_GRANTS : Object.freeze(kept) }) };
44817
45149
  }
45150
+ var UTILITY_TOOL_NAMES = /* @__PURE__ */ new Set(["weather_forecast", "currency_convert"]);
45151
+ function withUtilityCapability(m, consumerHasFeature) {
45152
+ if (consumerHasFeature) return m;
45153
+ const a = m.authorization;
45154
+ if (!a) return m;
45155
+ const kept = a.tool_grants.filter((g) => !UTILITY_TOOL_NAMES.has(g.tool));
45156
+ if (kept.length === a.tool_grants.length) return m;
45157
+ return { ...m, authorization: Object.freeze({ ...a, tool_grants: kept.length === 0 ? NO_GRANTS : Object.freeze(kept) }) };
45158
+ }
44818
45159
 
44819
45160
  // src/http-server.ts
44820
45161
  function writeSse(res, msgs, onWriteError) {
@@ -44838,7 +45179,7 @@ data: ${JSON.stringify(msgs[i])}
44838
45179
  function deliverSseBatch(res, batch, agent, onWriteError) {
44839
45180
  const withheld = agent.harnessGrantsWithheld();
44840
45181
  const provider = agent.signedSearchProvider();
44841
- const left3 = writeSse(res, batch.map((m) => withSearchCapability(withManifestCapability(withHarnessGrants(withConsumerGrants(m, false), withheld), false), false, provider)), onWriteError);
45182
+ const left3 = writeSse(res, batch.map((m) => withUtilityCapability(withSearchCapability(withManifestCapability(withHarnessGrants(withConsumerGrants(m, false), withheld), false), false, provider), false)), onWriteError);
44842
45183
  const leftIds = new Set(left3.map((message) => message.message_id));
44843
45184
  agent.ackRuntimeMessages(batch.filter((message) => !leftIds.has(message.message_id)).map((message) => message.message_id));
44844
45185
  if (left3.length) agent.requeueFront(batch.filter((message) => leftIds.has(message.message_id)));
@@ -44870,7 +45211,7 @@ var OTHER_CAP = 8 * 1024;
44870
45211
  var a2aSchema = z3.object({
44871
45212
  a2a_version: z3.string().min(1),
44872
45213
  kind: z3.enum(["message", "task", "artifact"]),
44873
- object: z3.record(z3.unknown())
45214
+ object: z3.record(z3.string(), z3.unknown())
44874
45215
  });
44875
45216
  var sendAttachmentSchema = z3.object({
44876
45217
  channel_id: z3.string().min(1),
@@ -44945,7 +45286,7 @@ function jsonError(res, code, detail) {
44945
45286
  json(res, httpStatus(code), errorBody(code, detail));
44946
45287
  }
44947
45288
  function readBody(req, cap) {
44948
- return new Promise((resolve4, reject) => {
45289
+ return new Promise((resolve5, reject) => {
44949
45290
  let size = 0;
44950
45291
  let done = false;
44951
45292
  const chunks = [];
@@ -44965,7 +45306,7 @@ function readBody(req, cap) {
44965
45306
  req.on("end", () => {
44966
45307
  if (!done) {
44967
45308
  done = true;
44968
- resolve4(Buffer.concat(chunks).toString("utf8"));
45309
+ resolve5(Buffer.concat(chunks).toString("utf8"));
44969
45310
  }
44970
45311
  });
44971
45312
  req.on("error", (e) => {
@@ -45043,6 +45384,9 @@ function createHttpServer(agent, opts) {
45043
45384
  }
45044
45385
  return json(res, 200, r);
45045
45386
  }
45387
+ if (method === "GET" && path === "/v1/vault-outbox") {
45388
+ return json(res, 200, { path: agent.vaultOutboxPath() });
45389
+ }
45046
45390
  if (method === "POST" && path === "/v1/send-attachment") {
45047
45391
  const parsed = sendAttachmentSchema.safeParse(JSON.parse(await readBody(req, OTHER_CAP)));
45048
45392
  if (!parsed.success) return jsonError(res, "invalid_request", parsed.error.issues);
@@ -45112,7 +45456,7 @@ function createHttpServer(agent, opts) {
45112
45456
  const drained = await agent.drain(channel, wait);
45113
45457
  const withheld = agent.harnessGrantsWithheld();
45114
45458
  const provider = agent.signedSearchProvider();
45115
- return json(res, 200, drained.map((m) => withSearchCapability(withManifestCapability(withHarnessGrants(withConsumerGrants(m, false), withheld), false), false, provider)));
45459
+ return json(res, 200, drained.map((m) => withUtilityCapability(withSearchCapability(withManifestCapability(withHarnessGrants(withConsumerGrants(m, false), withheld), false), false, provider), false)));
45116
45460
  }
45117
45461
  if (method === "GET" && path === "/v1/events") {
45118
45462
  const channel = url.searchParams.get("channel_id") ?? void 0;
@@ -45154,6 +45498,10 @@ function createHttpServer(agent, opts) {
45154
45498
  if (e instanceof SendForbiddenError) return jsonError(res, "send_forbidden");
45155
45499
  if (e instanceof GroupSupersededSendError) return jsonError(res, "group_superseded");
45156
45500
  if (e instanceof IdempotencyConflictError) return jsonError(res, "invalid_request", e.message);
45501
+ if (e instanceof PathOutsideOutboxError) {
45502
+ writeServiceLog("warn", "vault_outbox.refused");
45503
+ return jsonError(res, "path_outside_outbox");
45504
+ }
45157
45505
  if (e instanceof InvalidPathError) return jsonError(res, "invalid_request", e.message);
45158
45506
  if (e instanceof VaultPinMismatchError) return jsonError(res, "vault_ref_changed", { pin: e.pin });
45159
45507
  if (e instanceof VaultIntegrityError) return jsonError(res, "vault_integrity_failed", e.message);
@@ -45287,6 +45635,7 @@ function attachAgentWebSocket(server, agent, opts) {
45287
45635
  perToolGrants: false,
45288
45636
  manifestGrants: false,
45289
45637
  searchProviders: false,
45638
+ utilityTools: false,
45290
45639
  // helloWaitMs 0 keeps today's synchronous initial flush exactly (the opt-out the existing tests use).
45291
45640
  deliveryOpen: helloWaitMs <= 0,
45292
45641
  deliveryToken: /* @__PURE__ */ new Map(),
@@ -45340,14 +45689,14 @@ function attachAgentWebSocket(server, agent, opts) {
45340
45689
  continue;
45341
45690
  }
45342
45691
  const out = withConsumerNotes(
45343
- withSearchCapability(
45692
+ withUtilityCapability(withSearchCapability(
45344
45693
  withManifestCapability(
45345
45694
  withHarnessGrants(withConsumerGrants(batch[i], session.perToolGrants), !session.perToolGrants && agent.harnessGrantsWithheld()),
45346
45695
  session.manifestGrants
45347
45696
  ),
45348
45697
  session.searchProviders,
45349
45698
  agent.signedSearchProvider()
45350
- ),
45699
+ ), session.utilityTools),
45351
45700
  batch[i],
45352
45701
  session,
45353
45702
  agent
@@ -45404,6 +45753,7 @@ function attachAgentWebSocket(server, agent, opts) {
45404
45753
  if (session.perToolGrants) agent.clearPerToolConsumer(session);
45405
45754
  if (session.manifestGrants) agent.clearManifestConsumer(session);
45406
45755
  agent.clearSearchProvidersSocket(session);
45756
+ agent.clearUtilityToolsSocket(session);
45407
45757
  };
45408
45758
  current = { sock, cleanup, sweepStale };
45409
45759
  sock.on("close", cleanup);
@@ -45444,7 +45794,10 @@ function sendJson(sock, body) {
45444
45794
  } catch {
45445
45795
  }
45446
45796
  }
45797
+ var ReplyCreditUnknownError = class extends Error {
45798
+ };
45447
45799
  function sendErrorCode(e) {
45800
+ if (e instanceof ReplyCreditUnknownError) return "reply_credit_unknown";
45448
45801
  if (e instanceof ChannelPendingError) return "channel_pending";
45449
45802
  if (e instanceof RateLimitedError) return "rate_limited";
45450
45803
  if (e instanceof AgentPausedSendError) return "agent_paused";
@@ -45505,6 +45858,8 @@ async function handleInbound(sock, agent, data, inflight, flush, session) {
45505
45858
  agent.noteConsumerCapability(manifest ? "ok" : session.harnessClaimed ? "harness_consumer" : "missing");
45506
45859
  session.searchProviders = session.consumer === "runtime" && session.harnessClaimed !== true && perTool && Array.isArray(m.features) && m.features.includes(SEARCH_PROVIDERS_FEATURE);
45507
45860
  agent.noteSearchProvidersSocket(session, session.searchProviders);
45861
+ session.utilityTools = session.consumer === "runtime" && session.harnessClaimed !== true && perTool && Array.isArray(m.features) && m.features.includes(UTILITY_TOOLS_FEATURE);
45862
+ agent.noteUtilityToolsSocket(session, session.utilityTools);
45508
45863
  if (session.consumer === "runtime" && session.harnessClaimed !== true && Array.isArray(m.features) && m.features.includes(MEMORY_NOTES_FEATURE)) {
45509
45864
  agent.noteNotesConsumer();
45510
45865
  }
@@ -45512,7 +45867,7 @@ async function handleInbound(sock, agent, data, inflight, flush, session) {
45512
45867
  if (session.recallConsumer) agent.noteRecallConsumer();
45513
45868
  session.unifiedConsumer = agent.unifiedFeatureOn() && session.consumer === "runtime" && session.harnessClaimed !== true && Array.isArray(m.features) && m.features.includes(MEMORY_UNIFIED_FEATURE);
45514
45869
  if (session.unifiedConsumer) agent.noteUnifiedConsumer();
45515
- sendJson(sock, { type: "hello", features: ["nack", MEMORY_NOTES_FEATURE, MEMORY_RECALL_FEATURE, ...agent.unifiedFeatureOn() ? [MEMORY_UNIFIED_FEATURE] : [], MANIFEST_GRANTS_FEATURE, SEARCH_PROVIDERS_FEATURE] });
45870
+ sendJson(sock, { type: "hello", features: ["nack", MEMORY_NOTES_FEATURE, MEMORY_RECALL_FEATURE, ...agent.unifiedFeatureOn() ? [MEMORY_UNIFIED_FEATURE] : [], MANIFEST_GRANTS_FEATURE, SEARCH_PROVIDERS_FEATURE, REPLY_CREDIT_FEATURE] });
45516
45871
  if (!session.deliveryOpen) {
45517
45872
  session.deliveryOpen = true;
45518
45873
  flush();
@@ -45541,7 +45896,8 @@ async function handleInbound(sock, agent, data, inflight, flush, session) {
45541
45896
  }
45542
45897
  const replyFailed = (e, inReplyTo) => {
45543
45898
  const code = sendErrorCode(e);
45544
- if ((code === "agent_paused" || code === "send_forbidden") && typeof inReplyTo === "string") {
45899
+ const terminal = code === "agent_paused" || code === "send_forbidden" || code === "reply_credit_unknown";
45900
+ if (terminal && typeof inReplyTo === "string") {
45545
45901
  inflight.delete(inReplyTo);
45546
45902
  session.deliveryToken.delete(inReplyTo);
45547
45903
  session.shownRecall.delete(inReplyTo);
@@ -45549,7 +45905,7 @@ async function handleInbound(sock, agent, data, inflight, flush, session) {
45549
45905
  agent.ackRuntimeMessages([inReplyTo]);
45550
45906
  }
45551
45907
  sendJson(sock, { ...wsErrorFrame(code), ...typeof inReplyTo === "string" ? { in_reply_to: inReplyTo } : {} });
45552
- if (code === "agent_paused" || code === "send_forbidden") flush();
45908
+ if (terminal) flush();
45553
45909
  };
45554
45910
  if (m.type === "send") {
45555
45911
  if (typeof m.channel_id !== "string" || !m.channel_id) {
@@ -45566,8 +45922,14 @@ async function handleInbound(sock, agent, data, inflight, flush, session) {
45566
45922
  }
45567
45923
  const record = typeof m.in_reply_to === "string" ? inflight.get(m.in_reply_to) : void 0;
45568
45924
  const callBufferReply = session.consumer === "runtime" && session.perToolGrants === true && session.harnessClaimed !== true && typeof m.in_reply_to === "string" && record !== void 0 && session.deliveryToken.get(m.in_reply_to) !== void 0 && record.channel_id === m.channel_id && !agent.isRuntimeMessageRetired(m.in_reply_to);
45925
+ if (m.credit !== void 0 && (m.a2a !== void 0 || !isReplyCredit(m.credit))) {
45926
+ writeServiceLog("error", "reply.credit_unknown");
45927
+ replyFailed(new ReplyCreditUnknownError(), m.in_reply_to);
45928
+ return;
45929
+ }
45569
45930
  const sendOpts = {
45570
45931
  agentTurn: isAgentTurn(agent, m),
45932
+ ...isReplyCredit(m.credit) ? { credit: m.credit } : {},
45571
45933
  ...typeof m.in_reply_to === "string" ? { inReplyTo: m.in_reply_to } : {},
45572
45934
  ...typeof m.idempotency_key === "string" ? { idempotencyKey: m.idempotency_key } : {},
45573
45935
  ...callBufferReply ? { callBufferReply: true } : {}
@@ -45741,18 +46103,18 @@ async function handleInbound(sock, agent, data, inflight, flush, session) {
45741
46103
  var LOWER_UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
45742
46104
 
45743
46105
  // src/bin.ts
45744
- import { readFileSync as readFileSync17, mkdirSync as mkdirSync8, writeFileSync as writeFileSync5, rmSync as rmSync8, existsSync as existsSync7, realpathSync as realpathSync3, readdirSync as readdirSync4, statSync as statSync8, chmodSync as chmodSync6 } from "node:fs";
45745
- import { join as join25, dirname as dirname13, isAbsolute as isAbsolute3, resolve as resolve3 } from "node:path";
46106
+ import { readFileSync as readFileSync17, mkdirSync as mkdirSync9, writeFileSync as writeFileSync5, rmSync as rmSync8, existsSync as existsSync7, realpathSync as realpathSync4, readdirSync as readdirSync4, statSync as statSync8, chmodSync as chmodSync7 } from "node:fs";
46107
+ import { join as join26, dirname as dirname14, isAbsolute as isAbsolute3, resolve as resolve4 } from "node:path";
45746
46108
  import { fileURLToPath as fileURLToPath4 } from "node:url";
45747
46109
  import { homedir as homedir5, userInfo } from "node:os";
45748
46110
  import { spawnSync } from "node:child_process";
45749
46111
  import { createServer as netCreateServer } from "node:net";
45750
46112
 
45751
46113
  // src/service/install.ts
45752
- import { dirname as dirname8 } from "node:path";
46114
+ import { dirname as dirname9 } from "node:path";
45753
46115
 
45754
46116
  // src/service/config.ts
45755
- import { basename as basename5, extname, isAbsolute as isAbsolute2, resolve } from "node:path";
46117
+ import { basename as basename6, extname, isAbsolute as isAbsolute2, resolve as resolve2 } from "node:path";
45756
46118
 
45757
46119
  // src/service/resolve-node.ts
45758
46120
  var REQUIRED_NODE_MAJOR = 24;
@@ -45830,11 +46192,11 @@ function deriveServiceConfig(input) {
45830
46192
  `The Node at ${input.execPath} moves when it is upgraded, so the background service can't use it. Install Node ${REQUIRED_NODE_MAJOR} from https://nodejs.org, then run the install command again.`
45831
46193
  );
45832
46194
  }
45833
- const stem = input.label ?? basename5(input.credentialPath, extname(input.credentialPath));
46195
+ const stem = input.label ?? basename6(input.credentialPath, extname(input.credentialPath));
45834
46196
  const slug = slugify(stem);
45835
- const cred = isAbsolute2(input.credentialPath) ? input.credentialPath : resolve(input.credentialPath);
46197
+ const cred = isAbsolute2(input.credentialPath) ? input.credentialPath : resolve2(input.credentialPath);
45836
46198
  const args2 = ["--credential", cred];
45837
- if (input.storePath) args2.push("--store", isAbsolute2(input.storePath) ? input.storePath : resolve(input.storePath));
46199
+ if (input.storePath) args2.push("--store", isAbsolute2(input.storePath) ? input.storePath : resolve2(input.storePath));
45838
46200
  if (input.port != null) args2.push("--port", String(input.port));
45839
46201
  return {
45840
46202
  slug,
@@ -45929,7 +46291,7 @@ function waitForPreviousExit(deps, pid) {
45929
46291
  }
45930
46292
  }
45931
46293
  function tokenHint(input) {
45932
- return `${dirname8(input.credentialPath)}/bridge-token`;
46294
+ return `${dirname9(input.credentialPath)}/bridge-token`;
45933
46295
  }
45934
46296
  function requireOk(r, what) {
45935
46297
  if (r.status !== 0) throw new Error(`${what} failed (exit ${r.status})${r.stderr ? `: ${r.stderr.trim()}` : ""}`);
@@ -45937,8 +46299,8 @@ function requireOk(r, what) {
45937
46299
  function installService(input, deps) {
45938
46300
  const cfg = deriveServiceConfig(input);
45939
46301
  if (deps.platform === "darwin") {
45940
- deps.mkdirp(dirname8(cfg.plistPath));
45941
- deps.mkdirp(dirname8(cfg.logPath));
46302
+ deps.mkdirp(dirname9(cfg.plistPath));
46303
+ deps.mkdirp(dirname9(cfg.logPath));
45942
46304
  deps.writeFile(cfg.plistPath, renderLaunchdPlist(cfg));
45943
46305
  const target = `gui/${cfg.uid}/${cfg.label}`;
45944
46306
  const previousPid = launchdPid(deps, target);
@@ -45950,7 +46312,7 @@ function installService(input, deps) {
45950
46312
  return { label: cfg.label, filePath: cfg.plistPath, logHint: cfg.logPath, tokenHint: tokenHint(input) };
45951
46313
  }
45952
46314
  if (deps.platform === "linux") {
45953
- deps.mkdirp(dirname8(cfg.unitPath));
46315
+ deps.mkdirp(dirname9(cfg.unitPath));
45954
46316
  deps.writeFile(cfg.unitPath, renderSystemdUnit(cfg));
45955
46317
  deps.run("systemctl", ["--user", "daemon-reload"]);
45956
46318
  deps.run("loginctl", ["enable-linger", deps.user]);
@@ -45979,10 +46341,10 @@ function uninstallService(input, deps) {
45979
46341
  }
45980
46342
 
45981
46343
  // src/service/install-prep.ts
45982
- import { join as join21 } from "node:path";
46344
+ import { join as join22 } from "node:path";
45983
46345
 
45984
46346
  // src/service/wire.ts
45985
- import { join as join20, dirname as dirname9 } from "node:path";
46347
+ import { join as join21, dirname as dirname10 } from "node:path";
45986
46348
  import { fileURLToPath as fileURLToPath3 } from "node:url";
45987
46349
 
45988
46350
  // src/service/hermes-config.ts
@@ -46157,8 +46519,8 @@ function checkBindingConflict(prev, next, allowRebind) {
46157
46519
  };
46158
46520
  }
46159
46521
  function connectorsSrcDir(importMetaUrl, harness) {
46160
- const pkgRoot = dirname9(dirname9(fileURLToPath3(importMetaUrl)));
46161
- return join20(pkgRoot, "connectors", harness);
46522
+ const pkgRoot = dirname10(dirname10(fileURLToPath3(importMetaUrl)));
46523
+ return join21(pkgRoot, "connectors", harness);
46162
46524
  }
46163
46525
  function hermesConfigPath(home) {
46164
46526
  return `${home}/.hermes/config.yaml`;
@@ -46420,10 +46782,10 @@ function assertReadableCredential(absPath, deps) {
46420
46782
  return { deviceId: json2.device_id, identityId: json2.identity_id, issuedAt };
46421
46783
  }
46422
46784
  function durableDir(home, slug) {
46423
- return join21(home, `securecoms-agent-${slug}`);
46785
+ return join22(home, `securecoms-agent-${slug}`);
46424
46786
  }
46425
46787
  function durableCredentialPath(home, slug) {
46426
- return join21(durableDir(home, slug), `${slug}.json`);
46788
+ return join22(durableDir(home, slug), `${slug}.json`);
46427
46789
  }
46428
46790
  var DURABLE_PREFIX = "securecoms-agent-";
46429
46791
  var InstallRefusedError = class extends Error {
@@ -46444,8 +46806,8 @@ function refuseExistingIdentity(args2, deps) {
46444
46806
  const others = names.filter((n) => n.startsWith(DURABLE_PREFIX) && n !== destName).sort();
46445
46807
  for (const name of [destName, ...others]) {
46446
46808
  const slug = name.slice(DURABLE_PREFIX.length);
46447
- const dir = join21(args2.home, name);
46448
- const credPath = join21(dir, `${slug}.json`);
46809
+ const dir = join22(args2.home, name);
46810
+ const credPath = join22(dir, `${slug}.json`);
46449
46811
  if (!deps.fileExists(credPath)) continue;
46450
46812
  let existing = null;
46451
46813
  try {
@@ -46454,7 +46816,7 @@ function refuseExistingIdentity(args2, deps) {
46454
46816
  } catch {
46455
46817
  }
46456
46818
  if (!existing || typeof existing.identity_id !== "string" || !existing.identity_id) {
46457
- if (deps.fileExists(join21(dir, "bridge-state.db"))) throw existingIdentityError(dir, slug);
46819
+ if (deps.fileExists(join22(dir, "bridge-state.db"))) throw existingIdentityError(dir, slug);
46458
46820
  continue;
46459
46821
  }
46460
46822
  if (existing.identity_id !== args2.parsed.identityId) continue;
@@ -46509,7 +46871,7 @@ function readSiblingDefs(dir, prefix, deps) {
46509
46871
  for (const n of names) {
46510
46872
  if (!n.startsWith(prefix)) continue;
46511
46873
  try {
46512
- out.push(deps.readFile(join21(dir, n)));
46874
+ out.push(deps.readFile(join22(dir, n)));
46513
46875
  } catch {
46514
46876
  }
46515
46877
  }
@@ -46590,7 +46952,7 @@ async function orchestrateInstall(ctx) {
46590
46952
  }
46591
46953
 
46592
46954
  // src/service/fleet.ts
46593
- import { dirname as dirname10 } from "node:path";
46955
+ import { dirname as dirname11 } from "node:path";
46594
46956
  function parseArgFromDef(def, flag) {
46595
46957
  const esc = flag.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
46596
46958
  const re = new RegExp(`${esc}(?:<\\/string>\\s*<string>|\\s+)([^<\\s]+)`);
@@ -46634,7 +46996,7 @@ function readFleet(deps) {
46634
46996
  let identityId = `unknown:${slug}`, deviceId = "", label = slug, env = "unknown";
46635
46997
  let credDir = null;
46636
46998
  if (credPath) {
46637
- credDir = dirname10(credPath);
46999
+ credDir = dirname11(credPath);
46638
47000
  try {
46639
47001
  const c = JSON.parse(deps.readFile(credPath));
46640
47002
  if (c.identity_id) identityId = c.identity_id;
@@ -46702,31 +47064,31 @@ function renderStatusTable(rows) {
46702
47064
 
46703
47065
  // src/service/connection-file.ts
46704
47066
  import {
46705
- chmodSync as chmodSync5,
46706
- mkdirSync as mkdirSync6,
47067
+ chmodSync as chmodSync6,
47068
+ mkdirSync as mkdirSync7,
46707
47069
  readFileSync as readFileSync13,
46708
47070
  renameSync as renameSync4,
46709
47071
  unlinkSync as unlinkSync2,
46710
47072
  writeFileSync as writeFileSync3
46711
47073
  } from "node:fs";
46712
47074
  import { randomUUID as randomUUID2 } from "node:crypto";
46713
- import { join as join22 } from "node:path";
47075
+ import { join as join23 } from "node:path";
46714
47076
  var REAL_FS2 = {
46715
- mkdir: (p, mode) => mkdirSync6(p, { recursive: true, mode }),
46716
- chmod: (p, mode) => chmodSync5(p, mode),
47077
+ mkdir: (p, mode) => mkdirSync7(p, { recursive: true, mode }),
47078
+ chmod: (p, mode) => chmodSync6(p, mode),
46717
47079
  writeFile: (p, data, mode) => writeFileSync3(p, data, { mode }),
46718
47080
  rename: (a, b) => renameSync4(a, b),
46719
47081
  readFile: (p) => readFileSync13(p, "utf8"),
46720
47082
  unlink: (p) => unlinkSync2(p)
46721
47083
  };
46722
47084
  function securecomsDir(home) {
46723
- return join22(home, ".securecoms");
47085
+ return join23(home, ".securecoms");
46724
47086
  }
46725
47087
  function connectionsDir(home) {
46726
- return join22(securecomsDir(home), "connections");
47088
+ return join23(securecomsDir(home), "connections");
46727
47089
  }
46728
47090
  function connectionFilePath(home, identityId) {
46729
- return join22(connectionsDir(home), `${identityId}.json`);
47091
+ return join23(connectionsDir(home), `${identityId}.json`);
46730
47092
  }
46731
47093
  function newInstanceId() {
46732
47094
  return randomUUID2();
@@ -46765,9 +47127,9 @@ function removeConnectionFileIfOwned(home, identityId, instanceId, deps = REAL_F
46765
47127
 
46766
47128
  // src/service/instance-lock.ts
46767
47129
  import { execFileSync } from "node:child_process";
46768
- import { closeSync as closeSync4, fsyncSync, mkdirSync as mkdirSync7, openSync as openSync4, readFileSync as readFileSync14, statSync as statSync6, unlinkSync as unlinkSync3, utimesSync, writeSync as writeSync2 } from "node:fs";
47130
+ import { closeSync as closeSync4, fsyncSync, mkdirSync as mkdirSync8, openSync as openSync4, readFileSync as readFileSync14, statSync as statSync6, unlinkSync as unlinkSync3, utimesSync, writeSync as writeSync2 } from "node:fs";
46769
47131
  import { hostname, uptime as uptime2 } from "node:os";
46770
- import { dirname as dirname11 } from "node:path";
47132
+ import { dirname as dirname12 } from "node:path";
46771
47133
  var LOCK_PARTIAL_WRITE_GRACE_MS = 1e4;
46772
47134
  var LOCK_TAKEOVER_GUARD_STALE_MS = 3e4;
46773
47135
  var BOOT_TOLERANCE_SEC = 120;
@@ -46996,7 +47358,7 @@ var InstanceLock = class {
46996
47358
  create() {
46997
47359
  let fd;
46998
47360
  try {
46999
- mkdirSync7(dirname11(this.path), { recursive: true, mode: 448 });
47361
+ mkdirSync8(dirname12(this.path), { recursive: true, mode: 448 });
47000
47362
  fd = openSync4(this.path, "wx", 384);
47001
47363
  } catch (error) {
47002
47364
  if (errno(error) === "EEXIST") return "exists";
@@ -47087,15 +47449,15 @@ import {
47087
47449
  closeSync as closeSync5,
47088
47450
  fchmodSync,
47089
47451
  fsyncSync as fsyncSync2,
47090
- lstatSync as lstatSync8,
47452
+ lstatSync as lstatSync9,
47091
47453
  openSync as openSync5,
47092
47454
  readFileSync as readFileSync15,
47093
- realpathSync as realpathSync2,
47455
+ realpathSync as realpathSync3,
47094
47456
  renameSync as renameSync5,
47095
47457
  unlinkSync as unlinkSync4,
47096
47458
  writeSync as writeSync3
47097
47459
  } from "node:fs";
47098
- import { dirname as dirname12, join as join23, resolve as resolve2 } from "node:path";
47460
+ import { dirname as dirname13, join as join24, resolve as resolve3 } from "node:path";
47099
47461
  var PIN_COPY = Object.freeze({
47100
47462
  saved: "pin saved. Restart the Connector to use it.",
47101
47463
  // [COPY, approved by Eric 2026-10-12]
@@ -47119,7 +47481,7 @@ var PIN_COPY = Object.freeze({
47119
47481
  var refuse2 = (reason) => ({ ok: false, reason });
47120
47482
  function isSymlink(path) {
47121
47483
  try {
47122
- return lstatSync8(path).isSymbolicLink();
47484
+ return lstatSync9(path).isSymbolicLink();
47123
47485
  } catch (e) {
47124
47486
  if (e?.code === "ENOENT") return null;
47125
47487
  throw e;
@@ -47143,10 +47505,10 @@ function writeTemp(path, bytes) {
47143
47505
  }
47144
47506
  }
47145
47507
  function runPinCommand(opts) {
47146
- const credDir = dirname12(resolve2(opts.credentialPath));
47147
- const target = join23(credDir, PIN_FILE_NAME);
47508
+ const credDir = dirname13(resolve3(opts.credentialPath));
47509
+ const target = join24(credDir, PIN_FILE_NAME);
47148
47510
  try {
47149
- if (realpathSync2(dirname12(resolve2(opts.pinSourcePath))) !== realpathSync2(credDir)) return refuse2("directory_mismatch");
47511
+ if (realpathSync3(dirname13(resolve3(opts.pinSourcePath))) !== realpathSync3(credDir)) return refuse2("directory_mismatch");
47150
47512
  } catch {
47151
47513
  return refuse2("io_error");
47152
47514
  }
@@ -47188,7 +47550,7 @@ function runPinCommand(opts) {
47188
47550
  const pinBytes = JSON.stringify(pinObject, null, 2) + "\n";
47189
47551
  const credBytes = JSON.stringify({ ...cred, owner_signing_key: pinObject }, null, 2) + "\n";
47190
47552
  const pinTmp = `${target}.tmp-${process.pid}`;
47191
- const credTmp = `${resolve2(opts.credentialPath)}.tmp-${process.pid}`;
47553
+ const credTmp = `${resolve3(opts.credentialPath)}.tmp-${process.pid}`;
47192
47554
  const created = [];
47193
47555
  const fail = () => {
47194
47556
  for (const p of created) {
@@ -47222,7 +47584,7 @@ function runPinCommand(opts) {
47222
47584
  created.splice(created.indexOf(pinTmp), 1);
47223
47585
  step("pin_renamed");
47224
47586
  try {
47225
- renameSync5(credTmp, resolve2(opts.credentialPath));
47587
+ renameSync5(credTmp, resolve3(opts.credentialPath));
47226
47588
  } catch {
47227
47589
  return fail();
47228
47590
  }
@@ -47303,7 +47665,7 @@ function parseVerb(argv) {
47303
47665
  return v === "install" || v === "uninstall" || v === "status" || v === "wire" || v === "unwire" || v === "models" || v === "pin" ? v : null;
47304
47666
  }
47305
47667
  function slugFromCredentialPath(credentialPath) {
47306
- const dir = dirname13(credentialPath);
47668
+ const dir = dirname14(credentialPath);
47307
47669
  const base = dir.split(/[\\/]/).pop() ?? "";
47308
47670
  const m = base.match(/^securecoms-agent-(.+)$/);
47309
47671
  if (m) return m[1];
@@ -47319,7 +47681,7 @@ function realServiceDeps() {
47319
47681
  },
47320
47682
  writeFile: (p, data) => writeFileSync5(p, data, { mode: 420 }),
47321
47683
  removeFile: (p) => rmSync8(p, { force: true }),
47322
- mkdirp: (p) => mkdirSync8(p, { recursive: true }),
47684
+ mkdirp: (p) => mkdirSync9(p, { recursive: true }),
47323
47685
  fileExists: (p) => existsSync7(p),
47324
47686
  user: userInfo().username,
47325
47687
  pidAlive: pidAliveByKill,
@@ -47349,7 +47711,7 @@ function realResolveNodeDeps() {
47349
47711
  return m ? Number(m[1]) : null;
47350
47712
  };
47351
47713
  return {
47352
- realpath: (p) => realpathSync3(p),
47714
+ realpath: (p) => realpathSync4(p),
47353
47715
  fileExists: (p) => existsSync7(p),
47354
47716
  nodeMajorOf: majorOf,
47355
47717
  arch: () => process.arch,
@@ -47373,10 +47735,10 @@ function realPrepDeps() {
47373
47735
  readFile: (p) => readFileSync17(p, "utf8"),
47374
47736
  writeFileMode: (p, data, mode) => writeFileSync5(p, data, { mode }),
47375
47737
  mkdirp: (p, mode) => {
47376
- mkdirSync8(p, { recursive: true, mode: mode ?? 493 });
47738
+ mkdirSync9(p, { recursive: true, mode: mode ?? 493 });
47377
47739
  },
47378
47740
  fileExists: (p) => existsSync7(p),
47379
- realpath: (p) => realpathSync3(p),
47741
+ realpath: (p) => realpathSync4(p),
47380
47742
  listDir: (p) => {
47381
47743
  try {
47382
47744
  return readdirSync4(p);
@@ -47438,33 +47800,33 @@ function runStatus() {
47438
47800
  }
47439
47801
  function realCopyConnector(srcDir, dstDir, harness) {
47440
47802
  const allowedExt = CONNECTOR_FILE_EXTENSIONS[harness];
47441
- mkdirSync8(dstDir, { recursive: true });
47803
+ mkdirSync9(dstDir, { recursive: true });
47442
47804
  for (const name of readdirSync4(srcDir)) {
47443
- const srcPath = join25(srcDir, name);
47805
+ const srcPath = join26(srcDir, name);
47444
47806
  if (statSync8(srcPath).isDirectory()) {
47445
47807
  if (CONNECTOR_SKIP_DIRS.has(name)) continue;
47446
- realCopyConnector(srcPath, join25(dstDir, name), harness);
47808
+ realCopyConnector(srcPath, join26(dstDir, name), harness);
47447
47809
  continue;
47448
47810
  }
47449
47811
  if (name.includes(".test.")) continue;
47450
47812
  if (!allowedExt.some((ext) => name.endsWith(ext))) continue;
47451
- writeFileSync5(join25(dstDir, name), readFileSync17(srcPath));
47813
+ writeFileSync5(join26(dstDir, name), readFileSync17(srcPath));
47452
47814
  }
47453
47815
  }
47454
47816
  function realWriteBinding(dstDir, identityId) {
47455
- mkdirSync8(dstDir, { recursive: true });
47456
- writeFileSync5(join25(dstDir, "securecoms.binding.json"), JSON.stringify({ identity_id: identityId }) + "\n", { mode: 384 });
47817
+ mkdirSync9(dstDir, { recursive: true });
47818
+ writeFileSync5(join26(dstDir, "securecoms.binding.json"), JSON.stringify({ identity_id: identityId }) + "\n", { mode: 384 });
47457
47819
  }
47458
47820
  function realReadBinding(dstDir) {
47459
47821
  try {
47460
- const parsed = JSON.parse(readFileSync17(join25(dstDir, "securecoms.binding.json"), "utf8"));
47822
+ const parsed = JSON.parse(readFileSync17(join26(dstDir, "securecoms.binding.json"), "utf8"));
47461
47823
  return parsed.identity_id ?? null;
47462
47824
  } catch {
47463
47825
  return null;
47464
47826
  }
47465
47827
  }
47466
47828
  function realRemoveBinding(dstDir) {
47467
- rmSync8(join25(dstDir, "securecoms.binding.json"), { force: true });
47829
+ rmSync8(join26(dstDir, "securecoms.binding.json"), { force: true });
47468
47830
  }
47469
47831
  function realStatDir(p) {
47470
47832
  try {
@@ -47540,7 +47902,7 @@ async function runServiceVerb(verb) {
47540
47902
  }
47541
47903
  try {
47542
47904
  if (verb === "install") {
47543
- const credAbs = isAbsolute3(credentialPath) ? credentialPath : resolve3(credentialPath);
47905
+ const credAbs = isAbsolute3(credentialPath) ? credentialPath : resolve4(credentialPath);
47544
47906
  const installExecPath = chooseInstallExecPath(
47545
47907
  process.execPath,
47546
47908
  argOrEnv("--exec-path", "SECURECOMS_EXEC_PATH"),
@@ -47751,11 +48113,11 @@ async function runMode(deps) {
47751
48113
  refuse3("identity_id_invalid", { field: "identity_id" });
47752
48114
  return deps.exit(1);
47753
48115
  }
47754
- const lockDir = join25(deps.home, ".securecoms", "locks");
47755
- const lockPath = join25(lockDir, `${cfg.identityId.toLowerCase()}.lock`);
48116
+ const lockDir = join26(deps.home, ".securecoms", "locks");
48117
+ const lockPath = join26(lockDir, `${cfg.identityId.toLowerCase()}.lock`);
47756
48118
  try {
47757
- mkdirSync8(lockDir, { recursive: true, mode: 448 });
47758
- chmodSync6(lockDir, 448);
48119
+ mkdirSync9(lockDir, { recursive: true, mode: 448 });
48120
+ chmodSync7(lockDir, 448);
47759
48121
  } catch (e) {
47760
48122
  log("error", "startup.instance_lock_failed", {}, e);
47761
48123
  return deps.exit(1);
@@ -47802,7 +48164,7 @@ async function runMode(deps) {
47802
48164
  return deps.exit(1);
47803
48165
  }
47804
48166
  deps.onExit(() => lock.release());
47805
- const tokenFile = daemon.tokenFile ?? join25(dirname13(credentialPath), "bridge-token");
48167
+ const tokenFile = daemon.tokenFile ?? join26(dirname14(credentialPath), "bridge-token");
47806
48168
  const token = resolveToken({ tokenFile, token: daemon.token });
47807
48169
  const preBound = deps.net.createServer((sock) => sock.destroy());
47808
48170
  const bindError = await new Promise((resolveBind) => {
@@ -47951,7 +48313,7 @@ async function main() {
47951
48313
  await runMode(realRunModeDeps(credentialPath, storePath));
47952
48314
  }
47953
48315
  var here = fileURLToPath4(import.meta.url);
47954
- var isMain = !!process.argv[1] && refersToSameFile(here, process.argv[1], realpathSync3);
48316
+ var isMain = !!process.argv[1] && refersToSameFile(here, process.argv[1], realpathSync4);
47955
48317
  if (isMain) {
47956
48318
  main().catch((e) => {
47957
48319
  writeServiceLog("error", "startup.fatal", {}, e);