@hermespilot/link 0.8.1-beta.2 → 0.8.1-beta.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/dist/{chunk-ZXE3GXSC.js → chunk-IJSMGOPK.js} +750 -86
- package/dist/cli/index.js +1 -1
- package/dist/http/app.d.ts +14 -0
- package/dist/http/app.js +1 -1
- package/package.json +1 -1
|
@@ -4,7 +4,7 @@ import Router from "@koa/router";
|
|
|
4
4
|
|
|
5
5
|
// src/conversations/conversation-service.ts
|
|
6
6
|
import { EventEmitter } from "events";
|
|
7
|
-
import { createHash as
|
|
7
|
+
import { createHash as createHash9, randomUUID as randomUUID12 } from "crypto";
|
|
8
8
|
import path27 from "path";
|
|
9
9
|
|
|
10
10
|
// src/database/link-database.ts
|
|
@@ -7714,7 +7714,7 @@ function isConversationMissingError(error) {
|
|
|
7714
7714
|
}
|
|
7715
7715
|
|
|
7716
7716
|
// src/constants.ts
|
|
7717
|
-
var LINK_VERSION = "0.8.1-beta.
|
|
7717
|
+
var LINK_VERSION = "0.8.1-beta.3";
|
|
7718
7718
|
var LINK_COMMAND = "hermeslink";
|
|
7719
7719
|
var LINK_DEFAULT_PORT = 52379;
|
|
7720
7720
|
var LINK_RUNTIME_DIR_NAME = ".hermeslink";
|
|
@@ -10687,6 +10687,13 @@ function delay3(ms) {
|
|
|
10687
10687
|
}
|
|
10688
10688
|
|
|
10689
10689
|
// src/hermes/tui-gateway-rpc.ts
|
|
10690
|
+
var TuiGatewaySessionBusyError = class extends LinkHttpError {
|
|
10691
|
+
constructor(message, details = { profileName: "default" }) {
|
|
10692
|
+
super(409, "tui_gateway_session_busy", message);
|
|
10693
|
+
this.details = details;
|
|
10694
|
+
}
|
|
10695
|
+
details;
|
|
10696
|
+
};
|
|
10690
10697
|
var CONNECT_TIMEOUT_MS = 15e3;
|
|
10691
10698
|
var REQUEST_TIMEOUT_MS = 12e4;
|
|
10692
10699
|
var USAGE_REQUEST_TIMEOUT_MS = 1e4;
|
|
@@ -10699,12 +10706,60 @@ var WS_HEARTBEAT_INTERVAL_MS = 6e4;
|
|
|
10699
10706
|
var WS_RECONNECT_BASE_DELAY_MS = 1e3;
|
|
10700
10707
|
var WS_RECONNECT_MAX_DELAY_MS = 15e3;
|
|
10701
10708
|
var INTERRUPT_DRAIN_TIMEOUT_MS = 15e3;
|
|
10709
|
+
var SESSION_BUSY_RETRY_TIMEOUT_MS = 6e3;
|
|
10710
|
+
var SESSION_BUSY_RETRY_INTERVAL_MS = 150;
|
|
10702
10711
|
var PROFILE_NAME_PATTERN2 = /^[a-zA-Z0-9._-]{1,64}$/u;
|
|
10703
10712
|
var backendEntries = /* @__PURE__ */ new Map();
|
|
10704
10713
|
var backendStarts = /* @__PURE__ */ new Map();
|
|
10705
10714
|
var clients = /* @__PURE__ */ new Map();
|
|
10706
10715
|
var sessionRefs = /* @__PURE__ */ new Map();
|
|
10707
10716
|
var backendReaper = null;
|
|
10717
|
+
function isTuiGatewaySessionBusyError(error) {
|
|
10718
|
+
if (error instanceof TuiGatewaySessionBusyError) {
|
|
10719
|
+
return true;
|
|
10720
|
+
}
|
|
10721
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
10722
|
+
return /session busy/i.test(message);
|
|
10723
|
+
}
|
|
10724
|
+
function createTuiGatewayRpcError(error) {
|
|
10725
|
+
const message = error?.message ?? "Hermes RPC failed";
|
|
10726
|
+
if (error?.code === 4009 && /session busy/i.test(message)) {
|
|
10727
|
+
return new TuiGatewaySessionBusyError(message);
|
|
10728
|
+
}
|
|
10729
|
+
return new Error(message);
|
|
10730
|
+
}
|
|
10731
|
+
function withBusyContext(error, details) {
|
|
10732
|
+
if (error instanceof TuiGatewaySessionBusyError) {
|
|
10733
|
+
return new TuiGatewaySessionBusyError(error.message, {
|
|
10734
|
+
...error.details,
|
|
10735
|
+
...details
|
|
10736
|
+
});
|
|
10737
|
+
}
|
|
10738
|
+
if (isTuiGatewaySessionBusyError(error)) {
|
|
10739
|
+
return new TuiGatewaySessionBusyError(
|
|
10740
|
+
error instanceof Error ? error.message : String(error),
|
|
10741
|
+
details
|
|
10742
|
+
);
|
|
10743
|
+
}
|
|
10744
|
+
return error instanceof Error ? error : new Error(String(error));
|
|
10745
|
+
}
|
|
10746
|
+
async function withSessionBusyRetry(call, signal) {
|
|
10747
|
+
const deadline = Date.now() + SESSION_BUSY_RETRY_TIMEOUT_MS;
|
|
10748
|
+
for (; ; ) {
|
|
10749
|
+
try {
|
|
10750
|
+
return await call();
|
|
10751
|
+
} catch (error) {
|
|
10752
|
+
if (!isTuiGatewaySessionBusyError(error) || Date.now() >= deadline) {
|
|
10753
|
+
throw error;
|
|
10754
|
+
}
|
|
10755
|
+
await delay4(
|
|
10756
|
+
Math.min(SESSION_BUSY_RETRY_INTERVAL_MS, deadline - Date.now()),
|
|
10757
|
+
void 0,
|
|
10758
|
+
{ signal }
|
|
10759
|
+
);
|
|
10760
|
+
}
|
|
10761
|
+
}
|
|
10762
|
+
}
|
|
10708
10763
|
async function streamTuiGatewayRun(input) {
|
|
10709
10764
|
const profileName = normalizeProfileName2(input.profileName);
|
|
10710
10765
|
const client = await getTuiGatewayClient({
|
|
@@ -10762,6 +10817,77 @@ async function interruptTuiGatewaySession(input) {
|
|
|
10762
10817
|
});
|
|
10763
10818
|
await client.interruptSession(sessionId);
|
|
10764
10819
|
}
|
|
10820
|
+
async function readTuiGatewayLiveSession(input) {
|
|
10821
|
+
const runtimeSessionId = input.runtimeSessionId?.trim();
|
|
10822
|
+
const storedSessionId = input.storedSessionId?.trim();
|
|
10823
|
+
if (!runtimeSessionId && !storedSessionId) {
|
|
10824
|
+
return null;
|
|
10825
|
+
}
|
|
10826
|
+
const profileName = normalizeProfileName2(input.profileName);
|
|
10827
|
+
const client = await getTuiGatewayClient({
|
|
10828
|
+
profileName,
|
|
10829
|
+
paths: input.paths,
|
|
10830
|
+
logger: input.logger
|
|
10831
|
+
});
|
|
10832
|
+
try {
|
|
10833
|
+
const result = await client.request(
|
|
10834
|
+
"session.active_list",
|
|
10835
|
+
runtimeSessionId ? { current_session_id: runtimeSessionId } : {},
|
|
10836
|
+
1e4
|
|
10837
|
+
);
|
|
10838
|
+
const sessions = Array.isArray(result.sessions) ? result.sessions : [];
|
|
10839
|
+
for (const item of sessions) {
|
|
10840
|
+
const liveSession = normalizeTuiGatewayLiveSessionRecord(item);
|
|
10841
|
+
if (!liveSession) {
|
|
10842
|
+
continue;
|
|
10843
|
+
}
|
|
10844
|
+
const runtimeMatches = runtimeSessionId !== void 0 && liveSession.runtimeSessionId === runtimeSessionId;
|
|
10845
|
+
const storedMatches = storedSessionId !== void 0 && liveSession.storedSessionId === storedSessionId;
|
|
10846
|
+
if (!runtimeMatches && !storedMatches) {
|
|
10847
|
+
continue;
|
|
10848
|
+
}
|
|
10849
|
+
return liveSession;
|
|
10850
|
+
}
|
|
10851
|
+
} catch (error) {
|
|
10852
|
+
void input.logger?.debug("tui_gateway_live_session_read_failed", {
|
|
10853
|
+
profile: profileName,
|
|
10854
|
+
runtime_session_id: runtimeSessionId ?? null,
|
|
10855
|
+
stored_session_id: storedSessionId ?? null,
|
|
10856
|
+
error: error instanceof Error ? error.message : String(error)
|
|
10857
|
+
});
|
|
10858
|
+
}
|
|
10859
|
+
return null;
|
|
10860
|
+
}
|
|
10861
|
+
function normalizeTuiGatewayLiveSessionRecord(value) {
|
|
10862
|
+
const record = toRecord3(value);
|
|
10863
|
+
const runtimeId = readString5(record, "id");
|
|
10864
|
+
if (!runtimeId) {
|
|
10865
|
+
return null;
|
|
10866
|
+
}
|
|
10867
|
+
const storedId = readString5(record, "session_key") ?? readString5(record, "sessionKey");
|
|
10868
|
+
const status = (readString5(record, "status") ?? "unknown").toLowerCase();
|
|
10869
|
+
const inflight = typeof record.inflight === "object" && record.inflight !== null ? toRecord3(record.inflight) : void 0;
|
|
10870
|
+
const running = readBoolean(record.running) || status === "working" || status === "waiting" || status === "starting" || inflight !== void 0 && Object.keys(inflight).length > 0;
|
|
10871
|
+
const title = readString5(record, "title");
|
|
10872
|
+
const preview = readString5(record, "preview");
|
|
10873
|
+
const model = readString5(record, "model");
|
|
10874
|
+
const lastActiveAt = readFiniteNumber(record.last_active);
|
|
10875
|
+
const startedAt = readFiniteNumber(record.started_at);
|
|
10876
|
+
return {
|
|
10877
|
+
runtimeSessionId: runtimeId,
|
|
10878
|
+
storedSessionId: storedId ?? runtimeId,
|
|
10879
|
+
status,
|
|
10880
|
+
running,
|
|
10881
|
+
...readBoolean(record.current) ? { current: true } : {},
|
|
10882
|
+
...title ? { title } : {},
|
|
10883
|
+
...preview ? { preview } : {},
|
|
10884
|
+
...model ? { model } : {},
|
|
10885
|
+
...lastActiveAt !== void 0 ? { lastActiveAt } : {},
|
|
10886
|
+
...startedAt !== void 0 ? { startedAt } : {},
|
|
10887
|
+
...inflight ? { inflight } : {},
|
|
10888
|
+
raw: record
|
|
10889
|
+
};
|
|
10890
|
+
}
|
|
10765
10891
|
async function dispatchTuiGatewayCommand(input) {
|
|
10766
10892
|
const profileName = normalizeProfileName2(input.profileName);
|
|
10767
10893
|
const client = await getTuiGatewayClient({
|
|
@@ -11009,32 +11135,50 @@ async function setTuiGatewaySessionModelConfig(input) {
|
|
|
11009
11135
|
}
|
|
11010
11136
|
const raw = [];
|
|
11011
11137
|
if (model) {
|
|
11012
|
-
const result = await
|
|
11013
|
-
|
|
11014
|
-
|
|
11015
|
-
|
|
11016
|
-
|
|
11017
|
-
|
|
11018
|
-
|
|
11019
|
-
|
|
11020
|
-
|
|
11021
|
-
|
|
11022
|
-
|
|
11023
|
-
|
|
11024
|
-
|
|
11025
|
-
|
|
11138
|
+
const result = await withSessionBusyRetry(
|
|
11139
|
+
() => client.request(
|
|
11140
|
+
"config.set",
|
|
11141
|
+
{
|
|
11142
|
+
session_id: started.runtimeSessionId,
|
|
11143
|
+
key: "model",
|
|
11144
|
+
value: formatSessionModelConfigValue(
|
|
11145
|
+
model,
|
|
11146
|
+
input.provider,
|
|
11147
|
+
hasNativeSessionModelConfig
|
|
11148
|
+
),
|
|
11149
|
+
confirm_expensive_model: true
|
|
11150
|
+
},
|
|
11151
|
+
REQUEST_TIMEOUT_MS
|
|
11152
|
+
)
|
|
11153
|
+
).catch((error) => {
|
|
11154
|
+
throw withBusyContext(error, {
|
|
11155
|
+
profileName,
|
|
11156
|
+
runtimeSessionId: started.runtimeSessionId,
|
|
11157
|
+
storedSessionId: started.storedSessionId,
|
|
11158
|
+
method: "config.set:model"
|
|
11159
|
+
});
|
|
11160
|
+
});
|
|
11026
11161
|
raw.push(result);
|
|
11027
11162
|
}
|
|
11028
11163
|
if (reasoningEffort) {
|
|
11029
|
-
const result = await
|
|
11030
|
-
|
|
11031
|
-
|
|
11032
|
-
|
|
11033
|
-
|
|
11034
|
-
|
|
11035
|
-
|
|
11036
|
-
|
|
11037
|
-
|
|
11164
|
+
const result = await withSessionBusyRetry(
|
|
11165
|
+
() => client.request(
|
|
11166
|
+
"config.set",
|
|
11167
|
+
{
|
|
11168
|
+
session_id: started.runtimeSessionId,
|
|
11169
|
+
key: "reasoning",
|
|
11170
|
+
value: reasoningEffort
|
|
11171
|
+
},
|
|
11172
|
+
REQUEST_TIMEOUT_MS
|
|
11173
|
+
)
|
|
11174
|
+
).catch((error) => {
|
|
11175
|
+
throw withBusyContext(error, {
|
|
11176
|
+
profileName,
|
|
11177
|
+
runtimeSessionId: started.runtimeSessionId,
|
|
11178
|
+
storedSessionId: started.storedSessionId,
|
|
11179
|
+
method: "config.set:reasoning"
|
|
11180
|
+
});
|
|
11181
|
+
});
|
|
11038
11182
|
raw.push(result);
|
|
11039
11183
|
}
|
|
11040
11184
|
rememberSessionRef(profileName, input.hermesSessionId, started);
|
|
@@ -11706,12 +11850,21 @@ var TuiGatewayClient = class {
|
|
|
11706
11850
|
cleanup();
|
|
11707
11851
|
};
|
|
11708
11852
|
input.signal?.addEventListener("abort", abort, { once: true });
|
|
11709
|
-
void
|
|
11710
|
-
|
|
11711
|
-
|
|
11712
|
-
|
|
11853
|
+
void withSessionBusyRetry(
|
|
11854
|
+
() => this.request(
|
|
11855
|
+
"prompt.submit",
|
|
11856
|
+
{ session_id: input.sessionId, text: input.text },
|
|
11857
|
+
PROMPT_SUBMIT_TIMEOUT_MS
|
|
11858
|
+
),
|
|
11859
|
+
input.signal
|
|
11713
11860
|
).catch((error) => {
|
|
11714
|
-
queue.pushError(
|
|
11861
|
+
queue.pushError(
|
|
11862
|
+
withBusyContext(error, {
|
|
11863
|
+
profileName: this.profileName,
|
|
11864
|
+
runtimeSessionId: input.sessionId,
|
|
11865
|
+
method: "prompt.submit"
|
|
11866
|
+
})
|
|
11867
|
+
);
|
|
11715
11868
|
});
|
|
11716
11869
|
queue.closed.finally(cleanup).catch(() => void 0);
|
|
11717
11870
|
return queue;
|
|
@@ -11845,7 +11998,7 @@ var TuiGatewayClient = class {
|
|
|
11845
11998
|
this.pending.delete(frame.id);
|
|
11846
11999
|
clearTimeout(pending.timer);
|
|
11847
12000
|
if (frame.error) {
|
|
11848
|
-
pending.reject(
|
|
12001
|
+
pending.reject(createTuiGatewayRpcError(frame.error));
|
|
11849
12002
|
} else {
|
|
11850
12003
|
pending.resolve(frame.result);
|
|
11851
12004
|
}
|
|
@@ -12546,6 +12699,19 @@ function readString5(payload, key) {
|
|
|
12546
12699
|
const value = payload[key];
|
|
12547
12700
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
12548
12701
|
}
|
|
12702
|
+
function readBoolean(value) {
|
|
12703
|
+
return value === true || value === "true" || value === 1;
|
|
12704
|
+
}
|
|
12705
|
+
function readFiniteNumber(value) {
|
|
12706
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
12707
|
+
return value;
|
|
12708
|
+
}
|
|
12709
|
+
if (typeof value === "string" && value.trim()) {
|
|
12710
|
+
const parsed = Number.parseFloat(value);
|
|
12711
|
+
return Number.isFinite(parsed) ? parsed : void 0;
|
|
12712
|
+
}
|
|
12713
|
+
return void 0;
|
|
12714
|
+
}
|
|
12549
12715
|
function readStringList2(value) {
|
|
12550
12716
|
if (!Array.isArray(value)) {
|
|
12551
12717
|
return [];
|
|
@@ -17481,7 +17647,10 @@ var ConversationOrchestrationCoordinator = class {
|
|
|
17481
17647
|
);
|
|
17482
17648
|
}
|
|
17483
17649
|
startRunWorkerAndDrain(conversationId, runId, input) {
|
|
17484
|
-
|
|
17650
|
+
let shouldDrainQueue = true;
|
|
17651
|
+
void this.deps.runLifecycle.startRunWorker(conversationId, runId, input).then((outcome) => {
|
|
17652
|
+
shouldDrainQueue = outcome.shouldDrainQueue;
|
|
17653
|
+
}).catch(async (error) => {
|
|
17485
17654
|
if (isConversationNotFoundError(error)) {
|
|
17486
17655
|
return;
|
|
17487
17656
|
}
|
|
@@ -17507,6 +17676,9 @@ var ConversationOrchestrationCoordinator = class {
|
|
|
17507
17676
|
});
|
|
17508
17677
|
}
|
|
17509
17678
|
}).finally(() => {
|
|
17679
|
+
if (!shouldDrainQueue) {
|
|
17680
|
+
return;
|
|
17681
|
+
}
|
|
17510
17682
|
void this.startNextQueuedRun(conversationId).catch((error) => {
|
|
17511
17683
|
void this.deps.logger.warn("conversation_queue_drain_failed", {
|
|
17512
17684
|
conversation_id: conversationId,
|
|
@@ -20514,7 +20686,7 @@ function isNodeError11(error, code) {
|
|
|
20514
20686
|
}
|
|
20515
20687
|
|
|
20516
20688
|
// src/conversations/hermes-session-sync.ts
|
|
20517
|
-
import { randomUUID as randomUUID11 } from "crypto";
|
|
20689
|
+
import { createHash as createHash6, randomUUID as randomUUID11 } from "crypto";
|
|
20518
20690
|
import { readdir as readdir7, readFile as readFile11, stat as stat12 } from "fs/promises";
|
|
20519
20691
|
import path21 from "path";
|
|
20520
20692
|
|
|
@@ -21074,6 +21246,131 @@ async function syncHermesCronSessionIntoConversations(paths, logger, input) {
|
|
|
21074
21246
|
reprojected: false
|
|
21075
21247
|
};
|
|
21076
21248
|
}
|
|
21249
|
+
async function syncHermesConversationMessages(paths, logger, input) {
|
|
21250
|
+
const store = input.store ?? new ConversationStore(paths);
|
|
21251
|
+
const manifest = await store.readActiveManifest(input.conversationId);
|
|
21252
|
+
const snapshot = await store.readSnapshot(input.conversationId);
|
|
21253
|
+
const targets = collectHermesSessionDeleteTargets(manifest, snapshot);
|
|
21254
|
+
const result = {
|
|
21255
|
+
conversation_id: input.conversationId,
|
|
21256
|
+
hermes_session_ids: targets.map((target) => target.sessionId),
|
|
21257
|
+
appended_count: 0,
|
|
21258
|
+
changed: false,
|
|
21259
|
+
errors: []
|
|
21260
|
+
};
|
|
21261
|
+
if (targets.length === 0) {
|
|
21262
|
+
return result;
|
|
21263
|
+
}
|
|
21264
|
+
const candidates = await collectConversationSyncCandidates(targets);
|
|
21265
|
+
const knownExactKeys = collectKnownHermesRowKeys(snapshot);
|
|
21266
|
+
const representedMessages = collectRepresentedMessageSignatures(snapshot);
|
|
21267
|
+
const projectedMessages = [];
|
|
21268
|
+
const candidateProfiles = /* @__PURE__ */ new Map();
|
|
21269
|
+
for (const candidate of candidates) {
|
|
21270
|
+
try {
|
|
21271
|
+
const candidateMessages = await readHermesLineageMessages(candidate);
|
|
21272
|
+
if (candidateMessages.length === 0) {
|
|
21273
|
+
continue;
|
|
21274
|
+
}
|
|
21275
|
+
const profile = await resolveConversationProfileTarget(
|
|
21276
|
+
paths,
|
|
21277
|
+
candidate.profileName
|
|
21278
|
+
);
|
|
21279
|
+
candidateProfiles.set(conversationSyncCandidateKey(candidate), {
|
|
21280
|
+
candidate,
|
|
21281
|
+
profileUid: profile.profileUid,
|
|
21282
|
+
profileName: profile.profileName
|
|
21283
|
+
});
|
|
21284
|
+
projectedMessages.push(
|
|
21285
|
+
...toLinkMessages({
|
|
21286
|
+
conversationId: input.conversationId,
|
|
21287
|
+
profileName: profile.profileName,
|
|
21288
|
+
profileUid: profile.profileUid,
|
|
21289
|
+
profileDisplayName: profile.profileDisplayName,
|
|
21290
|
+
sessionId: candidate.session.id,
|
|
21291
|
+
messages: candidateMessages
|
|
21292
|
+
})
|
|
21293
|
+
);
|
|
21294
|
+
} catch (error) {
|
|
21295
|
+
result.errors.push({
|
|
21296
|
+
profile: candidate.profileName,
|
|
21297
|
+
session_id: candidate.session.id,
|
|
21298
|
+
message: error instanceof Error ? error.message : String(error)
|
|
21299
|
+
});
|
|
21300
|
+
}
|
|
21301
|
+
}
|
|
21302
|
+
const appendedMessages = [];
|
|
21303
|
+
for (const message of projectedMessages.sort(compareLinkMessagesByCreatedAt)) {
|
|
21304
|
+
const exactKeys = collectMessageHermesRowKeys(message);
|
|
21305
|
+
if (exactKeys.length > 0 && exactKeys.every((key) => knownExactKeys.has(key))) {
|
|
21306
|
+
consumeRepresentedMessageSignature(representedMessages, message);
|
|
21307
|
+
continue;
|
|
21308
|
+
}
|
|
21309
|
+
if (consumeRepresentedMessageSignature(representedMessages, message)) {
|
|
21310
|
+
exactKeys.forEach((key) => knownExactKeys.add(key));
|
|
21311
|
+
continue;
|
|
21312
|
+
}
|
|
21313
|
+
appendedMessages.push(message);
|
|
21314
|
+
exactKeys.forEach((key) => knownExactKeys.add(key));
|
|
21315
|
+
}
|
|
21316
|
+
if (appendedMessages.length === 0) {
|
|
21317
|
+
return result;
|
|
21318
|
+
}
|
|
21319
|
+
await store.writeSnapshot(input.conversationId, {
|
|
21320
|
+
...snapshot,
|
|
21321
|
+
messages: [...snapshot.messages, ...appendedMessages]
|
|
21322
|
+
});
|
|
21323
|
+
await hydrateImportedConversationMedia({
|
|
21324
|
+
paths,
|
|
21325
|
+
store,
|
|
21326
|
+
logger,
|
|
21327
|
+
conversationId: input.conversationId
|
|
21328
|
+
});
|
|
21329
|
+
const hydratedSnapshot = await store.readSnapshot(input.conversationId);
|
|
21330
|
+
const appendedIds = new Set(appendedMessages.map((message) => message.id));
|
|
21331
|
+
const hydratedAppendedMessages = hydratedSnapshot.messages.filter(
|
|
21332
|
+
(message) => appendedIds.has(message.id)
|
|
21333
|
+
);
|
|
21334
|
+
let nextManifest = await store.readManifest(input.conversationId);
|
|
21335
|
+
for (const profile of candidateProfiles.values()) {
|
|
21336
|
+
nextManifest = mergeHermesLineageIntoManifest({
|
|
21337
|
+
manifest: nextManifest,
|
|
21338
|
+
candidate: profile.candidate,
|
|
21339
|
+
snapshot: hydratedSnapshot,
|
|
21340
|
+
profileUid: profile.profileUid,
|
|
21341
|
+
profileName: profile.profileName,
|
|
21342
|
+
updatedAt: isoFromHermesTime(profile.candidate.session.last_active) ?? nextManifest.updated_at
|
|
21343
|
+
});
|
|
21344
|
+
}
|
|
21345
|
+
const stats = buildConversationStats(nextManifest, hydratedSnapshot);
|
|
21346
|
+
nextManifest = {
|
|
21347
|
+
...nextManifest,
|
|
21348
|
+
stats,
|
|
21349
|
+
updated_at: latestTimestamp(
|
|
21350
|
+
nextManifest.updated_at,
|
|
21351
|
+
latestMessageTimestamp(hydratedAppendedMessages)
|
|
21352
|
+
)
|
|
21353
|
+
};
|
|
21354
|
+
await store.writeManifest(nextManifest);
|
|
21355
|
+
await upsertConversationStats(paths, toStatsIndexRecord(nextManifest, stats));
|
|
21356
|
+
const appendEvent = input.appendEvent ?? ((conversationId, event) => store.appendEvent(conversationId, event));
|
|
21357
|
+
let lastEventSeq;
|
|
21358
|
+
for (const message of hydratedAppendedMessages) {
|
|
21359
|
+
const event = await appendEvent(input.conversationId, {
|
|
21360
|
+
type: "message.created",
|
|
21361
|
+
message_id: message.id,
|
|
21362
|
+
payload: { message, imported_from: "hermes" },
|
|
21363
|
+
raw: message.raw
|
|
21364
|
+
});
|
|
21365
|
+
lastEventSeq = event.seq;
|
|
21366
|
+
}
|
|
21367
|
+
result.appended_count = hydratedAppendedMessages.length;
|
|
21368
|
+
result.changed = hydratedAppendedMessages.length > 0;
|
|
21369
|
+
if (lastEventSeq !== void 0) {
|
|
21370
|
+
result.last_event_seq = lastEventSeq;
|
|
21371
|
+
}
|
|
21372
|
+
return result;
|
|
21373
|
+
}
|
|
21077
21374
|
async function importHermesSession(input) {
|
|
21078
21375
|
const { paths, store, logger, candidate } = input;
|
|
21079
21376
|
const profile = await resolveConversationProfileTarget(
|
|
@@ -21285,6 +21582,143 @@ function findKnownConversationIdsForCandidate(known, candidate) {
|
|
|
21285
21582
|
}
|
|
21286
21583
|
return conversationIds;
|
|
21287
21584
|
}
|
|
21585
|
+
async function collectConversationSyncCandidates(targets) {
|
|
21586
|
+
const sessionIdsByProfile = /* @__PURE__ */ new Map();
|
|
21587
|
+
for (const target of targets) {
|
|
21588
|
+
const profileName = target.profileName.trim() || DEFAULT_PROFILE_NAME;
|
|
21589
|
+
const sessionId = target.sessionId.trim();
|
|
21590
|
+
if (!sessionId) {
|
|
21591
|
+
continue;
|
|
21592
|
+
}
|
|
21593
|
+
const current = sessionIdsByProfile.get(profileName) ?? /* @__PURE__ */ new Set();
|
|
21594
|
+
current.add(sessionId);
|
|
21595
|
+
sessionIdsByProfile.set(profileName, current);
|
|
21596
|
+
}
|
|
21597
|
+
const candidates = [];
|
|
21598
|
+
const seenCandidates = /* @__PURE__ */ new Set();
|
|
21599
|
+
for (const [profileName, targetSessionIds] of sessionIdsByProfile) {
|
|
21600
|
+
const profileDir = resolveHermesProfileDir(profileName);
|
|
21601
|
+
const dbPath = path21.join(profileDir, "state.db");
|
|
21602
|
+
const coveredSessionIds = /* @__PURE__ */ new Set();
|
|
21603
|
+
const sessions = await listProfileSessions(dbPath).catch(() => []);
|
|
21604
|
+
for (const session of sessions) {
|
|
21605
|
+
if (isDeletedSession(session) || isHiddenSession(session)) {
|
|
21606
|
+
continue;
|
|
21607
|
+
}
|
|
21608
|
+
const candidate = {
|
|
21609
|
+
profileName,
|
|
21610
|
+
profileDir,
|
|
21611
|
+
dbPath,
|
|
21612
|
+
session
|
|
21613
|
+
};
|
|
21614
|
+
const candidateSessionIds = lineageSessionIds(candidate);
|
|
21615
|
+
if (!candidateSessionIds.some(
|
|
21616
|
+
(sessionId) => targetSessionIds.has(sessionId)
|
|
21617
|
+
)) {
|
|
21618
|
+
continue;
|
|
21619
|
+
}
|
|
21620
|
+
for (const sessionId of candidateSessionIds) {
|
|
21621
|
+
if (targetSessionIds.has(sessionId)) {
|
|
21622
|
+
coveredSessionIds.add(sessionId);
|
|
21623
|
+
}
|
|
21624
|
+
}
|
|
21625
|
+
const key = conversationSyncCandidateKey(candidate);
|
|
21626
|
+
if (!seenCandidates.has(key)) {
|
|
21627
|
+
seenCandidates.add(key);
|
|
21628
|
+
candidates.push(candidate);
|
|
21629
|
+
}
|
|
21630
|
+
}
|
|
21631
|
+
for (const sessionId of targetSessionIds) {
|
|
21632
|
+
if (coveredSessionIds.has(sessionId)) {
|
|
21633
|
+
continue;
|
|
21634
|
+
}
|
|
21635
|
+
const candidate = {
|
|
21636
|
+
profileName,
|
|
21637
|
+
profileDir,
|
|
21638
|
+
dbPath,
|
|
21639
|
+
session: { id: sessionId }
|
|
21640
|
+
};
|
|
21641
|
+
const key = conversationSyncCandidateKey(candidate);
|
|
21642
|
+
if (!seenCandidates.has(key)) {
|
|
21643
|
+
seenCandidates.add(key);
|
|
21644
|
+
candidates.push(candidate);
|
|
21645
|
+
}
|
|
21646
|
+
}
|
|
21647
|
+
}
|
|
21648
|
+
return candidates;
|
|
21649
|
+
}
|
|
21650
|
+
function conversationSyncCandidateKey(candidate) {
|
|
21651
|
+
return `${candidate.profileName}\0${candidate.session.id}`;
|
|
21652
|
+
}
|
|
21653
|
+
function collectKnownHermesRowKeys(snapshot) {
|
|
21654
|
+
const keys = /* @__PURE__ */ new Set();
|
|
21655
|
+
for (const message of snapshot.messages) {
|
|
21656
|
+
collectMessageHermesRowKeys(message).forEach((key) => keys.add(key));
|
|
21657
|
+
}
|
|
21658
|
+
return keys;
|
|
21659
|
+
}
|
|
21660
|
+
function collectMessageHermesRowKeys(message) {
|
|
21661
|
+
const keys = [];
|
|
21662
|
+
readHermesRawMessageRows(message.raw).forEach((row, index) => {
|
|
21663
|
+
keys.push(hermesRowKey(row, index));
|
|
21664
|
+
});
|
|
21665
|
+
const hermes = toRecord11(message.hermes);
|
|
21666
|
+
const sessionId = readString11(hermes, "session_id") ?? "";
|
|
21667
|
+
const messageIds = Array.isArray(hermes.message_ids) ? hermes.message_ids : hermes.message_id === void 0 || hermes.message_id === null ? [] : [hermes.message_id];
|
|
21668
|
+
for (const messageId of messageIds) {
|
|
21669
|
+
if (messageId === void 0 || messageId === null) {
|
|
21670
|
+
continue;
|
|
21671
|
+
}
|
|
21672
|
+
keys.push(hermesMessageIdKey(sessionId, messageId));
|
|
21673
|
+
}
|
|
21674
|
+
return [...new Set(keys)];
|
|
21675
|
+
}
|
|
21676
|
+
function collectRepresentedMessageSignatures(snapshot) {
|
|
21677
|
+
const counts = /* @__PURE__ */ new Map();
|
|
21678
|
+
for (const message of snapshot.messages) {
|
|
21679
|
+
const signature = representedMessageSignature(message);
|
|
21680
|
+
if (!signature) {
|
|
21681
|
+
continue;
|
|
21682
|
+
}
|
|
21683
|
+
counts.set(signature, (counts.get(signature) ?? 0) + 1);
|
|
21684
|
+
}
|
|
21685
|
+
return counts;
|
|
21686
|
+
}
|
|
21687
|
+
function consumeRepresentedMessageSignature(counts, message) {
|
|
21688
|
+
const signature = representedMessageSignature(message);
|
|
21689
|
+
if (!signature) {
|
|
21690
|
+
return false;
|
|
21691
|
+
}
|
|
21692
|
+
const count = counts.get(signature) ?? 0;
|
|
21693
|
+
if (count <= 0) {
|
|
21694
|
+
return false;
|
|
21695
|
+
}
|
|
21696
|
+
if (count === 1) {
|
|
21697
|
+
counts.delete(signature);
|
|
21698
|
+
} else {
|
|
21699
|
+
counts.set(signature, count - 1);
|
|
21700
|
+
}
|
|
21701
|
+
return true;
|
|
21702
|
+
}
|
|
21703
|
+
function representedMessageSignature(message) {
|
|
21704
|
+
const text = messageText(message);
|
|
21705
|
+
if (!text) {
|
|
21706
|
+
return null;
|
|
21707
|
+
}
|
|
21708
|
+
return `${message.role}:${hashText(text)}`;
|
|
21709
|
+
}
|
|
21710
|
+
function hashText(value) {
|
|
21711
|
+
return createHash6("sha256").update(value).digest("hex");
|
|
21712
|
+
}
|
|
21713
|
+
function compareLinkMessagesByCreatedAt(left, right) {
|
|
21714
|
+
return Date.parse(left.created_at) - Date.parse(right.created_at) || left.id.localeCompare(right.id);
|
|
21715
|
+
}
|
|
21716
|
+
function latestMessageTimestamp(messages2) {
|
|
21717
|
+
return messages2.reduce(
|
|
21718
|
+
(latest, message) => latestTimestamp(latest, message.updated_at || message.created_at),
|
|
21719
|
+
messages2[0]?.updated_at ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
21720
|
+
);
|
|
21721
|
+
}
|
|
21288
21722
|
function lineageSessionIds(candidate) {
|
|
21289
21723
|
return normalizeSessionIds([
|
|
21290
21724
|
candidate.session._lineage_root_id,
|
|
@@ -21574,9 +22008,12 @@ function appendHermesRowOnce(rows, seen, row) {
|
|
|
21574
22008
|
}
|
|
21575
22009
|
function hermesRowKey(row, fallbackIndex) {
|
|
21576
22010
|
if (row.id !== void 0 && row.id !== null) {
|
|
21577
|
-
return
|
|
22011
|
+
return hermesMessageIdKey(readString11(row, "session_id") ?? "", row.id);
|
|
21578
22012
|
}
|
|
21579
|
-
return `fallback:${fallbackIndex}:${row.role ?? ""}:${row.timestamp ?? ""}:${normalizeContent2(row.content)}`;
|
|
22013
|
+
return `fallback:${readString11(row, "session_id") ?? ""}:${fallbackIndex}:${row.role ?? ""}:${row.timestamp ?? ""}:${normalizeContent2(row.content)}`;
|
|
22014
|
+
}
|
|
22015
|
+
function hermesMessageIdKey(sessionId, messageId) {
|
|
22016
|
+
return `id:${sessionId}:${String(messageId)}`;
|
|
21580
22017
|
}
|
|
21581
22018
|
function hasHermesToolMetadata(row) {
|
|
21582
22019
|
return normalizeMessageRole(row.role) === "tool" || readHermesToolCalls(row).length > 0 || Boolean(readString11(row, "tool_call_id")) || Boolean(readString11(row, "tool_name"));
|
|
@@ -22629,13 +23066,13 @@ function toRecord11(value) {
|
|
|
22629
23066
|
return typeof value === "object" && value !== null ? value : {};
|
|
22630
23067
|
}
|
|
22631
23068
|
function isDeletedSession(session) {
|
|
22632
|
-
return
|
|
23069
|
+
return readBoolean2(session.deleted) || readBoolean2(session.is_deleted) || Boolean(readString11(session, "deleted_at")) || ["deleted", "removed"].includes(readString11(session, "status") ?? "");
|
|
22633
23070
|
}
|
|
22634
23071
|
function isHiddenSession(session) {
|
|
22635
23072
|
const source = readString11(session, "source")?.toLowerCase();
|
|
22636
23073
|
const status = readString11(session, "status")?.toLowerCase();
|
|
22637
23074
|
const visibility = readString11(session, "visibility")?.toLowerCase();
|
|
22638
|
-
return Boolean(source && HIDDEN_SESSION_SOURCES.has(source)) ||
|
|
23075
|
+
return Boolean(source && HIDDEN_SESSION_SOURCES.has(source)) || readBoolean2(session.hidden) || readBoolean2(session.archived) || Boolean(readString11(session, "archived_at")) || status === "hidden" || status === "archived" || visibility === "hidden" || visibility === "hide";
|
|
22639
23076
|
}
|
|
22640
23077
|
function readTableColumns2(db, tableName) {
|
|
22641
23078
|
try {
|
|
@@ -22676,7 +23113,7 @@ function readString11(payload, key) {
|
|
|
22676
23113
|
function readNumber3(value) {
|
|
22677
23114
|
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
22678
23115
|
}
|
|
22679
|
-
function
|
|
23116
|
+
function readBoolean2(value) {
|
|
22680
23117
|
if (value === true || value === 1) {
|
|
22681
23118
|
return true;
|
|
22682
23119
|
}
|
|
@@ -22894,7 +23331,7 @@ function isNodeError14(error, code) {
|
|
|
22894
23331
|
}
|
|
22895
23332
|
|
|
22896
23333
|
// src/conversations/run-lifecycle.ts
|
|
22897
|
-
import { createHash as
|
|
23334
|
+
import { createHash as createHash8 } from "crypto";
|
|
22898
23335
|
import { readdir as readdir9 } from "fs/promises";
|
|
22899
23336
|
|
|
22900
23337
|
// src/hermes/api-server.ts
|
|
@@ -23344,9 +23781,9 @@ function parseHermesApiCapabilities(payload) {
|
|
|
23344
23781
|
const runStop = isRecord2(endpoints.run_stop) ? endpoints.run_stop : {};
|
|
23345
23782
|
return {
|
|
23346
23783
|
source: "reported",
|
|
23347
|
-
authRequired:
|
|
23348
|
-
responsesStreaming:
|
|
23349
|
-
runStopPath:
|
|
23784
|
+
authRequired: readBoolean3(auth, "required"),
|
|
23785
|
+
responsesStreaming: readBoolean3(features, "responses_streaming"),
|
|
23786
|
+
runStopPath: readBoolean3(features, "run_stop") === false ? null : readString13(runStop, "path"),
|
|
23350
23787
|
sessionContinuityHeader: readString13(
|
|
23351
23788
|
features,
|
|
23352
23789
|
"session_continuity_header"
|
|
@@ -23588,7 +24025,7 @@ function readString13(payload, key) {
|
|
|
23588
24025
|
const value = payload[key];
|
|
23589
24026
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
23590
24027
|
}
|
|
23591
|
-
function
|
|
24028
|
+
function readBoolean3(payload, key) {
|
|
23592
24029
|
const value = payload[key];
|
|
23593
24030
|
return typeof value === "boolean" ? value : null;
|
|
23594
24031
|
}
|
|
@@ -25964,7 +26401,7 @@ function isNodeError17(error, code) {
|
|
|
25964
26401
|
}
|
|
25965
26402
|
|
|
25966
26403
|
// src/conversations/run-tool-event-ids.ts
|
|
25967
|
-
import { createHash as
|
|
26404
|
+
import { createHash as createHash7 } from "crypto";
|
|
25968
26405
|
var RunToolEventIdCoalescer = class {
|
|
25969
26406
|
scope;
|
|
25970
26407
|
ordinal = 0;
|
|
@@ -26129,7 +26566,7 @@ function stableStringify2(value) {
|
|
|
26129
26566
|
}
|
|
26130
26567
|
}
|
|
26131
26568
|
function hashStableValue(value) {
|
|
26132
|
-
return
|
|
26569
|
+
return createHash7("sha256").update(value).digest("hex").slice(0, 16);
|
|
26133
26570
|
}
|
|
26134
26571
|
function readString17(payload, key) {
|
|
26135
26572
|
const value = payload[key];
|
|
@@ -26646,7 +27083,7 @@ var ConversationRunLifecycle = class {
|
|
|
26646
27083
|
const snapshot = await this.deps.readSnapshot(conversationId);
|
|
26647
27084
|
const run = snapshot.runs.find((item) => item.id === runId);
|
|
26648
27085
|
if (!run || run.status !== "running") {
|
|
26649
|
-
return;
|
|
27086
|
+
return { shouldDrainQueue: true };
|
|
26650
27087
|
}
|
|
26651
27088
|
const controller = new AbortController();
|
|
26652
27089
|
this.deps.activeRunControllers.set(runId, { conversationId, controller });
|
|
@@ -27000,7 +27437,7 @@ var ConversationRunLifecycle = class {
|
|
|
27000
27437
|
return null;
|
|
27001
27438
|
});
|
|
27002
27439
|
if (!response) {
|
|
27003
|
-
return;
|
|
27440
|
+
return { shouldDrainQueue: true };
|
|
27004
27441
|
}
|
|
27005
27442
|
await this.consumeHermesEventStream({
|
|
27006
27443
|
backend,
|
|
@@ -27022,7 +27459,15 @@ var ConversationRunLifecycle = class {
|
|
|
27022
27459
|
reason: "cancelled by app"
|
|
27023
27460
|
})
|
|
27024
27461
|
);
|
|
27025
|
-
return;
|
|
27462
|
+
return { shouldDrainQueue: true };
|
|
27463
|
+
}
|
|
27464
|
+
if (isTuiGatewaySessionBusyError(error)) {
|
|
27465
|
+
await this.markRunBlockedByTuiGatewayBusy({
|
|
27466
|
+
conversationId,
|
|
27467
|
+
runId,
|
|
27468
|
+
error
|
|
27469
|
+
});
|
|
27470
|
+
return { shouldDrainQueue: false };
|
|
27026
27471
|
}
|
|
27027
27472
|
throw error;
|
|
27028
27473
|
} finally {
|
|
@@ -27031,6 +27476,148 @@ var ConversationRunLifecycle = class {
|
|
|
27031
27476
|
}
|
|
27032
27477
|
unregisterDeliveryContext(runId);
|
|
27033
27478
|
}
|
|
27479
|
+
return { shouldDrainQueue: true };
|
|
27480
|
+
}
|
|
27481
|
+
async markRunBlockedByTuiGatewayBusy(input) {
|
|
27482
|
+
const details = input.error instanceof TuiGatewaySessionBusyError ? input.error.details : void 0;
|
|
27483
|
+
const snapshot = await this.deps.readSnapshot(input.conversationId);
|
|
27484
|
+
const run = snapshot.runs.find((item) => item.id === input.runId);
|
|
27485
|
+
if (!run) {
|
|
27486
|
+
return;
|
|
27487
|
+
}
|
|
27488
|
+
const liveSession = await readTuiGatewayLiveSession({
|
|
27489
|
+
profileName: run.profile,
|
|
27490
|
+
runtimeSessionId: details?.runtimeSessionId ?? run.hermes_rpc_session_id,
|
|
27491
|
+
storedSessionId: details?.storedSessionId ?? run.hermes_session_id,
|
|
27492
|
+
logger: this.deps.logger,
|
|
27493
|
+
paths: this.deps.paths
|
|
27494
|
+
}).catch((error) => {
|
|
27495
|
+
void this.deps.logger.debug("tui_gateway_busy_live_probe_failed", {
|
|
27496
|
+
conversation_id: input.conversationId,
|
|
27497
|
+
run_id: input.runId,
|
|
27498
|
+
error: error instanceof Error ? error.message : String(error)
|
|
27499
|
+
});
|
|
27500
|
+
return null;
|
|
27501
|
+
});
|
|
27502
|
+
await this.markRunBlockedByTuiGatewayBusyLocked({
|
|
27503
|
+
conversationId: input.conversationId,
|
|
27504
|
+
runId: input.runId,
|
|
27505
|
+
error: input.error,
|
|
27506
|
+
liveSession,
|
|
27507
|
+
runtimeSessionId: liveSession?.runtimeSessionId ?? details?.runtimeSessionId ?? run.hermes_rpc_session_id,
|
|
27508
|
+
storedSessionId: liveSession?.storedSessionId ?? details?.storedSessionId ?? run.hermes_session_id,
|
|
27509
|
+
method: details?.method
|
|
27510
|
+
});
|
|
27511
|
+
await this.deps.syncConversationMessages(input.conversationId).catch(
|
|
27512
|
+
(error) => {
|
|
27513
|
+
void this.deps.logger.warn("tui_gateway_busy_message_sync_failed", {
|
|
27514
|
+
conversation_id: input.conversationId,
|
|
27515
|
+
run_id: input.runId,
|
|
27516
|
+
error: error instanceof Error ? error.message : String(error)
|
|
27517
|
+
});
|
|
27518
|
+
}
|
|
27519
|
+
);
|
|
27520
|
+
}
|
|
27521
|
+
async markRunBlockedByTuiGatewayBusyLocked(input) {
|
|
27522
|
+
await this.deps.withConversationLock(input.conversationId, async () => {
|
|
27523
|
+
const snapshot = await this.deps.readSnapshot(input.conversationId);
|
|
27524
|
+
const run = snapshot.runs.find((item) => item.id === input.runId);
|
|
27525
|
+
if (!run || run.status !== "running") {
|
|
27526
|
+
return;
|
|
27527
|
+
}
|
|
27528
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
27529
|
+
const assistant = snapshot.messages.find(
|
|
27530
|
+
(item) => item.id === run.assistant_message_id
|
|
27531
|
+
);
|
|
27532
|
+
const errorMessage5 = input.error instanceof Error ? input.error.message : String(input.error);
|
|
27533
|
+
run.hermes_backend = "tui_gateway";
|
|
27534
|
+
run.hermes_rpc_session_id = input.runtimeSessionId;
|
|
27535
|
+
if (input.storedSessionId) {
|
|
27536
|
+
run.hermes_session_id = input.storedSessionId;
|
|
27537
|
+
}
|
|
27538
|
+
run.error_detail = "tui_gateway_session_busy";
|
|
27539
|
+
run.error_message = errorMessage5;
|
|
27540
|
+
if (assistant) {
|
|
27541
|
+
assistant.status = "streaming";
|
|
27542
|
+
assistant.updated_at = now;
|
|
27543
|
+
assistant.hermes = {
|
|
27544
|
+
...assistant.hermes ?? {},
|
|
27545
|
+
upstream_busy: true,
|
|
27546
|
+
upstream_busy_at: now,
|
|
27547
|
+
upstream_busy_message: errorMessage5,
|
|
27548
|
+
...input.method ? { upstream_busy_method: input.method } : {},
|
|
27549
|
+
...input.runtimeSessionId ? { upstream_runtime_session_id: input.runtimeSessionId } : {},
|
|
27550
|
+
...input.storedSessionId ? { upstream_session_id: input.storedSessionId } : {},
|
|
27551
|
+
...input.liveSession ? {
|
|
27552
|
+
upstream_status: input.liveSession.status,
|
|
27553
|
+
upstream_running: input.liveSession.running,
|
|
27554
|
+
...input.liveSession.preview ? { upstream_preview: input.liveSession.preview } : {}
|
|
27555
|
+
} : {}
|
|
27556
|
+
};
|
|
27557
|
+
}
|
|
27558
|
+
await this.deps.writeSnapshot(input.conversationId, snapshot);
|
|
27559
|
+
if (input.storedSessionId) {
|
|
27560
|
+
const manifest = await this.deps.readRunnableManifest(
|
|
27561
|
+
input.conversationId
|
|
27562
|
+
);
|
|
27563
|
+
const nextManifest = addHermesSessionIdForProfileToManifest(
|
|
27564
|
+
manifest,
|
|
27565
|
+
input.storedSessionId,
|
|
27566
|
+
run.profile
|
|
27567
|
+
);
|
|
27568
|
+
if (nextManifest !== manifest) {
|
|
27569
|
+
await this.deps.writeManifest(nextManifest);
|
|
27570
|
+
}
|
|
27571
|
+
}
|
|
27572
|
+
if (assistant) {
|
|
27573
|
+
await this.deps.appendEvent(input.conversationId, {
|
|
27574
|
+
type: "message.updated",
|
|
27575
|
+
message_id: assistant.id,
|
|
27576
|
+
run_id: run.id,
|
|
27577
|
+
payload: {
|
|
27578
|
+
message: assistant,
|
|
27579
|
+
reason: "tui_gateway_session_busy",
|
|
27580
|
+
upstream_busy: true,
|
|
27581
|
+
...input.liveSession ? {
|
|
27582
|
+
upstream_session: sanitizeLiveSessionForEvent(
|
|
27583
|
+
input.liveSession
|
|
27584
|
+
)
|
|
27585
|
+
} : {}
|
|
27586
|
+
}
|
|
27587
|
+
});
|
|
27588
|
+
}
|
|
27589
|
+
const event = await this.deps.appendEvent(input.conversationId, {
|
|
27590
|
+
type: "run.upstream_busy",
|
|
27591
|
+
message_id: assistant?.id,
|
|
27592
|
+
run_id: run.id,
|
|
27593
|
+
payload: {
|
|
27594
|
+
run,
|
|
27595
|
+
reason: "tui_gateway_session_busy",
|
|
27596
|
+
error: errorMessage5,
|
|
27597
|
+
...input.method ? { method: input.method } : {},
|
|
27598
|
+
...input.liveSession ? {
|
|
27599
|
+
upstream_session: sanitizeLiveSessionForEvent(
|
|
27600
|
+
input.liveSession
|
|
27601
|
+
)
|
|
27602
|
+
} : {
|
|
27603
|
+
upstream_session: {
|
|
27604
|
+
...input.runtimeSessionId ? { id: input.runtimeSessionId } : {},
|
|
27605
|
+
...input.storedSessionId ? { session_key: input.storedSessionId } : {}
|
|
27606
|
+
}
|
|
27607
|
+
}
|
|
27608
|
+
}
|
|
27609
|
+
});
|
|
27610
|
+
await this.deps.persistConversationStats(input.conversationId, snapshot);
|
|
27611
|
+
void this.deps.logger.warn("conversation_run_upstream_busy", {
|
|
27612
|
+
conversation_id: input.conversationId,
|
|
27613
|
+
run_id: input.runId,
|
|
27614
|
+
event_seq: event.seq,
|
|
27615
|
+
runtime_session_id: input.runtimeSessionId ?? null,
|
|
27616
|
+
stored_session_id: input.storedSessionId ?? null,
|
|
27617
|
+
upstream_status: input.liveSession?.status ?? null,
|
|
27618
|
+
method: input.method ?? null
|
|
27619
|
+
});
|
|
27620
|
+
});
|
|
27034
27621
|
}
|
|
27035
27622
|
async ensureUsageProbeBeforeRun(input) {
|
|
27036
27623
|
if (!shouldUseHermesUsageProbe(input.backend)) {
|
|
@@ -27256,6 +27843,9 @@ var ConversationRunLifecycle = class {
|
|
|
27256
27843
|
await this.cancelRunAfterAbort(input.conversationId, input.runId);
|
|
27257
27844
|
return;
|
|
27258
27845
|
}
|
|
27846
|
+
if (isTuiGatewaySessionBusyError(error)) {
|
|
27847
|
+
throw error;
|
|
27848
|
+
}
|
|
27259
27849
|
streamError = error;
|
|
27260
27850
|
await this.deps.logger.warn("tui_gateway_event_stream_interrupted", {
|
|
27261
27851
|
backend: input.backend,
|
|
@@ -29040,7 +29630,7 @@ ${details.join("\n")}` : localizedEmptyHermesResponseMessage(language);
|
|
|
29040
29630
|
if (raw.length <= 200) {
|
|
29041
29631
|
return raw;
|
|
29042
29632
|
}
|
|
29043
|
-
return `hermespilot:${
|
|
29633
|
+
return `hermespilot:${createHash8("sha256").update(raw).digest("hex")}`;
|
|
29044
29634
|
}
|
|
29045
29635
|
async assistantMessageIdForRun(conversationId, runId) {
|
|
29046
29636
|
const snapshot = await this.deps.readSnapshot(conversationId).catch(() => null);
|
|
@@ -29672,6 +30262,21 @@ function expirePendingMessageApprovals(message, resolvedAt) {
|
|
|
29672
30262
|
}
|
|
29673
30263
|
return expired;
|
|
29674
30264
|
}
|
|
30265
|
+
function sanitizeLiveSessionForEvent(liveSession) {
|
|
30266
|
+
return {
|
|
30267
|
+
id: liveSession.runtimeSessionId,
|
|
30268
|
+
session_key: liveSession.storedSessionId,
|
|
30269
|
+
status: liveSession.status,
|
|
30270
|
+
running: liveSession.running,
|
|
30271
|
+
...liveSession.current ? { current: true } : {},
|
|
30272
|
+
...liveSession.title ? { title: liveSession.title } : {},
|
|
30273
|
+
...liveSession.preview ? { preview: liveSession.preview } : {},
|
|
30274
|
+
...liveSession.model ? { model: liveSession.model } : {},
|
|
30275
|
+
...liveSession.lastActiveAt !== void 0 ? { last_active: liveSession.lastActiveAt } : {},
|
|
30276
|
+
...liveSession.startedAt !== void 0 ? { started_at: liveSession.startedAt } : {},
|
|
30277
|
+
...liveSession.inflight ? { inflight: liveSession.inflight } : {}
|
|
30278
|
+
};
|
|
30279
|
+
}
|
|
29675
30280
|
function previewText2(message) {
|
|
29676
30281
|
if (!message) {
|
|
29677
30282
|
return null;
|
|
@@ -29680,15 +30285,15 @@ function previewText2(message) {
|
|
|
29680
30285
|
return text ? text.slice(0, 512) : null;
|
|
29681
30286
|
}
|
|
29682
30287
|
function runNotificationSourceEventId(conversationId, runId, eventKind) {
|
|
29683
|
-
const digest =
|
|
30288
|
+
const digest = createHash8("sha256").update(`${conversationId}:${runId}:${eventKind}`).digest("hex").slice(0, 24);
|
|
29684
30289
|
return `${conversationId}:${eventKind}:${digest}`;
|
|
29685
30290
|
}
|
|
29686
30291
|
function approvalNotificationSourceEventId(conversationId, runId, approvalId) {
|
|
29687
|
-
const digest =
|
|
30292
|
+
const digest = createHash8("sha256").update(`${conversationId}:${runId}:${approvalId}:approval_required`).digest("hex").slice(0, 24);
|
|
29688
30293
|
return `${conversationId}:approval_required:${digest}`;
|
|
29689
30294
|
}
|
|
29690
30295
|
function inputRequestNotificationSourceEventId(conversationId, runId, inputRequestId) {
|
|
29691
|
-
const digest =
|
|
30296
|
+
const digest = createHash8("sha256").update(`${conversationId}:${runId}:${inputRequestId}:input_required`).digest("hex").slice(0, 24);
|
|
29692
30297
|
return `${conversationId}:input_required:${digest}`;
|
|
29693
30298
|
}
|
|
29694
30299
|
async function closeSseIterator(iterator) {
|
|
@@ -29799,6 +30404,7 @@ var ConversationService = class {
|
|
|
29799
30404
|
isConversationRunnable: (conversationId) => this.store.isConversationRunnable(conversationId),
|
|
29800
30405
|
writeBlob: (conversationId, input) => this.maintenance.writeBlob(conversationId, input),
|
|
29801
30406
|
syncCronDeliveries: () => this.syncCronDeliveries(),
|
|
30407
|
+
syncConversationMessages: (conversationId) => this.syncHermesConversationMessages(conversationId),
|
|
29802
30408
|
scheduleTitleRefresh: (conversationId) => this.metadata.scheduleGeneratedTitleRefresh(conversationId)
|
|
29803
30409
|
});
|
|
29804
30410
|
this.orchestration = new ConversationOrchestrationCoordinator({
|
|
@@ -30377,6 +30983,16 @@ var ConversationService = class {
|
|
|
30377
30983
|
}
|
|
30378
30984
|
}
|
|
30379
30985
|
}
|
|
30986
|
+
async syncHermesConversationMessages(conversationId) {
|
|
30987
|
+
return this.withConversationLock(
|
|
30988
|
+
conversationId,
|
|
30989
|
+
() => syncHermesConversationMessages(this.paths, this.logger, {
|
|
30990
|
+
conversationId,
|
|
30991
|
+
store: this.store,
|
|
30992
|
+
appendEvent: (targetConversationId, event) => this.appendEvent(targetConversationId, event)
|
|
30993
|
+
})
|
|
30994
|
+
);
|
|
30995
|
+
}
|
|
30380
30996
|
async deliverStagedFiles(stagingDir) {
|
|
30381
30997
|
const target = resolveDeliveryStagingTarget(this.paths, stagingDir);
|
|
30382
30998
|
return this.withConversationLock(target.conversationId, async () => {
|
|
@@ -30446,6 +31062,16 @@ var ConversationService = class {
|
|
|
30446
31062
|
};
|
|
30447
31063
|
}
|
|
30448
31064
|
async getMessages(conversationId, options = {}) {
|
|
31065
|
+
if (options.syncHermes === true && !options.beforeMessageId) {
|
|
31066
|
+
await this.syncHermesConversationMessages(conversationId).catch(
|
|
31067
|
+
(error) => {
|
|
31068
|
+
void this.logger.warn("hermes_conversation_message_sync_failed", {
|
|
31069
|
+
conversation_id: conversationId,
|
|
31070
|
+
error: error instanceof Error ? error.message : String(error)
|
|
31071
|
+
});
|
|
31072
|
+
}
|
|
31073
|
+
);
|
|
31074
|
+
}
|
|
30449
31075
|
return this.queries.getMessages(conversationId, options);
|
|
30450
31076
|
}
|
|
30451
31077
|
async setConversationModel(conversationId, input) {
|
|
@@ -31490,7 +32116,7 @@ function findApproval(snapshot, approvalId) {
|
|
|
31490
32116
|
return null;
|
|
31491
32117
|
}
|
|
31492
32118
|
function cronNotificationSourceEventId(conversationId, jobId, outputPath, eventKind) {
|
|
31493
|
-
const digest =
|
|
32119
|
+
const digest = createHash9("sha256").update(`${conversationId}:${jobId}:${outputPath}:${eventKind}`).digest("hex").slice(0, 24);
|
|
31494
32120
|
return `${conversationId}:${eventKind}:${digest}`;
|
|
31495
32121
|
}
|
|
31496
32122
|
function conversationHermesSessionIds(manifest) {
|
|
@@ -31918,7 +32544,7 @@ function approvalRestartText(language, zh, en) {
|
|
|
31918
32544
|
}
|
|
31919
32545
|
|
|
31920
32546
|
// src/security/devices.ts
|
|
31921
|
-
import { randomBytes as randomBytes3, randomUUID as randomUUID13, timingSafeEqual, createHash as
|
|
32547
|
+
import { randomBytes as randomBytes3, randomUUID as randomUUID13, timingSafeEqual, createHash as createHash10 } from "crypto";
|
|
31922
32548
|
var ACCESS_TOKEN_TTL_MS = 15 * 60 * 1e3;
|
|
31923
32549
|
var REFRESH_TOKEN_TTL_MS = 90 * 24 * 60 * 60 * 1e3;
|
|
31924
32550
|
var DEVICE_SEEN_WRITE_INTERVAL_MS = 60 * 60 * 1e3;
|
|
@@ -32214,7 +32840,7 @@ function randomToken(prefix) {
|
|
|
32214
32840
|
return `${prefix}${randomBytes3(24).toString("base64url")}`;
|
|
32215
32841
|
}
|
|
32216
32842
|
function sha256(value) {
|
|
32217
|
-
return
|
|
32843
|
+
return createHash10("sha256").update(value).digest("hex");
|
|
32218
32844
|
}
|
|
32219
32845
|
function safeEqual(left, right) {
|
|
32220
32846
|
const leftBytes = Buffer.from(left);
|
|
@@ -32494,7 +33120,7 @@ function readPositiveInteger2(value) {
|
|
|
32494
33120
|
}
|
|
32495
33121
|
return void 0;
|
|
32496
33122
|
}
|
|
32497
|
-
function
|
|
33123
|
+
function readBoolean4(value) {
|
|
32498
33124
|
if (typeof value === "boolean") {
|
|
32499
33125
|
return value;
|
|
32500
33126
|
}
|
|
@@ -32615,7 +33241,7 @@ function readMessageAttachments(value) {
|
|
|
32615
33241
|
}
|
|
32616
33242
|
const kind = readAttachmentString(record.kind);
|
|
32617
33243
|
const type = readAttachmentString(record.type);
|
|
32618
|
-
const isVoiceNote =
|
|
33244
|
+
const isVoiceNote = readBoolean4(record.is_voice_note) ?? readBoolean4(record.isVoiceNote);
|
|
32619
33245
|
const durationMs = readPositiveInteger2(record.duration_ms) ?? readPositiveInteger2(record.durationMs);
|
|
32620
33246
|
const waveform = readAttachmentWaveform2(
|
|
32621
33247
|
record.waveform ?? record.waveform_samples ?? record.waveformSamples
|
|
@@ -32909,7 +33535,8 @@ function registerConversationRoutes(router, options) {
|
|
|
32909
33535
|
ctx.set("cache-control", "no-store");
|
|
32910
33536
|
const result = await conversations.getMessages(ctx.params.conversationId, {
|
|
32911
33537
|
limit: readLimit(ctx.query.limit),
|
|
32912
|
-
beforeMessageId: readQueryString(ctx.query.before_message_id) ?? readQueryString(ctx.query.before)
|
|
33538
|
+
beforeMessageId: readQueryString(ctx.query.before_message_id) ?? readQueryString(ctx.query.before),
|
|
33539
|
+
syncHermes: readConversationMessagesSyncHermes(ctx.query)
|
|
32913
33540
|
});
|
|
32914
33541
|
ctx.body = {
|
|
32915
33542
|
ok: true,
|
|
@@ -33496,6 +34123,13 @@ function registerConversationRoutes(router, options) {
|
|
|
33496
34123
|
}
|
|
33497
34124
|
);
|
|
33498
34125
|
}
|
|
34126
|
+
function readConversationMessagesSyncHermes(query) {
|
|
34127
|
+
const explicit = readBoolean4(query.sync_hermes) ?? readBoolean4(query.syncHermes);
|
|
34128
|
+
if (explicit !== void 0) {
|
|
34129
|
+
return explicit;
|
|
34130
|
+
}
|
|
34131
|
+
return readQueryString(query.sync)?.toLowerCase() === "hermes";
|
|
34132
|
+
}
|
|
33499
34133
|
async function prepareConversationListRead(conversations, logger, auth, options = {}) {
|
|
33500
34134
|
if (options.syncHermesSessions) {
|
|
33501
34135
|
await conversations.syncHermesSessions().catch((error) => {
|
|
@@ -33525,7 +34159,7 @@ function readConversationListCursor(query) {
|
|
|
33525
34159
|
}
|
|
33526
34160
|
function readConversationListForce(query) {
|
|
33527
34161
|
const raw = Array.isArray(query.force) ? query.force[0] : query.force;
|
|
33528
|
-
return
|
|
34162
|
+
return readBoolean4(raw) === true;
|
|
33529
34163
|
}
|
|
33530
34164
|
function readConversationWorkspaceFilter(query) {
|
|
33531
34165
|
const workspace = readQueryString(query.workspace) ?? readQueryString(query.workspace_id);
|
|
@@ -34145,7 +34779,7 @@ function readCronJobUpdateInput(body) {
|
|
|
34145
34779
|
if (repeat !== void 0) {
|
|
34146
34780
|
input.repeat = repeat;
|
|
34147
34781
|
}
|
|
34148
|
-
const enabled =
|
|
34782
|
+
const enabled = readBoolean4(body.enabled);
|
|
34149
34783
|
if (enabled !== void 0) {
|
|
34150
34784
|
input.enabled = enabled;
|
|
34151
34785
|
}
|
|
@@ -34215,7 +34849,7 @@ function assertCronJobId(jobId) {
|
|
|
34215
34849
|
}
|
|
34216
34850
|
|
|
34217
34851
|
// src/http/routes/model-configs.ts
|
|
34218
|
-
import { createHash as
|
|
34852
|
+
import { createHash as createHash11 } from "crypto";
|
|
34219
34853
|
|
|
34220
34854
|
// src/model-catalog/catalog.ts
|
|
34221
34855
|
import { randomInt } from "crypto";
|
|
@@ -35444,7 +36078,7 @@ function modelProviderIdFromParts(input) {
|
|
|
35444
36078
|
input.keyEnv ?? "",
|
|
35445
36079
|
input.authType ?? ""
|
|
35446
36080
|
].join("");
|
|
35447
|
-
return `mp_${
|
|
36081
|
+
return `mp_${createHash11("sha256").update(identity).digest("hex").slice(0, 18)}`;
|
|
35448
36082
|
}
|
|
35449
36083
|
function mergeCredentialState(left, right) {
|
|
35450
36084
|
if (left === "configured" || right === "configured") {
|
|
@@ -35743,7 +36377,7 @@ function readModelConfigInput(body) {
|
|
|
35743
36377
|
body.context_length ?? body.contextLength
|
|
35744
36378
|
),
|
|
35745
36379
|
keyEnv: readString21(body, "key_env") ?? readString21(body, "keyEnv") ?? void 0,
|
|
35746
|
-
setDefault:
|
|
36380
|
+
setDefault: readBoolean4(body.set_default ?? body.setDefault),
|
|
35747
36381
|
reasoningEffort: readString21(body, "reasoning_effort") ?? readString21(body, "reasoningEffort") ?? void 0,
|
|
35748
36382
|
reasoningSupportPolicy: readString21(body, "reasoning_support_policy") ?? readString21(body, "reasoningSupportPolicy") ?? readString21(body, "reasoning_support") ?? readString21(body, "reasoningSupport") ?? void 0,
|
|
35749
36383
|
supportsVision: readNullableBoolean2(
|
|
@@ -35812,14 +36446,14 @@ function readModelConfigImportInput(body) {
|
|
|
35812
36446
|
provider: readString21(body, "provider") ?? readString21(body, "provider_key") ?? readString21(body, "providerKey") ?? void 0,
|
|
35813
36447
|
baseUrl: readString21(body, "base_url") ?? readString21(body, "baseUrl") ?? void 0,
|
|
35814
36448
|
apiMode: readString21(body, "api_mode") ?? readString21(body, "apiMode") ?? void 0,
|
|
35815
|
-
setDefault:
|
|
36449
|
+
setDefault: readBoolean4(body.set_default ?? body.setDefault)
|
|
35816
36450
|
};
|
|
35817
36451
|
}
|
|
35818
36452
|
function shouldReloadGatewayAfterModelConfigChange(body, options = {}) {
|
|
35819
|
-
if (
|
|
36453
|
+
if (readBoolean4(body.skip_gateway_reload ?? body.skipGatewayReload) === true) {
|
|
35820
36454
|
return false;
|
|
35821
36455
|
}
|
|
35822
|
-
if (
|
|
36456
|
+
if (readBoolean4(body.reload_gateway ?? body.reloadGateway) === true) {
|
|
35823
36457
|
return true;
|
|
35824
36458
|
}
|
|
35825
36459
|
return options.defaultReload === true;
|
|
@@ -36055,13 +36689,20 @@ hermeslink logs -f
|
|
|
36055
36689
|
hermeslink logs --all --level debug -f
|
|
36056
36690
|
\`\`\`
|
|
36057
36691
|
|
|
36058
|
-
If the daemon appears stuck,
|
|
36692
|
+
If the daemon appears stuck, tell the user to run this in a separate computer
|
|
36693
|
+
terminal, outside of Hermes Agent:
|
|
36059
36694
|
|
|
36060
36695
|
\`\`\`bash
|
|
36061
36696
|
hermeslink restart
|
|
36062
36697
|
\`\`\`
|
|
36063
36698
|
|
|
36064
|
-
|
|
36699
|
+
Never execute \`hermeslink restart\` yourself through Hermes Agent, terminal
|
|
36700
|
+
tools, shell tools, code execution, or any other in-session tool call.
|
|
36701
|
+
Restarting Hermes Link from inside the current HermesPilot App run can kill the
|
|
36702
|
+
Link/tui_gateway process that is carrying the response, interrupt the event
|
|
36703
|
+
stream, and leave the current run unrecoverable. If a restart is needed, only
|
|
36704
|
+
instruct the user to run it manually in their own terminal, then wait for the
|
|
36705
|
+
mobile app to reconnect.
|
|
36065
36706
|
|
|
36066
36707
|
## Troubleshooting Flow
|
|
36067
36708
|
|
|
@@ -36078,6 +36719,11 @@ Never reveal API keys, access tokens, refresh tokens, private keys, or full .env
|
|
|
36078
36719
|
|
|
36079
36720
|
Do not recommend exposing port 52379 directly to the public internet without TLS, VPN, Tailscale, WireGuard, or another access-control layer.
|
|
36080
36721
|
|
|
36722
|
+
Do not execute Hermes Link service-control commands yourself from inside a
|
|
36723
|
+
Hermes Agent session. This includes \`hermeslink restart\`, \`hermeslink stop\`,
|
|
36724
|
+
\`hermeslink uninstall\`, and Link package update commands. Provide the command
|
|
36725
|
+
for the user to run in an external terminal instead.
|
|
36726
|
+
|
|
36081
36727
|
Do not modify Hermes profiles, delete user data, edit config files, or kill processes unless the user explicitly asks.
|
|
36082
36728
|
`;
|
|
36083
36729
|
async function ensureHermesLinkSkillInstalledForProfiles(options = {}) {
|
|
@@ -37135,14 +37781,14 @@ function readProfilePermissionsInput(body) {
|
|
|
37135
37781
|
containerDisk: readPositiveInteger2(
|
|
37136
37782
|
terminal.container_disk ?? terminal.containerDisk
|
|
37137
37783
|
),
|
|
37138
|
-
containerPersistent:
|
|
37784
|
+
containerPersistent: readBoolean4(
|
|
37139
37785
|
terminal.container_persistent ?? terminal.containerPersistent
|
|
37140
37786
|
)
|
|
37141
37787
|
};
|
|
37142
37788
|
}
|
|
37143
37789
|
const sudo = readOptionalObject(body, "sudo");
|
|
37144
37790
|
if (sudo) {
|
|
37145
|
-
const clear =
|
|
37791
|
+
const clear = readBoolean4(sudo.clear ?? sudo.remove ?? sudo.delete);
|
|
37146
37792
|
const sudoInput = {
|
|
37147
37793
|
password: readRawString2(sudo, "password") ?? readRawString2(sudo, "sudo_password") ?? readRawString2(sudo, "sudoPassword") ?? void 0,
|
|
37148
37794
|
clear: clear === true ? true : void 0
|
|
@@ -37158,7 +37804,7 @@ function readProfilePermissionsInput(body) {
|
|
|
37158
37804
|
toolsets.enabled_toolsets ?? toolsets.enabledToolsets ?? toolsets.enabled,
|
|
37159
37805
|
"toolsets.enabled"
|
|
37160
37806
|
) ?? void 0,
|
|
37161
|
-
mcpEnabled:
|
|
37807
|
+
mcpEnabled: readBoolean4(toolsets.mcp_enabled ?? toolsets.mcpEnabled)
|
|
37162
37808
|
};
|
|
37163
37809
|
}
|
|
37164
37810
|
if (Object.keys(input).length === 0) {
|
|
@@ -38767,7 +39413,7 @@ function booleanSetting(key, label, value) {
|
|
|
38767
39413
|
return {
|
|
38768
39414
|
key,
|
|
38769
39415
|
label,
|
|
38770
|
-
value:
|
|
39416
|
+
value: readBoolean5(value) ?? false,
|
|
38771
39417
|
editable: true,
|
|
38772
39418
|
kind: "boolean"
|
|
38773
39419
|
};
|
|
@@ -38885,7 +39531,7 @@ function readPositiveInteger4(value) {
|
|
|
38885
39531
|
const numberValue = typeof value === "number" ? value : typeof value === "string" ? Number(value.trim()) : NaN;
|
|
38886
39532
|
return Number.isFinite(numberValue) && numberValue > 0 ? Math.floor(numberValue) : void 0;
|
|
38887
39533
|
}
|
|
38888
|
-
function
|
|
39534
|
+
function readBoolean5(value) {
|
|
38889
39535
|
if (typeof value === "boolean") {
|
|
38890
39536
|
return value;
|
|
38891
39537
|
}
|
|
@@ -39151,15 +39797,15 @@ function readMemorySettingsPatch(body, options = {}) {
|
|
|
39151
39797
|
if (containerTag !== void 0) {
|
|
39152
39798
|
input.containerTag = containerTag;
|
|
39153
39799
|
}
|
|
39154
|
-
const autoRecall =
|
|
39800
|
+
const autoRecall = readBoolean4(body.auto_recall ?? body.autoRecall);
|
|
39155
39801
|
if (autoRecall !== void 0) {
|
|
39156
39802
|
input.autoRecall = autoRecall;
|
|
39157
39803
|
}
|
|
39158
|
-
const autoCapture =
|
|
39804
|
+
const autoCapture = readBoolean4(body.auto_capture ?? body.autoCapture);
|
|
39159
39805
|
if (autoCapture !== void 0) {
|
|
39160
39806
|
input.autoCapture = autoCapture;
|
|
39161
39807
|
}
|
|
39162
|
-
const autoRetain =
|
|
39808
|
+
const autoRetain = readBoolean4(body.auto_retain ?? body.autoRetain);
|
|
39163
39809
|
if (autoRetain !== void 0) {
|
|
39164
39810
|
input.autoRetain = autoRetain;
|
|
39165
39811
|
}
|
|
@@ -39229,7 +39875,7 @@ function readMemorySettingsPatch(body, options = {}) {
|
|
|
39229
39875
|
if (writeFrequency) {
|
|
39230
39876
|
input.writeFrequency = writeFrequency;
|
|
39231
39877
|
}
|
|
39232
|
-
const saveMessages =
|
|
39878
|
+
const saveMessages = readBoolean4(body.save_messages ?? body.saveMessages);
|
|
39233
39879
|
if (saveMessages !== void 0) {
|
|
39234
39880
|
input.saveMessages = saveMessages;
|
|
39235
39881
|
}
|
|
@@ -39269,7 +39915,7 @@ function readMemorySettingsPatch(body, options = {}) {
|
|
|
39269
39915
|
if (agentId !== void 0) {
|
|
39270
39916
|
input.agentId = agentId;
|
|
39271
39917
|
}
|
|
39272
|
-
const rerank =
|
|
39918
|
+
const rerank = readBoolean4(body.rerank);
|
|
39273
39919
|
if (rerank !== void 0) {
|
|
39274
39920
|
input.rerank = rerank;
|
|
39275
39921
|
}
|
|
@@ -39293,7 +39939,7 @@ function readMemorySettingsPatch(body, options = {}) {
|
|
|
39293
39939
|
if (dbPath !== void 0) {
|
|
39294
39940
|
input.dbPath = dbPath;
|
|
39295
39941
|
}
|
|
39296
|
-
const autoExtract =
|
|
39942
|
+
const autoExtract = readBoolean4(body.auto_extract ?? body.autoExtract);
|
|
39297
39943
|
if (autoExtract !== void 0) {
|
|
39298
39944
|
input.autoExtract = autoExtract;
|
|
39299
39945
|
}
|
|
@@ -39760,7 +40406,7 @@ function registerProfileSkillRoutes(router, options) {
|
|
|
39760
40406
|
router.patch("/api/v1/profiles/:name/skills/:skillName", async (ctx) => {
|
|
39761
40407
|
await authenticateRequest(ctx, paths);
|
|
39762
40408
|
const body = await readJsonBody(ctx.req);
|
|
39763
|
-
const enabled =
|
|
40409
|
+
const enabled = readBoolean4(body.enabled);
|
|
39764
40410
|
if (enabled === void 0) {
|
|
39765
40411
|
throw new LinkHttpError(
|
|
39766
40412
|
400,
|
|
@@ -41564,6 +42210,7 @@ async function observePublicRoute(options) {
|
|
|
41564
42210
|
"content-type": "application/json",
|
|
41565
42211
|
...options.relayBootstrapToken ? { authorization: `Bearer ${options.relayBootstrapToken}` } : {}
|
|
41566
42212
|
},
|
|
42213
|
+
signal: options.signal,
|
|
41567
42214
|
body: JSON.stringify({
|
|
41568
42215
|
install_id: options.installId,
|
|
41569
42216
|
link_id: options.linkId,
|
|
@@ -41849,7 +42496,8 @@ async function reportLinkStatusToServer(options = {}) {
|
|
|
41849
42496
|
publicKeyPem: identity.public_key_pem,
|
|
41850
42497
|
observePublicRoute: true,
|
|
41851
42498
|
configuredLanHost: config.lanHost,
|
|
41852
|
-
fetchImpl: options.fetchImpl
|
|
42499
|
+
fetchImpl: options.fetchImpl,
|
|
42500
|
+
signal: options.signal
|
|
41853
42501
|
});
|
|
41854
42502
|
const routes = await mergeLastReportedPublicRoutes(paths, discoveredRoutes);
|
|
41855
42503
|
const systemInfo = readLinkSystemInfo();
|
|
@@ -41880,7 +42528,8 @@ async function reportLinkStatusToServer(options = {}) {
|
|
|
41880
42528
|
...payload,
|
|
41881
42529
|
public_key_pem: identity.public_key_pem,
|
|
41882
42530
|
signature
|
|
41883
|
-
})
|
|
42531
|
+
}),
|
|
42532
|
+
signal: options.signal
|
|
41884
42533
|
}
|
|
41885
42534
|
);
|
|
41886
42535
|
const body = await response.json().catch(() => null);
|
|
@@ -41928,19 +42577,28 @@ function startLanIpMonitor(options) {
|
|
|
41928
42577
|
let running = false;
|
|
41929
42578
|
let closed = false;
|
|
41930
42579
|
let current = Promise.resolve();
|
|
42580
|
+
let currentAbortController = null;
|
|
41931
42581
|
const check = (context = {}) => {
|
|
41932
42582
|
if (running || closed) {
|
|
41933
42583
|
return current;
|
|
41934
42584
|
}
|
|
41935
42585
|
running = true;
|
|
42586
|
+
const abortController = new AbortController();
|
|
42587
|
+
currentAbortController = abortController;
|
|
41936
42588
|
current = (async () => {
|
|
41937
42589
|
try {
|
|
41938
|
-
await checkLanIpChange(options, context);
|
|
42590
|
+
await checkLanIpChange({ ...options, signal: abortController.signal }, context);
|
|
41939
42591
|
} catch (error) {
|
|
42592
|
+
if (closed && isAbortError3(error)) {
|
|
42593
|
+
return;
|
|
42594
|
+
}
|
|
41940
42595
|
void options.logger.warn("lan_ip_monitor_failed", {
|
|
41941
42596
|
error: error instanceof Error ? error.message : String(error)
|
|
41942
42597
|
});
|
|
41943
42598
|
} finally {
|
|
42599
|
+
if (currentAbortController === abortController) {
|
|
42600
|
+
currentAbortController = null;
|
|
42601
|
+
}
|
|
41944
42602
|
running = false;
|
|
41945
42603
|
}
|
|
41946
42604
|
})();
|
|
@@ -41959,6 +42617,7 @@ function startLanIpMonitor(options) {
|
|
|
41959
42617
|
async close() {
|
|
41960
42618
|
closed = true;
|
|
41961
42619
|
clearInterval(timer);
|
|
42620
|
+
currentAbortController?.abort();
|
|
41962
42621
|
await current.catch(() => void 0);
|
|
41963
42622
|
}
|
|
41964
42623
|
};
|
|
@@ -41979,7 +42638,8 @@ async function checkLanIpChange(options, context = {}) {
|
|
|
41979
42638
|
publicKeyPem: identity.public_key_pem,
|
|
41980
42639
|
observePublicRoute: context.observePublicRoute === true,
|
|
41981
42640
|
configuredLanHost: config.lanHost,
|
|
41982
|
-
fetchImpl: options.fetchImpl
|
|
42641
|
+
fetchImpl: options.fetchImpl,
|
|
42642
|
+
signal: options.signal
|
|
41983
42643
|
});
|
|
41984
42644
|
const routes = await mergeLastReportedPublicRoutes(options.paths, discoveredRoutes);
|
|
41985
42645
|
if (context.publishToRelay) {
|
|
@@ -42004,7 +42664,8 @@ async function checkLanIpChange(options, context = {}) {
|
|
|
42004
42664
|
const result = await reportLinkStatusToServer({
|
|
42005
42665
|
paths: options.paths,
|
|
42006
42666
|
fetchImpl: options.fetchImpl,
|
|
42007
|
-
routes
|
|
42667
|
+
routes,
|
|
42668
|
+
signal: options.signal
|
|
42008
42669
|
});
|
|
42009
42670
|
if (result) {
|
|
42010
42671
|
options.onNetworkRoutes?.(routes);
|
|
@@ -42022,6 +42683,9 @@ async function checkLanIpChange(options, context = {}) {
|
|
|
42022
42683
|
});
|
|
42023
42684
|
}
|
|
42024
42685
|
}
|
|
42686
|
+
function isAbortError3(error) {
|
|
42687
|
+
return typeof error === "object" && error !== null && "name" in error && error.name === "AbortError";
|
|
42688
|
+
}
|
|
42025
42689
|
|
|
42026
42690
|
// src/daemon/process-guard.ts
|
|
42027
42691
|
var installed = false;
|
|
@@ -44898,7 +45562,7 @@ async function readActiveCronJobCount(profiles, logger) {
|
|
|
44898
45562
|
}, 0);
|
|
44899
45563
|
}
|
|
44900
45564
|
function isActiveCronJob(job) {
|
|
44901
|
-
const enabled =
|
|
45565
|
+
const enabled = readBoolean4(job.enabled) ?? true;
|
|
44902
45566
|
if (!enabled) {
|
|
44903
45567
|
return false;
|
|
44904
45568
|
}
|