@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/README.md +61 -3
- package/dist/attachments.d.ts +13 -0
- package/dist/bin.js +687 -325
- package/dist/bridge-agent.d.ts +53 -7
- package/dist/consumer-grants.d.ts +9 -0
- package/dist/errors.d.ts +1 -1
- package/dist/grants/binding.d.ts +18 -5
- package/dist/http-server.d.ts +10 -36
- package/dist/index.js +626 -264
- package/dist/outbox.d.ts +42 -0
- package/dist/reply-credit.d.ts +22 -0
- package/dist/reply-intent.d.ts +6 -1
- package/dist/service/runtime-config-dir.d.ts +3 -0
- package/dist/version.d.ts +1 -1
- package/dist/ws-server.d.ts +1 -1
- package/package.json +2 -2
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
|
|
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
|
|
7421
|
+
import { basename as basename3, dirname as dirname5, join as join10 } from "node:path";
|
|
7422
7422
|
function kind(p) {
|
|
7423
7423
|
try {
|
|
7424
|
-
return
|
|
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 =
|
|
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
|
-
|
|
7437
|
+
mkdirSync2(modelsDir, { recursive: true, mode: 448 });
|
|
7438
7438
|
if (kind(modelsDir) !== "dir") throw new LayoutRefused();
|
|
7439
7439
|
}
|
|
7440
|
-
const chain = opts.fromModelsDir ? 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 =
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
7488
|
+
chmodSync2(dest, 292);
|
|
7489
7489
|
} finally {
|
|
7490
|
-
if (dir)
|
|
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()) =>
|
|
7499
|
-
partialDir = (root3) =>
|
|
7500
|
-
filePath = (root3, modelId, f) =>
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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((
|
|
25400
|
-
waiter =
|
|
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((
|
|
25721
|
-
this.waiter =
|
|
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
|
|
30399
|
-
|
|
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 =
|
|
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 =
|
|
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(
|
|
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
|
|
31162
|
+
import { join as join5 } from "node:path";
|
|
30966
31163
|
function expandHome(p) {
|
|
30967
|
-
return p === "~" || p.startsWith("~/") ?
|
|
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 =
|
|
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
|
|
31180
|
+
import { join as join6 } from "node:path";
|
|
30984
31181
|
var HermesStore = class {
|
|
30985
31182
|
dbPath;
|
|
30986
31183
|
constructor(baseDir) {
|
|
30987
|
-
this.dbPath =
|
|
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
|
|
31326
|
+
import { join as join7 } from "node:path";
|
|
31130
31327
|
function resolveOwnerDmSession(baseDir, agent) {
|
|
31131
31328
|
try {
|
|
31132
|
-
const path =
|
|
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
|
|
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(
|
|
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
|
|
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(
|
|
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 ?
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
|
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
|
|
32201
|
+
import { chmodSync as chmodSync3, rmSync as rmSync2 } from "node:fs";
|
|
32005
32202
|
import { open } from "node:fs/promises";
|
|
32006
|
-
import { join as
|
|
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
|
-
|
|
32023
|
-
rmSync2(
|
|
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 =
|
|
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
|
|
32148
|
-
import { join as
|
|
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 =
|
|
32351
|
+
const p = join12(dir, identityId);
|
|
32155
32352
|
let fd;
|
|
32156
32353
|
try {
|
|
32157
|
-
fd = openSync(p,
|
|
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
|
|
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
|
-
|
|
32179
|
-
for (const e of readdirSync2(p)) makeWritable(
|
|
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 =
|
|
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 =
|
|
32396
|
+
const tree = join12(root3, modelId);
|
|
32200
32397
|
if (!isRealDir(tree)) continue;
|
|
32201
|
-
const lastUse = newestMarkerMs(
|
|
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
|
|
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()) =>
|
|
32509
|
+
var embedderRunDir = (home = homedir3()) => join13(home, ".securecoms", "run", "embedder");
|
|
32313
32510
|
var embedderPaths = (dir, key) => ({
|
|
32314
|
-
sock:
|
|
32315
|
-
token:
|
|
32316
|
-
lock:
|
|
32317
|
-
status:
|
|
32318
|
-
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
|
|
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
|
|
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
|
|
32333
|
-
import { dirname as
|
|
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 =
|
|
32337
|
-
const parent3 =
|
|
32338
|
-
if (!existsSync(parent3))
|
|
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
|
-
|
|
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
|
|
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
|
|
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,
|
|
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,
|
|
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 =
|
|
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
|
|
32671
|
+
return realpathSync2(p);
|
|
32475
32672
|
} catch {
|
|
32476
|
-
const up =
|
|
32477
|
-
return up === p ? 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)
|
|
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(
|
|
32695
|
+
...withReal(dirname7(o.distDir)),
|
|
32499
32696
|
...existsSync2(defaultModelsRoot(o.home)) ? withReal(defaultModelsRoot(o.home)) : [defaultModelsRoot(o.home)],
|
|
32500
|
-
...both(
|
|
32501
|
-
...win ? both(
|
|
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(
|
|
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:
|
|
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
|
|
32526
|
-
import { join as
|
|
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 =
|
|
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 =
|
|
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((
|
|
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
|
-
|
|
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
|
-
|
|
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((
|
|
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
|
-
|
|
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
|
|
33487
|
-
import { join as
|
|
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
|
|
33491
|
-
import { join as
|
|
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 =
|
|
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(
|
|
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(
|
|
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
|
|
33847
|
+
import { join as join18 } from "node:path";
|
|
33651
33848
|
function workerTransport(init, distDir) {
|
|
33652
|
-
const w = new Worker(
|
|
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((
|
|
33909
|
+
return new Promise((resolve5) => {
|
|
33713
33910
|
const timer = setTimeout(() => {
|
|
33714
33911
|
this.waiting.delete(id);
|
|
33715
33912
|
this.misses++;
|
|
33716
|
-
|
|
33913
|
+
resolve5("late");
|
|
33717
33914
|
}, Math.max(0, deadlineAt - Date.now()));
|
|
33718
33915
|
this.waiting.set(id, { resolve: (m) => {
|
|
33719
33916
|
clearTimeout(timer);
|
|
33720
|
-
|
|
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
|
-
|
|
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
|
|
35254
|
+
chmodSync as chmodSync5,
|
|
35058
35255
|
existsSync as existsSync4,
|
|
35059
|
-
lstatSync as
|
|
35060
|
-
mkdirSync as
|
|
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
|
|
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) =>
|
|
35080
|
-
chmod: (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
|
|
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 !==
|
|
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(
|
|
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 =
|
|
35307
|
+
const parent3 = join19(home, ".securecoms", "runtime-config");
|
|
35111
35308
|
const dir = runtimeConfigDir(home, identityId);
|
|
35112
|
-
for (const d of [
|
|
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(
|
|
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 =
|
|
35332
|
+
const parent3 = join19(home, ".securecoms", "runtime-config");
|
|
35136
35333
|
const dir = runtimeConfigDir(home, identityId);
|
|
35137
|
-
for (const d of [
|
|
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(
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
35707
|
-
|
|
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
|
|
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",
|
|
35714
|
-
...switches.room ? signedManifestTools(manifest, "team_room",
|
|
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 },
|
|
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",
|
|
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 &&
|
|
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,
|
|
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((
|
|
36371
|
-
const t = setTimeout(
|
|
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
|
|
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
|
|
36522
|
-
await
|
|
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,
|
|
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 ?
|
|
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((
|
|
36800
|
-
const t = setTimeout(
|
|
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 ??
|
|
36828
|
-
this.
|
|
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.
|
|
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.
|
|
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(
|
|
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(
|
|
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((
|
|
38021
|
-
const t = setTimeout(() =>
|
|
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((
|
|
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
|
-
/**
|
|
39139
|
-
|
|
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(
|
|
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(
|
|
39145
|
-
this.callBuffer.appendAgent(channelId,
|
|
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,
|
|
39149
|
-
if (this.unifiedOn && body.kind === "text" && !bridgeLine) this.spawnUnifiedReply(channelId,
|
|
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
|
-
() =>
|
|
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
|
-
|
|
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) =>
|
|
39521
|
-
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
|
-
|
|
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`
|
|
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
|
-
|
|
40101
|
-
let bytes;
|
|
40385
|
+
let read;
|
|
40102
40386
|
try {
|
|
40103
|
-
|
|
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
|
-
|
|
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((
|
|
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
|
-
|
|
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((
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
*
|
|
44273
|
-
*
|
|
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
|
-
|
|
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.
|
|
44586
|
+
this.onCapabilityMaybeChanged();
|
|
44284
44587
|
}
|
|
44285
44588
|
clearSearchProvidersSocket(token) {
|
|
44286
|
-
if (this.searchProvidersSockets.delete(token)) this.
|
|
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
|
-
|
|
44314
|
-
const
|
|
44315
|
-
const prev = this.
|
|
44316
|
-
if (
|
|
44317
|
-
this.
|
|
44318
|
-
this.recomputeManifestPolicy(
|
|
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 && !
|
|
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(
|
|
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 },
|
|
44674
|
+
this.grantWriters.writeManifestPolicy(this.home, this.cfg.identityId, manifestPolicyFor(manifest, { ownerDm: this.ownerDmManifestGrants, room: this.roomManifestGrants }, features));
|
|
44352
44675
|
return;
|
|
44353
44676
|
}
|
|
44354
|
-
|
|
44355
|
-
|
|
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.
|
|
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
|
|
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((
|
|
44581
|
-
timer = setTimeout(
|
|
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((
|
|
44592
|
-
timer = setTimeout(
|
|
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((
|
|
44602
|
-
timer = setTimeout(
|
|
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((
|
|
44613
|
-
timer = setTimeout(
|
|
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((
|
|
44624
|
-
timer = setTimeout(
|
|
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((
|
|
44637
|
-
timer = setTimeout(
|
|
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((
|
|
44647
|
-
timer = setTimeout(
|
|
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((
|
|
44657
|
-
timer = setTimeout(
|
|
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((
|
|
44668
|
-
timer = setTimeout(
|
|
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
|
|
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
|
|
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((
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
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
|
|
45745
|
-
import { join as
|
|
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
|
|
46114
|
+
import { dirname as dirname9 } from "node:path";
|
|
45753
46115
|
|
|
45754
46116
|
// src/service/config.ts
|
|
45755
|
-
import { basename as
|
|
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 ??
|
|
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 :
|
|
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 :
|
|
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 `${
|
|
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(
|
|
45941
|
-
deps.mkdirp(
|
|
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(
|
|
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
|
|
46344
|
+
import { join as join22 } from "node:path";
|
|
45983
46345
|
|
|
45984
46346
|
// src/service/wire.ts
|
|
45985
|
-
import { join as
|
|
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 =
|
|
46161
|
-
return
|
|
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
|
|
46785
|
+
return join22(home, `securecoms-agent-${slug}`);
|
|
46424
46786
|
}
|
|
46425
46787
|
function durableCredentialPath(home, slug) {
|
|
46426
|
-
return
|
|
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 =
|
|
46448
|
-
const credPath =
|
|
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(
|
|
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(
|
|
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
|
|
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 =
|
|
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
|
|
46706
|
-
mkdirSync as
|
|
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
|
|
47075
|
+
import { join as join23 } from "node:path";
|
|
46714
47076
|
var REAL_FS2 = {
|
|
46715
|
-
mkdir: (p, mode) =>
|
|
46716
|
-
chmod: (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
|
|
47085
|
+
return join23(home, ".securecoms");
|
|
46724
47086
|
}
|
|
46725
47087
|
function connectionsDir(home) {
|
|
46726
|
-
return
|
|
47088
|
+
return join23(securecomsDir(home), "connections");
|
|
46727
47089
|
}
|
|
46728
47090
|
function connectionFilePath(home, identityId) {
|
|
46729
|
-
return
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
47452
|
+
lstatSync as lstatSync9,
|
|
47091
47453
|
openSync as openSync5,
|
|
47092
47454
|
readFileSync as readFileSync15,
|
|
47093
|
-
realpathSync as
|
|
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
|
|
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
|
|
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 =
|
|
47147
|
-
const target =
|
|
47508
|
+
const credDir = dirname13(resolve3(opts.credentialPath));
|
|
47509
|
+
const target = join24(credDir, PIN_FILE_NAME);
|
|
47148
47510
|
try {
|
|
47149
|
-
if (
|
|
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 = `${
|
|
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,
|
|
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 =
|
|
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) =>
|
|
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) =>
|
|
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
|
-
|
|
47738
|
+
mkdirSync9(p, { recursive: true, mode: mode ?? 493 });
|
|
47377
47739
|
},
|
|
47378
47740
|
fileExists: (p) => existsSync7(p),
|
|
47379
|
-
realpath: (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
|
-
|
|
47803
|
+
mkdirSync9(dstDir, { recursive: true });
|
|
47442
47804
|
for (const name of readdirSync4(srcDir)) {
|
|
47443
|
-
const srcPath =
|
|
47805
|
+
const srcPath = join26(srcDir, name);
|
|
47444
47806
|
if (statSync8(srcPath).isDirectory()) {
|
|
47445
47807
|
if (CONNECTOR_SKIP_DIRS.has(name)) continue;
|
|
47446
|
-
realCopyConnector(srcPath,
|
|
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(
|
|
47813
|
+
writeFileSync5(join26(dstDir, name), readFileSync17(srcPath));
|
|
47452
47814
|
}
|
|
47453
47815
|
}
|
|
47454
47816
|
function realWriteBinding(dstDir, identityId) {
|
|
47455
|
-
|
|
47456
|
-
writeFileSync5(
|
|
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(
|
|
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(
|
|
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 :
|
|
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 =
|
|
47755
|
-
const lockPath =
|
|
48116
|
+
const lockDir = join26(deps.home, ".securecoms", "locks");
|
|
48117
|
+
const lockPath = join26(lockDir, `${cfg.identityId.toLowerCase()}.lock`);
|
|
47756
48118
|
try {
|
|
47757
|
-
|
|
47758
|
-
|
|
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 ??
|
|
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],
|
|
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);
|