@agentvault/agentvault 0.23.30 → 0.23.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/channel.d.ts +77 -0
- package/dist/channel.d.ts.map +1 -1
- package/dist/cli.js +250 -10
- package/dist/cli.js.map +3 -3
- package/dist/doctor.d.ts +25 -0
- package/dist/doctor.d.ts.map +1 -1
- package/dist/index.js +188 -7
- package/dist/index.js.map +2 -2
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
package/dist/doctor.d.ts
CHANGED
|
@@ -36,6 +36,31 @@ export declare function checkPluginsAllow(config: any): CheckResult;
|
|
|
36
36
|
export declare function checkPlist(): CheckResult;
|
|
37
37
|
export declare function checkGatewayPort(): CheckResult;
|
|
38
38
|
export declare function checkAgentDataDirs(config: any): CheckResult;
|
|
39
|
+
/**
|
|
40
|
+
* State the key-material exposure plainly (#622).
|
|
41
|
+
*
|
|
42
|
+
* An agent with Work on runs as the SAME UID as the bridge, so it can read
|
|
43
|
+
* `agentvault.json` and every `mls-*.json` in its data dir — MLS group state
|
|
44
|
+
* including private keys. Measured on a live agent 2026-08-14, permission
|
|
45
|
+
* hygiene is already correct (700 on the directories, 600 on the files), which
|
|
46
|
+
* is exactly why no file mode addresses this and why encrypting those files
|
|
47
|
+
* under the device secret would be theatre: the secret sits in the same
|
|
48
|
+
* directory under the same uid.
|
|
49
|
+
*
|
|
50
|
+
* DECIDED 2026-08-31 (Chris): an agent with Work on is a TRUSTED principal, and
|
|
51
|
+
* the answer is cheap rotation rather than prevention. That is already what the
|
|
52
|
+
* product says — "keys are generated locally and never leave this machine",
|
|
53
|
+
* "only you and your agent hold the keys" — both true, with the agent holding
|
|
54
|
+
* the keys being the design. Unlike #547 there is no overstated claim here;
|
|
55
|
+
* what was missing was anywhere that told an OPERATOR so.
|
|
56
|
+
*
|
|
57
|
+
* ⚠️ Accepted-and-working reports `pass`, never a standing `warn`. A warning
|
|
58
|
+
* that is always on is noise, and noise is how a real signal gets ignored. The
|
|
59
|
+
* check earns its place by being able to genuinely fail: the decision assumes
|
|
60
|
+
* 600/700, and if that is not true then the reasoning behind accepting the
|
|
61
|
+
* exposure does not hold either.
|
|
62
|
+
*/
|
|
63
|
+
export declare function checkKeyMaterialExposure(config: any): CheckResult;
|
|
39
64
|
export declare function checkPm2Status(): CheckResult;
|
|
40
65
|
export declare function plistPath(): string;
|
|
41
66
|
export declare function parsePlist(plistFile: string): PlistInfo | null;
|
package/dist/doctor.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../src/doctor.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAiCH,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;AAE5D,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,WAAW,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,SAAS;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,IAAI,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,eAAe,CAAC;IAC9D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACvB;AAKD;;;;GAIG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,GAAG,IAAI,CAQpD;AAID,wBAAgB,sBAAsB,IAAI,WAAW,CAYpD;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAmB3D;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,GAAG,GAAG,WAAW,CAgB/D;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,GAAG,GAAG,WAAW,CAoB1D;AAED,wBAAgB,UAAU,IAAI,WAAW,CAyBxC;AAED,wBAAgB,gBAAgB,IAAI,WAAW,CAW9C;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,GAAG,GAAG,WAAW,CAsC3D;AAED,wBAAgB,cAAc,IAAI,WAAW,CAoB5C;AAID,wBAAgB,SAAS,IAAI,MAAM,CAGlC;AAED,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CA2B9D;AAED,wBAAgB,aAAa,IAAI,qBAAqB,CA2BrD;AAID,wBAAgB,aAAa,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAsEtD;AAID,wBAAsB,gBAAgB,CAAC,OAAO,CAAC,EAAE;IAAE,GAAG,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../src/doctor.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAiCH,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;AAE5D,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,WAAW,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,SAAS;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,IAAI,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,eAAe,CAAC;IAC9D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACvB;AAKD;;;;GAIG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,GAAG,IAAI,CAQpD;AAID,wBAAgB,sBAAsB,IAAI,WAAW,CAYpD;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAmB3D;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,GAAG,GAAG,WAAW,CAgB/D;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,GAAG,GAAG,WAAW,CAoB1D;AAED,wBAAgB,UAAU,IAAI,WAAW,CAyBxC;AAED,wBAAgB,gBAAgB,IAAI,WAAW,CAW9C;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,GAAG,GAAG,WAAW,CAsC3D;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,GAAG,GAAG,WAAW,CAuDjE;AAmBD,wBAAgB,cAAc,IAAI,WAAW,CAoB5C;AAID,wBAAgB,SAAS,IAAI,MAAM,CAGlC;AAED,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CA2B9D;AAED,wBAAgB,aAAa,IAAI,qBAAqB,CA2BrD;AAID,wBAAgB,aAAa,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAsEtD;AAID,wBAAsB,gBAAgB,CAAC,OAAO,CAAC,EAAE;IAAE,GAAG,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA4IjF"}
|
package/dist/index.js
CHANGED
|
@@ -63250,11 +63250,11 @@ var init_mls_kp_pool = __esm({
|
|
|
63250
63250
|
|
|
63251
63251
|
// src/client-version.ts
|
|
63252
63252
|
function ownIdentity() {
|
|
63253
|
-
const v2 = true ? "0.23.
|
|
63253
|
+
const v2 = true ? "0.23.31" : FALLBACK;
|
|
63254
63254
|
return `${PACKAGE}@${v2}`;
|
|
63255
63255
|
}
|
|
63256
63256
|
function buildSha() {
|
|
63257
|
-
const raw = true ? "
|
|
63257
|
+
const raw = true ? "3e1243a-dirty" : "";
|
|
63258
63258
|
const cleaned = (raw ?? "").trim();
|
|
63259
63259
|
return !cleaned || cleaned === "unknown" ? null : cleaned;
|
|
63260
63260
|
}
|
|
@@ -65016,6 +65016,118 @@ var init_channel = __esm({
|
|
|
65016
65016
|
* Encrypt and send a message to ALL owner devices (fanout).
|
|
65017
65017
|
* Each session gets the same plaintext encrypted independently.
|
|
65018
65018
|
*/
|
|
65019
|
+
/**
|
|
65020
|
+
* #952 — sends held because MLS is expected for this conversation but the
|
|
65021
|
+
* group is not joined yet.
|
|
65022
|
+
*
|
|
65023
|
+
* ⚠️ IN MEMORY ONLY. These entries hold PLAINTEXT. `_persisted` is written to
|
|
65024
|
+
* disk, and the product's entire premise is that plaintext does not rest
|
|
65025
|
+
* where it does not have to; a crash losing a held greeting is the correct
|
|
65026
|
+
* trade. This is why the existing `outboundQueue` could not be reused — that
|
|
65027
|
+
* one is persisted, and holds already-DR-encrypted bytes, which is exactly
|
|
65028
|
+
* the encryption we are trying not to perform yet.
|
|
65029
|
+
*/
|
|
65030
|
+
_mlsHold = /* @__PURE__ */ new Map();
|
|
65031
|
+
/** How long a send waits for the Welcome. The two measured bursts were each
|
|
65032
|
+
* confined to a single minute, with the group already existing server-side
|
|
65033
|
+
* in the same second, so this is generous rather than tight. */
|
|
65034
|
+
static MLS_HOLD_MS = 3e4;
|
|
65035
|
+
/** Bound on held sends per group — see `_holdForMls`. */
|
|
65036
|
+
static MLS_HOLD_MAX = 50;
|
|
65037
|
+
/**
|
|
65038
|
+
* Should this 1:1 send wait for MLS rather than go out over Double Ratchet?
|
|
65039
|
+
*
|
|
65040
|
+
* The discriminator exists at the moment of the bug: `_sessionGroupIds`
|
|
65041
|
+
* already carries the conversation's group id — the server told us the group
|
|
65042
|
+
* exists — while `_mlsGroups` has no usable manager for it. Group expected,
|
|
65043
|
+
* group not ready.
|
|
65044
|
+
*
|
|
65045
|
+
* The negative case is load-bearing. A conversation with NO group id is
|
|
65046
|
+
* legitimately Double Ratchet, and holding it would break owner-to-agent
|
|
65047
|
+
* messaging outright, which is far worse than the bug this fixes. "Present"
|
|
65048
|
+
* is also not "ready": a manager parked at epoch 0 has not joined (#1092),
|
|
65049
|
+
* and reading presence as readiness is how #991 shipped.
|
|
65050
|
+
*/
|
|
65051
|
+
_shouldHoldForMls(convGroupId, mlsGroup, mlsGroupId) {
|
|
65052
|
+
if (!convGroupId) return false;
|
|
65053
|
+
const ready = Boolean(mlsGroup?.isInitialized) && Boolean(mlsGroupId) && Number(mlsGroup?.epoch) > 0;
|
|
65054
|
+
return !ready;
|
|
65055
|
+
}
|
|
65056
|
+
/** Park a send until the Welcome lands, or until the hold expires. */
|
|
65057
|
+
_holdForMls(convGroupId, convId, plaintext, options) {
|
|
65058
|
+
let entry = this._mlsHold.get(convGroupId);
|
|
65059
|
+
if (!entry) {
|
|
65060
|
+
const timer = setTimeout(() => {
|
|
65061
|
+
void this._releaseHeldOverDr(convGroupId);
|
|
65062
|
+
}, _SecureChannel.MLS_HOLD_MS);
|
|
65063
|
+
timer.unref?.();
|
|
65064
|
+
entry = { items: [], timer };
|
|
65065
|
+
this._mlsHold.set(convGroupId, entry);
|
|
65066
|
+
}
|
|
65067
|
+
if (entry.items.length >= _SecureChannel.MLS_HOLD_MAX) {
|
|
65068
|
+
entry.items.shift();
|
|
65069
|
+
console.warn(
|
|
65070
|
+
`[SecureChannel] MLS hold full for group ${convGroupId.slice(0, 8)} \u2014 dropped the oldest held message`
|
|
65071
|
+
);
|
|
65072
|
+
}
|
|
65073
|
+
entry.items.push({ convId, plaintext, options });
|
|
65074
|
+
console.log(
|
|
65075
|
+
`[SecureChannel] Holding send for group ${convGroupId.slice(0, 8)} until its Welcome lands (held=${entry.items.length})`
|
|
65076
|
+
);
|
|
65077
|
+
}
|
|
65078
|
+
_takeHold(convGroupId) {
|
|
65079
|
+
const entry = this._mlsHold.get(convGroupId);
|
|
65080
|
+
if (!entry) return null;
|
|
65081
|
+
clearTimeout(entry.timer);
|
|
65082
|
+
this._mlsHold.delete(convGroupId);
|
|
65083
|
+
return entry;
|
|
65084
|
+
}
|
|
65085
|
+
/** Re-drive one held send through the normal path, now that MLS is ready. */
|
|
65086
|
+
async _resendHeld(item) {
|
|
65087
|
+
await this.send(item.plaintext, {
|
|
65088
|
+
...item.options ?? {},
|
|
65089
|
+
conversationId: item.convId,
|
|
65090
|
+
bypassMlsHold: true
|
|
65091
|
+
});
|
|
65092
|
+
}
|
|
65093
|
+
/** The Welcome landed — release everything held for this group, in order. */
|
|
65094
|
+
async _flushMlsHold(convGroupId) {
|
|
65095
|
+
const entry = this._takeHold(convGroupId);
|
|
65096
|
+
if (!entry) return;
|
|
65097
|
+
console.log(
|
|
65098
|
+
`[SecureChannel] Welcome landed for group ${convGroupId.slice(0, 8)} \u2014 releasing ${entry.items.length} held message(s) over MLS`
|
|
65099
|
+
);
|
|
65100
|
+
for (const item of entry.items) {
|
|
65101
|
+
try {
|
|
65102
|
+
await this._resendHeld(item);
|
|
65103
|
+
} catch (err) {
|
|
65104
|
+
console.warn("[SecureChannel] Held message failed to send after Welcome:", err);
|
|
65105
|
+
}
|
|
65106
|
+
}
|
|
65107
|
+
}
|
|
65108
|
+
/**
|
|
65109
|
+
* The hold expired. Send over Double Ratchet exactly as before.
|
|
65110
|
+
*
|
|
65111
|
+
* This is deliberately NOT a drop. #853 and #831 are Welcomes that never
|
|
65112
|
+
* arrive; under those an unbounded hold turns a lost minute into an agent
|
|
65113
|
+
* that never speaks again. The hold removes the race — it must not invent a
|
|
65114
|
+
* new failure mode. The warning is the part that is new: today the loss is
|
|
65115
|
+
* silent.
|
|
65116
|
+
*/
|
|
65117
|
+
async _releaseHeldOverDr(convGroupId) {
|
|
65118
|
+
const entry = this._takeHold(convGroupId);
|
|
65119
|
+
if (!entry) return;
|
|
65120
|
+
console.warn(
|
|
65121
|
+
`[SecureChannel] No Welcome for group ${convGroupId.slice(0, 8)} within ${_SecureChannel.MLS_HOLD_MS}ms \u2014 releasing ${entry.items.length} message(s) over Double Ratchet. The recipient may not hold a ratchet with this device, in which case they are undeliverable (#952).`
|
|
65122
|
+
);
|
|
65123
|
+
for (const item of entry.items) {
|
|
65124
|
+
try {
|
|
65125
|
+
await this._resendHeld(item);
|
|
65126
|
+
} catch (err) {
|
|
65127
|
+
console.warn("[SecureChannel] Held message failed on Double Ratchet release:", err);
|
|
65128
|
+
}
|
|
65129
|
+
}
|
|
65130
|
+
}
|
|
65019
65131
|
async send(plaintext, options) {
|
|
65020
65132
|
if (isAbstention(plaintext)) return;
|
|
65021
65133
|
if (this._state === "error" || this._state === "idle") {
|
|
@@ -65177,6 +65289,10 @@ var init_channel = __esm({
|
|
|
65177
65289
|
);
|
|
65178
65290
|
addressedMlsGroupIds.push(mlsGroupId);
|
|
65179
65291
|
if (convGroupId) sentSharedGroupIds.add(convGroupId);
|
|
65292
|
+
} else if (!options?.bypassMlsHold && this._shouldHoldForMls(convGroupId, mlsGroup, mlsGroupId)) {
|
|
65293
|
+
this._holdForMls(convGroupId, convId, plaintext, options);
|
|
65294
|
+
sentCount++;
|
|
65295
|
+
continue;
|
|
65180
65296
|
} else {
|
|
65181
65297
|
const encrypted = session.ratchet.encrypt(plaintext);
|
|
65182
65298
|
const transport = encryptedMessageToTransport(encrypted);
|
|
@@ -65346,7 +65462,7 @@ var init_channel = __esm({
|
|
|
65346
65462
|
*/
|
|
65347
65463
|
sendActivitySpan(spanData) {
|
|
65348
65464
|
if (!this._ws || this._ws.readyState !== WebSocket.OPEN) return;
|
|
65349
|
-
const pluginVersion = true ? "0.23.
|
|
65465
|
+
const pluginVersion = true ? "0.23.31" : "0.0.0-dev";
|
|
65350
65466
|
const agentName = this.config.agentName ?? "Agent";
|
|
65351
65467
|
const resource = {
|
|
65352
65468
|
"service.name": "agentvault-agent",
|
|
@@ -67284,7 +67400,7 @@ var init_channel = __esm({
|
|
|
67284
67400
|
agentVersion: this.config.agentVersion ?? "0.0.0",
|
|
67285
67401
|
// __AV_VERSION__ is injected by esbuild from package.json at build time.
|
|
67286
67402
|
// Falls back to "0.0.0-dev" in non-bundled contexts (tests).
|
|
67287
|
-
pluginVersion: true ? "0.23.
|
|
67403
|
+
pluginVersion: true ? "0.23.31" : "0.0.0-dev"
|
|
67288
67404
|
});
|
|
67289
67405
|
this._telemetryReporter.startAutoFlush(3e4);
|
|
67290
67406
|
}
|
|
@@ -67608,7 +67724,7 @@ var init_channel = __esm({
|
|
|
67608
67724
|
agentVersion: this.config.agentVersion ?? "0.0.0",
|
|
67609
67725
|
// __AV_VERSION__ is injected by esbuild from package.json at build time.
|
|
67610
67726
|
// Falls back to "0.0.0-dev" in non-bundled contexts (tests).
|
|
67611
|
-
pluginVersion: true ? "0.23.
|
|
67727
|
+
pluginVersion: true ? "0.23.31" : "0.0.0-dev"
|
|
67612
67728
|
});
|
|
67613
67729
|
this._telemetryReporter.startAutoFlush(3e4);
|
|
67614
67730
|
}
|
|
@@ -68024,11 +68140,13 @@ var init_channel = __esm({
|
|
|
68024
68140
|
let text = plaintext;
|
|
68025
68141
|
let messageType = "text";
|
|
68026
68142
|
let topicId = data.topic_id;
|
|
68143
|
+
let mlsAttachment = null;
|
|
68027
68144
|
try {
|
|
68028
68145
|
const parsed = JSON.parse(plaintext);
|
|
68029
68146
|
text = parsed.text ?? plaintext;
|
|
68030
68147
|
messageType = parsed.type ?? "text";
|
|
68031
68148
|
topicId = parsed.topicId ?? topicId;
|
|
68149
|
+
if (parsed.attachment) mlsAttachment = parsed.attachment;
|
|
68032
68150
|
} catch {
|
|
68033
68151
|
}
|
|
68034
68152
|
if (messageType === "history_catchup_request") {
|
|
@@ -68095,6 +68213,9 @@ var init_channel = __esm({
|
|
|
68095
68213
|
console.log(`[SecureChannel] Session ${convId.slice(0, 8)} activated via MLS message`);
|
|
68096
68214
|
}
|
|
68097
68215
|
}
|
|
68216
|
+
if (mlsAttachment) {
|
|
68217
|
+
text = await this._augmentWithAttachment(mlsAttachment, text);
|
|
68218
|
+
}
|
|
68098
68219
|
const metadata = {
|
|
68099
68220
|
messageId: data.message_id ?? `mls_${Date.now()}`,
|
|
68100
68221
|
conversationId: convId ?? this._primaryConversationId,
|
|
@@ -68344,6 +68465,65 @@ ${messageText}`;
|
|
|
68344
68465
|
* Download an encrypted attachment blob, decrypt it, verify integrity,
|
|
68345
68466
|
* and save the plaintext file to disk.
|
|
68346
68467
|
*/
|
|
68468
|
+
/** Where an attachment may be written — always directly inside `attachDir` (#1143).
|
|
68469
|
+
*
|
|
68470
|
+
* The filename arrives INSIDE the envelope and is chosen by whoever sent the
|
|
68471
|
+
* message. `join(attachDir, info.filename)` resolves `..` normally, so
|
|
68472
|
+
* `../../agentvault.json` wrote over the device credential store. Only the
|
|
68473
|
+
* final path component is honoured, and anything that is not a usable name
|
|
68474
|
+
* falls back to a fixed one.
|
|
68475
|
+
*/
|
|
68476
|
+
_safeAttachmentPath(attachDir, filename) {
|
|
68477
|
+
const raw = String(filename ?? "");
|
|
68478
|
+
const last = raw.split(/[\\/]/).pop() ?? "";
|
|
68479
|
+
const cleaned = last.replace(/\0/g, "").trim();
|
|
68480
|
+
const safe = cleaned === "" || cleaned === "." || cleaned === ".." ? "attachment.bin" : cleaned;
|
|
68481
|
+
return join4(attachDir, safe);
|
|
68482
|
+
}
|
|
68483
|
+
/** Download an attachment and fold it into the text the agent sees (#1139).
|
|
68484
|
+
*
|
|
68485
|
+
* Extracted so the MLS receive path can use it too. It used to live inline in
|
|
68486
|
+
* `_handleIncomingMessage`, which meant attachments worked on the Double
|
|
68487
|
+
* Ratchet path ONLY — and hermes drops DR by design (`secure_channel.py:594`,
|
|
68488
|
+
* "DR fallback is logged-and-dropped"), so no single transport could serve
|
|
68489
|
+
* both client families. That is why an attachment reached ohtani and never
|
|
68490
|
+
* reached smartcoder2 on 2026-08-31.
|
|
68491
|
+
*
|
|
68492
|
+
* Returns the text unchanged when there is no attachment or the fetch fails —
|
|
68493
|
+
* but a failure is LOGGED and announced in the text, never silent.
|
|
68494
|
+
*/
|
|
68495
|
+
async _augmentWithAttachment(attachmentInfo, baseText) {
|
|
68496
|
+
if (!attachmentInfo) return baseText;
|
|
68497
|
+
try {
|
|
68498
|
+
const { filePath, decrypted } = await this._downloadAndDecryptAttachment(attachmentInfo);
|
|
68499
|
+
const mime = String(attachmentInfo.mime ?? "application/octet-stream");
|
|
68500
|
+
const name = String(attachmentInfo.filename ?? "attachment");
|
|
68501
|
+
const textMimes = ["text/", "application/json", "application/xml", "application/csv"];
|
|
68502
|
+
if (textMimes.some((m2) => mime.startsWith(m2))) {
|
|
68503
|
+
const content = new TextDecoder().decode(decrypted);
|
|
68504
|
+
return `[Attachment: ${name} (${mime}) saved to ${filePath}]
|
|
68505
|
+
---
|
|
68506
|
+
${content}
|
|
68507
|
+
---
|
|
68508
|
+
|
|
68509
|
+
${baseText}`;
|
|
68510
|
+
}
|
|
68511
|
+
if (mime.startsWith("image/")) {
|
|
68512
|
+
return `[Image attachment: ${name} saved to ${filePath}]
|
|
68513
|
+
Use your Read tool to view this image file.
|
|
68514
|
+
|
|
68515
|
+
${baseText}`;
|
|
68516
|
+
}
|
|
68517
|
+
return `[Attachment: ${name} saved to ${filePath}]
|
|
68518
|
+
|
|
68519
|
+
${baseText}`;
|
|
68520
|
+
} catch (err) {
|
|
68521
|
+
console.error("[SecureChannel] Failed to download attachment:", err);
|
|
68522
|
+
return `[Attachment: ${String(attachmentInfo.filename ?? "file")} was sent but could not be retrieved: ${err}]
|
|
68523
|
+
|
|
68524
|
+
${baseText}`;
|
|
68525
|
+
}
|
|
68526
|
+
}
|
|
68347
68527
|
async _downloadAndDecryptAttachment(info) {
|
|
68348
68528
|
const attachDir = join4(this.config.dataDir, "attachments");
|
|
68349
68529
|
await mkdir3(attachDir, { recursive: true });
|
|
@@ -68363,7 +68543,7 @@ ${messageText}`;
|
|
|
68363
68543
|
const fileKey = base64ToBytes(info.fileKey);
|
|
68364
68544
|
const fileNonce = base64ToBytes(info.fileNonce);
|
|
68365
68545
|
const decrypted = decryptFile(encryptedData, fileKey, fileNonce);
|
|
68366
|
-
const filePath =
|
|
68546
|
+
const filePath = this._safeAttachmentPath(attachDir, info.filename);
|
|
68367
68547
|
await writeFile3(filePath, decrypted);
|
|
68368
68548
|
console.log(`[SecureChannel] Attachment saved: ${filePath} (${decrypted.length} bytes)`);
|
|
68369
68549
|
return { filePath, decrypted };
|
|
@@ -69784,6 +69964,7 @@ ${messageText}`;
|
|
|
69784
69964
|
await this._persistState();
|
|
69785
69965
|
}
|
|
69786
69966
|
console.log(`[SecureChannel] Joined shared 1:1 MLS group for group ${conversationGroupId.slice(0, 8)} via Welcome (epoch=${mgr.epoch})`);
|
|
69967
|
+
await this._flushMlsHold(conversationGroupId);
|
|
69787
69968
|
return;
|
|
69788
69969
|
}
|
|
69789
69970
|
if (conversationId) {
|
|
@@ -98347,7 +98528,7 @@ var init_index = __esm({
|
|
|
98347
98528
|
await init_skill_telemetry();
|
|
98348
98529
|
await init_policy_enforcer();
|
|
98349
98530
|
init_room_protocol();
|
|
98350
|
-
VERSION = true ? "0.23.
|
|
98531
|
+
VERSION = true ? "0.23.31" : "0.0.0-dev";
|
|
98351
98532
|
}
|
|
98352
98533
|
});
|
|
98353
98534
|
await init_index();
|