khala-cli 0.4.9 → 0.4.11
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/THIRD_PARTY_NOTICES.txt +28 -0
- package/dist/{chunk-DJM23SIU.mjs → chunk-3KHLSF4A.mjs} +7 -8
- package/dist/{chunk-PDZ5Z5ZE.mjs → chunk-4CENUF7M.mjs} +24 -9
- package/dist/{chunk-QKWEZXR3.mjs → chunk-4PUNVPC3.mjs} +12 -4
- package/dist/{chunk-WDOHRV3D.mjs → chunk-7FFHZ7EI.mjs} +14 -14
- package/dist/chunk-AQX2OYEF.mjs +19 -0
- package/dist/{chunk-ITFD3JZS.mjs → chunk-BVG7PJNZ.mjs} +424 -78
- package/dist/{chunk-NGSYOVQ4.mjs → chunk-CXVANPY5.mjs} +2 -2
- package/dist/chunk-CYINL2V3.mjs +20 -0
- package/dist/{chunk-KMLXOPBQ.mjs → chunk-E5QRD6W3.mjs} +4 -2
- package/dist/{chunk-FO5GXCST.mjs → chunk-FGHPOKXG.mjs} +5 -5
- package/dist/{chunk-FZEH2IXZ.mjs → chunk-GVZD4TLW.mjs} +180 -26
- package/dist/{chunk-V4F3SYKW.mjs → chunk-H2L5GQ5W.mjs} +25 -17
- package/dist/chunk-HZH74L5T.mjs +901 -0
- package/dist/{chunk-NPSQNCOY.mjs → chunk-JNSB3E6C.mjs} +13 -4
- package/dist/{chunk-7R3EIDGR.mjs → chunk-LDW4HOQW.mjs} +1 -1
- package/dist/chunk-LNKI3CZO.mjs +15 -0
- package/dist/{chunk-TEFOQXBK.mjs → chunk-MAIMFERG.mjs} +3 -3
- package/dist/chunk-MYIZQCRW.mjs +27 -0
- package/dist/{chunk-TANCKTDK.mjs → chunk-N6UYX3SY.mjs} +72 -5
- package/dist/{chunk-6BO3JSUP.mjs → chunk-NE4S7N3R.mjs} +2 -2
- package/dist/{chunk-BLCRVRIK.mjs → chunk-NJ5ICCVZ.mjs} +456 -87
- package/dist/{chunk-3R6HFSLL.mjs → chunk-NN5RHU6C.mjs} +92 -38
- package/dist/{chunk-NIX5F4LT.mjs → chunk-NOKTVPSY.mjs} +330 -66
- package/dist/{chunk-3KDOTR54.mjs → chunk-NUQSDSVT.mjs} +1 -1
- package/dist/{chunk-BEO7WIIM.mjs → chunk-OSVXLZKJ.mjs} +28 -8
- package/dist/{chunk-SSO5RFOX.mjs → chunk-R5NDTJRA.mjs} +1 -1
- package/dist/{chunk-K7V7X6WU.mjs → chunk-TVQ4MSEI.mjs} +21 -4
- package/dist/{chunk-BV45NK7K.mjs → chunk-VWWI4S5A.mjs} +100 -6
- package/dist/{chunk-UK5GJO4Y.mjs → chunk-WA5LB5GN.mjs} +6 -5
- package/dist/chunk-X3BAMHAR.mjs +14 -0
- package/dist/{chunk-PAJ4JJPV.mjs → chunk-YDEZFVF7.mjs} +43 -6
- package/dist/{chunk-GSZADJMW.mjs → chunk-YFX5NDSL.mjs} +338 -81
- package/dist/{chunk-ANSR4IAW.mjs → chunk-YIKJI26V.mjs} +2 -2
- package/dist/{chunk-T7IL7V7Q.mjs → chunk-YNUCRFM2.mjs} +13 -10
- package/dist/iterm2_send.py +56 -0
- package/dist/khala.mjs +11 -11
- package/dist/web/assets/local-DPcMk9Xz.css +1 -0
- package/dist/web/assets/local-SDOFp8OA.js +9 -0
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
- package/dist/chunk-47XNLAJG.mjs +0 -38
- package/dist/chunk-LBT47HLZ.mjs +0 -19
- package/dist/chunk-P3YIN7G7.mjs +0 -26
- package/dist/chunk-XBQDIKB5.mjs +0 -18
- package/dist/chunk-XUCWSYVS.mjs +0 -148
- package/dist/web/assets/local-CFzRHZpB.js +0 -9
- package/dist/web/assets/local-ciypUFoK.css +0 -1
|
@@ -5,10 +5,10 @@ import {
|
|
|
5
5
|
object,
|
|
6
6
|
readMatrixUserId,
|
|
7
7
|
version
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-JNSB3E6C.mjs";
|
|
9
9
|
import {
|
|
10
10
|
init_define_KHALA_BUNDLE
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-LDW4HOQW.mjs";
|
|
12
12
|
|
|
13
13
|
// ../contracts/src/m1/listening-mode.ts
|
|
14
14
|
init_define_KHALA_BUNDLE();
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { createRequire as __khalaCreateRequire } from 'node:module'; const require = __khalaCreateRequire(import.meta.url);
|
|
2
|
+
import {
|
|
3
|
+
resolveHarness,
|
|
4
|
+
resolveSession,
|
|
5
|
+
resolveSessionId
|
|
6
|
+
} from "./chunk-NE4S7N3R.mjs";
|
|
7
|
+
import "./chunk-BVG7PJNZ.mjs";
|
|
8
|
+
import "./chunk-4PUNVPC3.mjs";
|
|
9
|
+
import "./chunk-NOKTVPSY.mjs";
|
|
10
|
+
import "./chunk-CXVANPY5.mjs";
|
|
11
|
+
import "./chunk-N6UYX3SY.mjs";
|
|
12
|
+
import "./chunk-VWWI4S5A.mjs";
|
|
13
|
+
import "./chunk-JNSB3E6C.mjs";
|
|
14
|
+
import "./chunk-TVQ4MSEI.mjs";
|
|
15
|
+
import "./chunk-LDW4HOQW.mjs";
|
|
16
|
+
export {
|
|
17
|
+
resolveHarness,
|
|
18
|
+
resolveSession,
|
|
19
|
+
resolveSessionId
|
|
20
|
+
};
|
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
import { createRequire as __khalaCreateRequire } from 'node:module'; const require = __khalaCreateRequire(import.meta.url);
|
|
2
2
|
import {
|
|
3
3
|
cancelAttempt,
|
|
4
|
+
confirmWakeText,
|
|
4
5
|
failAttempt,
|
|
5
6
|
readWakeState,
|
|
6
7
|
recordAttempt,
|
|
7
8
|
resetWakeDriver,
|
|
8
9
|
settleAttempts,
|
|
9
10
|
takeWakeDisableNotices
|
|
10
|
-
} from "./chunk-
|
|
11
|
-
import "./chunk-
|
|
11
|
+
} from "./chunk-TVQ4MSEI.mjs";
|
|
12
|
+
import "./chunk-LDW4HOQW.mjs";
|
|
12
13
|
export {
|
|
13
14
|
cancelAttempt,
|
|
15
|
+
confirmWakeText,
|
|
14
16
|
failAttempt,
|
|
15
17
|
readWakeState,
|
|
16
18
|
recordAttempt,
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { createRequire as __khalaCreateRequire } from 'node:module'; const require = __khalaCreateRequire(import.meta.url);
|
|
2
2
|
import {
|
|
3
3
|
KHALA_AGENT_VERSION
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-YIKJI26V.mjs";
|
|
5
5
|
import {
|
|
6
6
|
bundle
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-NUQSDSVT.mjs";
|
|
8
8
|
import {
|
|
9
9
|
LISTENING_MODES
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-CXVANPY5.mjs";
|
|
11
11
|
import {
|
|
12
12
|
HARNESS_REGISTRY,
|
|
13
13
|
M1_LABEL_MAX_BYTES,
|
|
@@ -35,10 +35,10 @@ import {
|
|
|
35
35
|
utcTimestamp,
|
|
36
36
|
utf8Length,
|
|
37
37
|
version
|
|
38
|
-
} from "./chunk-
|
|
38
|
+
} from "./chunk-JNSB3E6C.mjs";
|
|
39
39
|
import {
|
|
40
40
|
init_define_KHALA_BUNDLE
|
|
41
|
-
} from "./chunk-
|
|
41
|
+
} from "./chunk-LDW4HOQW.mjs";
|
|
42
42
|
|
|
43
43
|
// src/client.ts
|
|
44
44
|
init_define_KHALA_BUNDLE();
|
|
@@ -1,26 +1,32 @@
|
|
|
1
1
|
import { createRequire as __khalaCreateRequire } from 'node:module'; const require = __khalaCreateRequire(import.meta.url);
|
|
2
|
-
import "./chunk-SSO5RFOX.mjs";
|
|
3
2
|
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
retryUnexpired
|
|
4
|
+
} from "./chunk-LNKI3CZO.mjs";
|
|
5
|
+
import {
|
|
6
|
+
RESTORE_HISTORY_MAX_PAGES
|
|
7
|
+
} from "./chunk-X3BAMHAR.mjs";
|
|
8
|
+
import "./chunk-R5NDTJRA.mjs";
|
|
8
9
|
import {
|
|
9
10
|
LISTENING_MODE_COMMAND_TYPE,
|
|
10
11
|
LISTENING_MODE_MEMBER_KEY,
|
|
11
12
|
memberListeningMode
|
|
12
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-CXVANPY5.mjs";
|
|
14
|
+
import {
|
|
15
|
+
CHANNEL_EVENT_TYPE,
|
|
16
|
+
encodeChannelEvent,
|
|
17
|
+
memberRenameContent
|
|
18
|
+
} from "./chunk-VWWI4S5A.mjs";
|
|
13
19
|
import {
|
|
14
20
|
StateError
|
|
15
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-JNSB3E6C.mjs";
|
|
16
22
|
import {
|
|
17
23
|
init_define_KHALA_BUNDLE
|
|
18
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-LDW4HOQW.mjs";
|
|
19
25
|
|
|
20
26
|
// src/matrix/session.ts
|
|
21
27
|
init_define_KHALA_BUNDLE();
|
|
22
28
|
import { randomUUID } from "node:crypto";
|
|
23
|
-
import { createClient, ClientEvent, RoomEvent, MatrixEventEvent, SyncState, Direction, Method, EventType } from "matrix-js-sdk";
|
|
29
|
+
import { createClient, ClientEvent, RoomEvent, MatrixEventEvent, SyncState, SetPresence, Direction, Method, EventType } from "matrix-js-sdk";
|
|
24
30
|
function message(event) {
|
|
25
31
|
const type = event.getType();
|
|
26
32
|
const eventId = event.getId();
|
|
@@ -44,7 +50,7 @@ async function createAgentMatrixSession(creds, opts) {
|
|
|
44
50
|
const started = Date.now();
|
|
45
51
|
const rejoinTxnId = `khala.rejoin.${randomUUID()}`;
|
|
46
52
|
const log = (line) => opts?.log?.(line);
|
|
47
|
-
const persistent = opts?.cryptoStore ? await (await import("./chunk-
|
|
53
|
+
const persistent = opts?.cryptoStore ? await (await import("./chunk-YDEZFVF7.mjs")).openCryptoStore(opts.cryptoStore.dir, opts.cryptoStore.root, creds) : void 0;
|
|
48
54
|
const client = createClient({ ...persistent ? { store: persistent.sync } : {}, baseUrl: creds.homeserver, userId: creds.userId, accessToken: creds.accessToken, deviceId: creds.deviceId });
|
|
49
55
|
const handlers = /* @__PURE__ */ new Set();
|
|
50
56
|
const ended = /* @__PURE__ */ new Set();
|
|
@@ -52,13 +58,38 @@ async function createAgentMatrixSession(creds, opts) {
|
|
|
52
58
|
const inviters = /* @__PURE__ */ new Map();
|
|
53
59
|
const emitted = /* @__PURE__ */ new Set();
|
|
54
60
|
const liveEvents = /* @__PURE__ */ new Set();
|
|
61
|
+
const undecryptableIds = new Map((persistent?.undecryptableEventIds ?? []).map((entry) => [entry.id, entry]));
|
|
62
|
+
let retrySave = Promise.resolve();
|
|
63
|
+
const saveRetries = () => {
|
|
64
|
+
const entries = [...undecryptableIds.values()];
|
|
65
|
+
retrySave = retrySave.then(() => persistent?.rememberUndecryptable(entries)).catch(() => log("decryption_retry_save_failed"));
|
|
66
|
+
};
|
|
67
|
+
const rememberDecryption = (event) => {
|
|
68
|
+
const id = event.getId();
|
|
69
|
+
if (stopped || !persistent || !id) return;
|
|
70
|
+
const failed = event.getType() === "m.room.encrypted" || event.isDecryptionFailure();
|
|
71
|
+
if (failed) {
|
|
72
|
+
if (undecryptableIds.has(id)) return;
|
|
73
|
+
undecryptableIds.set(id, { id, firstSeen: Date.now() });
|
|
74
|
+
} else if (!undecryptableIds.delete(id)) return;
|
|
75
|
+
if (undecryptableIds.size > 100) undecryptableIds.delete(undecryptableIds.keys().next().value);
|
|
76
|
+
saveRetries();
|
|
77
|
+
};
|
|
55
78
|
const joinTimes = /* @__PURE__ */ new Map();
|
|
56
79
|
const cancellations = /* @__PURE__ */ new Set();
|
|
57
80
|
let joinedRoom;
|
|
58
81
|
let stopped = false;
|
|
82
|
+
let presenceTimer;
|
|
83
|
+
let away;
|
|
84
|
+
const publishPresence = () => {
|
|
85
|
+
void client.setSyncPresence(away ? SetPresence.Offline : void 0);
|
|
86
|
+
void client.setPresence(away ? { presence: "offline", status_msg: away } : { presence: "online", status_msg: "" }).catch(() => log("presence_failed"));
|
|
87
|
+
};
|
|
59
88
|
let recovering = persistent?.restored ?? false;
|
|
60
89
|
const deliver = (event) => {
|
|
61
|
-
if (stopped
|
|
90
|
+
if (stopped) return;
|
|
91
|
+
if (liveEvents.has(event)) rememberDecryption(event);
|
|
92
|
+
if (recovering || !liveEvents.has(event)) return;
|
|
62
93
|
const command = event.getType() === LISTENING_MODE_COMMAND_TYPE && !event.isDecryptionFailure() && event.getId() && event.getRoomId() && event.getSender() ? { eventId: event.getId(), roomId: event.getRoomId(), sender: event.getSender(), ts: event.getTs(), content: event.getContent() } : void 0;
|
|
63
94
|
const m = command ?? message(event);
|
|
64
95
|
if (!m) {
|
|
@@ -96,7 +127,10 @@ async function createAgentMatrixSession(creds, opts) {
|
|
|
96
127
|
const timeline = (event, _room, toStart, _removed, data) => {
|
|
97
128
|
if (stopped || toStart || data?.liveEvent === false && !persistent?.restored) return;
|
|
98
129
|
liveEvents.add(event);
|
|
99
|
-
void client.decryptEventIfNeeded(event).then(() => deliver(event), () =>
|
|
130
|
+
void client.decryptEventIfNeeded(event).then(() => deliver(event), () => {
|
|
131
|
+
rememberDecryption(event);
|
|
132
|
+
log("live_decryption_failed");
|
|
133
|
+
});
|
|
100
134
|
};
|
|
101
135
|
const decrypted = (event) => deliver(event);
|
|
102
136
|
const syncLog = (state, _previous, data) => {
|
|
@@ -167,6 +201,24 @@ async function createAgentMatrixSession(creds, opts) {
|
|
|
167
201
|
const stopSession = async () => {
|
|
168
202
|
if (stopped) return;
|
|
169
203
|
stopped = true;
|
|
204
|
+
clearInterval(presenceTimer);
|
|
205
|
+
if (presenceTimer && !wipe) {
|
|
206
|
+
await Promise.race([
|
|
207
|
+
client.setPresence({ presence: "offline", status_msg: "session ended" }).catch(() => log("presence_failed")),
|
|
208
|
+
new Promise((resolve) => setTimeout(resolve, 3e3).unref())
|
|
209
|
+
]);
|
|
210
|
+
}
|
|
211
|
+
await retrySave;
|
|
212
|
+
if (wipe) {
|
|
213
|
+
try {
|
|
214
|
+
await client.http.authedRequest(Method.Post, "/logout", void 0, {}, { localTimeoutMs: 5e3 });
|
|
215
|
+
} catch (error) {
|
|
216
|
+
if (matrixCode(error) !== "M_UNKNOWN_TOKEN" && !(typeof error === "object" && error !== null && "httpStatus" in error && error.httpStatus === 401)) {
|
|
217
|
+
log("discarded_device_logout_failed");
|
|
218
|
+
console.error("khala: discarded_device_logout_failed");
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
170
222
|
if (wipe && persistent?.forgetIdentity) {
|
|
171
223
|
try {
|
|
172
224
|
await persistent.forgetIdentity();
|
|
@@ -236,8 +288,7 @@ async function createAgentMatrixSession(creds, opts) {
|
|
|
236
288
|
await client.initRustCrypto(persistent ? { useIndexedDB: true, cryptoDatabasePrefix: persistent.prefix } : { useIndexedDB: false });
|
|
237
289
|
} catch (error) {
|
|
238
290
|
if (!persistent?.restored || error instanceof StateError && error.code === "unsafe_state_dir") throw error;
|
|
239
|
-
|
|
240
|
-
throw new (await import("./chunk-PAJ4JJPV.mjs")).CryptoStoreCorruptError();
|
|
291
|
+
throw new (await import("./chunk-YDEZFVF7.mjs")).CryptoStoreCorruptError();
|
|
241
292
|
}
|
|
242
293
|
const crypto = client.getCrypto();
|
|
243
294
|
if (!crypto) throw new Error("crypto_unavailable");
|
|
@@ -264,6 +315,9 @@ async function createAgentMatrixSession(creds, opts) {
|
|
|
264
315
|
}, () => client.getSyncState() === SyncState.Prepared || client.getSyncState() === SyncState.Syncing, 3e4, "sync_timeout");
|
|
265
316
|
await Promise.all([client.startClient({ initialSyncLimit: 30 }), prepared]);
|
|
266
317
|
log(`session_ready_ms=${Date.now() - started}`);
|
|
318
|
+
publishPresence();
|
|
319
|
+
presenceTimer = setInterval(publishPresence, 6e4);
|
|
320
|
+
presenceTimer.unref();
|
|
267
321
|
} catch (error) {
|
|
268
322
|
await stop();
|
|
269
323
|
throw error;
|
|
@@ -282,6 +336,7 @@ async function createAgentMatrixSession(creds, opts) {
|
|
|
282
336
|
return {
|
|
283
337
|
userId: creds.userId,
|
|
284
338
|
...persistent?.recovered ? { cryptoReset: true } : {},
|
|
339
|
+
...persistent?.restored ? { recoversOnJoin: true } : {},
|
|
285
340
|
onEnded(handler) {
|
|
286
341
|
if (!stopped) ended.add(handler);
|
|
287
342
|
return () => {
|
|
@@ -317,7 +372,6 @@ async function createAgentMatrixSession(creds, opts) {
|
|
|
317
372
|
async join(roomId) {
|
|
318
373
|
if (stopped) throw new Error("session_stopped");
|
|
319
374
|
if (joinedRoom === roomId) return;
|
|
320
|
-
const rejoinedAt = Date.now();
|
|
321
375
|
const membership = client.getRoom(roomId)?.getMyMembership();
|
|
322
376
|
if (membership !== "join") {
|
|
323
377
|
if (membership !== "invite") throw new Error("not_invited");
|
|
@@ -338,30 +392,80 @@ async function createAgentMatrixSession(creds, opts) {
|
|
|
338
392
|
const content = { ...ownJoin.getContent(), membership: "join", "com.khala.invited_by": inviters.get(roomId) };
|
|
339
393
|
await client.sendStateEvent(roomId, EventType.RoomMember, content, creds.userId);
|
|
340
394
|
}
|
|
341
|
-
const joinedAt = persistent?.joinedAt ??
|
|
395
|
+
const joinedAt = persistent?.joinedAt ?? ownJoin.getTs();
|
|
342
396
|
await persistent?.rememberJoin(joinedAt);
|
|
343
397
|
joinTimes.set(roomId, joinedAt);
|
|
344
398
|
if (persistent?.restored) {
|
|
399
|
+
let pruned = false;
|
|
400
|
+
for (const [id, entry] of [...undecryptableIds]) {
|
|
401
|
+
if (stopped) throw new Error("session_stopped");
|
|
402
|
+
if (!retryUnexpired(entry)) {
|
|
403
|
+
undecryptableIds.delete(id);
|
|
404
|
+
pruned = true;
|
|
405
|
+
continue;
|
|
406
|
+
}
|
|
407
|
+
try {
|
|
408
|
+
const raw = await client.fetchRoomEvent(roomId, id);
|
|
409
|
+
const event = client.getEventMapper()({ ...raw, room_id: roomId });
|
|
410
|
+
if (event.getTs() < joinedAt) {
|
|
411
|
+
undecryptableIds.delete(id);
|
|
412
|
+
pruned = true;
|
|
413
|
+
continue;
|
|
414
|
+
}
|
|
415
|
+
liveEvents.add(event);
|
|
416
|
+
await client.decryptEventIfNeeded(event).catch(() => log("recovery_decryption_failed"));
|
|
417
|
+
rememberDecryption(event);
|
|
418
|
+
} catch (error) {
|
|
419
|
+
const status = typeof error === "object" && error !== null && "httpStatus" in error ? error.httpStatus : void 0;
|
|
420
|
+
if (matrixCode(error) === "M_NOT_FOUND" || matrixCode(error) === "M_FORBIDDEN" || status === 404 || status === 403) {
|
|
421
|
+
undecryptableIds.delete(id);
|
|
422
|
+
pruned = true;
|
|
423
|
+
}
|
|
424
|
+
log("recovery_event_retry_failed");
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
if (pruned) saveRetries();
|
|
345
428
|
let token = null;
|
|
346
429
|
const seenTokens = /* @__PURE__ */ new Set();
|
|
430
|
+
let pages = 0;
|
|
431
|
+
let undecryptable = 0;
|
|
347
432
|
for (; ; ) {
|
|
348
433
|
if (stopped) throw new Error("session_stopped");
|
|
349
434
|
const page = await client.createMessagesRequest(roomId, token, 100, Direction.Backward);
|
|
350
|
-
|
|
435
|
+
pages++;
|
|
436
|
+
let reachedBoundary = false;
|
|
351
437
|
for (const raw of page.chunk) {
|
|
352
438
|
const event = client.getEventMapper()({ ...raw, room_id: roomId });
|
|
353
|
-
if (event.getTs() < joinedAt) {
|
|
354
|
-
|
|
355
|
-
|
|
439
|
+
if (opts?.restoreStopAtEventId && event.getId() === opts.restoreStopAtEventId || event.getTs() < joinedAt) {
|
|
440
|
+
reachedBoundary = true;
|
|
441
|
+
break;
|
|
356
442
|
}
|
|
357
443
|
liveEvents.add(event);
|
|
358
444
|
await client.decryptEventIfNeeded(event).catch(() => log("recovery_decryption_failed"));
|
|
445
|
+
rememberDecryption(event);
|
|
446
|
+
if (event.getType() === "m.room.encrypted" || event.isDecryptionFailure()) {
|
|
447
|
+
undecryptable++;
|
|
448
|
+
liveEvents.delete(event);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
if (reachedBoundary || page.chunk.length === 0 || !page.end) break;
|
|
452
|
+
if (pages >= RESTORE_HISTORY_MAX_PAGES) {
|
|
453
|
+
const count = `restore_catchup_truncated pages=${pages}`;
|
|
454
|
+
if (opts?.log) log(count);
|
|
455
|
+
else process.stderr.write(`${count}
|
|
456
|
+
`);
|
|
457
|
+
break;
|
|
359
458
|
}
|
|
360
|
-
if (reachedJoin || page.chunk.length === 0 || !page.end) break;
|
|
361
459
|
if (seenTokens.has(page.end)) throw new Error("recovery_pagination_stalled");
|
|
362
460
|
seenTokens.add(page.end);
|
|
363
461
|
token = page.end;
|
|
364
462
|
}
|
|
463
|
+
if (undecryptable > 0) {
|
|
464
|
+
const count = `history_undecryptable=${undecryptable}`;
|
|
465
|
+
if (opts?.log) log(count);
|
|
466
|
+
else process.stderr.write(`${count}
|
|
467
|
+
`);
|
|
468
|
+
}
|
|
365
469
|
}
|
|
366
470
|
recovering = false;
|
|
367
471
|
if (!joinedRoom) client.on(RoomEvent.MyMembership, membershipEnded);
|
|
@@ -370,7 +474,8 @@ async function createAgentMatrixSession(creds, opts) {
|
|
|
370
474
|
if (event.getRoomId() === roomId) deliver(event);
|
|
371
475
|
}
|
|
372
476
|
},
|
|
373
|
-
|
|
477
|
+
joinedAt: (roomId) => joinTimes.get(roomId),
|
|
478
|
+
async history(roomId, limit, before, options) {
|
|
374
479
|
if (stopped) throw new Error("session_stopped");
|
|
375
480
|
let token = null;
|
|
376
481
|
if (before !== void 0) {
|
|
@@ -380,16 +485,26 @@ async function createAgentMatrixSession(creds, opts) {
|
|
|
380
485
|
const res = await guarded(roomId, () => client.createMessagesRequest(roomId, token, limit, Direction.Backward));
|
|
381
486
|
const messages = [];
|
|
382
487
|
let undecryptable = 0;
|
|
488
|
+
let oldest;
|
|
489
|
+
let oldestTs;
|
|
490
|
+
let reachedBoundary = false;
|
|
383
491
|
for (const raw of res.chunk) {
|
|
384
492
|
const event = client.getEventMapper()({ ...raw, room_id: roomId });
|
|
493
|
+
oldest = event.getId();
|
|
494
|
+
oldestTs = Math.min(oldestTs ?? event.getTs(), event.getTs());
|
|
495
|
+
if (options?.stopAtEventId && oldest === options.stopAtEventId) {
|
|
496
|
+
reachedBoundary = true;
|
|
497
|
+
break;
|
|
498
|
+
}
|
|
385
499
|
try {
|
|
386
500
|
await client.decryptEventIfNeeded(event);
|
|
387
501
|
} catch {
|
|
388
502
|
}
|
|
503
|
+
rememberDecryption(event);
|
|
389
504
|
if (event.getType() === "m.room.encrypted" || event.isDecryptionFailure()) {
|
|
390
505
|
undecryptable++;
|
|
391
506
|
const eventId = event.getId(), sender = event.getSender();
|
|
392
|
-
if (eventId && sender && eventId !== before) {
|
|
507
|
+
if (options?.includeUnavailable !== false && eventId && sender && eventId !== before) {
|
|
393
508
|
const body = "[Encrypted message unavailable: this device does not have its key. Keys may not have been shared for messages sent before joining or while this agent was offline.]";
|
|
394
509
|
messages.push({
|
|
395
510
|
eventId,
|
|
@@ -406,10 +521,16 @@ async function createAgentMatrixSession(creds, opts) {
|
|
|
406
521
|
const m = message(event);
|
|
407
522
|
if (m && m.eventId !== before) messages.push(m);
|
|
408
523
|
}
|
|
409
|
-
|
|
524
|
+
const count = `history_undecryptable=${undecryptable}`;
|
|
525
|
+
log(count);
|
|
526
|
+
if (options?.includeUnavailable === false && undecryptable > 0 && !opts?.log) process.stderr.write(`${count}
|
|
527
|
+
`);
|
|
410
528
|
messages.reverse();
|
|
411
|
-
|
|
412
|
-
|
|
529
|
+
return {
|
|
530
|
+
messages,
|
|
531
|
+
...typeof res.end === "string" && oldest ? { nextBefore: oldest } : {},
|
|
532
|
+
...options?.includeUnavailable === false || options?.stopAtEventId ? { ...oldestTs === void 0 ? {} : { oldestTs }, reachedBoundary } : {}
|
|
533
|
+
};
|
|
413
534
|
},
|
|
414
535
|
async send(roomId, text) {
|
|
415
536
|
if (stopped) throw new Error("session_stopped");
|
|
@@ -428,6 +549,39 @@ async function createAgentMatrixSession(creds, opts) {
|
|
|
428
549
|
const value = joinedRoom && client.getRoom(joinedRoom)?.currentState.getStateEvents("m.room.member", userId)?.getContent().displayname;
|
|
429
550
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
430
551
|
},
|
|
552
|
+
members(roomId) {
|
|
553
|
+
return (client.getRoom(roomId)?.getJoinedMembers() ?? []).filter((member) => member.userId !== creds.userId).map((member) => {
|
|
554
|
+
const user = client.getUser(member.userId);
|
|
555
|
+
const event = user?.events.presence;
|
|
556
|
+
if (!user || !event) return { name: member.name, presence: "unknown" };
|
|
557
|
+
if (user.presence !== "offline") return { name: member.name, presence: "online" };
|
|
558
|
+
const reason = event.getContent().status_msg;
|
|
559
|
+
return {
|
|
560
|
+
name: member.name,
|
|
561
|
+
presence: "away",
|
|
562
|
+
since: new Date(user.lastPresenceTs - (user.lastActiveAgo ?? 0)).toISOString(),
|
|
563
|
+
...typeof reason === "string" && reason ? { reason: reason.slice(0, 80) } : {}
|
|
564
|
+
};
|
|
565
|
+
});
|
|
566
|
+
},
|
|
567
|
+
seenBy(roomId, eventId) {
|
|
568
|
+
const room = client.getRoom(roomId);
|
|
569
|
+
if (!room?.findEventById(eventId)) return [];
|
|
570
|
+
return room.getJoinedMembers().filter((member) => member.userId !== creds.userId && room.hasUserReadEvent(member.userId, eventId)).map((member) => member.name);
|
|
571
|
+
},
|
|
572
|
+
setAway(reason) {
|
|
573
|
+
if (reason === away) return;
|
|
574
|
+
away = reason;
|
|
575
|
+
if (presenceTimer && !stopped) publishPresence();
|
|
576
|
+
},
|
|
577
|
+
async markRead(roomId, eventId) {
|
|
578
|
+
if (stopped) throw new Error("session_stopped");
|
|
579
|
+
await guarded(roomId, () => client.setRoomReadMarkersHttpRequest(roomId, eventId, eventId));
|
|
580
|
+
},
|
|
581
|
+
async setTyping(roomId, typing) {
|
|
582
|
+
if (stopped) return;
|
|
583
|
+
await client.sendTyping(roomId, typing, 3e4);
|
|
584
|
+
},
|
|
431
585
|
stop
|
|
432
586
|
};
|
|
433
587
|
}
|
|
@@ -1,25 +1,30 @@
|
|
|
1
1
|
import { createRequire as __khalaCreateRequire } from 'node:module'; const require = __khalaCreateRequire(import.meta.url);
|
|
2
2
|
import {
|
|
3
3
|
sessionStartWakeChannels
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-MAIMFERG.mjs";
|
|
5
5
|
import {
|
|
6
6
|
adapterFor
|
|
7
|
-
} from "./chunk-
|
|
8
|
-
import "./chunk-
|
|
7
|
+
} from "./chunk-BVG7PJNZ.mjs";
|
|
8
|
+
import "./chunk-4PUNVPC3.mjs";
|
|
9
9
|
import {
|
|
10
|
-
driverAllowed
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
import "./chunk-
|
|
10
|
+
driverAllowed,
|
|
11
|
+
readListeningMode
|
|
12
|
+
} from "./chunk-NOKTVPSY.mjs";
|
|
13
|
+
import "./chunk-CXVANPY5.mjs";
|
|
14
|
+
import {
|
|
15
|
+
wakesAgent
|
|
16
|
+
} from "./chunk-N6UYX3SY.mjs";
|
|
17
|
+
import "./chunk-VWWI4S5A.mjs";
|
|
14
18
|
import {
|
|
15
19
|
filesForDir,
|
|
20
|
+
readJson,
|
|
16
21
|
resolveStateDir,
|
|
17
22
|
stateRoot
|
|
18
|
-
} from "./chunk-
|
|
19
|
-
import "./chunk-
|
|
23
|
+
} from "./chunk-JNSB3E6C.mjs";
|
|
24
|
+
import "./chunk-TVQ4MSEI.mjs";
|
|
20
25
|
import {
|
|
21
26
|
init_define_KHALA_BUNDLE
|
|
22
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-LDW4HOQW.mjs";
|
|
23
28
|
|
|
24
29
|
// hooks/claude-wake.ts
|
|
25
30
|
init_define_KHALA_BUNDLE();
|
|
@@ -30,7 +35,7 @@ import { setTimeout as sleep } from "node:timers/promises";
|
|
|
30
35
|
var NOTICE = "Khala: new channel messages. They arrive in the next hook context.\n";
|
|
31
36
|
var DEADLINE_MS = 24 * 60 * 60 * 1e3;
|
|
32
37
|
var POLL_MS = 500;
|
|
33
|
-
async function
|
|
38
|
+
async function readJson2(file) {
|
|
34
39
|
try {
|
|
35
40
|
return JSON.parse(await fs.readFile(file, "utf8"));
|
|
36
41
|
} catch (error) {
|
|
@@ -39,7 +44,10 @@ async function readJson(file) {
|
|
|
39
44
|
}
|
|
40
45
|
}
|
|
41
46
|
async function unreadChannelMessages(dir) {
|
|
42
|
-
const
|
|
47
|
+
const files = filesForDir(dir);
|
|
48
|
+
const mode = await readListeningMode(files);
|
|
49
|
+
const you = (await readJson(files.status))?.displayName;
|
|
50
|
+
const cursor = await readJson2(path.join(dir, "cursor.json"));
|
|
43
51
|
const delivered = cursor && (cursor.lastDeliveredEventId === null || typeof cursor.lastDeliveredEventId === "string") && Number.isSafeInteger(cursor.deliveredCount) && typeof cursor.deliveredCount === "number" && cursor.deliveredCount >= 0 ? cursor.deliveredCount : 0;
|
|
44
52
|
let content;
|
|
45
53
|
try {
|
|
@@ -57,7 +65,7 @@ async function unreadChannelMessages(dir) {
|
|
|
57
65
|
continue;
|
|
58
66
|
}
|
|
59
67
|
if (!entry || !["eventId", "roomId", "ts", "sender", "senderLabel", "body"].every((key) => typeof entry[key] === "string") || !["human", "agent", "unknown"].includes(entry.senderKind) || !["message", "event"].includes(entry.kind)) continue;
|
|
60
|
-
if (index++ >= delivered && entry
|
|
68
|
+
if (index++ >= delivered && wakesAgent(entry, mode, you)) messages++;
|
|
61
69
|
}
|
|
62
70
|
return messages;
|
|
63
71
|
}
|
|
@@ -72,7 +80,7 @@ async function unreadMessages(dir) {
|
|
|
72
80
|
}
|
|
73
81
|
let messages = 0;
|
|
74
82
|
for (const directory of directories) {
|
|
75
|
-
if ((await
|
|
83
|
+
if ((await readJson2(path.join(directory, "mode.json")))?.mode === "async") continue;
|
|
76
84
|
messages += await unreadChannelMessages(directory);
|
|
77
85
|
}
|
|
78
86
|
return messages;
|
|
@@ -128,16 +136,16 @@ async function watch(stdin, _argv, io = { stderr: process.stderr, env: process.e
|
|
|
128
136
|
};
|
|
129
137
|
const deadline = started.getTime() + testDuration(io.env.KHALA_WAKE_TEST_DEADLINE_MS, DEADLINE_MS);
|
|
130
138
|
const poll = testDuration(io.env.KHALA_WAKE_TEST_POLL_MS, POLL_MS);
|
|
131
|
-
const owns = async () => (await
|
|
139
|
+
const owns = async () => (await readJson2(owner))?.nonce === nonce;
|
|
132
140
|
const idle = async () => {
|
|
133
|
-
const activity = await
|
|
141
|
+
const activity = await readJson2(path.join(dir, "activity.json"));
|
|
134
142
|
return activity?.state === "idle" && typeof activity.updatedAt === "string" && Number.isFinite(Date.parse(activity.updatedAt));
|
|
135
143
|
};
|
|
136
144
|
const unread = async () => {
|
|
137
145
|
if (input.hook_event_name !== "SessionStart") return unreadMessages(dir);
|
|
138
146
|
let messages = 0;
|
|
139
147
|
for (const channel of await sessionStartWakeChannels(filesForDir(dir), io.env)) {
|
|
140
|
-
if (channel.connected && (await
|
|
148
|
+
if (channel.connected && (await readJson2(channel.files.mode))?.mode !== "async") messages += await unreadChannelMessages(channel.files.dir);
|
|
141
149
|
}
|
|
142
150
|
return messages;
|
|
143
151
|
};
|