@securecoms/agent-bridge 0.30.1 → 0.30.3
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 +77 -6
- package/dist/bin.d.ts +20 -1
- package/dist/bin.js +413 -63
- package/dist/bridge-agent.d.ts +60 -0
- package/dist/config.d.ts +13 -0
- package/dist/consumer-grants.d.ts +15 -0
- package/dist/grants/binding.d.ts +30 -2
- package/dist/grants/manifest.d.ts +11 -1
- package/dist/grants/search-providers.d.ts +9 -0
- package/dist/http-server.d.ts +2 -0
- package/dist/index.js +266 -32
- package/dist/preflight-upgrade.d.cts +1 -2
- package/dist/preflight.cjs +52 -49
- package/dist/preflight.d.cts +14 -0
- package/dist/service/credential-arg.d.ts +23 -0
- package/dist/service/install.d.ts +7 -0
- package/dist/service/resolve-node.d.ts +1 -0
- package/dist/service/runtime-config-dir.d.ts +4 -0
- package/dist/version.d.ts +1 -1
- package/dist/ws-server.d.ts +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -17457,7 +17457,13 @@ var CODES = [
|
|
|
17457
17457
|
// lookup missed and before any write, so a retry can never land: the
|
|
17458
17458
|
// pending commit is dropped instead of being resent forever. The same pair
|
|
17459
17459
|
// answers an application send into a deleted channel (#325 F5).
|
|
17460
|
-
{ status: 409, code: "channel_unavailable" }
|
|
17460
|
+
{ status: 409, code: "channel_unavailable" },
|
|
17461
|
+
// ADR-0023 B.1: an Add or Welcome into a humans-only enclave's group naming
|
|
17462
|
+
// a device that is not provably a person's. Refused after the replay lookup
|
|
17463
|
+
// missed and before any write, so nothing landed. Definite: the committer's
|
|
17464
|
+
// work cannot name an agent in a humans-only enclave (admission refuses
|
|
17465
|
+
// one), so dropping the commit starts no resend loop.
|
|
17466
|
+
{ status: 403, code: "humans_only_enclave" }
|
|
17461
17467
|
];
|
|
17462
17468
|
var DISTRIBUTE_DEFINITE_CODES = Object.freeze(CODES.map((c) => Object.freeze({ ...c })));
|
|
17463
17469
|
var DEFINITE_KEYS = new Set(DISTRIBUTE_DEFINITE_CODES.map((c) => `${c.status} ${c.code}`));
|
|
@@ -29095,7 +29101,7 @@ function loadPackageMeta(importMetaUrl) {
|
|
|
29095
29101
|
}
|
|
29096
29102
|
|
|
29097
29103
|
// src/bridge-agent.ts
|
|
29098
|
-
import { existsSync as existsSync5, promises as fsp, statSync as statSync4 } from "node:fs";
|
|
29104
|
+
import { existsSync as existsSync5, promises as fsp, readFileSync as readFileSync10, statSync as statSync4 } from "node:fs";
|
|
29099
29105
|
import { join as join17, dirname as dirname5 } from "node:path";
|
|
29100
29106
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
29101
29107
|
|
|
@@ -34026,6 +34032,15 @@ import {
|
|
|
34026
34032
|
writeFileSync
|
|
34027
34033
|
} from "node:fs";
|
|
34028
34034
|
import { basename as basename4, isAbsolute, join as join15 } from "node:path";
|
|
34035
|
+
|
|
34036
|
+
// src/grants/search-providers.ts
|
|
34037
|
+
var SEARCH_PROVIDERS = /* @__PURE__ */ new Set(["brave", "tavily", "parallel", "exa", "firecrawl"]);
|
|
34038
|
+
var OWNER_DM_SEARCH_DAILY_CAP_MAX = 2e3;
|
|
34039
|
+
function isSearchProvider(value) {
|
|
34040
|
+
return typeof value === "string" && SEARCH_PROVIDERS.has(value);
|
|
34041
|
+
}
|
|
34042
|
+
|
|
34043
|
+
// src/service/runtime-config-dir.ts
|
|
34029
34044
|
var MEMORY_FILE = "MEMORY.md";
|
|
34030
34045
|
var REAL_FS = {
|
|
34031
34046
|
mkdir: (p, mode) => mkdirSync5(p, { recursive: true, mode }),
|
|
@@ -34208,10 +34223,14 @@ function readLocalReport(home, identityId) {
|
|
|
34208
34223
|
const persona = raw && typeof raw === "object" && !Array.isArray(raw) ? Object.fromEntries(
|
|
34209
34224
|
Object.entries(raw).filter(([, v]) => typeof v === "string")
|
|
34210
34225
|
) : {};
|
|
34226
|
+
const capRaw = doc.owner_dm_search_cap_effective;
|
|
34227
|
+
const ownerDmCap = typeof capRaw === "number" && Number.isInteger(capRaw) && capRaw >= 0 && capRaw <= OWNER_DM_SEARCH_DAILY_CAP_MAX ? capRaw : void 0;
|
|
34211
34228
|
return {
|
|
34212
34229
|
persona,
|
|
34213
34230
|
has_search_key: hasSearchKey,
|
|
34214
34231
|
has_llm_key: hasLlmKey,
|
|
34232
|
+
has_extract_provider: doc.has_extract_provider === true,
|
|
34233
|
+
...ownerDmCap !== void 0 ? { owner_dm_search_cap_effective: ownerDmCap } : {},
|
|
34215
34234
|
legacy_memory: localLegacySources(doc).map(({ path: _path, ...source }) => source),
|
|
34216
34235
|
secret_paths: parseSecretPathsReport(doc.secret_paths)
|
|
34217
34236
|
};
|
|
@@ -34410,7 +34429,8 @@ function loadOwnerGrantPin(opts) {
|
|
|
34410
34429
|
// src/grants/manifest.ts
|
|
34411
34430
|
var TOOL_GRANTS_DOMAIN = "SECURECOMS-TOOL-GRANTS-V1:";
|
|
34412
34431
|
var MANIFEST_KNOWN_TOOLS = Object.freeze({
|
|
34413
|
-
owner_dm: Object.freeze(["web_search", "fetch_url", "http_request", "sql_query"]),
|
|
34432
|
+
owner_dm: Object.freeze(["web_search", "web_extract", "fetch_url", "http_request", "sql_query"]),
|
|
34433
|
+
// web_extract: owner DM only, never team_room (spec §5.4, D5)
|
|
34414
34434
|
team_room: Object.freeze(["web_search"])
|
|
34415
34435
|
});
|
|
34416
34436
|
function manifestSignedBytes(manifest) {
|
|
@@ -34485,7 +34505,11 @@ function parseConfig(v) {
|
|
|
34485
34505
|
}
|
|
34486
34506
|
if ("search" in v) {
|
|
34487
34507
|
const sr = v.search;
|
|
34488
|
-
if (!isPlainObject(sr) || !exactKeys(sr, ["provider"]
|
|
34508
|
+
if (!isPlainObject(sr) || !exactKeys(sr, ["provider"], ["owner_dm_daily_cap"]) || !isSearchProvider(sr.provider)) return null;
|
|
34509
|
+
if ("owner_dm_daily_cap" in sr) {
|
|
34510
|
+
const cap = sr.owner_dm_daily_cap;
|
|
34511
|
+
if (typeof cap !== "number" || !Number.isSafeInteger(cap) || cap < 0 || cap > OWNER_DM_SEARCH_DAILY_CAP_MAX) return null;
|
|
34512
|
+
}
|
|
34489
34513
|
}
|
|
34490
34514
|
const mem = v.memory;
|
|
34491
34515
|
if (!isPlainObject(mem) || !exactKeys(mem, ["mode"], ["recall_days"])) return null;
|
|
@@ -34495,7 +34519,12 @@ function parseConfig(v) {
|
|
|
34495
34519
|
return Object.freeze({
|
|
34496
34520
|
persona_sha256: v.persona_sha256,
|
|
34497
34521
|
...isPlainObject(v.llm) ? { llm: Object.freeze({ provider: v.llm.provider, model: v.llm.model }) } : {},
|
|
34498
|
-
...isPlainObject(v.search) ? {
|
|
34522
|
+
...isPlainObject(v.search) ? {
|
|
34523
|
+
search: Object.freeze({
|
|
34524
|
+
provider: v.search.provider,
|
|
34525
|
+
..."owner_dm_daily_cap" in v.search ? { owner_dm_daily_cap: v.search.owner_dm_daily_cap } : {}
|
|
34526
|
+
})
|
|
34527
|
+
} : {},
|
|
34499
34528
|
...isStringArray(v.tools_allow) ? { tools_allow: Object.freeze([...v.tools_allow]) } : {},
|
|
34500
34529
|
flags: Object.freeze([...v.flags]),
|
|
34501
34530
|
memory: Object.freeze({ mode: mem.mode, ..."recall_days" in mem ? { recall_days: mem.recall_days } : {} }),
|
|
@@ -34726,7 +34755,20 @@ function bindTargets(raw, signed, equals) {
|
|
|
34726
34755
|
}
|
|
34727
34756
|
return { written, mismatches };
|
|
34728
34757
|
}
|
|
34729
|
-
function
|
|
34758
|
+
function signedManifestTools(manifest, audience, searchProvidersCapable) {
|
|
34759
|
+
return MANIFEST_KNOWN_TOOLS[audience].filter((t) => (manifest.grants[audience] ?? []).includes(t) && (t !== "web_extract" || searchProvidersCapable));
|
|
34760
|
+
}
|
|
34761
|
+
function manifestPolicyFor(manifest, switches, searchProvidersCapable) {
|
|
34762
|
+
return {
|
|
34763
|
+
v: 1,
|
|
34764
|
+
manifest_tools: [.../* @__PURE__ */ new Set([
|
|
34765
|
+
...switches.ownerDm ? signedManifestTools(manifest, "owner_dm", searchProvidersCapable) : [],
|
|
34766
|
+
...switches.room ? signedManifestTools(manifest, "team_room", searchProvidersCapable) : []
|
|
34767
|
+
])].sort(),
|
|
34768
|
+
secret_path_mode: "enforce"
|
|
34769
|
+
};
|
|
34770
|
+
}
|
|
34771
|
+
function bindRuntimeConfig(manifest, payload, prior, switches = { ownerDm: true, room: true }, searchProvidersCapable = false) {
|
|
34730
34772
|
const mismatches = /* @__PURE__ */ new Set();
|
|
34731
34773
|
const cfg = manifest.config;
|
|
34732
34774
|
const out = {};
|
|
@@ -34755,6 +34797,7 @@ function bindRuntimeConfig(manifest, payload, prior, switches = { ownerDm: true,
|
|
|
34755
34797
|
for (const name of Object.keys(files)) {
|
|
34756
34798
|
if (name !== LLM_SOURCE && !PERSONA_FILES.includes(name)) mismatches.add("files");
|
|
34757
34799
|
}
|
|
34800
|
+
const ownerDmTools = signedManifestTools(manifest, "owner_dm", searchProvidersCapable);
|
|
34758
34801
|
const sj = isObj2(payload.skills_json) ? payload.skills_json : void 0;
|
|
34759
34802
|
const eps = bindTargets(sj?.endpoints, manifest.endpoints, endpointEquals);
|
|
34760
34803
|
const dbs = bindTargets(sj?.databases, manifest.databases, databaseEquals);
|
|
@@ -34772,14 +34815,20 @@ function bindRuntimeConfig(manifest, payload, prior, switches = { ownerDm: true,
|
|
|
34772
34815
|
appliedSearch = searchBlock;
|
|
34773
34816
|
} else {
|
|
34774
34817
|
mismatches.add("search");
|
|
34775
|
-
|
|
34818
|
+
const usablePrior = cfg.search !== void 0 && prior.search?.provider === cfg.search.provider ? prior.search : void 0;
|
|
34819
|
+
searchBlock = cfg.search === void 0 ? void 0 : usablePrior ?? Object.freeze({ provider: cfg.search.provider });
|
|
34776
34820
|
}
|
|
34777
34821
|
const skillsDoc = {
|
|
34778
34822
|
endpoints: eps.written,
|
|
34779
34823
|
databases: dbs.written,
|
|
34780
34824
|
flags: [...cfg.flags],
|
|
34781
34825
|
...deny !== void 0 || cfg.tools_allow !== void 0 ? { tools: { ...deny !== void 0 ? { deny } : {}, ...cfg.tools_allow !== void 0 ? { allow: [...cfg.tools_allow] } : {} } } : {},
|
|
34782
|
-
...searchBlock ? { search: searchBlock } : {}
|
|
34826
|
+
...searchBlock ? { search: searchBlock } : {},
|
|
34827
|
+
// D31: the marker the runtime keys D24 (iii) on; written under every manifest, true when config.search is signed.
|
|
34828
|
+
search_signed: cfg.search !== void 0,
|
|
34829
|
+
// D36, D39: the signed owner-DM cap, only when the owner-DM switch is on and the manifest grants an owner-DM tool;
|
|
34830
|
+
// otherwise none, so the runtime clamps a signed raise to the default. Never read from the payload.
|
|
34831
|
+
...cfg.search?.owner_dm_daily_cap !== void 0 && switches.ownerDm && ownerDmTools.length > 0 ? { owner_dm_search_cap: cfg.search.owner_dm_daily_cap } : {}
|
|
34783
34832
|
};
|
|
34784
34833
|
out["skills.json"] = JSON.stringify(skillsDoc, null, 2) + "\n";
|
|
34785
34834
|
const signedMemory = { mode: cfg.memory.mode, ...cfg.memory.recall_days !== void 0 ? { recall_days: cfg.memory.recall_days } : {} };
|
|
@@ -34810,12 +34859,7 @@ function bindRuntimeConfig(manifest, payload, prior, switches = { ownerDm: true,
|
|
|
34810
34859
|
mismatches.add("legacy_assign");
|
|
34811
34860
|
legacyDecisions = decisions.filter((d) => isObj2(d) && d.action === "delete");
|
|
34812
34861
|
}
|
|
34813
|
-
const
|
|
34814
|
-
const policy = {
|
|
34815
|
-
v: 1,
|
|
34816
|
-
manifest_tools: [.../* @__PURE__ */ new Set([...switches.ownerDm ? signedTools("owner_dm") : [], ...switches.room ? signedTools("team_room") : []])].sort(),
|
|
34817
|
-
secret_path_mode: "enforce"
|
|
34818
|
-
};
|
|
34862
|
+
const policy = manifestPolicyFor(manifest, switches, searchProvidersCapable);
|
|
34819
34863
|
return {
|
|
34820
34864
|
mismatches: CONFIG_BINDING_FIELDS.filter((f) => mismatches.has(f)).sort(),
|
|
34821
34865
|
targetMismatches: { endpoint: eps.mismatches, database: dbs.mismatches },
|
|
@@ -34954,6 +34998,8 @@ var TOOL_ROOM_CLASSES = Object.freeze({
|
|
|
34954
34998
|
current_time: Object.freeze(["room_safe_read"]),
|
|
34955
34999
|
echo: Object.freeze(["room_safe_read"]),
|
|
34956
35000
|
web_search: Object.freeze(["owner_credential"]),
|
|
35001
|
+
web_extract: Object.freeze(["owner_credential", "egress"]),
|
|
35002
|
+
// D5: spends the owner's key and sends URLs out
|
|
34957
35003
|
fetch_url: Object.freeze(["egress"]),
|
|
34958
35004
|
sql_query: Object.freeze(["owner_credential"]),
|
|
34959
35005
|
http_request: Object.freeze(["owner_credential", "egress", "side_effect"])
|
|
@@ -35375,6 +35421,8 @@ function hexToBytes4(h) {
|
|
|
35375
35421
|
return new Uint8Array(Buffer.from(h, "hex"));
|
|
35376
35422
|
}
|
|
35377
35423
|
var BOOT_DRAIN_WAIT_MS_DEFAULT = 6e4;
|
|
35424
|
+
var SEARCH_CAPABILITY_REFETCH_DEBOUNCE_MS = 1e3;
|
|
35425
|
+
var SEARCH_CAPABILITY_REFETCH_MAX_WAIT_MS = 1e4;
|
|
35378
35426
|
var BootDrainRotationRefusedError = class extends Error {
|
|
35379
35427
|
constructor() {
|
|
35380
35428
|
super("key-package rotation refused: the boot drain has not settled");
|
|
@@ -35570,6 +35618,8 @@ var BridgeAgent = class _BridgeAgent {
|
|
|
35570
35618
|
this.actionClock = deps.actionThrottleNow ?? Date.now;
|
|
35571
35619
|
this.rng = deps.rng ?? Math.random;
|
|
35572
35620
|
this.bootDrainWaitMs = deps.bootDrainWaitMs ?? BOOT_DRAIN_WAIT_MS_DEFAULT;
|
|
35621
|
+
this.searchCapabilityRefetchDebounceMs = deps.searchCapabilityRefetchDebounceMs ?? SEARCH_CAPABILITY_REFETCH_DEBOUNCE_MS;
|
|
35622
|
+
this.searchCapabilityRefetchMaxWaitMs = deps.searchCapabilityRefetchMaxWaitMs ?? SEARCH_CAPABILITY_REFETCH_MAX_WAIT_MS;
|
|
35573
35623
|
this.replySleep = deps.replySleep ?? ((ms) => new Promise((resolve) => {
|
|
35574
35624
|
const t = setTimeout(resolve, ms);
|
|
35575
35625
|
t.unref?.();
|
|
@@ -36008,6 +36058,8 @@ var BridgeAgent = class _BridgeAgent {
|
|
|
36008
36058
|
bootSweepTimer;
|
|
36009
36059
|
rng;
|
|
36010
36060
|
bootDrainWaitMs;
|
|
36061
|
+
searchCapabilityRefetchDebounceMs;
|
|
36062
|
+
searchCapabilityRefetchMaxWaitMs;
|
|
36011
36063
|
/** Resolved (and cleared) each time an ingest cycle settles: see start(). */
|
|
36012
36064
|
ingestSettledWaiters = [];
|
|
36013
36065
|
/** The boot key-package publish, when start() had to defer it. Test seam. */
|
|
@@ -36453,9 +36505,14 @@ var BridgeAgent = class _BridgeAgent {
|
|
|
36453
36505
|
}
|
|
36454
36506
|
const skillsJson = payload.skills_json ?? {};
|
|
36455
36507
|
if (version && inForce === null) {
|
|
36508
|
+
let asDelivered = skillsJson;
|
|
36509
|
+
if (skillsJson !== null && typeof skillsJson === "object" && !Array.isArray(skillsJson)) {
|
|
36510
|
+
const { owner_dm_search_cap: _cap, search_signed: _signed, ...rest } = skillsJson;
|
|
36511
|
+
asDelivered = rest;
|
|
36512
|
+
}
|
|
36456
36513
|
this.grantWriters.writeManagedFiles(this.home, this.cfg.identityId, {
|
|
36457
36514
|
...payload.files ?? {},
|
|
36458
|
-
"skills.json": `${JSON.stringify(
|
|
36515
|
+
"skills.json": `${JSON.stringify(asDelivered, null, 2)}
|
|
36459
36516
|
`
|
|
36460
36517
|
});
|
|
36461
36518
|
}
|
|
@@ -36510,7 +36567,7 @@ var BridgeAgent = class _BridgeAgent {
|
|
|
36510
36567
|
}
|
|
36511
36568
|
let bound;
|
|
36512
36569
|
try {
|
|
36513
|
-
bound = this.grantWriters.bindRuntimeConfig(manifest, payload, { search: prior }, { ownerDm: this.ownerDmManifestGrants, room: this.roomManifestGrants });
|
|
36570
|
+
bound = this.grantWriters.bindRuntimeConfig(manifest, payload, { search: prior }, { ownerDm: this.ownerDmManifestGrants, room: this.roomManifestGrants }, this.searchProvidersCapable());
|
|
36514
36571
|
} catch (e) {
|
|
36515
36572
|
bound = null;
|
|
36516
36573
|
return this.bindFailed(manifest, payload.memory, e);
|
|
@@ -36529,9 +36586,9 @@ var BridgeAgent = class _BridgeAgent {
|
|
|
36529
36586
|
return true;
|
|
36530
36587
|
}
|
|
36531
36588
|
try {
|
|
36532
|
-
this.grantWriters.writeManifestPolicy(this.home, this.cfg.identityId, bound.policy);
|
|
36533
36589
|
this.grantWriters.writeManagedFiles(this.home, this.cfg.identityId, bound.files);
|
|
36534
36590
|
if (manifest.config.llm === void 0) this.grantWriters.removeManagedLlmSource(this.home, this.cfg.identityId);
|
|
36591
|
+
this.grantWriters.writeManifestPolicy(this.home, this.cfg.identityId, manifestPolicyFor(manifest, { ownerDm: this.ownerDmManifestGrants, room: this.roomManifestGrants }, this.searchProvidersCapable()));
|
|
36535
36592
|
} catch (e) {
|
|
36536
36593
|
this.manifestBinding = null;
|
|
36537
36594
|
throw e;
|
|
@@ -36584,6 +36641,7 @@ var BridgeAgent = class _BridgeAgent {
|
|
|
36584
36641
|
/** The report half of refetchRuntimeConfigOnce (split out for the plan 4 Task 11 bind_failed path). */
|
|
36585
36642
|
async postRuntimeConfigReport(base, version, skillsJson) {
|
|
36586
36643
|
const local = readLocalReport(this.home, this.cfg.identityId);
|
|
36644
|
+
const reportedSearchProviders = this.searchProvidersCapable();
|
|
36587
36645
|
const report = {
|
|
36588
36646
|
applied_version: version,
|
|
36589
36647
|
paths: checkConfiguredPaths(skillsJson),
|
|
@@ -36598,6 +36656,13 @@ var BridgeAgent = class _BridgeAgent {
|
|
|
36598
36656
|
// to the backend. The backend needs it to generate a truthful tool
|
|
36599
36657
|
// paragraph, so it rides along here.
|
|
36600
36658
|
has_search_key: local?.has_search_key ?? false,
|
|
36659
|
+
// L-2 (spec §7.9): only the runtime knows whether an extract-capable provider key is readable; never derived
|
|
36660
|
+
// from has_search_key. An older runtime omits it: false.
|
|
36661
|
+
// Final review I-2 (controller ruling): AND the current search_providers_v2, so the web never shows extract while
|
|
36662
|
+
// the bridge withholds web_extract from the runtime.
|
|
36663
|
+
has_extract_provider: (local?.has_extract_provider ?? false) && reportedSearchProviders,
|
|
36664
|
+
// D39: the owner-DM daily cap the runtime enforces, passed through when it reported a valid one; never the signed value.
|
|
36665
|
+
...local?.owner_dm_search_cap_effective !== void 0 ? { owner_dm_search_cap_effective: local.owner_dm_search_cap_effective } : {},
|
|
36601
36666
|
// Same idea, for the BYOK LLM key file: only the runtime can see
|
|
36602
36667
|
// whether one is configured in its own environment.
|
|
36603
36668
|
has_llm_key: local?.has_llm_key ?? false,
|
|
@@ -36637,7 +36702,11 @@ var BridgeAgent = class _BridgeAgent {
|
|
|
36637
36702
|
// Plan 4 Task 15 (OQ-5): sorted, unique enums; capability is the latest hello's class.
|
|
36638
36703
|
config_binding_mismatches: [...this.configBindingMismatches],
|
|
36639
36704
|
target_mismatches: ["database", "endpoint"].filter((k) => this.targetMismatches[k] > 0),
|
|
36640
|
-
capability: this.lastConsumerCapability
|
|
36705
|
+
capability: this.lastConsumerCapability,
|
|
36706
|
+
// D28 (§11.1 step 3): a separate key, never a value in the capability enum. False with no runtime attached.
|
|
36707
|
+
search_providers_v2: reportedSearchProviders,
|
|
36708
|
+
// D19 item 4: content-free; the signed provider is one the attached runtime(s) cannot honour.
|
|
36709
|
+
search_provider_unsupported: this.searchProviderUnsupported(reportedSearchProviders)
|
|
36641
36710
|
}
|
|
36642
36711
|
};
|
|
36643
36712
|
const reportedVerifyFailures = this.ownerVerifyFailures;
|
|
@@ -36658,6 +36727,7 @@ var BridgeAgent = class _BridgeAgent {
|
|
|
36658
36727
|
return;
|
|
36659
36728
|
}
|
|
36660
36729
|
this.ownerVerifyFailures = Math.max(0, this.ownerVerifyFailures - reportedVerifyFailures);
|
|
36730
|
+
this.lastReportedSearchProviders = reportedSearchProviders;
|
|
36661
36731
|
if (version) writeServiceLog2("info", "runtime_config.applied", { version });
|
|
36662
36732
|
else writeServiceLog2("info", "runtime_config.legacy_memory_reported");
|
|
36663
36733
|
}
|
|
@@ -36727,6 +36797,7 @@ var BridgeAgent = class _BridgeAgent {
|
|
|
36727
36797
|
this.startedAt = Date.now();
|
|
36728
36798
|
await this.restoreRuntimeInbox();
|
|
36729
36799
|
await this.loadToolGrantState();
|
|
36800
|
+
this.recomputeManifestPolicy(false);
|
|
36730
36801
|
try {
|
|
36731
36802
|
this.store.pruneRoomSearchCap(utcDayOf(Date.now()));
|
|
36732
36803
|
} catch (e) {
|
|
@@ -42518,6 +42589,115 @@ ${channelId}`;
|
|
|
42518
42589
|
manifestConsumerAttached() {
|
|
42519
42590
|
return this.manifestConsumer !== null;
|
|
42520
42591
|
}
|
|
42592
|
+
/**
|
|
42593
|
+
* D28 (B-4): every socket that said hello, with whether its latest hello declared search_providers_v2 on the
|
|
42594
|
+
* manifest_grants_v1 predicate (ws-server.ts). The agent value is the AND over all of them, absent (false) with
|
|
42595
|
+
* none: unlike manifestConsumer, never newest wins. The per-delivery filter judges each socket by its own hello.
|
|
42596
|
+
*/
|
|
42597
|
+
searchProvidersSockets = /* @__PURE__ */ new Map();
|
|
42598
|
+
/**
|
|
42599
|
+
* The D28 value the files were last recomputed for; null until the first socket observation, so that observation
|
|
42600
|
+
* always recomputes (final review I-1), even when it is non-capable.
|
|
42601
|
+
*/
|
|
42602
|
+
searchProvidersState = null;
|
|
42603
|
+
searchCapabilityRefetchTimer = null;
|
|
42604
|
+
/** When the current burst's first change happened (valid while the timer is set). */
|
|
42605
|
+
searchCapabilityBurstStart = 0;
|
|
42606
|
+
/** The D28 value in the last report posted (null until one is); a change needs a refetch to reach the web. */
|
|
42607
|
+
lastReportedSearchProviders = null;
|
|
42608
|
+
noteSearchProvidersSocket(token, declared) {
|
|
42609
|
+
this.searchProvidersSockets.set(token, declared);
|
|
42610
|
+
this.onSearchProvidersMaybeChanged();
|
|
42611
|
+
}
|
|
42612
|
+
clearSearchProvidersSocket(token) {
|
|
42613
|
+
if (this.searchProvidersSockets.delete(token)) this.onSearchProvidersMaybeChanged();
|
|
42614
|
+
}
|
|
42615
|
+
/** D28: every attached runtime socket declared search_providers_v2; no socket attached means absent (false). */
|
|
42616
|
+
searchProvidersCapable() {
|
|
42617
|
+
if (this.searchProvidersSockets.size === 0) return false;
|
|
42618
|
+
for (const v of this.searchProvidersSockets.values()) if (!v) return false;
|
|
42619
|
+
return true;
|
|
42620
|
+
}
|
|
42621
|
+
/**
|
|
42622
|
+
* D19 item 3 (B-6): the search provider signed in the stored verified manifest in force (a pin loaded and a good
|
|
42623
|
+
* manifest), never the server payload; undefined with no pin, no manifest, or no signed search block. The
|
|
42624
|
+
* per-delivery filter (consumer-grants.ts withSearchCapability) and the S-28 report reason both read this.
|
|
42625
|
+
*/
|
|
42626
|
+
signedSearchProvider() {
|
|
42627
|
+
const pin = this.cfg.ownerGrantPin?.pin ?? null;
|
|
42628
|
+
return pin !== null ? this.toolGrantManifest?.config.search?.provider : void 0;
|
|
42629
|
+
}
|
|
42630
|
+
/** D19 item 4, D34: the in-force signed provider is not brave, and the attached runtime(s) lack the capability. */
|
|
42631
|
+
searchProviderUnsupported(capable) {
|
|
42632
|
+
if (capable) return false;
|
|
42633
|
+
const provider = this.signedSearchProvider();
|
|
42634
|
+
return provider !== void 0 && provider !== "brave";
|
|
42635
|
+
}
|
|
42636
|
+
/**
|
|
42637
|
+
* D35 (P-4): on a capability change, rewrite manifest-policy.json at once from the stored manifest, the kill
|
|
42638
|
+
* switches and the new capability, then a debounced refetch (one per burst) only when needed.
|
|
42639
|
+
*/
|
|
42640
|
+
onSearchProvidersMaybeChanged() {
|
|
42641
|
+
const capable = this.searchProvidersCapable();
|
|
42642
|
+
const prev = this.searchProvidersState;
|
|
42643
|
+
if (capable === prev) return;
|
|
42644
|
+
this.searchProvidersState = capable;
|
|
42645
|
+
this.recomputeManifestPolicy(capable);
|
|
42646
|
+
if (this.closingDown) return;
|
|
42647
|
+
if (prev === null && !capable) return;
|
|
42648
|
+
const now = Date.now();
|
|
42649
|
+
if (this.searchCapabilityRefetchTimer !== null) clearTimeout(this.searchCapabilityRefetchTimer);
|
|
42650
|
+
else this.searchCapabilityBurstStart = now;
|
|
42651
|
+
const delay = Math.max(0, Math.min(this.searchCapabilityRefetchDebounceMs, this.searchCapabilityBurstStart + this.searchCapabilityRefetchMaxWaitMs - now));
|
|
42652
|
+
this.searchCapabilityRefetchTimer = setTimeout(() => {
|
|
42653
|
+
this.searchCapabilityRefetchTimer = null;
|
|
42654
|
+
if (this.searchCapabilityRefetchNeeded()) void this.refetchRuntimeConfig();
|
|
42655
|
+
}, delay);
|
|
42656
|
+
this.searchCapabilityRefetchTimer.unref?.();
|
|
42657
|
+
}
|
|
42658
|
+
/**
|
|
42659
|
+
* A refetch re-renders skills.json (owner_dm_search_cap counts web_extract only with the capability, D39) and posts
|
|
42660
|
+
* the report. Needed under a manifest in force, or when the reported value is stale. Never before the boot fetch.
|
|
42661
|
+
*/
|
|
42662
|
+
searchCapabilityRefetchNeeded() {
|
|
42663
|
+
if (!this.bootConfigSettled || this.closingDown) return false;
|
|
42664
|
+
const inForce = (this.cfg.ownerGrantPin?.pin ?? null) !== null && this.toolGrantManifest !== null;
|
|
42665
|
+
return inForce || this.lastReportedSearchProviders !== this.searchProvidersCapable();
|
|
42666
|
+
}
|
|
42667
|
+
/**
|
|
42668
|
+
* D35: the local recompute. Widening (web_extract back in) only while the binding for the in-force manifest stands,
|
|
42669
|
+
* so a policy is never written ahead of its skills.json. Narrowing never waits: with no binding in force, a file that
|
|
42670
|
+
* lists web_extract loses it at once, so an older runtime never reads it as unreadable (which drops every grant).
|
|
42671
|
+
* Content-free; a write failure is logged and the debounced refetch tries again.
|
|
42672
|
+
*/
|
|
42673
|
+
recomputeManifestPolicy(capable) {
|
|
42674
|
+
const pin = this.cfg.ownerGrantPin?.pin ?? null;
|
|
42675
|
+
const manifest = pin !== null ? this.toolGrantManifest : null;
|
|
42676
|
+
try {
|
|
42677
|
+
if (manifest !== null && this.manifestBinding !== null && this.manifestBinding.counter === manifest.counter) {
|
|
42678
|
+
this.grantWriters.writeManifestPolicy(this.home, this.cfg.identityId, manifestPolicyFor(manifest, { ownerDm: this.ownerDmManifestGrants, room: this.roomManifestGrants }, capable));
|
|
42679
|
+
return;
|
|
42680
|
+
}
|
|
42681
|
+
if (capable) return;
|
|
42682
|
+
const path = join17(runtimeConfigDir(this.home, this.cfg.identityId), MANIFEST_POLICY_FILE);
|
|
42683
|
+
if (!existsSync5(path)) return;
|
|
42684
|
+
let doc;
|
|
42685
|
+
try {
|
|
42686
|
+
doc = JSON.parse(readFileSync10(path, "utf8"));
|
|
42687
|
+
} catch {
|
|
42688
|
+
return;
|
|
42689
|
+
}
|
|
42690
|
+
const tools = doc?.manifest_tools;
|
|
42691
|
+
if (!Array.isArray(tools) || !tools.includes("web_extract")) return;
|
|
42692
|
+
this.grantWriters.writeManifestPolicy(this.home, this.cfg.identityId, {
|
|
42693
|
+
v: 1,
|
|
42694
|
+
manifest_tools: tools.filter((t) => typeof t === "string" && t !== "web_extract"),
|
|
42695
|
+
secret_path_mode: "enforce"
|
|
42696
|
+
});
|
|
42697
|
+
} catch (e) {
|
|
42698
|
+
writeServiceLog2("warn", "authz.search_capability", { state: ServiceLogEnum.of("policy_write_failed", ["policy_write_failed"]) }, e);
|
|
42699
|
+
}
|
|
42700
|
+
}
|
|
42521
42701
|
noteConsumerCapability(state) {
|
|
42522
42702
|
this.lastConsumerCapability = state;
|
|
42523
42703
|
if (state !== "ok") {
|
|
@@ -42709,6 +42889,8 @@ ${channelId}`;
|
|
|
42709
42889
|
this.supersedeRecheckTimers.clear();
|
|
42710
42890
|
if (this.redeliveryTimer !== null) clearTimeout(this.redeliveryTimer);
|
|
42711
42891
|
this.redeliveryTimer = null;
|
|
42892
|
+
if (this.searchCapabilityRefetchTimer !== null) clearTimeout(this.searchCapabilityRefetchTimer);
|
|
42893
|
+
this.searchCapabilityRefetchTimer = null;
|
|
42712
42894
|
this.pingHandle?.close();
|
|
42713
42895
|
if (this.safetyTimer) clearInterval(this.safetyTimer);
|
|
42714
42896
|
if (this.runtimeConfigTimer) clearInterval(this.runtimeConfigTimer);
|
|
@@ -42818,11 +43000,33 @@ ${channelId}`;
|
|
|
42818
43000
|
};
|
|
42819
43001
|
|
|
42820
43002
|
// src/config.ts
|
|
42821
|
-
import { readFileSync as
|
|
43003
|
+
import { readFileSync as readFileSync11, statSync as statSync5 } from "node:fs";
|
|
42822
43004
|
import { dirname as dirname6, join as join18 } from "node:path";
|
|
42823
43005
|
var REQUIRED = ["api_key", "identity_id", "device_id", "signing_private_key", "enclave_id", "api_base"];
|
|
43006
|
+
var CONFIG_ERROR_MESSAGE = {
|
|
43007
|
+
credential_not_found: "credential file not found",
|
|
43008
|
+
credential_unreadable: "credential file could not be read",
|
|
43009
|
+
credential_not_json: "credential file is not valid JSON",
|
|
43010
|
+
credential_missing_field: "credential file missing required field",
|
|
43011
|
+
credential_voice_invalid: "credential file voice config invalid"
|
|
43012
|
+
};
|
|
43013
|
+
var ConfigError = class extends Error {
|
|
43014
|
+
constructor(reason, field, source) {
|
|
43015
|
+
super(CONFIG_ERROR_MESSAGE[reason] + (field ? `: ${field}` : ""));
|
|
43016
|
+
this.reason = reason;
|
|
43017
|
+
this.field = field;
|
|
43018
|
+
this.source = source;
|
|
43019
|
+
this.name = "ConfigError";
|
|
43020
|
+
}
|
|
43021
|
+
};
|
|
42824
43022
|
function loadConfig(opts) {
|
|
42825
|
-
|
|
43023
|
+
let raw;
|
|
43024
|
+
try {
|
|
43025
|
+
raw = readFileSync11(opts.credentialPath, "utf8");
|
|
43026
|
+
} catch (e) {
|
|
43027
|
+
const code = e?.code;
|
|
43028
|
+
throw new ConfigError(code === "ENOENT" ? "credential_not_found" : "credential_unreadable");
|
|
43029
|
+
}
|
|
42826
43030
|
try {
|
|
42827
43031
|
const mode = statSync5(opts.credentialPath).mode & 511;
|
|
42828
43032
|
if (mode & 63) writeServiceLog2("warn", "config.credential_permissions_unsafe");
|
|
@@ -42832,9 +43036,9 @@ function loadConfig(opts) {
|
|
|
42832
43036
|
try {
|
|
42833
43037
|
json2 = JSON.parse(raw);
|
|
42834
43038
|
} catch {
|
|
42835
|
-
throw new
|
|
43039
|
+
throw new ConfigError("credential_not_json");
|
|
42836
43040
|
}
|
|
42837
|
-
for (const k of REQUIRED) if (typeof json2[k] !== "string" || !json2[k]) throw new
|
|
43041
|
+
for (const k of REQUIRED) if (typeof json2[k] !== "string" || !json2[k]) throw new ConfigError("credential_missing_field", k);
|
|
42838
43042
|
const storePath = opts.storePath ?? join18(dirname6(opts.credentialPath), "bridge-state.db");
|
|
42839
43043
|
const ownerGrantPin = loadOwnerGrantPin({ credentialPath: opts.credentialPath, credentialJson: json2 });
|
|
42840
43044
|
writeServiceLog2(ownerGrantPin.state === "ok" || ownerGrantPin.state === "no_pin" ? "info" : "warn", "authz.pin", {
|
|
@@ -42872,8 +43076,14 @@ function loadConfig(opts) {
|
|
|
42872
43076
|
let voice;
|
|
42873
43077
|
try {
|
|
42874
43078
|
voice = parseVoiceConfig(json2.voice, process.env);
|
|
42875
|
-
} catch
|
|
42876
|
-
|
|
43079
|
+
} catch {
|
|
43080
|
+
let source = "env";
|
|
43081
|
+
try {
|
|
43082
|
+
parseVoiceConfig(json2.voice, {});
|
|
43083
|
+
} catch {
|
|
43084
|
+
source = "file";
|
|
43085
|
+
}
|
|
43086
|
+
throw new ConfigError("credential_voice_invalid", void 0, source);
|
|
42877
43087
|
}
|
|
42878
43088
|
const modelSource = json2.memory?.model_source === "import_only" ? "import_only" : "download";
|
|
42879
43089
|
const enabledRaw = json2.memory?.enabled;
|
|
@@ -42942,11 +43152,11 @@ import { z as z3 } from "zod";
|
|
|
42942
43152
|
|
|
42943
43153
|
// src/auth.ts
|
|
42944
43154
|
import { randomBytes as randomBytes11, timingSafeEqual as timingSafeEqual2 } from "node:crypto";
|
|
42945
|
-
import { existsSync as existsSync6, readFileSync as
|
|
43155
|
+
import { existsSync as existsSync6, readFileSync as readFileSync12, writeFileSync as writeFileSync2 } from "node:fs";
|
|
42946
43156
|
function resolveToken(opts) {
|
|
42947
43157
|
if (opts.token) return opts.token;
|
|
42948
43158
|
const f = opts.tokenFile;
|
|
42949
|
-
if (f && existsSync6(f)) return
|
|
43159
|
+
if (f && existsSync6(f)) return readFileSync12(f, "utf8").trim();
|
|
42950
43160
|
const tok = randomBytes11(32).toString("base64url");
|
|
42951
43161
|
if (f) writeFileSync2(f, tok, { mode: 384 });
|
|
42952
43162
|
return tok;
|
|
@@ -43042,6 +43252,7 @@ function withConsumerGrants(m, perToolConsumer) {
|
|
|
43042
43252
|
return { ...m, authorization: Object.freeze({ ...rest, ...stripGrants ? { tool_grants: NO_GRANTS } : {} }) };
|
|
43043
43253
|
}
|
|
43044
43254
|
var MANIFEST_GRANTS_FEATURE = "manifest_grants_v1";
|
|
43255
|
+
var SEARCH_PROVIDERS_FEATURE = "search_providers_v2";
|
|
43045
43256
|
var CONSTANT_TOOL_NAMES = Object.freeze({
|
|
43046
43257
|
owner_dm: Object.freeze(["current_time", "web_search"]),
|
|
43047
43258
|
team_room: Object.freeze(["current_time"])
|
|
@@ -43064,6 +43275,15 @@ function withHarnessGrants(m, withheld) {
|
|
|
43064
43275
|
if (!a || a.tool_grants.length === 0) return m;
|
|
43065
43276
|
return { ...m, authorization: Object.freeze({ ...a, tool_grants: NO_GRANTS }) };
|
|
43066
43277
|
}
|
|
43278
|
+
function withSearchCapability(m, consumerHasFeature, signedProvider) {
|
|
43279
|
+
if (consumerHasFeature) return m;
|
|
43280
|
+
const a = m.authorization;
|
|
43281
|
+
if (!a) return m;
|
|
43282
|
+
const stripSearch = signedProvider !== void 0 && signedProvider !== "brave";
|
|
43283
|
+
const kept = a.tool_grants.filter((g) => g.tool !== "web_extract" && !(stripSearch && g.tool === "web_search"));
|
|
43284
|
+
if (kept.length === a.tool_grants.length) return m;
|
|
43285
|
+
return { ...m, authorization: Object.freeze({ ...a, tool_grants: kept.length === 0 ? NO_GRANTS : Object.freeze(kept) }) };
|
|
43286
|
+
}
|
|
43067
43287
|
|
|
43068
43288
|
// src/http-server.ts
|
|
43069
43289
|
function writeSse(res, msgs, onWriteError) {
|
|
@@ -43086,7 +43306,8 @@ data: ${JSON.stringify(msgs[i])}
|
|
|
43086
43306
|
}
|
|
43087
43307
|
function deliverSseBatch(res, batch, agent, onWriteError) {
|
|
43088
43308
|
const withheld = agent.harnessGrantsWithheld();
|
|
43089
|
-
const
|
|
43309
|
+
const provider = agent.signedSearchProvider();
|
|
43310
|
+
const left3 = writeSse(res, batch.map((m) => withSearchCapability(withManifestCapability(withHarnessGrants(withConsumerGrants(m, false), withheld), false), false, provider)), onWriteError);
|
|
43090
43311
|
const leftIds = new Set(left3.map((message) => message.message_id));
|
|
43091
43312
|
agent.ackRuntimeMessages(batch.filter((message) => !leftIds.has(message.message_id)).map((message) => message.message_id));
|
|
43092
43313
|
if (left3.length) agent.requeueFront(batch.filter((message) => leftIds.has(message.message_id)));
|
|
@@ -43357,7 +43578,10 @@ function createHttpServer(agent, opts) {
|
|
|
43357
43578
|
if (method === "GET" && path === "/v1/messages") {
|
|
43358
43579
|
const channel = url.searchParams.get("channel_id") ?? void 0;
|
|
43359
43580
|
const wait = Math.max(0, Math.min(50, Number(url.searchParams.get("wait") ?? 0)));
|
|
43360
|
-
|
|
43581
|
+
const drained = await agent.drain(channel, wait);
|
|
43582
|
+
const withheld = agent.harnessGrantsWithheld();
|
|
43583
|
+
const provider = agent.signedSearchProvider();
|
|
43584
|
+
return json(res, 200, drained.map((m) => withSearchCapability(withManifestCapability(withHarnessGrants(withConsumerGrants(m, false), withheld), false), false, provider)));
|
|
43361
43585
|
}
|
|
43362
43586
|
if (method === "GET" && path === "/v1/events") {
|
|
43363
43587
|
const channel = url.searchParams.get("channel_id") ?? void 0;
|
|
@@ -43531,6 +43755,7 @@ function attachAgentWebSocket(server, agent, opts) {
|
|
|
43531
43755
|
consumer: "unknown",
|
|
43532
43756
|
perToolGrants: false,
|
|
43533
43757
|
manifestGrants: false,
|
|
43758
|
+
searchProviders: false,
|
|
43534
43759
|
// helloWaitMs 0 keeps today's synchronous initial flush exactly (the opt-out the existing tests use).
|
|
43535
43760
|
deliveryOpen: helloWaitMs <= 0,
|
|
43536
43761
|
deliveryToken: /* @__PURE__ */ new Map(),
|
|
@@ -43584,9 +43809,13 @@ function attachAgentWebSocket(server, agent, opts) {
|
|
|
43584
43809
|
continue;
|
|
43585
43810
|
}
|
|
43586
43811
|
const out = withConsumerNotes(
|
|
43587
|
-
|
|
43588
|
-
|
|
43589
|
-
|
|
43812
|
+
withSearchCapability(
|
|
43813
|
+
withManifestCapability(
|
|
43814
|
+
withHarnessGrants(withConsumerGrants(batch[i], session.perToolGrants), !session.perToolGrants && agent.harnessGrantsWithheld()),
|
|
43815
|
+
session.manifestGrants
|
|
43816
|
+
),
|
|
43817
|
+
session.searchProviders,
|
|
43818
|
+
agent.signedSearchProvider()
|
|
43590
43819
|
),
|
|
43591
43820
|
batch[i],
|
|
43592
43821
|
session,
|
|
@@ -43622,6 +43851,7 @@ function attachAgentWebSocket(server, agent, opts) {
|
|
|
43622
43851
|
const cleanup = () => {
|
|
43623
43852
|
if (cleaned) return;
|
|
43624
43853
|
cleaned = true;
|
|
43854
|
+
session.closed = true;
|
|
43625
43855
|
if (helloTimer) clearTimeout(helloTimer);
|
|
43626
43856
|
agent.noteSessionClose();
|
|
43627
43857
|
unsubscribe();
|
|
@@ -43642,6 +43872,7 @@ function attachAgentWebSocket(server, agent, opts) {
|
|
|
43642
43872
|
if (session.harness) agent.clearHarnessReport(session.harness);
|
|
43643
43873
|
if (session.perToolGrants) agent.clearPerToolConsumer(session);
|
|
43644
43874
|
if (session.manifestGrants) agent.clearManifestConsumer(session);
|
|
43875
|
+
agent.clearSearchProvidersSocket(session);
|
|
43645
43876
|
};
|
|
43646
43877
|
current = { sock, cleanup, sweepStale };
|
|
43647
43878
|
sock.on("close", cleanup);
|
|
@@ -43717,6 +43948,7 @@ async function handleInbound(sock, agent, data, inflight, flush, session) {
|
|
|
43717
43948
|
return;
|
|
43718
43949
|
}
|
|
43719
43950
|
if (m.type === "hello") {
|
|
43951
|
+
if (session.closed) return;
|
|
43720
43952
|
const report = m.harness === void 0 ? null : parseHarnessReport(m.harness);
|
|
43721
43953
|
if (report) {
|
|
43722
43954
|
session.harness = report;
|
|
@@ -43740,6 +43972,8 @@ async function handleInbound(sock, agent, data, inflight, flush, session) {
|
|
|
43740
43972
|
agent.clearManifestConsumer(session);
|
|
43741
43973
|
}
|
|
43742
43974
|
agent.noteConsumerCapability(manifest ? "ok" : session.harnessClaimed ? "harness_consumer" : "missing");
|
|
43975
|
+
session.searchProviders = session.consumer === "runtime" && session.harnessClaimed !== true && perTool && Array.isArray(m.features) && m.features.includes(SEARCH_PROVIDERS_FEATURE);
|
|
43976
|
+
agent.noteSearchProvidersSocket(session, session.searchProviders);
|
|
43743
43977
|
if (session.consumer === "runtime" && session.harnessClaimed !== true && Array.isArray(m.features) && m.features.includes(MEMORY_NOTES_FEATURE)) {
|
|
43744
43978
|
agent.noteNotesConsumer();
|
|
43745
43979
|
}
|
|
@@ -43747,7 +43981,7 @@ async function handleInbound(sock, agent, data, inflight, flush, session) {
|
|
|
43747
43981
|
if (session.recallConsumer) agent.noteRecallConsumer();
|
|
43748
43982
|
session.unifiedConsumer = agent.unifiedFeatureOn() && session.consumer === "runtime" && session.harnessClaimed !== true && Array.isArray(m.features) && m.features.includes(MEMORY_UNIFIED_FEATURE);
|
|
43749
43983
|
if (session.unifiedConsumer) agent.noteUnifiedConsumer();
|
|
43750
|
-
sendJson(sock, { type: "hello", features: ["nack", MEMORY_NOTES_FEATURE, MEMORY_RECALL_FEATURE, ...agent.unifiedFeatureOn() ? [MEMORY_UNIFIED_FEATURE] : [], MANIFEST_GRANTS_FEATURE] });
|
|
43984
|
+
sendJson(sock, { type: "hello", features: ["nack", MEMORY_NOTES_FEATURE, MEMORY_RECALL_FEATURE, ...agent.unifiedFeatureOn() ? [MEMORY_UNIFIED_FEATURE] : [], MANIFEST_GRANTS_FEATURE, SEARCH_PROVIDERS_FEATURE] });
|
|
43751
43985
|
if (!session.deliveryOpen) {
|
|
43752
43986
|
session.deliveryOpen = true;
|
|
43753
43987
|
flush();
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export declare const DIST_BASE = "https://nodejs.org/dist/latest-v24.x";
|
|
1
2
|
export declare function parseShasums(text: string, arch: string): {
|
|
2
3
|
filename: string;
|
|
3
4
|
sha256: string;
|
|
@@ -14,8 +15,6 @@ export interface UpgradeDeps {
|
|
|
14
15
|
isTTY: boolean;
|
|
15
16
|
stderr: (s: string) => void;
|
|
16
17
|
ask: (q: string) => Promise<string>;
|
|
17
|
-
hasBrew: () => boolean;
|
|
18
|
-
runBrew: () => number;
|
|
19
18
|
fetchText: (url: string) => Promise<string>;
|
|
20
19
|
fetchBinary: (url: string, destPath: string) => Promise<void>;
|
|
21
20
|
fileSha256: (p: string) => string;
|