khala-cli 0.4.6 → 0.4.8
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 +18 -1
- package/dist/{chunk-2U4E7AM7.mjs → chunk-2DHIAFEV.mjs} +294 -101
- package/dist/{chunk-CWHGNVZV.mjs → chunk-2VCAW4GH.mjs} +1 -1
- package/dist/{chunk-P5CP32X2.mjs → chunk-54XFVSEC.mjs} +1 -1
- package/dist/chunk-5BQTYU66.mjs +886 -0
- package/dist/{chunk-RRBIVI77.mjs → chunk-5LIE5GUU.mjs} +1 -1
- package/dist/{chunk-6E3UCO22.mjs → chunk-5PUQT3YS.mjs} +200 -20
- package/dist/{chunk-55KBLEX5.mjs → chunk-7HBRFY3Z.mjs} +1 -1
- package/dist/{chunk-PFDUXY3N.mjs → chunk-APMWDF6B.mjs} +1 -1
- package/dist/{chunk-PY2ZNVNY.mjs → chunk-BKLW2SPG.mjs} +55 -8
- package/dist/{chunk-OW3DSTEZ.mjs → chunk-CBPLCCRZ.mjs} +5 -5
- package/dist/{chunk-JG5J3SHW.mjs → chunk-CJTCU755.mjs} +4 -4
- package/dist/{chunk-AWBKVTLV.mjs → chunk-FH34B3I3.mjs} +14 -17
- package/dist/chunk-FV32AQTC.mjs +40 -0
- package/dist/{chunk-FH2CZX5T.mjs → chunk-H3K4GA2P.mjs} +83 -36
- package/dist/{chunk-AUJEUKVM.mjs → chunk-IQJQ4RF5.mjs} +1 -1
- package/dist/{chunk-SN3STAO2.mjs → chunk-NKX3SLS7.mjs} +40 -3
- package/dist/{chunk-Z4EDYB6Y.mjs → chunk-RNDUFTBF.mjs} +97 -57
- package/dist/{chunk-OECJ45NM.mjs → chunk-VAI7IUI4.mjs} +24 -8
- package/dist/{chunk-XUJIJ4KB.mjs → chunk-VKQ3KUYO.mjs} +16 -2
- package/dist/{chunk-4X35OUAN.mjs → chunk-VUSV2ADL.mjs} +92 -97
- package/dist/{chunk-2XTYRBYX.mjs → chunk-W6R4UJUQ.mjs} +12 -5
- package/dist/{chunk-B2MOLCJM.mjs → chunk-Z4JF2M5C.mjs} +7 -9
- package/dist/{chunk-SZIEG3RS.mjs → chunk-ZJ4W7P33.mjs} +12 -14
- package/dist/khala.mjs +11 -11
- package/dist/web/assets/local-BQl2KrE1.js +9 -0
- package/dist/web/assets/local-OwDq47O4.css +1 -0
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
- package/dist/chunk-DPW43YNC.mjs +0 -36
- package/dist/chunk-IULQYQJZ.mjs +0 -144
- package/dist/chunk-WHONG7UF.mjs +0 -267
- package/dist/chunk-ZHMGB3PP.mjs +0 -82
- package/dist/web/assets/local-K8EcTVxW.js +0 -9
- package/dist/web/assets/local-R2y0Gl4T.css +0 -1
|
@@ -5,21 +5,20 @@ import {
|
|
|
5
5
|
reportReady,
|
|
6
6
|
requestJoin,
|
|
7
7
|
saveHostedUsername
|
|
8
|
-
} from "./chunk-
|
|
9
|
-
import {
|
|
10
|
-
KHALA_AGENT_VERSION
|
|
11
|
-
} from "./chunk-RRBIVI77.mjs";
|
|
8
|
+
} from "./chunk-VAI7IUI4.mjs";
|
|
12
9
|
import {
|
|
13
10
|
createEventKeyFilter,
|
|
14
11
|
isWakeEntry,
|
|
15
12
|
toEventInboxEntry
|
|
16
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-2VCAW4GH.mjs";
|
|
17
14
|
import {
|
|
18
15
|
KhalaClientError,
|
|
19
|
-
LOCAL_OWNER_USER_ID
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
16
|
+
LOCAL_OWNER_USER_ID
|
|
17
|
+
} from "./chunk-2DHIAFEV.mjs";
|
|
18
|
+
import {
|
|
19
|
+
KHALA_AGENT_VERSION
|
|
20
|
+
} from "./chunk-5LIE5GUU.mjs";
|
|
21
|
+
import "./chunk-APMWDF6B.mjs";
|
|
23
22
|
import "./chunk-5GLTKWJ5.mjs";
|
|
24
23
|
import {
|
|
25
24
|
CHANNEL_EVENT_TYPE,
|
|
@@ -27,19 +26,20 @@ import {
|
|
|
27
26
|
formatChannelEventLine,
|
|
28
27
|
memberRenameContent,
|
|
29
28
|
statusFor
|
|
30
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-54XFVSEC.mjs";
|
|
31
30
|
import {
|
|
32
31
|
monitorArmed,
|
|
33
32
|
resolveHarness,
|
|
34
|
-
|
|
35
|
-
} from "./chunk-
|
|
36
|
-
import {
|
|
37
|
-
adapterFor
|
|
38
|
-
} from "./chunk-WHONG7UF.mjs";
|
|
33
|
+
resolveSession
|
|
34
|
+
} from "./chunk-FH34B3I3.mjs";
|
|
39
35
|
import {
|
|
36
|
+
SESSION_UNKNOWN_HINT,
|
|
37
|
+
adapterFor,
|
|
40
38
|
applyListeningMode,
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
createWakeLadder,
|
|
40
|
+
readListeningMode,
|
|
41
|
+
readWakeState
|
|
42
|
+
} from "./chunk-5BQTYU66.mjs";
|
|
43
43
|
import {
|
|
44
44
|
appendInbox,
|
|
45
45
|
channelKey,
|
|
@@ -47,8 +47,7 @@ import {
|
|
|
47
47
|
migrateLegacy,
|
|
48
48
|
resolveChannel,
|
|
49
49
|
unreadCount
|
|
50
|
-
} from "./chunk-
|
|
51
|
-
import "./chunk-DPW43YNC.mjs";
|
|
50
|
+
} from "./chunk-VKQ3KUYO.mjs";
|
|
52
51
|
import {
|
|
53
52
|
StateError,
|
|
54
53
|
TERMINAL_SESSION_DETAILS,
|
|
@@ -64,18 +63,19 @@ import {
|
|
|
64
63
|
sessionFiles,
|
|
65
64
|
writeJoinFile,
|
|
66
65
|
writeStateFile
|
|
67
|
-
} from "./chunk-
|
|
66
|
+
} from "./chunk-CBPLCCRZ.mjs";
|
|
68
67
|
import {
|
|
69
68
|
decodeListeningModeCommand
|
|
70
|
-
} from "./chunk-
|
|
69
|
+
} from "./chunk-IQJQ4RF5.mjs";
|
|
71
70
|
import {
|
|
72
71
|
displayText,
|
|
72
|
+
harnessInfo,
|
|
73
73
|
isWellFormed,
|
|
74
74
|
label,
|
|
75
75
|
utf8Length,
|
|
76
76
|
validAgentRejoinSecret
|
|
77
|
-
} from "./chunk-
|
|
78
|
-
import "./chunk-
|
|
77
|
+
} from "./chunk-NKX3SLS7.mjs";
|
|
78
|
+
import "./chunk-7HBRFY3Z.mjs";
|
|
79
79
|
|
|
80
80
|
// src/mcp/main.ts
|
|
81
81
|
import { Console } from "node:console";
|
|
@@ -504,6 +504,7 @@ function resolveEventInput(args) {
|
|
|
504
504
|
|
|
505
505
|
// src/mcp/tools.ts
|
|
506
506
|
var ERROR_CODES = [
|
|
507
|
+
"update_required",
|
|
507
508
|
"channel_required",
|
|
508
509
|
"channel_unknown",
|
|
509
510
|
"channel_ambiguous",
|
|
@@ -522,19 +523,24 @@ function errorCode(error) {
|
|
|
522
523
|
return typeof code === "string" && ERROR_CODES.includes(code) ? code : "internal_error";
|
|
523
524
|
}
|
|
524
525
|
function createKhalaTools(input) {
|
|
526
|
+
const sessionProperties = input.harness === "opencode" ? { khala_session: { type: "string", minLength: 1, description: "Session ID stamped by the OpenCode plugin." } } : {};
|
|
527
|
+
const validSession = (args) => !Object.hasOwn(args, "khala_session") || input.harness === "opencode" && typeof args.khala_session === "string" && args.khala_session.length > 0;
|
|
528
|
+
const sessionMeta = (args, meta) => input.harness === "opencode" && Object.hasOwn(args, "khala_session") ? { ...meta, khala_session: args.khala_session } : meta;
|
|
525
529
|
function tool(name, description, properties, required, validate, invoke, render = JSON.stringify) {
|
|
526
530
|
return {
|
|
527
531
|
name,
|
|
528
|
-
definition: () => ({ name, description, inputSchema: { type: "object", properties, required, additionalProperties: false } }),
|
|
532
|
+
definition: () => ({ name, description, inputSchema: { type: "object", properties: { ...properties, ...sessionProperties }, required, additionalProperties: false } }),
|
|
529
533
|
async call(args, context) {
|
|
530
|
-
if (!hasOnly(args, Object.keys(properties)) || !validate(args)) return failure(context.id, -32602, "Invalid params");
|
|
534
|
+
if (!hasOnly(args, Object.keys({ ...properties, ...sessionProperties })) || !validSession(args) || !validate(args)) return failure(context.id, -32602, "Invalid params");
|
|
531
535
|
try {
|
|
532
|
-
const client = input.clientFor(context.meta);
|
|
533
|
-
if (client === null) return success(context.id, toolError("session_unknown"));
|
|
536
|
+
const client = await input.clientFor(sessionMeta(args, context.meta));
|
|
537
|
+
if (client === null) return success(context.id, toolError("session_unknown", { hint: SESSION_UNKNOWN_HINT }));
|
|
534
538
|
const structuredContent = await invoke(client, args);
|
|
535
539
|
return success(context.id, { content: [{ type: "text", text: render(structuredContent) }], structuredContent });
|
|
536
540
|
} catch (error) {
|
|
537
|
-
|
|
541
|
+
const code = errorCode(error);
|
|
542
|
+
const extra = error instanceof KhalaClientError ? { ...error.extra, ...code === "update_required" ? { message: error.message } : {} } : void 0;
|
|
543
|
+
return success(context.id, toolError(code, extra));
|
|
538
544
|
}
|
|
539
545
|
}
|
|
540
546
|
};
|
|
@@ -549,11 +555,11 @@ function createKhalaTools(input) {
|
|
|
549
555
|
type: "string",
|
|
550
556
|
minLength: 1,
|
|
551
557
|
maxLength: 40,
|
|
552
|
-
description: "Optional and ignored: Khala names you <OwnerUsername>-<
|
|
558
|
+
description: "Optional and ignored: Khala names you <OwnerUsername>-<Model>, and your owner can rename you."
|
|
553
559
|
} },
|
|
554
560
|
["link"],
|
|
555
561
|
(args) => typeof args.link === "string" && (!Object.hasOwn(args, "label") || typeof args.label === "string" && args.label.trim().length > 0 && [...args.label].length <= 40),
|
|
556
|
-
(client, args) => client.join(args.link, args.label ??
|
|
562
|
+
(client, args) => client.join(args.link, args.label ?? harnessInfo(input.harness).modelName),
|
|
557
563
|
(result) => {
|
|
558
564
|
const joined = result;
|
|
559
565
|
if (joined.state === "connected") return `Connected to ${joined.channelName}.`;
|
|
@@ -597,15 +603,16 @@ function createKhalaTools(input) {
|
|
|
597
603
|
description: 'Select channel by name or channel ID; required when joined to more than one channel. Post a compact progress event (PR, CI, ticket status) into the Khala channel. Events are progress signals, not channel messages: they never wake other agents. Pass Khala JSON as "event", or a raw Aiur bus event, wake record or alert as "aiur".',
|
|
598
604
|
inputSchema: {
|
|
599
605
|
type: "object",
|
|
600
|
-
properties: { channel: channelProperty, event: { type: "object" }, aiur: { type: "object" }, ticketPrefix: { type: "string", minLength: 0, maxLength: 16 } },
|
|
606
|
+
properties: { ...sessionProperties, channel: channelProperty, event: { type: "object" }, aiur: { type: "object" }, ticketPrefix: { type: "string", minLength: 0, maxLength: 16 } },
|
|
601
607
|
required: [],
|
|
602
608
|
additionalProperties: false,
|
|
603
609
|
oneOf: [{ required: ["event"], not: { required: ["aiur"] } }, { required: ["aiur"], not: { required: ["event"] } }]
|
|
604
610
|
}
|
|
605
611
|
}),
|
|
606
612
|
async call(args, context) {
|
|
607
|
-
if (!validChannel(args)) return failure(context.id, -32602, "Invalid params");
|
|
613
|
+
if (!validSession(args) || !validChannel(args)) return failure(context.id, -32602, "Invalid params");
|
|
608
614
|
const { channel, ...eventArgs } = args;
|
|
615
|
+
if (input.harness === "opencode") delete eventArgs.khala_session;
|
|
609
616
|
const resolved = resolveEventInput(eventArgs);
|
|
610
617
|
if (resolved.kind === "invalid") {
|
|
611
618
|
const error = { error: "invalid_event", path: resolved.path, code: resolved.code };
|
|
@@ -615,12 +622,14 @@ function createKhalaTools(input) {
|
|
|
615
622
|
return success(context.id, { content: [{ type: "text", text: "Skipped channel event." }], structuredContent: { skipped: true } });
|
|
616
623
|
}
|
|
617
624
|
try {
|
|
618
|
-
const client = input.clientFor(context.meta);
|
|
619
|
-
if (client === null) return success(context.id, toolError("session_unknown"));
|
|
625
|
+
const client = await input.clientFor(sessionMeta(args, context.meta));
|
|
626
|
+
if (client === null) return success(context.id, toolError("session_unknown", { hint: SESSION_UNKNOWN_HINT }));
|
|
620
627
|
const sent = await (channel === void 0 ? client.sendChannelEvent(resolved.content) : client.sendChannelEvent(resolved.content, channel));
|
|
621
628
|
return success(context.id, { content: [{ type: "text", text: `Posted channel event: ${resolved.content.body}` }], structuredContent: sent });
|
|
622
629
|
} catch (error) {
|
|
623
|
-
|
|
630
|
+
const code = errorCode(error);
|
|
631
|
+
const extra = error instanceof KhalaClientError ? { ...error.extra, ...code === "update_required" ? { message: error.message } : {} } : void 0;
|
|
632
|
+
return success(context.id, toolError(code, extra));
|
|
624
633
|
}
|
|
625
634
|
}
|
|
626
635
|
}
|
|
@@ -637,7 +646,7 @@ import * as fs from "node:fs/promises";
|
|
|
637
646
|
import { createHash, randomBytes } from "node:crypto";
|
|
638
647
|
|
|
639
648
|
// src/transport.ts
|
|
640
|
-
var startChannelSession = async (creds, options) => creds.transport === "local" ? (await import("./chunk-
|
|
649
|
+
var startChannelSession = async (creds, options) => creds.transport === "local" ? (await import("./chunk-ZJ4W7P33.mjs")).createLocalSession(creds) : (await import("./chunk-CJTCU755.mjs")).createAgentMatrixSession(creds, options);
|
|
641
650
|
|
|
642
651
|
// src/sender.ts
|
|
643
652
|
function localpart(userId) {
|
|
@@ -674,13 +683,15 @@ function errorDetail(error, fallback) {
|
|
|
674
683
|
return ["unsafe_state_dir", "storage_failed"].includes(failure2.message) ? failure2.message : fallback;
|
|
675
684
|
}
|
|
676
685
|
function createKhalaAgentClient(options) {
|
|
686
|
+
const asyncOnly = !adapterFor(options.harness)?.codec;
|
|
687
|
+
const defaultMode = asyncOnly ? "async" : "sync";
|
|
677
688
|
const dir = resolveStateDir(options.harness, options.sessionId, options.env);
|
|
678
689
|
const now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
679
690
|
const fetchDeps = options.fetch ? { fetch: options.fetch } : {};
|
|
680
691
|
const api = options.joinApi ?? { requestJoin, pollJoin, reportReady };
|
|
681
692
|
let status = { state: "idle", updatedAt: now().toISOString() };
|
|
682
693
|
let initialization;
|
|
683
|
-
const rejoinable = adapterFor(options.harness)?.rejoinable(options.sessionId) ?? false;
|
|
694
|
+
const rejoinable = options.rejoinable ?? adapterFor(options.harness)?.sessionSources[0]?.rejoinable(options.sessionId) ?? false;
|
|
684
695
|
let rejoinSecret;
|
|
685
696
|
let savedSecret;
|
|
686
697
|
let resuming;
|
|
@@ -962,8 +973,9 @@ function createKhalaAgentClient(options) {
|
|
|
962
973
|
if (!decoded.ok || decoded.value.agent !== session.userId) return;
|
|
963
974
|
attempt.appends = attempt.appends.then(async () => {
|
|
964
975
|
if (!current(attempt)) return;
|
|
965
|
-
|
|
966
|
-
await
|
|
976
|
+
const mode = asyncOnly ? "async" : decoded.value.mode;
|
|
977
|
+
await applyListeningMode(attempt.files, mode, { changedBy: "owner", eventId: command.eventId }, now);
|
|
978
|
+
await publishMode(attempt, session, credentials.roomId, mode);
|
|
967
979
|
}).catch(async () => {
|
|
968
980
|
if (current(attempt)) await setStatus(attempt, "disconnected", "internal_error").catch(() => {
|
|
969
981
|
});
|
|
@@ -975,7 +987,10 @@ function createKhalaAgentClient(options) {
|
|
|
975
987
|
await wait(session.waitForInvite(credentials.roomId, options.inviteTimeoutMs ?? 12e4));
|
|
976
988
|
await wait(session.join(credentials.roomId));
|
|
977
989
|
if (!current(attempt)) return;
|
|
978
|
-
if (
|
|
990
|
+
if (asyncOnly) {
|
|
991
|
+
await writeStateFile(channelDir, "mode.json", { mode: "async" });
|
|
992
|
+
await publishMode(attempt, session, credentials.roomId, "async");
|
|
993
|
+
} else if (session.listeningMode) await writeStateFile(channelDir, "mode.json", { mode: session.listeningMode(credentials.roomId) });
|
|
979
994
|
else await removeStateFile(channelDir, "mode.json");
|
|
980
995
|
attempt.joined = true;
|
|
981
996
|
attempt.status.channelName = session.roomName(credentials.roomId) ?? credentials.roomId;
|
|
@@ -1072,7 +1087,7 @@ function createKhalaAgentClient(options) {
|
|
|
1072
1087
|
signal.addEventListener("abort", releaseAbort, { once: true });
|
|
1073
1088
|
if (signal.aborted) releaseAbort();
|
|
1074
1089
|
});
|
|
1075
|
-
const request = restore?.localCredentials ? Promise.resolve({ ...attempt.created, origin: new URL(link).origin, autoConfirmed: true }) : api.requestJoin({ link, harness: options.harness, label: label2, ...rejoinSecret === void 0 ? {} : { sessionId: options.sessionId, rejoinSecret } }, restore ? { fetch: (input, init) => (options.fetch ?? fetch)(input, { ...init, signal: init?.signal ? AbortSignal.any([signal, init.signal]) : signal }) } : fetchDeps);
|
|
1090
|
+
const request = restore?.localCredentials ? Promise.resolve({ ...attempt.created, origin: new URL(link).origin, autoConfirmed: true }) : api.requestJoin({ link, harness: options.harness, label: label2, ...rejoinSecret === void 0 ? {} : { sessionId: options.sessionId, rejoinSecret } }, { ...options.env ? { env: options.env } : {}, ...restore ? { fetch: (input, init) => (options.fetch ?? fetch)(input, { ...init, signal: init?.signal ? AbortSignal.any([signal, init.signal]) : signal }) } : fetchDeps });
|
|
1076
1091
|
const created = await Promise.race([request, aborted]).finally(() => {
|
|
1077
1092
|
if (releaseAbort) signal.removeEventListener("abort", releaseAbort);
|
|
1078
1093
|
});
|
|
@@ -1163,7 +1178,7 @@ function createKhalaAgentClient(options) {
|
|
|
1163
1178
|
link: attempt.link,
|
|
1164
1179
|
state: "joining",
|
|
1165
1180
|
unread: 0,
|
|
1166
|
-
listeningMode:
|
|
1181
|
+
listeningMode: defaultMode
|
|
1167
1182
|
});
|
|
1168
1183
|
}
|
|
1169
1184
|
}
|
|
@@ -1174,11 +1189,11 @@ function createKhalaAgentClient(options) {
|
|
|
1174
1189
|
...single?.detail !== void 0 ? { detail: single.detail } : channel === void 0 && status.detail ? { detail: status.detail } : {},
|
|
1175
1190
|
...single ? {
|
|
1176
1191
|
channelName: single.channel,
|
|
1177
|
-
...single.you !== void 0 ? { displayName: single.you } : {},
|
|
1192
|
+
...single.you !== void 0 ? { displayName: single.you, you: single.you } : {},
|
|
1178
1193
|
...single.agentUserId !== void 0 ? { agentUserId: single.agentUserId } : {}
|
|
1179
1194
|
} : {},
|
|
1180
1195
|
unread: items.reduce((sum, item) => sum + item.unread, 0),
|
|
1181
|
-
...single ? { listeningMode: single.listeningMode } : items.length === 0 ? { listeningMode:
|
|
1196
|
+
...single ? { listeningMode: single.listeningMode } : items.length === 0 ? { listeningMode: defaultMode } : {},
|
|
1182
1197
|
channels: items
|
|
1183
1198
|
};
|
|
1184
1199
|
},
|
|
@@ -1284,14 +1299,22 @@ function createKhalaAgentClient(options) {
|
|
|
1284
1299
|
|
|
1285
1300
|
// src/mcp/wiring.ts
|
|
1286
1301
|
function createRealClientFactory(env, deps = {}) {
|
|
1287
|
-
return ({ harness, sessionId }) => {
|
|
1302
|
+
return ({ harness, sessionId, rejoinable }) => {
|
|
1288
1303
|
const files = sessionFiles(harness, sessionId, env);
|
|
1289
1304
|
const adapter = adapterFor(harness);
|
|
1290
|
-
const
|
|
1291
|
-
const waker = createWaker
|
|
1305
|
+
const drivers = adapter?.wakeLadder;
|
|
1306
|
+
const waker = drivers?.length ? deps.createWaker ? deps.createWaker({ files, threadId: sessionId }) : createWakeLadder({
|
|
1307
|
+
files,
|
|
1308
|
+
harness,
|
|
1309
|
+
sessionId,
|
|
1310
|
+
drivers,
|
|
1311
|
+
env,
|
|
1312
|
+
warningPrefix: adapter?.wakeWarningName ?? "wake"
|
|
1313
|
+
}) : void 0;
|
|
1292
1314
|
const client = (deps.createClient ?? createKhalaAgentClient)({
|
|
1293
1315
|
harness,
|
|
1294
1316
|
sessionId,
|
|
1317
|
+
...rejoinable !== void 0 ? { rejoinable } : {},
|
|
1295
1318
|
env,
|
|
1296
1319
|
...waker ? { onInboxAppend: () => waker.notify() } : {}
|
|
1297
1320
|
});
|
|
@@ -1314,7 +1337,22 @@ function createRealClientFactory(env, deps = {}) {
|
|
|
1314
1337
|
async status(channel) {
|
|
1315
1338
|
await initialize();
|
|
1316
1339
|
const status = await (channel === void 0 ? client.status() : client.status(channel));
|
|
1317
|
-
|
|
1340
|
+
let wakeDrivers;
|
|
1341
|
+
if (drivers?.length) {
|
|
1342
|
+
try {
|
|
1343
|
+
const states = await readWakeState(files.dir);
|
|
1344
|
+
wakeDrivers = await Promise.all(drivers.map(async (driver) => {
|
|
1345
|
+
const ctx = { files, harness, sessionId, env, now: Date.now(), signal: new AbortController().signal };
|
|
1346
|
+
const available = !states[driver.id]?.disabled && await driver.available(ctx);
|
|
1347
|
+
const reason = states[driver.id]?.disabled ? states[driver.id]?.reason : available ? void 0 : await driver.unavailableReason?.(ctx);
|
|
1348
|
+
return { id: driver.id, available, ...reason ? { reason } : {} };
|
|
1349
|
+
}));
|
|
1350
|
+
} catch {
|
|
1351
|
+
wakeDrivers = drivers.map((driver) => ({ id: driver.id, available: false, reason: "wake_status_unavailable" }));
|
|
1352
|
+
}
|
|
1353
|
+
}
|
|
1354
|
+
const withWake = wakeDrivers ? { ...status, wakeDrivers } : status;
|
|
1355
|
+
return adapter?.watcherStatus && ["connected", "send_failed"].includes(status.state) ? { ...withWake, watcherArmed: await monitorArmed(files) } : withWake;
|
|
1318
1356
|
},
|
|
1319
1357
|
async read(limit, before, channel) {
|
|
1320
1358
|
await initialize();
|
|
@@ -1386,21 +1424,23 @@ async function runMcpCommand(argv, deps) {
|
|
|
1386
1424
|
return 2;
|
|
1387
1425
|
}
|
|
1388
1426
|
const clients = /* @__PURE__ */ new Map();
|
|
1389
|
-
const clientForSession = (
|
|
1390
|
-
|
|
1427
|
+
const clientForSession = (session) => {
|
|
1428
|
+
const { sessionId, rejoinable } = session;
|
|
1429
|
+
const key = `${rejoinable}:${sessionId}`;
|
|
1430
|
+
let client = clients.get(key);
|
|
1391
1431
|
if (client === void 0) {
|
|
1392
|
-
client = deps.createClient({ harness, sessionId });
|
|
1393
|
-
clients.set(
|
|
1432
|
+
client = deps.createClient({ harness, sessionId, rejoinable });
|
|
1433
|
+
clients.set(key, client);
|
|
1394
1434
|
}
|
|
1395
1435
|
return client;
|
|
1396
1436
|
};
|
|
1397
|
-
const startupSession =
|
|
1398
|
-
if (startupSession !== null
|
|
1437
|
+
const startupSession = adapterFor(harness)?.restoreAtStartup ? await resolveSession(harness, void 0, env) : null;
|
|
1438
|
+
if (startupSession !== null) clientForSession(startupSession);
|
|
1399
1439
|
const tools = createKhalaTools({
|
|
1400
1440
|
harness,
|
|
1401
|
-
clientFor(meta) {
|
|
1402
|
-
const
|
|
1403
|
-
return
|
|
1441
|
+
async clientFor(meta) {
|
|
1442
|
+
const session = await resolveSession(harness, meta, env);
|
|
1443
|
+
return session === null ? null : clientForSession(session);
|
|
1404
1444
|
}
|
|
1405
1445
|
});
|
|
1406
1446
|
try {
|
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
import { createRequire as __khalaCreateRequire } from 'node:module'; const require = __khalaCreateRequire(import.meta.url);
|
|
2
2
|
import {
|
|
3
3
|
KhalaClientError,
|
|
4
|
-
MODEL_NAMES,
|
|
5
4
|
checkName,
|
|
6
|
-
isDefaultAgentName
|
|
7
|
-
|
|
5
|
+
isDefaultAgentName,
|
|
6
|
+
restartHelper
|
|
7
|
+
} from "./chunk-2DHIAFEV.mjs";
|
|
8
8
|
import {
|
|
9
9
|
StateError,
|
|
10
10
|
readJson,
|
|
11
11
|
stateRoot,
|
|
12
12
|
writeJsonAtomic
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-CBPLCCRZ.mjs";
|
|
14
14
|
import {
|
|
15
|
-
|
|
15
|
+
harnessInfo,
|
|
16
|
+
isHarnessId,
|
|
16
17
|
validateAgentName
|
|
17
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-NKX3SLS7.mjs";
|
|
18
19
|
|
|
19
20
|
// src/local/identity.ts
|
|
20
21
|
import * as fs from "node:fs/promises";
|
|
@@ -23,7 +24,7 @@ import path from "node:path";
|
|
|
23
24
|
var HOSTED_PROFILE_FILE = "hosted-profile.json";
|
|
24
25
|
var LOCAL_OWNER_FALLBACK_NAME = "User";
|
|
25
26
|
function hostedUsernameFromAgentName(displayName, harness) {
|
|
26
|
-
const suffix = new RegExp(`-${
|
|
27
|
+
const suffix = new RegExp(`-${harnessInfo(harness).modelName}(?:-\\d+)?$`, "iu");
|
|
27
28
|
if (!suffix.test(displayName)) return null;
|
|
28
29
|
const candidate = displayName.replace(suffix, "");
|
|
29
30
|
const checked = checkName(candidate, "username");
|
|
@@ -139,7 +140,7 @@ async function post(url, origin, body, deps, pollSecret) {
|
|
|
139
140
|
async function requestJoin(input, deps = {}) {
|
|
140
141
|
const parsed = parseChannelLink(input.link);
|
|
141
142
|
if (!parsed) throw new KhalaClientError("invalid_link", "invalid_link");
|
|
142
|
-
if (!
|
|
143
|
+
if (!isHarnessId(input.harness)) fail("invalid_harness");
|
|
143
144
|
const label = validateAgentName(input.label);
|
|
144
145
|
if (!label.ok || [...label.name].length > 40) fail("invalid_label");
|
|
145
146
|
const { origin } = parsed;
|
|
@@ -154,6 +155,21 @@ async function requestJoin(input, deps = {}) {
|
|
|
154
155
|
body = await json(response);
|
|
155
156
|
code = serverCode(body);
|
|
156
157
|
}
|
|
158
|
+
if (response.status === 400 && code === "invalid_harness") {
|
|
159
|
+
if (new URL(origin).protocol === "https:") {
|
|
160
|
+
throw new KhalaClientError("update_required", `Khala's hosted service does not accept ${harnessInfo(input.harness).displayName} agents yet. Local channels work now.`);
|
|
161
|
+
}
|
|
162
|
+
if (await (deps.restartHelper ?? ((origin2) => restartHelper(origin2, deps.env, deps.fetch ? { fetch: deps.fetch } : {})))(origin)) {
|
|
163
|
+
response = await post(url, origin, rejoin ? { ...base, sessionId: input.sessionId, rejoinSecret: input.rejoinSecret } : base, deps);
|
|
164
|
+
body = await json(response);
|
|
165
|
+
code = serverCode(body);
|
|
166
|
+
if (rejoin && response.status === 400 && code === "invalid_link") {
|
|
167
|
+
response = await post(url, origin, base, deps);
|
|
168
|
+
body = await json(response);
|
|
169
|
+
code = serverCode(body);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
157
173
|
if (response.status !== 201) {
|
|
158
174
|
if (response.status === 400 && code === "invalid_link") throw new KhalaClientError("invalid_link", "invalid_link");
|
|
159
175
|
if (response.status === 400 && (code === "invalid_label" || code === "invalid_harness")) fail(code);
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
stateKey,
|
|
12
12
|
writeJsonAtomic,
|
|
13
13
|
writeStateFile
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-CBPLCCRZ.mjs";
|
|
15
15
|
|
|
16
16
|
// src/inbox.ts
|
|
17
17
|
import * as fs from "node:fs/promises";
|
|
@@ -202,6 +202,18 @@ function resolveChannel(list, ref) {
|
|
|
202
202
|
return { ok: false, code: matches.length ? "channel_ambiguous" : "channel_unknown", channels };
|
|
203
203
|
}
|
|
204
204
|
|
|
205
|
+
// src/activity.ts
|
|
206
|
+
import path2 from "node:path";
|
|
207
|
+
async function readActivity(files) {
|
|
208
|
+
const activity = await readJson(path2.join(files.dir, "activity.json"));
|
|
209
|
+
return activity && ["idle", "busy"].includes(activity.state) && typeof activity.updatedAt === "string" && Number.isFinite(Date.parse(activity.updatedAt)) ? activity : { state: "busy", updatedAt: (/* @__PURE__ */ new Date(0)).toISOString() };
|
|
210
|
+
}
|
|
211
|
+
async function writeActivity(files, state, now = () => /* @__PURE__ */ new Date()) {
|
|
212
|
+
const activity = { state, updatedAt: now().toISOString() };
|
|
213
|
+
await writeJsonAtomic(path2.join(files.dir, "activity.json"), activity);
|
|
214
|
+
return activity;
|
|
215
|
+
}
|
|
216
|
+
|
|
205
217
|
export {
|
|
206
218
|
readEntries,
|
|
207
219
|
readCursor,
|
|
@@ -212,5 +224,7 @@ export {
|
|
|
212
224
|
channelKey,
|
|
213
225
|
listChannels,
|
|
214
226
|
migrateLegacy,
|
|
215
|
-
resolveChannel
|
|
227
|
+
resolveChannel,
|
|
228
|
+
readActivity,
|
|
229
|
+
writeActivity
|
|
216
230
|
};
|