@hermespilot/link 0.8.1-beta.1 → 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-WYVLVDXH.js → chunk-IJSMGOPK.js} +1801 -391
- package/dist/cli/index.js +3 -3
- package/dist/http/app.d.ts +14 -0
- package/dist/http/app.js +1 -1
- package/package.json +1 -1
|
@@ -4,8 +4,8 @@ import Router from "@koa/router";
|
|
|
4
4
|
|
|
5
5
|
// src/conversations/conversation-service.ts
|
|
6
6
|
import { EventEmitter } from "events";
|
|
7
|
-
import { createHash as
|
|
8
|
-
import
|
|
7
|
+
import { createHash as createHash9, randomUUID as randomUUID12 } from "crypto";
|
|
8
|
+
import path27 from "path";
|
|
9
9
|
|
|
10
10
|
// src/database/link-database.ts
|
|
11
11
|
import { mkdir } from "fs/promises";
|
|
@@ -207,6 +207,15 @@ var MIGRATIONS = [
|
|
|
207
207
|
ALTER TABLE live_activities ADD COLUMN last_detail_text TEXT;
|
|
208
208
|
ALTER TABLE live_activities ADD COLUMN last_visible_updated_at TEXT;
|
|
209
209
|
`
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
version: 6,
|
|
213
|
+
name: "live_activity_todo_progress",
|
|
214
|
+
sql: `
|
|
215
|
+
ALTER TABLE live_activities ADD COLUMN last_todo_done_count INTEGER;
|
|
216
|
+
ALTER TABLE live_activities ADD COLUMN last_todo_total_count INTEGER;
|
|
217
|
+
ALTER TABLE live_activities ADD COLUMN last_todo_progress_text TEXT;
|
|
218
|
+
`
|
|
210
219
|
}
|
|
211
220
|
];
|
|
212
221
|
async function migrateLinkDatabase(paths) {
|
|
@@ -335,13 +344,16 @@ async function upsertLiveActivity(paths, input) {
|
|
|
335
344
|
last_status_label,
|
|
336
345
|
last_progress_text,
|
|
337
346
|
last_detail_text,
|
|
347
|
+
last_todo_done_count,
|
|
348
|
+
last_todo_total_count,
|
|
349
|
+
last_todo_progress_text,
|
|
338
350
|
last_visible_updated_at,
|
|
339
351
|
push_count,
|
|
340
352
|
started_at,
|
|
341
353
|
ended_at,
|
|
342
354
|
created_at,
|
|
343
355
|
updated_at
|
|
344
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'active', ?, ?, ?, NULL, NULL, ?, ?, ?, ?, 0, ?, NULL, ?, ?)
|
|
356
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'active', ?, ?, ?, NULL, NULL, ?, ?, ?, ?, ?, ?, ?, 0, ?, NULL, ?, ?)
|
|
345
357
|
ON CONFLICT(activity_id) DO UPDATE SET
|
|
346
358
|
activity_token = excluded.activity_token,
|
|
347
359
|
apns_environment = excluded.apns_environment,
|
|
@@ -364,6 +376,9 @@ async function upsertLiveActivity(paths, input) {
|
|
|
364
376
|
last_status_label = excluded.last_status_label,
|
|
365
377
|
last_progress_text = excluded.last_progress_text,
|
|
366
378
|
last_detail_text = excluded.last_detail_text,
|
|
379
|
+
last_todo_done_count = excluded.last_todo_done_count,
|
|
380
|
+
last_todo_total_count = excluded.last_todo_total_count,
|
|
381
|
+
last_todo_progress_text = excluded.last_todo_progress_text,
|
|
367
382
|
last_visible_updated_at = excluded.last_visible_updated_at,
|
|
368
383
|
push_count = 0,
|
|
369
384
|
started_at = excluded.started_at,
|
|
@@ -390,6 +405,9 @@ async function upsertLiveActivity(paths, input) {
|
|
|
390
405
|
restoreState?.statusLabel ?? null,
|
|
391
406
|
restoreState?.progressText ?? null,
|
|
392
407
|
restoreState?.detailText ?? null,
|
|
408
|
+
restoreState?.todoDoneCount ?? null,
|
|
409
|
+
restoreState?.todoTotalCount ?? null,
|
|
410
|
+
restoreState?.todoProgressText ?? null,
|
|
393
411
|
restoreState?.updatedAt ?? null,
|
|
394
412
|
input.startedAt,
|
|
395
413
|
now,
|
|
@@ -481,6 +499,9 @@ async function updateLiveActivityPushState(paths, input) {
|
|
|
481
499
|
last_status_label = ?,
|
|
482
500
|
last_progress_text = ?,
|
|
483
501
|
last_detail_text = ?,
|
|
502
|
+
last_todo_done_count = ?,
|
|
503
|
+
last_todo_total_count = ?,
|
|
504
|
+
last_todo_progress_text = ?,
|
|
484
505
|
last_visible_updated_at = ?,
|
|
485
506
|
push_count = push_count + 1,
|
|
486
507
|
status = CASE WHEN ? THEN 'ended' ELSE status END,
|
|
@@ -495,6 +516,9 @@ async function updateLiveActivityPushState(paths, input) {
|
|
|
495
516
|
normalizeNullableText(input.statusLabel),
|
|
496
517
|
normalizeNullableText(input.progressText),
|
|
497
518
|
normalizeNullableText(input.detailText),
|
|
519
|
+
normalizeNullableCount(input.todoDoneCount),
|
|
520
|
+
normalizeNullableCount(input.todoTotalCount),
|
|
521
|
+
normalizeNullableText(input.todoProgressText),
|
|
498
522
|
input.visibleUpdatedAt,
|
|
499
523
|
input.terminal ? 1 : 0,
|
|
500
524
|
input.terminal ? 1 : 0,
|
|
@@ -1343,6 +1367,9 @@ function liveActivityFromRow(row) {
|
|
|
1343
1367
|
lastStatusLabel: readNullableString(row, "last_status_label"),
|
|
1344
1368
|
lastProgressText: readNullableString(row, "last_progress_text"),
|
|
1345
1369
|
lastDetailText: readNullableString(row, "last_detail_text"),
|
|
1370
|
+
lastTodoDoneCount: readNullableNumber(row, "last_todo_done_count"),
|
|
1371
|
+
lastTodoTotalCount: readNullableNumber(row, "last_todo_total_count"),
|
|
1372
|
+
lastTodoProgressText: readNullableString(row, "last_todo_progress_text"),
|
|
1346
1373
|
lastVisibleUpdatedAt: readNullableString(row, "last_visible_updated_at"),
|
|
1347
1374
|
pushCount: readNumber(row, "push_count"),
|
|
1348
1375
|
startedAt,
|
|
@@ -1358,14 +1385,38 @@ function liveActivityRestoreStateFromRow(row) {
|
|
|
1358
1385
|
if (!phase || !statusLabel || !progressText) {
|
|
1359
1386
|
return null;
|
|
1360
1387
|
}
|
|
1361
|
-
return {
|
|
1388
|
+
return sanitizeLiveActivityRestoreState({
|
|
1362
1389
|
phase,
|
|
1363
1390
|
statusLabel,
|
|
1364
1391
|
progressText,
|
|
1365
1392
|
detailText: readNullableString(row, "last_detail_text"),
|
|
1393
|
+
todoDoneCount: readNullableNumber(row, "last_todo_done_count"),
|
|
1394
|
+
todoTotalCount: readNullableNumber(row, "last_todo_total_count"),
|
|
1395
|
+
todoProgressText: readNullableString(row, "last_todo_progress_text"),
|
|
1366
1396
|
updatedAt: readNullableString(row, "last_visible_updated_at") ?? readNullableString(row, "updated_at")
|
|
1397
|
+
});
|
|
1398
|
+
}
|
|
1399
|
+
function sanitizeLiveActivityRestoreState(state) {
|
|
1400
|
+
if (!isCompletedTodoProgress(state.todoDoneCount, state.todoTotalCount)) {
|
|
1401
|
+
return state;
|
|
1402
|
+
}
|
|
1403
|
+
return {
|
|
1404
|
+
...state,
|
|
1405
|
+
todoDoneCount: null,
|
|
1406
|
+
todoTotalCount: null,
|
|
1407
|
+
todoProgressText: null
|
|
1367
1408
|
};
|
|
1368
1409
|
}
|
|
1410
|
+
function isCompletedTodoProgress(doneCount, totalCount) {
|
|
1411
|
+
if (typeof doneCount !== "number" || !Number.isFinite(doneCount) || typeof totalCount !== "number" || !Number.isFinite(totalCount)) {
|
|
1412
|
+
return false;
|
|
1413
|
+
}
|
|
1414
|
+
const normalizedTotal = Math.max(0, Math.trunc(totalCount));
|
|
1415
|
+
if (normalizedTotal <= 0) {
|
|
1416
|
+
return false;
|
|
1417
|
+
}
|
|
1418
|
+
return Math.max(0, Math.trunc(doneCount)) >= normalizedTotal;
|
|
1419
|
+
}
|
|
1369
1420
|
function isLiveActivityStatus(value) {
|
|
1370
1421
|
return value === "active" || value === "ended" || value === "expired" || value === "invalid";
|
|
1371
1422
|
}
|
|
@@ -1373,6 +1424,12 @@ function normalizeNullableText(value) {
|
|
|
1373
1424
|
const trimmed = value?.trim();
|
|
1374
1425
|
return trimmed ? trimmed : null;
|
|
1375
1426
|
}
|
|
1427
|
+
function normalizeNullableCount(value) {
|
|
1428
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
1429
|
+
return null;
|
|
1430
|
+
}
|
|
1431
|
+
return Math.max(0, Math.trunc(value));
|
|
1432
|
+
}
|
|
1376
1433
|
function rollback(db) {
|
|
1377
1434
|
try {
|
|
1378
1435
|
db.exec("ROLLBACK");
|
|
@@ -1393,6 +1450,20 @@ function readNumber(row, key) {
|
|
|
1393
1450
|
}
|
|
1394
1451
|
return 0;
|
|
1395
1452
|
}
|
|
1453
|
+
function readNullableNumber(row, key) {
|
|
1454
|
+
const value = row?.[key];
|
|
1455
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
1456
|
+
return Math.trunc(value);
|
|
1457
|
+
}
|
|
1458
|
+
if (typeof value === "bigint") {
|
|
1459
|
+
return Number(value);
|
|
1460
|
+
}
|
|
1461
|
+
if (typeof value === "string" && value.trim()) {
|
|
1462
|
+
const parsed = Number(value);
|
|
1463
|
+
return Number.isFinite(parsed) ? Math.trunc(parsed) : null;
|
|
1464
|
+
}
|
|
1465
|
+
return null;
|
|
1466
|
+
}
|
|
1396
1467
|
function readString(row, key) {
|
|
1397
1468
|
const value = row?.[key];
|
|
1398
1469
|
return typeof value === "string" && value.trim() ? value : void 0;
|
|
@@ -2600,6 +2671,7 @@ var AUTH_BACKED_MODEL_PROVIDERS = [
|
|
|
2600
2671
|
var AUTH_BACKED_MODEL_PROVIDER_BY_KEY = new Map(
|
|
2601
2672
|
AUTH_BACKED_MODEL_PROVIDERS.map((provider) => [provider.provider, provider])
|
|
2602
2673
|
);
|
|
2674
|
+
var RESERVED_PROVIDER_CONFIG_KEYS = /* @__PURE__ */ new Set(["auto", "main"]);
|
|
2603
2675
|
function isAuthBackedModelProviderKey(provider) {
|
|
2604
2676
|
return AUTH_BACKED_MODEL_PROVIDER_BY_KEY.has(provider.trim());
|
|
2605
2677
|
}
|
|
@@ -2859,6 +2931,7 @@ async function readHermesModelConfig(profileName = "default", configPath = resol
|
|
|
2859
2931
|
}
|
|
2860
2932
|
async function listHermesModelConfigs(profileName = "default", configPath = resolveHermesConfigPath(profileName)) {
|
|
2861
2933
|
const { config } = await readHermesConfigDocument(configPath);
|
|
2934
|
+
ensureProvidersRecordWithLegacyMigration(config);
|
|
2862
2935
|
const env = await readHermesEnvFile(profileName);
|
|
2863
2936
|
const defaultModel = readModelConfig(config.model).model ?? null;
|
|
2864
2937
|
const defaultReasoningEffort = readProfileReasoningEffort(config);
|
|
@@ -3556,6 +3629,7 @@ async function deleteHermesModelConfig(input, profileName = "default", configPat
|
|
|
3556
3629
|
throw new Error("model id is required");
|
|
3557
3630
|
}
|
|
3558
3631
|
const { document, config, existingRaw } = await readHermesConfigDocument(configPath);
|
|
3632
|
+
ensureProvidersRecordWithLegacyMigration(config);
|
|
3559
3633
|
const env = await readHermesEnvFile(profileName);
|
|
3560
3634
|
const authBackedProviders = await readHermesAuthBackedProviderState(profileName);
|
|
3561
3635
|
const existingModels = readManagedModelConfigs(
|
|
@@ -3669,6 +3743,7 @@ async function saveHermesModelDefaults(input, profileName = "default", configPat
|
|
|
3669
3743
|
);
|
|
3670
3744
|
}
|
|
3671
3745
|
const { document, config, existingRaw } = await readHermesConfigDocument(configPath);
|
|
3746
|
+
ensureProvidersRecordWithLegacyMigration(config);
|
|
3672
3747
|
const env = await readHermesEnvFile(profileName);
|
|
3673
3748
|
const authBackedProviders = await readHermesAuthBackedProviderState(profileName);
|
|
3674
3749
|
if (taskModelId) {
|
|
@@ -4352,10 +4427,32 @@ function ensureProvidersRecordWithLegacyMigration(config) {
|
|
|
4352
4427
|
providers[providerKey] = providers[providerKey] ? mergeProviderConfig(toRecord(providers[providerKey]), nextConfig) : nextConfig;
|
|
4353
4428
|
}
|
|
4354
4429
|
delete config.custom_providers;
|
|
4355
|
-
const providerAliases =
|
|
4430
|
+
const providerAliases = renameReservedProviderConfigs(providers);
|
|
4431
|
+
mergeProviderAliases(providerAliases, mergeDuplicateProviderConfigs(providers));
|
|
4356
4432
|
rewriteProviderReferences(config, providerAliases);
|
|
4357
4433
|
return providers;
|
|
4358
4434
|
}
|
|
4435
|
+
function renameReservedProviderConfigs(providers) {
|
|
4436
|
+
const aliases = /* @__PURE__ */ new Map();
|
|
4437
|
+
for (const key of Object.keys(providers)) {
|
|
4438
|
+
if (!isReservedProviderConfigKey(key)) {
|
|
4439
|
+
continue;
|
|
4440
|
+
}
|
|
4441
|
+
const nextKey = uniqueProviderConfigKey(
|
|
4442
|
+
`${slugifyProviderKey(key)}-provider`,
|
|
4443
|
+
providers
|
|
4444
|
+
);
|
|
4445
|
+
providers[nextKey] = providers[key];
|
|
4446
|
+
delete providers[key];
|
|
4447
|
+
aliases.set(key, nextKey);
|
|
4448
|
+
}
|
|
4449
|
+
return aliases;
|
|
4450
|
+
}
|
|
4451
|
+
function mergeProviderAliases(aliases, nextAliases) {
|
|
4452
|
+
for (const [from, to] of nextAliases) {
|
|
4453
|
+
aliases.set(from, to);
|
|
4454
|
+
}
|
|
4455
|
+
}
|
|
4359
4456
|
function mergeDuplicateProviderConfigs(providers) {
|
|
4360
4457
|
const groupKeys = /* @__PURE__ */ new Map();
|
|
4361
4458
|
const aliases = /* @__PURE__ */ new Map();
|
|
@@ -4545,14 +4642,18 @@ function providerConfigKeyBase(provider, providerName, baseUrl) {
|
|
|
4545
4642
|
return slugifyProviderKey(fromProvider || fromName || host || "provider");
|
|
4546
4643
|
}
|
|
4547
4644
|
function uniqueProviderConfigKey(base, existing) {
|
|
4548
|
-
|
|
4645
|
+
const normalizedBase = isReservedProviderConfigKey(base) ? `${base}-provider` : base;
|
|
4646
|
+
let key = normalizedBase || "provider";
|
|
4549
4647
|
let index = 2;
|
|
4550
|
-
while (Object.prototype.hasOwnProperty.call(existing, key)) {
|
|
4551
|
-
key = `${
|
|
4648
|
+
while (isReservedProviderConfigKey(key) || Object.prototype.hasOwnProperty.call(existing, key)) {
|
|
4649
|
+
key = `${normalizedBase}-${index}`;
|
|
4552
4650
|
index += 1;
|
|
4553
4651
|
}
|
|
4554
4652
|
return key;
|
|
4555
4653
|
}
|
|
4654
|
+
function isReservedProviderConfigKey(key) {
|
|
4655
|
+
return RESERVED_PROVIDER_CONFIG_KEYS.has(key.trim().toLowerCase());
|
|
4656
|
+
}
|
|
4556
4657
|
function slugifyProviderKey(value) {
|
|
4557
4658
|
const slug = value.trim().toLowerCase().replace(/[^a-z0-9]+/gu, "-").replace(/^-+|-+$/gu, "");
|
|
4558
4659
|
return slug || "provider";
|
|
@@ -7524,7 +7625,7 @@ async function listCronOutputFiles(profileName, jobId) {
|
|
|
7524
7625
|
orderTimeMs: fileStat.mtimeMs
|
|
7525
7626
|
});
|
|
7526
7627
|
}
|
|
7527
|
-
return files.sort((left, right) => left.orderTimeMs - right.orderTimeMs).map(({ path:
|
|
7628
|
+
return files.sort((left, right) => left.orderTimeMs - right.orderTimeMs).map(({ path: path38, mtime }) => ({ path: path38, mtime }));
|
|
7528
7629
|
}
|
|
7529
7630
|
function readCronOutputTimestamp(fileName) {
|
|
7530
7631
|
const match = fileName.match(
|
|
@@ -7613,7 +7714,7 @@ function isConversationMissingError(error) {
|
|
|
7613
7714
|
}
|
|
7614
7715
|
|
|
7615
7716
|
// src/constants.ts
|
|
7616
|
-
var LINK_VERSION = "0.8.1-beta.
|
|
7717
|
+
var LINK_VERSION = "0.8.1-beta.3";
|
|
7617
7718
|
var LINK_COMMAND = "hermeslink";
|
|
7618
7719
|
var LINK_DEFAULT_PORT = 52379;
|
|
7619
7720
|
var LINK_RUNTIME_DIR_NAME = ".hermeslink";
|
|
@@ -7962,7 +8063,9 @@ async function reportLiveActivityEventToServerNow(options) {
|
|
|
7962
8063
|
const event = liveActivityEventWithInheritedDetail(activity, options.event);
|
|
7963
8064
|
const occurredAt = normalizeIso2(options.event.occurredAt) ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
7964
8065
|
const summaryHash = hashLiveActivitySummary(event);
|
|
7965
|
-
const skipReason = resolveSkipReason(activity, event, summaryHash
|
|
8066
|
+
const skipReason = resolveSkipReason(activity, event, summaryHash, {
|
|
8067
|
+
hasExplicitTodoProgress: hasLiveActivityTodoProgress(options.event)
|
|
8068
|
+
});
|
|
7966
8069
|
if (skipReason) {
|
|
7967
8070
|
await options.logger.debug("live_activity_event_skipped", {
|
|
7968
8071
|
activity_id: activity.activityId,
|
|
@@ -8001,6 +8104,9 @@ async function reportLiveActivityEventToServerNow(options) {
|
|
|
8001
8104
|
addOptional2(payload, "status_label", event.statusLabel, 24);
|
|
8002
8105
|
addOptional2(payload, "progress_text", event.progressText, 120);
|
|
8003
8106
|
addOptional2(payload, "detail_text", event.detailText, 240);
|
|
8107
|
+
addOptionalCount(payload, "todo_done_count", event.todoDoneCount);
|
|
8108
|
+
addOptionalCount(payload, "todo_total_count", event.todoTotalCount);
|
|
8109
|
+
addOptional2(payload, "todo_progress_text", event.todoProgressText, 16);
|
|
8004
8110
|
addOptional2(payload, "language", activity.language, 16);
|
|
8005
8111
|
addOptional2(payload, "privacy_level", activity.privacyLevel, 32);
|
|
8006
8112
|
if (typeof event.requiresUserAction === "boolean") {
|
|
@@ -8037,7 +8143,10 @@ async function reportLiveActivityEventToServerNow(options) {
|
|
|
8037
8143
|
terminal: event.terminal === true,
|
|
8038
8144
|
statusLabel: event.statusLabel,
|
|
8039
8145
|
progressText: event.progressText,
|
|
8040
|
-
detailText: event.detailText
|
|
8146
|
+
detailText: event.detailText,
|
|
8147
|
+
todoDoneCount: event.todoDoneCount,
|
|
8148
|
+
todoTotalCount: event.todoTotalCount,
|
|
8149
|
+
todoProgressText: event.todoProgressText
|
|
8041
8150
|
});
|
|
8042
8151
|
const apnsReason = readApnsReason(body);
|
|
8043
8152
|
if (apnsReason && shouldInvalidateLiveActivity(apnsReason)) {
|
|
@@ -8052,17 +8161,39 @@ function liveActivityEventWithInheritedDetail(activity, event) {
|
|
|
8052
8161
|
phase,
|
|
8053
8162
|
statusLabel: activity.lastStatusLabel?.trim() || event.statusLabel,
|
|
8054
8163
|
progressText: activity.lastProgressText?.trim() || event.progressText,
|
|
8055
|
-
detailText: activity.lastDetailText?.trim() || event.detailText
|
|
8164
|
+
detailText: activity.lastDetailText?.trim() || event.detailText,
|
|
8165
|
+
...inheritedTodoProgress(activity, event)
|
|
8056
8166
|
};
|
|
8057
8167
|
}
|
|
8168
|
+
const eventWithTodoProgress = {
|
|
8169
|
+
...event,
|
|
8170
|
+
...inheritedTodoProgress(activity, event)
|
|
8171
|
+
};
|
|
8058
8172
|
if (event.detailText?.trim() || !isToolPhase(event.phase)) {
|
|
8059
|
-
return
|
|
8173
|
+
return eventWithTodoProgress;
|
|
8060
8174
|
}
|
|
8061
8175
|
const detailText = activity.lastDetailText?.trim();
|
|
8062
|
-
return detailText ? { ...
|
|
8176
|
+
return detailText ? { ...eventWithTodoProgress, detailText } : eventWithTodoProgress;
|
|
8177
|
+
}
|
|
8178
|
+
function inheritedTodoProgress(activity, event) {
|
|
8179
|
+
if (typeof event.todoDoneCount === "number" || typeof event.todoTotalCount === "number") {
|
|
8180
|
+
return {
|
|
8181
|
+
todoDoneCount: event.todoDoneCount,
|
|
8182
|
+
todoTotalCount: event.todoTotalCount,
|
|
8183
|
+
todoProgressText: event.todoProgressText
|
|
8184
|
+
};
|
|
8185
|
+
}
|
|
8186
|
+
if (typeof activity.lastTodoDoneCount === "number" && typeof activity.lastTodoTotalCount === "number") {
|
|
8187
|
+
return {
|
|
8188
|
+
todoDoneCount: activity.lastTodoDoneCount,
|
|
8189
|
+
todoTotalCount: activity.lastTodoTotalCount,
|
|
8190
|
+
todoProgressText: activity.lastTodoProgressText
|
|
8191
|
+
};
|
|
8192
|
+
}
|
|
8193
|
+
return {};
|
|
8063
8194
|
}
|
|
8064
|
-
function resolveSkipReason(activity, event, summaryHash) {
|
|
8065
|
-
if (shouldThrottleLiveActivityEvent(activity, event)) {
|
|
8195
|
+
function resolveSkipReason(activity, event, summaryHash, options) {
|
|
8196
|
+
if (shouldThrottleLiveActivityEvent(activity, event, options)) {
|
|
8066
8197
|
return "push_interval_throttled";
|
|
8067
8198
|
}
|
|
8068
8199
|
if (activity.pushCount >= 60 && event.terminal !== true) {
|
|
@@ -8076,10 +8207,13 @@ function resolveSkipReason(activity, event, summaryHash) {
|
|
|
8076
8207
|
}
|
|
8077
8208
|
return null;
|
|
8078
8209
|
}
|
|
8079
|
-
function shouldThrottleLiveActivityEvent(activity, event) {
|
|
8210
|
+
function shouldThrottleLiveActivityEvent(activity, event, options) {
|
|
8080
8211
|
if (event.terminal === true || event.requiresUserAction === true || event.phase === "needs_input" || event.phase === "needs_approval") {
|
|
8081
8212
|
return false;
|
|
8082
8213
|
}
|
|
8214
|
+
if (options?.hasExplicitTodoProgress === true) {
|
|
8215
|
+
return false;
|
|
8216
|
+
}
|
|
8083
8217
|
if (isLiveActivityTitleUpdateEvent(event)) {
|
|
8084
8218
|
return false;
|
|
8085
8219
|
}
|
|
@@ -8121,6 +8255,9 @@ function isLiveActivityTitleUpdateEvent(event) {
|
|
|
8121
8255
|
function isToolPhase(phase) {
|
|
8122
8256
|
return phase === "using_tool" || phase === "tool_completed";
|
|
8123
8257
|
}
|
|
8258
|
+
function hasLiveActivityTodoProgress(event) {
|
|
8259
|
+
return typeof event.todoDoneCount === "number" && Number.isFinite(event.todoDoneCount) && typeof event.todoTotalCount === "number" && Number.isFinite(event.todoTotalCount);
|
|
8260
|
+
}
|
|
8124
8261
|
function isLiveActivityPhase(value) {
|
|
8125
8262
|
return value === "connecting" || value === "accepted" || value === "running" || value === "using_tool" || value === "tool_completed" || value === "needs_input" || value === "needs_approval" || value === "goal_running" || value === "goal_paused" || value === "context_compressing" || value === "context_compressed" || value === "context_compression_failed" || value === "context_compression_timed_out" || value === "paused" || value === "recovering" || value === "completed" || value === "failed" || value === "cancelled";
|
|
8126
8263
|
}
|
|
@@ -8132,10 +8269,19 @@ function hashLiveActivitySummary(event) {
|
|
|
8132
8269
|
event.statusLabel?.trim() ?? "",
|
|
8133
8270
|
event.progressText?.trim() ?? "",
|
|
8134
8271
|
event.detailText?.trim() ?? "",
|
|
8272
|
+
event.todoDoneCount === void 0 || event.todoDoneCount === null ? "" : String(event.todoDoneCount),
|
|
8273
|
+
event.todoTotalCount === void 0 || event.todoTotalCount === null ? "" : String(event.todoTotalCount),
|
|
8274
|
+
event.todoProgressText?.trim() ?? "",
|
|
8135
8275
|
event.terminal === true ? "terminal" : "update"
|
|
8136
8276
|
].join("|")
|
|
8137
8277
|
).digest("hex");
|
|
8138
8278
|
}
|
|
8279
|
+
function addOptionalCount(payload, key, value) {
|
|
8280
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
8281
|
+
return;
|
|
8282
|
+
}
|
|
8283
|
+
payload[key] = Math.max(0, Math.trunc(value));
|
|
8284
|
+
}
|
|
8139
8285
|
function addOptional2(payload, key, value, maxLength) {
|
|
8140
8286
|
const normalized = value?.trim();
|
|
8141
8287
|
if (normalized) {
|
|
@@ -10169,8 +10315,8 @@ import { spawn as spawn2 } from "child_process";
|
|
|
10169
10315
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
10170
10316
|
import { readFile as readFile6, stat as stat5 } from "fs/promises";
|
|
10171
10317
|
import net2 from "net";
|
|
10172
|
-
import
|
|
10173
|
-
import { setTimeout as
|
|
10318
|
+
import path10 from "path";
|
|
10319
|
+
import { setTimeout as delay4 } from "timers/promises";
|
|
10174
10320
|
import WebSocket from "ws";
|
|
10175
10321
|
|
|
10176
10322
|
// src/conversations/goal-state.ts
|
|
@@ -10319,7 +10465,235 @@ function normalizeGoalStatus(value) {
|
|
|
10319
10465
|
}
|
|
10320
10466
|
}
|
|
10321
10467
|
|
|
10468
|
+
// src/hermes/tui-gateway-process-registry.ts
|
|
10469
|
+
import { execFile as execFile3 } from "child_process";
|
|
10470
|
+
import path9 from "path";
|
|
10471
|
+
import { promisify as promisify3 } from "util";
|
|
10472
|
+
var execFileAsync3 = promisify3(execFile3);
|
|
10473
|
+
var REGISTRY_VERSION2 = 1;
|
|
10474
|
+
var TERMINATE_GRACE_MS = 5e3;
|
|
10475
|
+
function tuiGatewayProcessRegistryPath(paths) {
|
|
10476
|
+
return path9.join(paths.runDir, "tui-gateway-processes.json");
|
|
10477
|
+
}
|
|
10478
|
+
async function readRegisteredTuiGatewayProcesses(paths) {
|
|
10479
|
+
return normalizeRegistry(
|
|
10480
|
+
await readJsonFile(
|
|
10481
|
+
tuiGatewayProcessRegistryPath(paths)
|
|
10482
|
+
)
|
|
10483
|
+
).processes;
|
|
10484
|
+
}
|
|
10485
|
+
async function registerTuiGatewayProcess(paths, processRecord) {
|
|
10486
|
+
await updateJsonFile(
|
|
10487
|
+
tuiGatewayProcessRegistryPath(paths),
|
|
10488
|
+
(current) => {
|
|
10489
|
+
const registry = normalizeRegistry(current);
|
|
10490
|
+
return {
|
|
10491
|
+
version: REGISTRY_VERSION2,
|
|
10492
|
+
processes: [
|
|
10493
|
+
...registry.processes.filter(
|
|
10494
|
+
(item) => item.profile !== processRecord.profile && item.pid !== processRecord.pid
|
|
10495
|
+
),
|
|
10496
|
+
processRecord
|
|
10497
|
+
]
|
|
10498
|
+
};
|
|
10499
|
+
}
|
|
10500
|
+
);
|
|
10501
|
+
}
|
|
10502
|
+
async function unregisterTuiGatewayProcess(paths, input) {
|
|
10503
|
+
await updateJsonFile(
|
|
10504
|
+
tuiGatewayProcessRegistryPath(paths),
|
|
10505
|
+
(current) => {
|
|
10506
|
+
const registry = normalizeRegistry(current);
|
|
10507
|
+
return {
|
|
10508
|
+
version: REGISTRY_VERSION2,
|
|
10509
|
+
processes: registry.processes.filter((item) => {
|
|
10510
|
+
if (input.pid !== void 0) {
|
|
10511
|
+
return item.pid !== input.pid;
|
|
10512
|
+
}
|
|
10513
|
+
if (input.profile !== void 0) {
|
|
10514
|
+
return item.profile !== input.profile;
|
|
10515
|
+
}
|
|
10516
|
+
return true;
|
|
10517
|
+
})
|
|
10518
|
+
};
|
|
10519
|
+
}
|
|
10520
|
+
);
|
|
10521
|
+
}
|
|
10522
|
+
async function cleanupRegisteredTuiGatewayProcesses(input) {
|
|
10523
|
+
const excludePids = new Set(
|
|
10524
|
+
[...input.excludePids ?? []].filter(
|
|
10525
|
+
(pid) => typeof pid === "number" && Number.isInteger(pid) && pid > 0
|
|
10526
|
+
)
|
|
10527
|
+
);
|
|
10528
|
+
const processes = await readRegisteredTuiGatewayProcesses(input.paths);
|
|
10529
|
+
const kept = [];
|
|
10530
|
+
const result = {
|
|
10531
|
+
stopped: [],
|
|
10532
|
+
missing: [],
|
|
10533
|
+
skipped: []
|
|
10534
|
+
};
|
|
10535
|
+
for (const processRecord of processes) {
|
|
10536
|
+
if (excludePids.has(processRecord.pid)) {
|
|
10537
|
+
kept.push(processRecord);
|
|
10538
|
+
result.skipped.push({ process: processRecord, reason: "active_backend" });
|
|
10539
|
+
continue;
|
|
10540
|
+
}
|
|
10541
|
+
const state = await inspectRegisteredProcess(processRecord);
|
|
10542
|
+
if (!state.alive) {
|
|
10543
|
+
result.missing.push(processRecord);
|
|
10544
|
+
continue;
|
|
10545
|
+
}
|
|
10546
|
+
if (!state.matches) {
|
|
10547
|
+
kept.push(processRecord);
|
|
10548
|
+
result.skipped.push({
|
|
10549
|
+
process: processRecord,
|
|
10550
|
+
reason: state.reason ?? "command_mismatch"
|
|
10551
|
+
});
|
|
10552
|
+
void input.logger?.warn("tui_gateway_registry_cleanup_skipped", {
|
|
10553
|
+
pid: processRecord.pid,
|
|
10554
|
+
profile: processRecord.profile,
|
|
10555
|
+
port: processRecord.port,
|
|
10556
|
+
reason: state.reason ?? "command_mismatch",
|
|
10557
|
+
command_line: state.commandLine ?? null
|
|
10558
|
+
});
|
|
10559
|
+
continue;
|
|
10560
|
+
}
|
|
10561
|
+
const stopped = await terminatePid(processRecord.pid);
|
|
10562
|
+
if (stopped) {
|
|
10563
|
+
result.stopped.push(processRecord);
|
|
10564
|
+
void input.logger?.warn("tui_gateway_registered_process_stopped", {
|
|
10565
|
+
pid: processRecord.pid,
|
|
10566
|
+
profile: processRecord.profile,
|
|
10567
|
+
port: processRecord.port
|
|
10568
|
+
});
|
|
10569
|
+
} else {
|
|
10570
|
+
kept.push(processRecord);
|
|
10571
|
+
result.skipped.push({ process: processRecord, reason: "terminate_failed" });
|
|
10572
|
+
void input.logger?.warn("tui_gateway_registered_process_stop_failed", {
|
|
10573
|
+
pid: processRecord.pid,
|
|
10574
|
+
profile: processRecord.profile,
|
|
10575
|
+
port: processRecord.port
|
|
10576
|
+
});
|
|
10577
|
+
}
|
|
10578
|
+
}
|
|
10579
|
+
await writeJsonFile(tuiGatewayProcessRegistryPath(input.paths), {
|
|
10580
|
+
version: REGISTRY_VERSION2,
|
|
10581
|
+
processes: kept
|
|
10582
|
+
});
|
|
10583
|
+
return result;
|
|
10584
|
+
}
|
|
10585
|
+
async function inspectRegisteredProcess(processRecord) {
|
|
10586
|
+
if (!isProcessAlive(processRecord.pid)) {
|
|
10587
|
+
return { alive: false, matches: false };
|
|
10588
|
+
}
|
|
10589
|
+
const commandLine = await readProcessCommandLine(processRecord.pid);
|
|
10590
|
+
if (!commandLine) {
|
|
10591
|
+
return {
|
|
10592
|
+
alive: true,
|
|
10593
|
+
matches: false,
|
|
10594
|
+
reason: "command_unavailable"
|
|
10595
|
+
};
|
|
10596
|
+
}
|
|
10597
|
+
if (!commandLineMatchesRecord(commandLine, processRecord)) {
|
|
10598
|
+
return {
|
|
10599
|
+
alive: true,
|
|
10600
|
+
matches: false,
|
|
10601
|
+
reason: "command_mismatch",
|
|
10602
|
+
commandLine
|
|
10603
|
+
};
|
|
10604
|
+
}
|
|
10605
|
+
return { alive: true, matches: true, commandLine };
|
|
10606
|
+
}
|
|
10607
|
+
async function readProcessCommandLine(pid) {
|
|
10608
|
+
if (process.platform === "win32") {
|
|
10609
|
+
return null;
|
|
10610
|
+
}
|
|
10611
|
+
try {
|
|
10612
|
+
const { stdout } = await execFileAsync3(
|
|
10613
|
+
"ps",
|
|
10614
|
+
["-p", String(pid), "-o", "command="],
|
|
10615
|
+
{
|
|
10616
|
+
timeout: 1e3,
|
|
10617
|
+
windowsHide: true,
|
|
10618
|
+
maxBuffer: 64 * 1024
|
|
10619
|
+
}
|
|
10620
|
+
);
|
|
10621
|
+
return stdout.trim() || null;
|
|
10622
|
+
} catch {
|
|
10623
|
+
return null;
|
|
10624
|
+
}
|
|
10625
|
+
}
|
|
10626
|
+
function commandLineMatchesRecord(commandLine, processRecord) {
|
|
10627
|
+
return /\bhermes\b/u.test(commandLine) && /\bdashboard\b/u.test(commandLine) && hasCommandArgument(commandLine, "--host", "127.0.0.1") && hasCommandArgument(commandLine, "--port", String(processRecord.port));
|
|
10628
|
+
}
|
|
10629
|
+
function hasCommandArgument(commandLine, name, value) {
|
|
10630
|
+
return commandLine.includes(`${name} ${value}`) || commandLine.includes(`${name}=${value}`);
|
|
10631
|
+
}
|
|
10632
|
+
async function terminatePid(pid) {
|
|
10633
|
+
try {
|
|
10634
|
+
process.kill(pid, "SIGTERM");
|
|
10635
|
+
} catch {
|
|
10636
|
+
return !isProcessAlive(pid);
|
|
10637
|
+
}
|
|
10638
|
+
if (await waitForPidExit(pid, TERMINATE_GRACE_MS)) {
|
|
10639
|
+
return true;
|
|
10640
|
+
}
|
|
10641
|
+
try {
|
|
10642
|
+
process.kill(pid, "SIGKILL");
|
|
10643
|
+
} catch {
|
|
10644
|
+
return !isProcessAlive(pid);
|
|
10645
|
+
}
|
|
10646
|
+
return await waitForPidExit(pid, 1e3);
|
|
10647
|
+
}
|
|
10648
|
+
async function waitForPidExit(pid, timeoutMs) {
|
|
10649
|
+
const deadline = Date.now() + timeoutMs;
|
|
10650
|
+
while (Date.now() < deadline) {
|
|
10651
|
+
if (!isProcessAlive(pid)) {
|
|
10652
|
+
return true;
|
|
10653
|
+
}
|
|
10654
|
+
await delay3(100);
|
|
10655
|
+
}
|
|
10656
|
+
return !isProcessAlive(pid);
|
|
10657
|
+
}
|
|
10658
|
+
function isProcessAlive(pid) {
|
|
10659
|
+
try {
|
|
10660
|
+
process.kill(pid, 0);
|
|
10661
|
+
return true;
|
|
10662
|
+
} catch {
|
|
10663
|
+
return false;
|
|
10664
|
+
}
|
|
10665
|
+
}
|
|
10666
|
+
function normalizeRegistry(value) {
|
|
10667
|
+
if (!value || !Array.isArray(value.processes)) {
|
|
10668
|
+
return { version: REGISTRY_VERSION2, processes: [] };
|
|
10669
|
+
}
|
|
10670
|
+
return {
|
|
10671
|
+
version: REGISTRY_VERSION2,
|
|
10672
|
+
processes: value.processes.filter(isProcessRecord)
|
|
10673
|
+
};
|
|
10674
|
+
}
|
|
10675
|
+
function isProcessRecord(value) {
|
|
10676
|
+
if (!value || typeof value !== "object") {
|
|
10677
|
+
return false;
|
|
10678
|
+
}
|
|
10679
|
+
const record = value;
|
|
10680
|
+
return record.owner === "hermeslink" && Number.isInteger(record.pid) && record.pid > 0 && typeof record.profile === "string" && record.profile.length > 0 && Number.isInteger(record.port) && record.port > 0 && typeof record.command === "string" && Array.isArray(record.args) && record.args.every((item) => typeof item === "string") && typeof record.runtime_home === "string" && typeof record.started_at === "string";
|
|
10681
|
+
}
|
|
10682
|
+
function delay3(ms) {
|
|
10683
|
+
return new Promise((resolve) => {
|
|
10684
|
+
const timer = setTimeout(resolve, ms);
|
|
10685
|
+
timer.unref?.();
|
|
10686
|
+
});
|
|
10687
|
+
}
|
|
10688
|
+
|
|
10322
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
|
+
};
|
|
10323
10697
|
var CONNECT_TIMEOUT_MS = 15e3;
|
|
10324
10698
|
var REQUEST_TIMEOUT_MS = 12e4;
|
|
10325
10699
|
var USAGE_REQUEST_TIMEOUT_MS = 1e4;
|
|
@@ -10332,12 +10706,60 @@ var WS_HEARTBEAT_INTERVAL_MS = 6e4;
|
|
|
10332
10706
|
var WS_RECONNECT_BASE_DELAY_MS = 1e3;
|
|
10333
10707
|
var WS_RECONNECT_MAX_DELAY_MS = 15e3;
|
|
10334
10708
|
var INTERRUPT_DRAIN_TIMEOUT_MS = 15e3;
|
|
10709
|
+
var SESSION_BUSY_RETRY_TIMEOUT_MS = 6e3;
|
|
10710
|
+
var SESSION_BUSY_RETRY_INTERVAL_MS = 150;
|
|
10335
10711
|
var PROFILE_NAME_PATTERN2 = /^[a-zA-Z0-9._-]{1,64}$/u;
|
|
10336
10712
|
var backendEntries = /* @__PURE__ */ new Map();
|
|
10337
10713
|
var backendStarts = /* @__PURE__ */ new Map();
|
|
10338
10714
|
var clients = /* @__PURE__ */ new Map();
|
|
10339
10715
|
var sessionRefs = /* @__PURE__ */ new Map();
|
|
10340
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
|
+
}
|
|
10341
10763
|
async function streamTuiGatewayRun(input) {
|
|
10342
10764
|
const profileName = normalizeProfileName2(input.profileName);
|
|
10343
10765
|
const client = await getTuiGatewayClient({
|
|
@@ -10395,6 +10817,77 @@ async function interruptTuiGatewaySession(input) {
|
|
|
10395
10817
|
});
|
|
10396
10818
|
await client.interruptSession(sessionId);
|
|
10397
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
|
+
}
|
|
10398
10891
|
async function dispatchTuiGatewayCommand(input) {
|
|
10399
10892
|
const profileName = normalizeProfileName2(input.profileName);
|
|
10400
10893
|
const client = await getTuiGatewayClient({
|
|
@@ -10642,32 +11135,50 @@ async function setTuiGatewaySessionModelConfig(input) {
|
|
|
10642
11135
|
}
|
|
10643
11136
|
const raw = [];
|
|
10644
11137
|
if (model) {
|
|
10645
|
-
const result = await
|
|
10646
|
-
|
|
10647
|
-
|
|
10648
|
-
|
|
10649
|
-
|
|
10650
|
-
|
|
10651
|
-
|
|
10652
|
-
|
|
10653
|
-
|
|
10654
|
-
|
|
10655
|
-
|
|
10656
|
-
|
|
10657
|
-
|
|
10658
|
-
|
|
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
|
+
});
|
|
10659
11161
|
raw.push(result);
|
|
10660
11162
|
}
|
|
10661
11163
|
if (reasoningEffort) {
|
|
10662
|
-
const result = await
|
|
10663
|
-
|
|
10664
|
-
|
|
10665
|
-
|
|
10666
|
-
|
|
10667
|
-
|
|
10668
|
-
|
|
10669
|
-
|
|
10670
|
-
|
|
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
|
+
});
|
|
10671
11182
|
raw.push(result);
|
|
10672
11183
|
}
|
|
10673
11184
|
rememberSessionRef(profileName, input.hermesSessionId, started);
|
|
@@ -10705,12 +11216,12 @@ async function restartTuiGatewayBackend(input) {
|
|
|
10705
11216
|
const entry = backendEntries.get(profileName);
|
|
10706
11217
|
backendEntries.delete(profileName);
|
|
10707
11218
|
backendStarts.delete(profileName);
|
|
10708
|
-
if (entry
|
|
10709
|
-
entry
|
|
10710
|
-
|
|
10711
|
-
|
|
10712
|
-
|
|
10713
|
-
}
|
|
11219
|
+
if (entry) {
|
|
11220
|
+
await terminateBackendEntry(entry, {
|
|
11221
|
+
paths: input.paths,
|
|
11222
|
+
logger: input.logger,
|
|
11223
|
+
graceMs: BACKEND_RESTART_GRACE_MS
|
|
11224
|
+
});
|
|
10714
11225
|
}
|
|
10715
11226
|
void input.logger?.warn("tui_gateway_backend_restart_requested", {
|
|
10716
11227
|
profile: profileName,
|
|
@@ -10835,23 +11346,58 @@ async function readTuiGatewayStatus(input = {}) {
|
|
|
10835
11346
|
};
|
|
10836
11347
|
}
|
|
10837
11348
|
}
|
|
10838
|
-
function closeTuiGatewayBackends() {
|
|
11349
|
+
async function closeTuiGatewayBackends(input = {}) {
|
|
10839
11350
|
for (const client of clients.values()) {
|
|
10840
11351
|
client.close();
|
|
10841
11352
|
}
|
|
10842
11353
|
clients.clear();
|
|
10843
11354
|
sessionRefs.clear();
|
|
10844
|
-
|
|
10845
|
-
if (!entry.process.killed) {
|
|
10846
|
-
entry.process.kill();
|
|
10847
|
-
}
|
|
10848
|
-
}
|
|
11355
|
+
const entries = [...backendEntries.values()];
|
|
10849
11356
|
backendEntries.clear();
|
|
11357
|
+
await Promise.all(
|
|
11358
|
+
entries.map(
|
|
11359
|
+
(entry) => terminateBackendEntry(entry, {
|
|
11360
|
+
paths: input.paths,
|
|
11361
|
+
logger: input.logger
|
|
11362
|
+
})
|
|
11363
|
+
)
|
|
11364
|
+
);
|
|
11365
|
+
if (input.cleanupRegistry && input.paths) {
|
|
11366
|
+
await cleanupRegisteredTuiGatewayProcesses({
|
|
11367
|
+
paths: input.paths,
|
|
11368
|
+
logger: input.logger
|
|
11369
|
+
});
|
|
11370
|
+
}
|
|
10850
11371
|
if (backendReaper) {
|
|
10851
11372
|
clearInterval(backendReaper);
|
|
10852
11373
|
backendReaper = null;
|
|
10853
11374
|
}
|
|
10854
11375
|
}
|
|
11376
|
+
async function terminateBackendEntry(entry, input = {}) {
|
|
11377
|
+
if (entry.process.exitCode === null && entry.process.signalCode === null) {
|
|
11378
|
+
if (!entry.process.killed) {
|
|
11379
|
+
entry.process.kill("SIGTERM");
|
|
11380
|
+
}
|
|
11381
|
+
await waitForProcessExit(
|
|
11382
|
+
entry.process,
|
|
11383
|
+
input.graceMs ?? BACKEND_RESTART_GRACE_MS
|
|
11384
|
+
);
|
|
11385
|
+
if (entry.process.exitCode === null && entry.process.signalCode === null) {
|
|
11386
|
+
entry.process.kill("SIGKILL");
|
|
11387
|
+
await waitForProcessExit(entry.process, 1e3);
|
|
11388
|
+
}
|
|
11389
|
+
}
|
|
11390
|
+
await unregisterTuiGatewayProcess(input.paths ?? entry.paths, {
|
|
11391
|
+
pid: entry.process.pid ?? void 0,
|
|
11392
|
+
profile: entry.profile
|
|
11393
|
+
}).catch((error) => {
|
|
11394
|
+
void input.logger?.debug("tui_gateway_registry_unregister_failed", {
|
|
11395
|
+
profile: entry.profile,
|
|
11396
|
+
pid: entry.process.pid ?? null,
|
|
11397
|
+
error: error instanceof Error ? error.message : String(error)
|
|
11398
|
+
});
|
|
11399
|
+
});
|
|
11400
|
+
}
|
|
10855
11401
|
function formatSessionModelConfigValue(model, provider, sessionOnly = false) {
|
|
10856
11402
|
const suffix = sessionOnly ? " --session" : "";
|
|
10857
11403
|
const normalizedProvider = provider?.trim();
|
|
@@ -11032,7 +11578,7 @@ async function startTuiGatewayBackend(input) {
|
|
|
11032
11578
|
];
|
|
11033
11579
|
const logWriter = createRotatingTextLogWriter({
|
|
11034
11580
|
paths,
|
|
11035
|
-
fileName:
|
|
11581
|
+
fileName: path10.basename(getGatewayRuntimeLogFile(paths))
|
|
11036
11582
|
});
|
|
11037
11583
|
const hermesBin = resolveHermesBin();
|
|
11038
11584
|
const child = spawn2(hermesBin, args, {
|
|
@@ -11053,6 +11599,16 @@ async function startTuiGatewayBackend(input) {
|
|
|
11053
11599
|
clients.get(profileName)?.close();
|
|
11054
11600
|
clients.delete(profileName);
|
|
11055
11601
|
backendEntries.delete(profileName);
|
|
11602
|
+
void unregisterTuiGatewayProcess(paths, {
|
|
11603
|
+
pid: child.pid ?? void 0,
|
|
11604
|
+
profile: profileName
|
|
11605
|
+
}).catch((error) => {
|
|
11606
|
+
void input.logger?.debug("tui_gateway_registry_unregister_failed", {
|
|
11607
|
+
profile: profileName,
|
|
11608
|
+
pid: child.pid ?? null,
|
|
11609
|
+
error: error instanceof Error ? error.message : String(error)
|
|
11610
|
+
});
|
|
11611
|
+
});
|
|
11056
11612
|
void input.logger?.warn("tui_gateway_backend_exited", {
|
|
11057
11613
|
profile: profileName,
|
|
11058
11614
|
pid: child.pid ?? null,
|
|
@@ -11083,6 +11639,32 @@ async function startTuiGatewayBackend(input) {
|
|
|
11083
11639
|
await logWriter.flush().catch(() => void 0);
|
|
11084
11640
|
throw error;
|
|
11085
11641
|
}
|
|
11642
|
+
if (!child.pid) {
|
|
11643
|
+
child.kill();
|
|
11644
|
+
await logWriter.flush().catch(() => void 0);
|
|
11645
|
+
throw new LinkHttpError(
|
|
11646
|
+
503,
|
|
11647
|
+
"tui_gateway_backend_unavailable",
|
|
11648
|
+
"Hermes dashboard backend started without a process id."
|
|
11649
|
+
);
|
|
11650
|
+
}
|
|
11651
|
+
try {
|
|
11652
|
+
await registerTuiGatewayProcess(paths, {
|
|
11653
|
+
owner: "hermeslink",
|
|
11654
|
+
pid: child.pid,
|
|
11655
|
+
profile: profileName,
|
|
11656
|
+
port,
|
|
11657
|
+
command: hermesBin,
|
|
11658
|
+
args,
|
|
11659
|
+
runtime_home: paths.homeDir,
|
|
11660
|
+
started_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
11661
|
+
});
|
|
11662
|
+
} catch (error) {
|
|
11663
|
+
child.kill();
|
|
11664
|
+
await waitForProcessExit(child, BACKEND_RESTART_GRACE_MS);
|
|
11665
|
+
await logWriter.flush().catch(() => void 0);
|
|
11666
|
+
throw error;
|
|
11667
|
+
}
|
|
11086
11668
|
const entry = {
|
|
11087
11669
|
profile: profileName,
|
|
11088
11670
|
port,
|
|
@@ -11090,6 +11672,7 @@ async function startTuiGatewayBackend(input) {
|
|
|
11090
11672
|
baseUrl,
|
|
11091
11673
|
wsUrl,
|
|
11092
11674
|
process: child,
|
|
11675
|
+
paths,
|
|
11093
11676
|
logPath: logWriter.filePath,
|
|
11094
11677
|
startedAt: Date.now(),
|
|
11095
11678
|
lastActiveAt: Date.now()
|
|
@@ -11267,12 +11850,21 @@ var TuiGatewayClient = class {
|
|
|
11267
11850
|
cleanup();
|
|
11268
11851
|
};
|
|
11269
11852
|
input.signal?.addEventListener("abort", abort, { once: true });
|
|
11270
|
-
void
|
|
11271
|
-
|
|
11272
|
-
|
|
11273
|
-
|
|
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
|
|
11274
11860
|
).catch((error) => {
|
|
11275
|
-
queue.pushError(
|
|
11861
|
+
queue.pushError(
|
|
11862
|
+
withBusyContext(error, {
|
|
11863
|
+
profileName: this.profileName,
|
|
11864
|
+
runtimeSessionId: input.sessionId,
|
|
11865
|
+
method: "prompt.submit"
|
|
11866
|
+
})
|
|
11867
|
+
);
|
|
11276
11868
|
});
|
|
11277
11869
|
queue.closed.finally(cleanup).catch(() => void 0);
|
|
11278
11870
|
return queue;
|
|
@@ -11406,7 +11998,7 @@ var TuiGatewayClient = class {
|
|
|
11406
11998
|
this.pending.delete(frame.id);
|
|
11407
11999
|
clearTimeout(pending.timer);
|
|
11408
12000
|
if (frame.error) {
|
|
11409
|
-
pending.reject(
|
|
12001
|
+
pending.reject(createTuiGatewayRpcError(frame.error));
|
|
11410
12002
|
} else {
|
|
11411
12003
|
pending.resolve(frame.result);
|
|
11412
12004
|
}
|
|
@@ -11527,14 +12119,14 @@ async function readCurrentStoredSessionId(client, runtimeSessionId, logger) {
|
|
|
11527
12119
|
return null;
|
|
11528
12120
|
}
|
|
11529
12121
|
async function waitForProcessExit(child, timeoutMs) {
|
|
11530
|
-
if (child.
|
|
12122
|
+
if (child.exitCode !== null || child.signalCode !== null) {
|
|
11531
12123
|
return;
|
|
11532
12124
|
}
|
|
11533
12125
|
await Promise.race([
|
|
11534
12126
|
new Promise((resolve) => {
|
|
11535
12127
|
child.once("exit", () => resolve());
|
|
11536
12128
|
}),
|
|
11537
|
-
|
|
12129
|
+
delay4(timeoutMs).then(() => void 0)
|
|
11538
12130
|
]);
|
|
11539
12131
|
}
|
|
11540
12132
|
var GatewayEventQueue = class {
|
|
@@ -11969,7 +12561,7 @@ async function waitForDashboardReady(baseUrl, token, logPath, signal) {
|
|
|
11969
12561
|
} catch (error) {
|
|
11970
12562
|
lastError = error;
|
|
11971
12563
|
}
|
|
11972
|
-
await
|
|
12564
|
+
await delay4(250, void 0, { signal }).catch(() => void 0);
|
|
11973
12565
|
}
|
|
11974
12566
|
throw new LinkHttpError(
|
|
11975
12567
|
503,
|
|
@@ -12056,23 +12648,28 @@ function startBackendReaper() {
|
|
|
12056
12648
|
return;
|
|
12057
12649
|
}
|
|
12058
12650
|
backendReaper = setInterval(() => {
|
|
12059
|
-
|
|
12060
|
-
for (const [profile, entry] of backendEntries) {
|
|
12061
|
-
if (now - entry.lastActiveAt < IDLE_BACKEND_TTL_MS) {
|
|
12062
|
-
continue;
|
|
12063
|
-
}
|
|
12064
|
-
clients.get(profile)?.close();
|
|
12065
|
-
clients.delete(profile);
|
|
12066
|
-
backendEntries.delete(profile);
|
|
12067
|
-
entry.process.kill();
|
|
12068
|
-
}
|
|
12069
|
-
if (backendEntries.size === 0 && backendReaper) {
|
|
12070
|
-
clearInterval(backendReaper);
|
|
12071
|
-
backendReaper = null;
|
|
12072
|
-
}
|
|
12651
|
+
void reapIdleBackends();
|
|
12073
12652
|
}, 6e4);
|
|
12074
12653
|
backendReaper.unref?.();
|
|
12075
12654
|
}
|
|
12655
|
+
async function reapIdleBackends() {
|
|
12656
|
+
const now = Date.now();
|
|
12657
|
+
const staleEntries = [];
|
|
12658
|
+
for (const [profile, entry] of backendEntries) {
|
|
12659
|
+
if (now - entry.lastActiveAt < IDLE_BACKEND_TTL_MS) {
|
|
12660
|
+
continue;
|
|
12661
|
+
}
|
|
12662
|
+
clients.get(profile)?.close();
|
|
12663
|
+
clients.delete(profile);
|
|
12664
|
+
backendEntries.delete(profile);
|
|
12665
|
+
staleEntries.push(entry);
|
|
12666
|
+
}
|
|
12667
|
+
await Promise.all(staleEntries.map((entry) => terminateBackendEntry(entry)));
|
|
12668
|
+
if (backendEntries.size === 0 && backendReaper) {
|
|
12669
|
+
clearInterval(backendReaper);
|
|
12670
|
+
backendReaper = null;
|
|
12671
|
+
}
|
|
12672
|
+
}
|
|
12076
12673
|
function normalizeProfileName2(profileName) {
|
|
12077
12674
|
const value = profileName?.trim() || "default";
|
|
12078
12675
|
if (!PROFILE_NAME_PATTERN2.test(value)) {
|
|
@@ -12102,6 +12699,19 @@ function readString5(payload, key) {
|
|
|
12102
12699
|
const value = payload[key];
|
|
12103
12700
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
12104
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
|
+
}
|
|
12105
12715
|
function readStringList2(value) {
|
|
12106
12716
|
if (!Array.isArray(value)) {
|
|
12107
12717
|
return [];
|
|
@@ -12867,11 +13477,11 @@ function workspaceNotFound() {
|
|
|
12867
13477
|
// src/conversations/blob-store.ts
|
|
12868
13478
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
12869
13479
|
import { mkdir as mkdir6, readFile as readFile7, readdir as readdir4, rm as rm3, stat as stat6, writeFile } from "fs/promises";
|
|
12870
|
-
import
|
|
13480
|
+
import path12 from "path";
|
|
12871
13481
|
|
|
12872
13482
|
// src/conversations/media.ts
|
|
12873
13483
|
import { createHash as createHash4 } from "crypto";
|
|
12874
|
-
import
|
|
13484
|
+
import path11 from "path";
|
|
12875
13485
|
|
|
12876
13486
|
// src/conversations/delivery-contract.ts
|
|
12877
13487
|
var HERMES_LINK_DELIVERY_TAG_PATTERN = /<hermes_link_delivery>\s*(\{[\s\S]*?\})\s*<\/hermes_link_delivery>/giu;
|
|
@@ -12961,7 +13571,7 @@ function cleanMessageTextParts(message) {
|
|
|
12961
13571
|
}
|
|
12962
13572
|
}
|
|
12963
13573
|
function inferMimeType(filePath) {
|
|
12964
|
-
const extension =
|
|
13574
|
+
const extension = path11.extname(filePath).toLowerCase();
|
|
12965
13575
|
return {
|
|
12966
13576
|
".png": "image/png",
|
|
12967
13577
|
".jpg": "image/jpeg",
|
|
@@ -13085,7 +13695,7 @@ function mediaSourceKey(sourcePath) {
|
|
|
13085
13695
|
return createHash4("sha256").update(resolveMediaSourcePath(sourcePath)).digest("hex").slice(0, 32);
|
|
13086
13696
|
}
|
|
13087
13697
|
function sanitizeFilename(value, fallback) {
|
|
13088
|
-
const base =
|
|
13698
|
+
const base = path11.basename((value ?? "").replace(/[\r\n\t]/gu, " ").trim());
|
|
13089
13699
|
const safe = base.replace(/[/:\\]/gu, "_").slice(0, 200).trim();
|
|
13090
13700
|
return safe || fallback;
|
|
13091
13701
|
}
|
|
@@ -13114,9 +13724,9 @@ function isBlobReferencedByMessages(snapshot, blobId) {
|
|
|
13114
13724
|
}
|
|
13115
13725
|
function resolveMediaSourcePath(sourcePath) {
|
|
13116
13726
|
const trimmed = sourcePath.trim();
|
|
13117
|
-
const expanded = trimmed.startsWith("~/") ?
|
|
13118
|
-
const resolved =
|
|
13119
|
-
if (!
|
|
13727
|
+
const expanded = trimmed.startsWith("~/") ? path11.join(process.env.HOME ?? "", trimmed.slice(2)) : trimmed;
|
|
13728
|
+
const resolved = path11.resolve(expanded);
|
|
13729
|
+
if (!path11.isAbsolute(expanded)) {
|
|
13120
13730
|
throw new LinkHttpError(
|
|
13121
13731
|
400,
|
|
13122
13732
|
"media_source_path_not_absolute",
|
|
@@ -13311,7 +13921,7 @@ function normalizeConversationIds(conversationIds) {
|
|
|
13311
13921
|
}
|
|
13312
13922
|
function blobPath(paths, blobId) {
|
|
13313
13923
|
assertValidBlobId(blobId);
|
|
13314
|
-
return
|
|
13924
|
+
return path12.join(paths.blobsDir, `${blobId}.bin`);
|
|
13315
13925
|
}
|
|
13316
13926
|
async function writeConversationBlob(paths, conversationId, input, options) {
|
|
13317
13927
|
assertValidConversationId(conversationId);
|
|
@@ -13320,7 +13930,7 @@ async function writeConversationBlob(paths, conversationId, input, options) {
|
|
|
13320
13930
|
}
|
|
13321
13931
|
const id = `blob_${randomUUID5().replaceAll("-", "")}`;
|
|
13322
13932
|
const filePath = blobPath(paths, id);
|
|
13323
|
-
await mkdir6(
|
|
13933
|
+
await mkdir6(path12.dirname(filePath), { recursive: true, mode: 448 });
|
|
13324
13934
|
await writeFile(filePath, input.bytes, { mode: 384 });
|
|
13325
13935
|
const blob = {
|
|
13326
13936
|
id,
|
|
@@ -13396,7 +14006,7 @@ async function materializeConversationBlob(paths, conversationId, blobId, manife
|
|
|
13396
14006
|
}
|
|
13397
14007
|
}
|
|
13398
14008
|
const targetDir = conversationAttachmentsDir(paths, conversationId);
|
|
13399
|
-
const targetPath =
|
|
14009
|
+
const targetPath = path12.join(
|
|
13400
14010
|
targetDir,
|
|
13401
14011
|
materializedAttachmentFilename(blobId, manifest.filename ?? blobId)
|
|
13402
14012
|
);
|
|
@@ -13449,7 +14059,7 @@ async function listConversationBlobIds(paths, conversationId) {
|
|
|
13449
14059
|
continue;
|
|
13450
14060
|
}
|
|
13451
14061
|
const manifest = await readJsonFile(
|
|
13452
|
-
|
|
14062
|
+
path12.join(paths.blobsDir, entry.name)
|
|
13453
14063
|
).catch(() => null);
|
|
13454
14064
|
if (manifest?.conversation_ids?.includes(conversationId)) {
|
|
13455
14065
|
blobIds.push(blobId);
|
|
@@ -13459,7 +14069,7 @@ async function listConversationBlobIds(paths, conversationId) {
|
|
|
13459
14069
|
}
|
|
13460
14070
|
function conversationAttachmentsDir(paths, conversationId) {
|
|
13461
14071
|
assertValidConversationId(conversationId);
|
|
13462
|
-
return
|
|
14072
|
+
return path12.join(paths.conversationsDir, conversationId, "attachments");
|
|
13463
14073
|
}
|
|
13464
14074
|
function isNodeError6(error, code) {
|
|
13465
14075
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
@@ -13471,7 +14081,7 @@ import { stat as stat8 } from "fs/promises";
|
|
|
13471
14081
|
// src/hermes/model-provider-disconnects.ts
|
|
13472
14082
|
import { stat as stat7 } from "fs/promises";
|
|
13473
14083
|
import os5 from "os";
|
|
13474
|
-
import
|
|
14084
|
+
import path13 from "path";
|
|
13475
14085
|
async function markAuthBackedModelProviderDisconnected(paths, profileName, providerKey) {
|
|
13476
14086
|
const profileKey = normalizeProfileName3(profileName);
|
|
13477
14087
|
const provider = providerKey.trim();
|
|
@@ -13549,15 +14159,15 @@ function credentialFilesForProvider(profileName, providerKey) {
|
|
|
13549
14159
|
const profileDir = resolveHermesProfileDir(normalizeProfileName3(profileName));
|
|
13550
14160
|
switch (providerKey) {
|
|
13551
14161
|
case "qwen-oauth":
|
|
13552
|
-
return [
|
|
14162
|
+
return [path13.join(os5.homedir(), ".qwen", "oauth_creds.json")];
|
|
13553
14163
|
case "google-gemini-cli":
|
|
13554
|
-
return [
|
|
14164
|
+
return [path13.join(profileDir, "auth", "google_oauth.json")];
|
|
13555
14165
|
default:
|
|
13556
|
-
return [
|
|
14166
|
+
return [path13.join(profileDir, "auth.json")];
|
|
13557
14167
|
}
|
|
13558
14168
|
}
|
|
13559
14169
|
function disconnectedProvidersPath(paths) {
|
|
13560
|
-
return
|
|
14170
|
+
return path13.join(paths.homeDir, "model-provider-disconnects.json");
|
|
13561
14171
|
}
|
|
13562
14172
|
function normalizeStore(value) {
|
|
13563
14173
|
return {
|
|
@@ -14688,7 +15298,7 @@ function commandText(language, zh, en) {
|
|
|
14688
15298
|
|
|
14689
15299
|
// src/conversations/delivery-staging.ts
|
|
14690
15300
|
import { mkdir as mkdir7, rm as rm4 } from "fs/promises";
|
|
14691
|
-
import
|
|
15301
|
+
import path14 from "path";
|
|
14692
15302
|
async function prepareDeliveryStagingRunDir(paths, conversationId, runId) {
|
|
14693
15303
|
const directory = deliveryStagingRunDir(paths, conversationId, runId);
|
|
14694
15304
|
await mkdir7(directory, { recursive: true, mode: 448 });
|
|
@@ -14701,14 +15311,14 @@ async function removeConversationDeliveryStaging(paths, conversationId) {
|
|
|
14701
15311
|
});
|
|
14702
15312
|
}
|
|
14703
15313
|
function deliveryStagingRunDir(paths, conversationId, runId) {
|
|
14704
|
-
return
|
|
15314
|
+
return path14.join(
|
|
14705
15315
|
deliveryStagingConversationDir(paths, conversationId),
|
|
14706
15316
|
safePathSegment(runId, "run")
|
|
14707
15317
|
);
|
|
14708
15318
|
}
|
|
14709
15319
|
function deliveryStagingConversationDir(paths, conversationId) {
|
|
14710
15320
|
assertValidConversationId(conversationId);
|
|
14711
|
-
return
|
|
15321
|
+
return path14.join(paths.conversationsDir, conversationId, "delivery-staging");
|
|
14712
15322
|
}
|
|
14713
15323
|
function safePathSegment(value, fallback) {
|
|
14714
15324
|
const safe = value.trim().replaceAll(/[^a-zA-Z0-9._-]/gu, "_");
|
|
@@ -14718,7 +15328,7 @@ function safePathSegment(value, fallback) {
|
|
|
14718
15328
|
// src/conversations/conversation-archive-plans.ts
|
|
14719
15329
|
import { randomUUID as randomUUID7 } from "crypto";
|
|
14720
15330
|
import { mkdir as mkdir8 } from "fs/promises";
|
|
14721
|
-
import
|
|
15331
|
+
import path15 from "path";
|
|
14722
15332
|
var PLAN_ID_PATTERN = /^archive_[a-f0-9]{32}$/u;
|
|
14723
15333
|
var ConversationArchivePlanStore = class {
|
|
14724
15334
|
constructor(paths) {
|
|
@@ -14761,10 +15371,10 @@ var ConversationArchivePlanStore = class {
|
|
|
14761
15371
|
await writeJsonFile(this.planPath(normalizedPlanId), plan);
|
|
14762
15372
|
}
|
|
14763
15373
|
plansDir() {
|
|
14764
|
-
return
|
|
15374
|
+
return path15.join(this.paths.indexesDir, "conversation-archive-plans");
|
|
14765
15375
|
}
|
|
14766
15376
|
planPath(planId) {
|
|
14767
|
-
return
|
|
15377
|
+
return path15.join(this.plansDir(), `${planId}.json`);
|
|
14768
15378
|
}
|
|
14769
15379
|
};
|
|
14770
15380
|
function normalizePlanId(planId) {
|
|
@@ -14782,7 +15392,7 @@ function normalizePlanId(planId) {
|
|
|
14782
15392
|
// src/conversations/conversation-clear-plans.ts
|
|
14783
15393
|
import { randomUUID as randomUUID8 } from "crypto";
|
|
14784
15394
|
import { mkdir as mkdir9 } from "fs/promises";
|
|
14785
|
-
import
|
|
15395
|
+
import path16 from "path";
|
|
14786
15396
|
var PLAN_ID_PATTERN2 = /^clear_[a-f0-9]{32}$/u;
|
|
14787
15397
|
var ConversationClearPlanStore = class {
|
|
14788
15398
|
constructor(paths) {
|
|
@@ -14826,10 +15436,10 @@ var ConversationClearPlanStore = class {
|
|
|
14826
15436
|
await writeJsonFile(this.planPath(normalizedPlanId), plan);
|
|
14827
15437
|
}
|
|
14828
15438
|
plansDir() {
|
|
14829
|
-
return
|
|
15439
|
+
return path16.join(this.paths.indexesDir, "conversation-clear-plans");
|
|
14830
15440
|
}
|
|
14831
15441
|
planPath(planId) {
|
|
14832
|
-
return
|
|
15442
|
+
return path16.join(this.plansDir(), `${planId}.json`);
|
|
14833
15443
|
}
|
|
14834
15444
|
};
|
|
14835
15445
|
function normalizePlanId2(planId) {
|
|
@@ -15684,14 +16294,14 @@ function readAttachmentWaveform(attachment) {
|
|
|
15684
16294
|
|
|
15685
16295
|
// src/hermes/session-title.ts
|
|
15686
16296
|
import { stat as stat9 } from "fs/promises";
|
|
15687
|
-
import
|
|
16297
|
+
import path17 from "path";
|
|
15688
16298
|
async function readHermesSessionTitle(sessionId, paths, profileName) {
|
|
15689
16299
|
const trimmedSessionId = sessionId.trim();
|
|
15690
16300
|
if (!trimmedSessionId) {
|
|
15691
16301
|
return void 0;
|
|
15692
16302
|
}
|
|
15693
16303
|
const resolvedProfileName = isValidProfileName(profileName) ? profileName : "default";
|
|
15694
|
-
const dbPath =
|
|
16304
|
+
const dbPath = path17.join(
|
|
15695
16305
|
resolveHermesProfileDir(resolvedProfileName),
|
|
15696
16306
|
"state.db"
|
|
15697
16307
|
);
|
|
@@ -15712,7 +16322,7 @@ async function readHermesCompressionTip(sessionId, paths, profileName) {
|
|
|
15712
16322
|
return void 0;
|
|
15713
16323
|
}
|
|
15714
16324
|
const resolvedProfileName = isValidProfileName(profileName) ? profileName : "default";
|
|
15715
|
-
const dbPath =
|
|
16325
|
+
const dbPath = path17.join(
|
|
15716
16326
|
resolveHermesProfileDir(resolvedProfileName),
|
|
15717
16327
|
"state.db"
|
|
15718
16328
|
);
|
|
@@ -15840,12 +16450,12 @@ var ConversationMetadataCoordinator = class {
|
|
|
15840
16450
|
return { ...next, last_event_seq: event.seq, updated_at: event.created_at };
|
|
15841
16451
|
}
|
|
15842
16452
|
scheduleGeneratedTitleRefresh(conversationId) {
|
|
15843
|
-
for (const
|
|
16453
|
+
for (const delay6 of GENERATED_TITLE_RETRY_DELAYS_MS) {
|
|
15844
16454
|
setTimeout(() => {
|
|
15845
16455
|
void this.generateTitleFromFirstRound(conversationId).catch(
|
|
15846
16456
|
() => void 0
|
|
15847
16457
|
);
|
|
15848
|
-
},
|
|
16458
|
+
}, delay6);
|
|
15849
16459
|
}
|
|
15850
16460
|
}
|
|
15851
16461
|
async renameConversation(conversationId, title, input) {
|
|
@@ -16145,7 +16755,7 @@ function stripCompressionTitleSuffix(value) {
|
|
|
16145
16755
|
|
|
16146
16756
|
// src/conversations/history-builder.ts
|
|
16147
16757
|
import { readFile as readFile8, stat as stat10 } from "fs/promises";
|
|
16148
|
-
import
|
|
16758
|
+
import path18 from "path";
|
|
16149
16759
|
var HISTORY_ROLES = /* @__PURE__ */ new Set(["user", "assistant"]);
|
|
16150
16760
|
var HERMES_HISTORY_COLUMNS = [
|
|
16151
16761
|
"role",
|
|
@@ -16217,13 +16827,13 @@ async function readHermesTranscriptHistory(sessionId, profileName) {
|
|
|
16217
16827
|
}
|
|
16218
16828
|
const normalizedProfileName = isValidProfileName2(profileName) ? profileName : "default";
|
|
16219
16829
|
const profileDir = resolveHermesProfileDir(normalizedProfileName);
|
|
16220
|
-
const dbPath =
|
|
16830
|
+
const dbPath = path18.join(profileDir, "state.db");
|
|
16221
16831
|
const sessionsDirConfig = await readHermesSessionsDir(normalizedProfileName).then((value) => ({
|
|
16222
16832
|
sessionsDir: value.sessionsDir,
|
|
16223
16833
|
configured: value.configured,
|
|
16224
16834
|
configError: false
|
|
16225
16835
|
})).catch(() => ({
|
|
16226
|
-
sessionsDir:
|
|
16836
|
+
sessionsDir: path18.join(profileDir, "sessions"),
|
|
16227
16837
|
configured: false,
|
|
16228
16838
|
configError: true
|
|
16229
16839
|
}));
|
|
@@ -16399,8 +17009,8 @@ async function readFirstExistingFile(paths) {
|
|
|
16399
17009
|
}
|
|
16400
17010
|
function candidateTranscriptPaths(sessionsDir, sessionId, extension) {
|
|
16401
17011
|
return [
|
|
16402
|
-
|
|
16403
|
-
|
|
17012
|
+
path18.join(sessionsDir, `session_${sessionId}.${extension}`),
|
|
17013
|
+
path18.join(sessionsDir, `${sessionId}.${extension}`)
|
|
16404
17014
|
];
|
|
16405
17015
|
}
|
|
16406
17016
|
function readHistoryRows(dbPath, sessionId) {
|
|
@@ -17037,7 +17647,10 @@ var ConversationOrchestrationCoordinator = class {
|
|
|
17037
17647
|
);
|
|
17038
17648
|
}
|
|
17039
17649
|
startRunWorkerAndDrain(conversationId, runId, input) {
|
|
17040
|
-
|
|
17650
|
+
let shouldDrainQueue = true;
|
|
17651
|
+
void this.deps.runLifecycle.startRunWorker(conversationId, runId, input).then((outcome) => {
|
|
17652
|
+
shouldDrainQueue = outcome.shouldDrainQueue;
|
|
17653
|
+
}).catch(async (error) => {
|
|
17041
17654
|
if (isConversationNotFoundError(error)) {
|
|
17042
17655
|
return;
|
|
17043
17656
|
}
|
|
@@ -17063,6 +17676,9 @@ var ConversationOrchestrationCoordinator = class {
|
|
|
17063
17676
|
});
|
|
17064
17677
|
}
|
|
17065
17678
|
}).finally(() => {
|
|
17679
|
+
if (!shouldDrainQueue) {
|
|
17680
|
+
return;
|
|
17681
|
+
}
|
|
17066
17682
|
void this.startNextQueuedRun(conversationId).catch((error) => {
|
|
17067
17683
|
void this.deps.logger.warn("conversation_queue_drain_failed", {
|
|
17068
17684
|
conversation_id: conversationId,
|
|
@@ -18356,6 +18972,10 @@ var APP_TOOL_EVENT_FIELDS_TO_DROP = /* @__PURE__ */ new Set([
|
|
|
18356
18972
|
"message"
|
|
18357
18973
|
]);
|
|
18358
18974
|
var LIFECYCLE_MARKER_FORMAT = "hermes-link-lifecycle-marker";
|
|
18975
|
+
var APP_VISIBLE_LIFECYCLE_MARKER_KINDS = /* @__PURE__ */ new Set([
|
|
18976
|
+
"context_compression",
|
|
18977
|
+
"goal_completion"
|
|
18978
|
+
]);
|
|
18359
18979
|
function projectConversationAgentEvent(event, language = readLanguage(event.payload)) {
|
|
18360
18980
|
if (!isAgentActivityEvent(event.type)) {
|
|
18361
18981
|
return null;
|
|
@@ -18576,7 +19196,7 @@ function retainAppVisibleRaw(value) {
|
|
|
18576
19196
|
return null;
|
|
18577
19197
|
}
|
|
18578
19198
|
const payload = toRecord8(raw.payload);
|
|
18579
|
-
if (payload.kind !== "
|
|
19199
|
+
if (typeof payload.kind !== "string" || !APP_VISIBLE_LIFECYCLE_MARKER_KINDS.has(payload.kind)) {
|
|
18580
19200
|
return null;
|
|
18581
19201
|
}
|
|
18582
19202
|
return {
|
|
@@ -18942,11 +19562,14 @@ var ConversationQueryCoordinator = class {
|
|
|
18942
19562
|
);
|
|
18943
19563
|
}
|
|
18944
19564
|
const startIndex = Math.max(0, endIndex - limit);
|
|
19565
|
+
const events = await this.listEvents(conversationId, 0).catch(() => []);
|
|
18945
19566
|
const messages2 = await this.hydrateAgentEventsForMessages(
|
|
18946
19567
|
conversationId,
|
|
18947
19568
|
snapshot.messages.slice(startIndex, endIndex),
|
|
18948
|
-
snapshot
|
|
19569
|
+
snapshot,
|
|
19570
|
+
events
|
|
18949
19571
|
);
|
|
19572
|
+
const todoSnapshot = latestTodoSnapshotFromEvents(conversationId, events);
|
|
18950
19573
|
return {
|
|
18951
19574
|
messages: messages2,
|
|
18952
19575
|
last_event_seq: manifest.last_event_seq,
|
|
@@ -18964,7 +19587,8 @@ var ConversationQueryCoordinator = class {
|
|
|
18964
19587
|
},
|
|
18965
19588
|
event_stream: buildConversationEventStreamState(snapshot),
|
|
18966
19589
|
input_requests: pendingInputRequests(snapshot),
|
|
18967
|
-
...normalizeConversationGoalState(manifest.command_state?.goal) ? { goal: normalizeConversationGoalState(manifest.command_state?.goal) } : {}
|
|
19590
|
+
...normalizeConversationGoalState(manifest.command_state?.goal) ? { goal: normalizeConversationGoalState(manifest.command_state?.goal) } : {},
|
|
19591
|
+
...todoSnapshot ? { todo_snapshot: todoSnapshot } : {}
|
|
18968
19592
|
};
|
|
18969
19593
|
}
|
|
18970
19594
|
async listEvents(conversationId, after = 0) {
|
|
@@ -18977,12 +19601,11 @@ var ConversationQueryCoordinator = class {
|
|
|
18977
19601
|
profile ?? await readConversationProfileSummary(this.deps.paths, manifest)
|
|
18978
19602
|
);
|
|
18979
19603
|
}
|
|
18980
|
-
async hydrateAgentEventsForMessages(conversationId, messages2, snapshot) {
|
|
19604
|
+
async hydrateAgentEventsForMessages(conversationId, messages2, snapshot, events) {
|
|
18981
19605
|
if (!messages2.some((message) => message.role === "assistant")) {
|
|
18982
19606
|
return messages2;
|
|
18983
19607
|
}
|
|
18984
19608
|
const eventsByMessageId = /* @__PURE__ */ new Map();
|
|
18985
|
-
const events = await this.listEvents(conversationId, 0).catch(() => []);
|
|
18986
19609
|
for (const event of events) {
|
|
18987
19610
|
if (!event.message_id) {
|
|
18988
19611
|
continue;
|
|
@@ -19018,6 +19641,81 @@ var ConversationQueryCoordinator = class {
|
|
|
19018
19641
|
});
|
|
19019
19642
|
}
|
|
19020
19643
|
};
|
|
19644
|
+
function latestTodoSnapshotFromEvents(conversationId, events) {
|
|
19645
|
+
for (let index = events.length - 1; index >= 0; index -= 1) {
|
|
19646
|
+
const snapshot = todoSnapshotFromEvent(conversationId, events[index]);
|
|
19647
|
+
if (snapshot) {
|
|
19648
|
+
return snapshot;
|
|
19649
|
+
}
|
|
19650
|
+
}
|
|
19651
|
+
return null;
|
|
19652
|
+
}
|
|
19653
|
+
function todoSnapshotFromEvent(conversationId, event) {
|
|
19654
|
+
const type = event.type.toLowerCase();
|
|
19655
|
+
if (type !== "tool.completed" && type !== "tool.complete") {
|
|
19656
|
+
return null;
|
|
19657
|
+
}
|
|
19658
|
+
const payload = readRecord(event.payload);
|
|
19659
|
+
const toolName = readToolName(payload)?.toLowerCase();
|
|
19660
|
+
if (toolName !== "todo") {
|
|
19661
|
+
return null;
|
|
19662
|
+
}
|
|
19663
|
+
const todos = readTodoItems(payload.todos) ?? readTodoItems(readRecord(payload.result).todos) ?? readTodoItems(readRecord(payload.output).todos) ?? readTodoItems(readRecord(payload.content).todos);
|
|
19664
|
+
if (!todos || todos.length === 0) {
|
|
19665
|
+
return null;
|
|
19666
|
+
}
|
|
19667
|
+
return {
|
|
19668
|
+
conversation_id: conversationId,
|
|
19669
|
+
...event.run_id ? { run_id: event.run_id } : {},
|
|
19670
|
+
updated_at: event.created_at,
|
|
19671
|
+
items: todos
|
|
19672
|
+
};
|
|
19673
|
+
}
|
|
19674
|
+
function readTodoItems(value) {
|
|
19675
|
+
if (!Array.isArray(value)) {
|
|
19676
|
+
return null;
|
|
19677
|
+
}
|
|
19678
|
+
const items = value.map((item, index) => readTodoItem(item, index)).filter((item) => item !== null);
|
|
19679
|
+
return items.length > 0 ? items : null;
|
|
19680
|
+
}
|
|
19681
|
+
function readTodoItem(value, index) {
|
|
19682
|
+
const record = readRecord(value);
|
|
19683
|
+
const content = readNonEmptyString(record.content) ?? readNonEmptyString(record.title);
|
|
19684
|
+
const status = readTodoStatus(readNonEmptyString(record.status));
|
|
19685
|
+
if (!content || !status) {
|
|
19686
|
+
return null;
|
|
19687
|
+
}
|
|
19688
|
+
return {
|
|
19689
|
+
id: readNonEmptyString(record.id) ?? `${index + 1}`,
|
|
19690
|
+
content,
|
|
19691
|
+
status
|
|
19692
|
+
};
|
|
19693
|
+
}
|
|
19694
|
+
function readTodoStatus(value) {
|
|
19695
|
+
switch (value?.trim().toLowerCase()) {
|
|
19696
|
+
case "pending":
|
|
19697
|
+
return "pending";
|
|
19698
|
+
case "in_progress":
|
|
19699
|
+
case "in-progress":
|
|
19700
|
+
case "running":
|
|
19701
|
+
return "in_progress";
|
|
19702
|
+
case "completed":
|
|
19703
|
+
case "complete":
|
|
19704
|
+
case "done":
|
|
19705
|
+
return "completed";
|
|
19706
|
+
case "cancelled":
|
|
19707
|
+
case "canceled":
|
|
19708
|
+
return "cancelled";
|
|
19709
|
+
default:
|
|
19710
|
+
return null;
|
|
19711
|
+
}
|
|
19712
|
+
}
|
|
19713
|
+
function readToolName(record) {
|
|
19714
|
+
return readNonEmptyString(record.tool_name) ?? readNonEmptyString(record.toolName) ?? readNonEmptyString(record.name) ?? readNonEmptyString(record.tool) ?? readNonEmptyString(readRecord(record.tool).name);
|
|
19715
|
+
}
|
|
19716
|
+
function readRecord(value) {
|
|
19717
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
|
|
19718
|
+
}
|
|
19021
19719
|
function readRunLanguage(snapshot, runId) {
|
|
19022
19720
|
if (!runId) {
|
|
19023
19721
|
return null;
|
|
@@ -19830,7 +20528,7 @@ import {
|
|
|
19830
20528
|
rm as rm5,
|
|
19831
20529
|
writeFile as writeFile2
|
|
19832
20530
|
} from "fs/promises";
|
|
19833
|
-
import
|
|
20531
|
+
import path19 from "path";
|
|
19834
20532
|
var ConversationStore = class {
|
|
19835
20533
|
constructor(paths) {
|
|
19836
20534
|
this.paths = paths;
|
|
@@ -19961,23 +20659,23 @@ var ConversationStore = class {
|
|
|
19961
20659
|
return manifest != null && manifest.status !== "deleted_soft";
|
|
19962
20660
|
}
|
|
19963
20661
|
removeConversationAttachments(conversationId) {
|
|
19964
|
-
return rm5(
|
|
20662
|
+
return rm5(path19.join(this.conversationDir(conversationId), "attachments"), {
|
|
19965
20663
|
recursive: true,
|
|
19966
20664
|
force: true
|
|
19967
20665
|
});
|
|
19968
20666
|
}
|
|
19969
20667
|
conversationDir(conversationId) {
|
|
19970
20668
|
assertValidConversationId(conversationId);
|
|
19971
|
-
return
|
|
20669
|
+
return path19.join(this.paths.conversationsDir, conversationId);
|
|
19972
20670
|
}
|
|
19973
20671
|
manifestPath(conversationId) {
|
|
19974
|
-
return
|
|
20672
|
+
return path19.join(this.conversationDir(conversationId), "manifest.json");
|
|
19975
20673
|
}
|
|
19976
20674
|
snapshotPath(conversationId) {
|
|
19977
|
-
return
|
|
20675
|
+
return path19.join(this.conversationDir(conversationId), "snapshot.json");
|
|
19978
20676
|
}
|
|
19979
20677
|
eventsPath(conversationId) {
|
|
19980
|
-
return
|
|
20678
|
+
return path19.join(this.conversationDir(conversationId), "events.ndjson");
|
|
19981
20679
|
}
|
|
19982
20680
|
};
|
|
19983
20681
|
function createEmptySnapshot2() {
|
|
@@ -19988,13 +20686,13 @@ function isNodeError11(error, code) {
|
|
|
19988
20686
|
}
|
|
19989
20687
|
|
|
19990
20688
|
// src/conversations/hermes-session-sync.ts
|
|
19991
|
-
import { randomUUID as randomUUID11 } from "crypto";
|
|
20689
|
+
import { createHash as createHash6, randomUUID as randomUUID11 } from "crypto";
|
|
19992
20690
|
import { readdir as readdir7, readFile as readFile11, stat as stat12 } from "fs/promises";
|
|
19993
|
-
import
|
|
20691
|
+
import path21 from "path";
|
|
19994
20692
|
|
|
19995
20693
|
// src/conversations/delivery-import.ts
|
|
19996
20694
|
import { lstat as lstat2, readFile as readFile10, readdir as readdir6, stat as stat11 } from "fs/promises";
|
|
19997
|
-
import
|
|
20695
|
+
import path20 from "path";
|
|
19998
20696
|
var MAX_IMPORTED_BLOB_BYTES = 100 * 1024 * 1024;
|
|
19999
20697
|
var MAX_MEDIA_IMPORT_FAILURES = 20;
|
|
20000
20698
|
var MAX_DELIVERY_FILES = 50;
|
|
@@ -20065,16 +20763,16 @@ var SUPPORTED_DELIVERY_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
20065
20763
|
".m4a"
|
|
20066
20764
|
]);
|
|
20067
20765
|
function resolveDeliveryStagingTarget(paths, stagingDir) {
|
|
20068
|
-
const resolvedDir =
|
|
20069
|
-
const relative =
|
|
20070
|
-
if (!relative || relative.startsWith("..") ||
|
|
20766
|
+
const resolvedDir = path20.resolve(stagingDir);
|
|
20767
|
+
const relative = path20.relative(path20.resolve(paths.conversationsDir), resolvedDir);
|
|
20768
|
+
if (!relative || relative.startsWith("..") || path20.isAbsolute(relative)) {
|
|
20071
20769
|
throw new LinkHttpError(
|
|
20072
20770
|
400,
|
|
20073
20771
|
"delivery_staging_invalid",
|
|
20074
20772
|
"delivery staging directory must be inside Hermes Link conversations"
|
|
20075
20773
|
);
|
|
20076
20774
|
}
|
|
20077
|
-
const segments = relative.split(
|
|
20775
|
+
const segments = relative.split(path20.sep);
|
|
20078
20776
|
if (segments.length !== 3 || segments[1] !== DELIVERY_STAGING_SEGMENT || !segments[0] || !segments[2]) {
|
|
20079
20777
|
throw new LinkHttpError(
|
|
20080
20778
|
400,
|
|
@@ -20110,7 +20808,7 @@ async function collectStagedDeliveryReferences(stagingDir) {
|
|
|
20110
20808
|
return entries.filter((entry) => entry.isFile() && !entry.name.startsWith(".")).filter((entry) => isSupportedDeliveryFilename(entry.name)).sort(
|
|
20111
20809
|
(left, right) => left.name.localeCompare(right.name, "en", { numeric: true })
|
|
20112
20810
|
).slice(0, MAX_DELIVERY_FILES).map((entry) => {
|
|
20113
|
-
const sourcePath =
|
|
20811
|
+
const sourcePath = path20.join(stagingDir, entry.name);
|
|
20114
20812
|
const mime = inferMimeType(sourcePath);
|
|
20115
20813
|
return {
|
|
20116
20814
|
path: sourcePath,
|
|
@@ -20296,7 +20994,7 @@ async function writeBlobFromFile(deps, conversationId, source) {
|
|
|
20296
20994
|
}
|
|
20297
20995
|
return deps.writeBlob(conversationId, {
|
|
20298
20996
|
bytes: await readFile10(sourcePath),
|
|
20299
|
-
filename:
|
|
20997
|
+
filename: path20.basename(sourcePath),
|
|
20300
20998
|
mime: source.mime ?? inferMimeType(sourcePath)
|
|
20301
20999
|
});
|
|
20302
21000
|
}
|
|
@@ -20309,7 +21007,7 @@ function describeMediaImportFailure(reference, sourceKey, error) {
|
|
|
20309
21007
|
};
|
|
20310
21008
|
}
|
|
20311
21009
|
function isSupportedDeliveryFilename(filename) {
|
|
20312
|
-
return SUPPORTED_DELIVERY_EXTENSIONS.has(
|
|
21010
|
+
return SUPPORTED_DELIVERY_EXTENSIONS.has(path20.extname(filename).toLowerCase());
|
|
20313
21011
|
}
|
|
20314
21012
|
function readString10(payload, key) {
|
|
20315
21013
|
const value = payload[key];
|
|
@@ -20367,7 +21065,7 @@ async function syncHermesSessionsIntoConversations(paths, logger, options = {})
|
|
|
20367
21065
|
const candidates = [];
|
|
20368
21066
|
for (const profileName of profileNames) {
|
|
20369
21067
|
const profileDir = resolveHermesProfileDir(profileName);
|
|
20370
|
-
const dbPath =
|
|
21068
|
+
const dbPath = path21.join(profileDir, "state.db");
|
|
20371
21069
|
const sessions = await listProfileSessions(dbPath).catch((error) => {
|
|
20372
21070
|
result.errors.push({
|
|
20373
21071
|
profile: profileName,
|
|
@@ -20483,7 +21181,7 @@ async function syncHermesCronSessionIntoConversations(paths, logger, input) {
|
|
|
20483
21181
|
const knownHermesSessions = await readKnownHermesSessions(store);
|
|
20484
21182
|
const profileName = input.profileName.trim() || DEFAULT_PROFILE_NAME;
|
|
20485
21183
|
const profileDir = resolveHermesProfileDir(profileName);
|
|
20486
|
-
const dbPath =
|
|
21184
|
+
const dbPath = path21.join(profileDir, "state.db");
|
|
20487
21185
|
const sessions = await listProfileSessionsByIdPrefix(
|
|
20488
21186
|
dbPath,
|
|
20489
21187
|
`cron_${jobId}_`
|
|
@@ -20548,6 +21246,131 @@ async function syncHermesCronSessionIntoConversations(paths, logger, input) {
|
|
|
20548
21246
|
reprojected: false
|
|
20549
21247
|
};
|
|
20550
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
|
+
}
|
|
20551
21374
|
async function importHermesSession(input) {
|
|
20552
21375
|
const { paths, store, logger, candidate } = input;
|
|
20553
21376
|
const profile = await resolveConversationProfileTarget(
|
|
@@ -20759,6 +21582,143 @@ function findKnownConversationIdsForCandidate(known, candidate) {
|
|
|
20759
21582
|
}
|
|
20760
21583
|
return conversationIds;
|
|
20761
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
|
+
}
|
|
20762
21722
|
function lineageSessionIds(candidate) {
|
|
20763
21723
|
return normalizeSessionIds([
|
|
20764
21724
|
candidate.session._lineage_root_id,
|
|
@@ -21048,9 +22008,12 @@ function appendHermesRowOnce(rows, seen, row) {
|
|
|
21048
22008
|
}
|
|
21049
22009
|
function hermesRowKey(row, fallbackIndex) {
|
|
21050
22010
|
if (row.id !== void 0 && row.id !== null) {
|
|
21051
|
-
return
|
|
22011
|
+
return hermesMessageIdKey(readString11(row, "session_id") ?? "", row.id);
|
|
21052
22012
|
}
|
|
21053
|
-
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)}`;
|
|
21054
22017
|
}
|
|
21055
22018
|
function hasHermesToolMetadata(row) {
|
|
21056
22019
|
return normalizeMessageRole(row.role) === "tool" || readHermesToolCalls(row).length > 0 || Boolean(readString11(row, "tool_call_id")) || Boolean(readString11(row, "tool_name"));
|
|
@@ -21794,8 +22757,8 @@ async function readJsonlMessages(profileName, sessionId) {
|
|
|
21794
22757
|
return [];
|
|
21795
22758
|
}
|
|
21796
22759
|
const profileDir = resolveHermesProfileDir(profileName);
|
|
21797
|
-
const sessionsDir = await readHermesSessionsDir(profileName).then((value) => value.sessionsDir).catch(() =>
|
|
21798
|
-
const transcriptPath =
|
|
22760
|
+
const sessionsDir = await readHermesSessionsDir(profileName).then((value) => value.sessionsDir).catch(() => path21.join(profileDir, "sessions"));
|
|
22761
|
+
const transcriptPath = path21.join(sessionsDir, `${sessionId}.jsonl`);
|
|
21799
22762
|
const raw = await readFile11(transcriptPath, "utf8").catch((error) => {
|
|
21800
22763
|
if (isNodeError13(error, "ENOENT")) {
|
|
21801
22764
|
return "";
|
|
@@ -22103,13 +23066,13 @@ function toRecord11(value) {
|
|
|
22103
23066
|
return typeof value === "object" && value !== null ? value : {};
|
|
22104
23067
|
}
|
|
22105
23068
|
function isDeletedSession(session) {
|
|
22106
|
-
return
|
|
23069
|
+
return readBoolean2(session.deleted) || readBoolean2(session.is_deleted) || Boolean(readString11(session, "deleted_at")) || ["deleted", "removed"].includes(readString11(session, "status") ?? "");
|
|
22107
23070
|
}
|
|
22108
23071
|
function isHiddenSession(session) {
|
|
22109
23072
|
const source = readString11(session, "source")?.toLowerCase();
|
|
22110
23073
|
const status = readString11(session, "status")?.toLowerCase();
|
|
22111
23074
|
const visibility = readString11(session, "visibility")?.toLowerCase();
|
|
22112
|
-
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";
|
|
22113
23076
|
}
|
|
22114
23077
|
function readTableColumns2(db, tableName) {
|
|
22115
23078
|
try {
|
|
@@ -22150,7 +23113,7 @@ function readString11(payload, key) {
|
|
|
22150
23113
|
function readNumber3(value) {
|
|
22151
23114
|
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
22152
23115
|
}
|
|
22153
|
-
function
|
|
23116
|
+
function readBoolean2(value) {
|
|
22154
23117
|
if (value === true || value === 1) {
|
|
22155
23118
|
return true;
|
|
22156
23119
|
}
|
|
@@ -22165,7 +23128,7 @@ function isNodeError13(error, code) {
|
|
|
22165
23128
|
|
|
22166
23129
|
// src/conversations/delivery-context.ts
|
|
22167
23130
|
import { copyFile, mkdir as mkdir11, stat as stat13 } from "fs/promises";
|
|
22168
|
-
import
|
|
23131
|
+
import path22 from "path";
|
|
22169
23132
|
var ACTIVE_CONTEXTS = /* @__PURE__ */ new Map();
|
|
22170
23133
|
var CONTEXT_TTL_MS = 2 * 60 * 60 * 1e3;
|
|
22171
23134
|
var MAX_DELIVERY_TOOL_FILES = 50;
|
|
@@ -22237,10 +23200,10 @@ function findDeliveryContext(input) {
|
|
|
22237
23200
|
);
|
|
22238
23201
|
}
|
|
22239
23202
|
async function copyFilesIntoDeliveryContext(paths, context, files) {
|
|
22240
|
-
const stagingDir =
|
|
22241
|
-
const conversationsRoot =
|
|
22242
|
-
const relative =
|
|
22243
|
-
if (!relative || relative.startsWith("..") ||
|
|
23203
|
+
const stagingDir = path22.resolve(context.stagingDir);
|
|
23204
|
+
const conversationsRoot = path22.resolve(paths.conversationsDir);
|
|
23205
|
+
const relative = path22.relative(conversationsRoot, stagingDir);
|
|
23206
|
+
if (!relative || relative.startsWith("..") || path22.isAbsolute(relative)) {
|
|
22244
23207
|
throw new LinkHttpError(
|
|
22245
23208
|
400,
|
|
22246
23209
|
"delivery_staging_invalid",
|
|
@@ -22251,13 +23214,13 @@ async function copyFilesIntoDeliveryContext(paths, context, files) {
|
|
|
22251
23214
|
const result = { staged: [], skipped: [] };
|
|
22252
23215
|
const usedNames = /* @__PURE__ */ new Set();
|
|
22253
23216
|
for (const [index, file] of files.slice(0, MAX_DELIVERY_TOOL_FILES).entries()) {
|
|
22254
|
-
const sourcePath =
|
|
23217
|
+
const sourcePath = path22.resolve(file.path);
|
|
22255
23218
|
let baseName = sanitizeFilename(
|
|
22256
|
-
file.caption ||
|
|
23219
|
+
file.caption || path22.basename(sourcePath),
|
|
22257
23220
|
`attachment-${index + 1}`
|
|
22258
23221
|
);
|
|
22259
|
-
const sourceExtension =
|
|
22260
|
-
if (!
|
|
23222
|
+
const sourceExtension = path22.extname(sourcePath);
|
|
23223
|
+
if (!path22.extname(baseName) && sourceExtension) {
|
|
22261
23224
|
baseName = `${baseName}${sourceExtension}`;
|
|
22262
23225
|
}
|
|
22263
23226
|
const filename = uniqueStagingFilename(
|
|
@@ -22284,7 +23247,7 @@ async function copyFilesIntoDeliveryContext(paths, context, files) {
|
|
|
22284
23247
|
});
|
|
22285
23248
|
continue;
|
|
22286
23249
|
}
|
|
22287
|
-
const targetPath =
|
|
23250
|
+
const targetPath = path22.join(stagingDir, filename);
|
|
22288
23251
|
await copyFile(sourcePath, targetPath);
|
|
22289
23252
|
result.staged.push({
|
|
22290
23253
|
source_path: sourcePath,
|
|
@@ -22327,7 +23290,7 @@ function normalizeDeliveryFileInputs(value) {
|
|
|
22327
23290
|
});
|
|
22328
23291
|
}
|
|
22329
23292
|
function uniqueStagingFilename(filename, usedNames) {
|
|
22330
|
-
const extension =
|
|
23293
|
+
const extension = path22.extname(filename);
|
|
22331
23294
|
const stem = filename.slice(0, filename.length - extension.length);
|
|
22332
23295
|
let candidate = filename;
|
|
22333
23296
|
let suffix = 2;
|
|
@@ -22368,7 +23331,7 @@ function isNodeError14(error, code) {
|
|
|
22368
23331
|
}
|
|
22369
23332
|
|
|
22370
23333
|
// src/conversations/run-lifecycle.ts
|
|
22371
|
-
import { createHash as
|
|
23334
|
+
import { createHash as createHash8 } from "crypto";
|
|
22372
23335
|
import { readdir as readdir9 } from "fs/promises";
|
|
22373
23336
|
|
|
22374
23337
|
// src/hermes/api-server.ts
|
|
@@ -22818,9 +23781,9 @@ function parseHermesApiCapabilities(payload) {
|
|
|
22818
23781
|
const runStop = isRecord2(endpoints.run_stop) ? endpoints.run_stop : {};
|
|
22819
23782
|
return {
|
|
22820
23783
|
source: "reported",
|
|
22821
|
-
authRequired:
|
|
22822
|
-
responsesStreaming:
|
|
22823
|
-
runStopPath:
|
|
23784
|
+
authRequired: readBoolean3(auth, "required"),
|
|
23785
|
+
responsesStreaming: readBoolean3(features, "responses_streaming"),
|
|
23786
|
+
runStopPath: readBoolean3(features, "run_stop") === false ? null : readString13(runStop, "path"),
|
|
22824
23787
|
sessionContinuityHeader: readString13(
|
|
22825
23788
|
features,
|
|
22826
23789
|
"session_continuity_header"
|
|
@@ -22828,10 +23791,10 @@ function parseHermesApiCapabilities(payload) {
|
|
|
22828
23791
|
sessionKeyHeader: readString13(features, "session_key_header")
|
|
22829
23792
|
};
|
|
22830
23793
|
}
|
|
22831
|
-
async function callHermesApi(
|
|
23794
|
+
async function callHermesApi(path38, init, options) {
|
|
22832
23795
|
const method = init.method ?? "GET";
|
|
22833
23796
|
const startedAt = Date.now();
|
|
22834
|
-
void options.logger?.debug("hermes_api_request_started", { method, path:
|
|
23797
|
+
void options.logger?.debug("hermes_api_request_started", { method, path: path38 });
|
|
22835
23798
|
const availability = await ensureHermesApiServerAvailable({
|
|
22836
23799
|
fetchImpl: options.fetchImpl,
|
|
22837
23800
|
logger: options.logger,
|
|
@@ -22840,7 +23803,7 @@ async function callHermesApi(path37, init, options) {
|
|
|
22840
23803
|
});
|
|
22841
23804
|
let config = availability.configResult.apiServer;
|
|
22842
23805
|
const fetcher = options.fetchImpl ?? fetch;
|
|
22843
|
-
const request = () => fetchHermesApi(fetcher, config,
|
|
23806
|
+
const request = () => fetchHermesApi(fetcher, config, path38, init, options);
|
|
22844
23807
|
let response;
|
|
22845
23808
|
try {
|
|
22846
23809
|
response = await request();
|
|
@@ -22848,7 +23811,7 @@ async function callHermesApi(path37, init, options) {
|
|
|
22848
23811
|
logHermesApiError(
|
|
22849
23812
|
options.logger,
|
|
22850
23813
|
method,
|
|
22851
|
-
|
|
23814
|
+
path38,
|
|
22852
23815
|
options.profileName,
|
|
22853
23816
|
startedAt,
|
|
22854
23817
|
error
|
|
@@ -22859,7 +23822,7 @@ async function callHermesApi(path37, init, options) {
|
|
|
22859
23822
|
logHermesApiResponse(
|
|
22860
23823
|
options.logger,
|
|
22861
23824
|
method,
|
|
22862
|
-
|
|
23825
|
+
path38,
|
|
22863
23826
|
options.profileName,
|
|
22864
23827
|
startedAt,
|
|
22865
23828
|
response
|
|
@@ -22868,7 +23831,7 @@ async function callHermesApi(path37, init, options) {
|
|
|
22868
23831
|
}
|
|
22869
23832
|
void options.logger?.warn("hermes_api_request_retrying_after_401", {
|
|
22870
23833
|
method,
|
|
22871
|
-
path:
|
|
23834
|
+
path: path38,
|
|
22872
23835
|
profile: options.profileName ?? "default",
|
|
22873
23836
|
port: config.port ?? null,
|
|
22874
23837
|
duration_ms: Date.now() - startedAt
|
|
@@ -22887,7 +23850,7 @@ async function callHermesApi(path37, init, options) {
|
|
|
22887
23850
|
logHermesApiError(
|
|
22888
23851
|
options.logger,
|
|
22889
23852
|
method,
|
|
22890
|
-
|
|
23853
|
+
path38,
|
|
22891
23854
|
options.profileName,
|
|
22892
23855
|
startedAt,
|
|
22893
23856
|
error
|
|
@@ -22897,7 +23860,7 @@ async function callHermesApi(path37, init, options) {
|
|
|
22897
23860
|
logHermesApiResponse(
|
|
22898
23861
|
options.logger,
|
|
22899
23862
|
method,
|
|
22900
|
-
|
|
23863
|
+
path38,
|
|
22901
23864
|
options.profileName,
|
|
22902
23865
|
startedAt,
|
|
22903
23866
|
response
|
|
@@ -22907,7 +23870,7 @@ async function callHermesApi(path37, init, options) {
|
|
|
22907
23870
|
}
|
|
22908
23871
|
void options.logger?.warn("hermes_api_request_repairing_after_401", {
|
|
22909
23872
|
method,
|
|
22910
|
-
path:
|
|
23873
|
+
path: path38,
|
|
22911
23874
|
profile: options.profileName ?? "default",
|
|
22912
23875
|
port: config.port ?? null,
|
|
22913
23876
|
duration_ms: Date.now() - startedAt
|
|
@@ -22928,7 +23891,7 @@ async function callHermesApi(path37, init, options) {
|
|
|
22928
23891
|
logHermesApiError(
|
|
22929
23892
|
options.logger,
|
|
22930
23893
|
method,
|
|
22931
|
-
|
|
23894
|
+
path38,
|
|
22932
23895
|
options.profileName,
|
|
22933
23896
|
startedAt,
|
|
22934
23897
|
error
|
|
@@ -22938,21 +23901,21 @@ async function callHermesApi(path37, init, options) {
|
|
|
22938
23901
|
logHermesApiResponse(
|
|
22939
23902
|
options.logger,
|
|
22940
23903
|
method,
|
|
22941
|
-
|
|
23904
|
+
path38,
|
|
22942
23905
|
options.profileName,
|
|
22943
23906
|
startedAt,
|
|
22944
23907
|
response
|
|
22945
23908
|
);
|
|
22946
23909
|
return response;
|
|
22947
23910
|
}
|
|
22948
|
-
async function fetchHermesApi(fetcher, config,
|
|
23911
|
+
async function fetchHermesApi(fetcher, config, path38, init, options) {
|
|
22949
23912
|
const headers = new Headers(init.headers);
|
|
22950
23913
|
headers.set("accept", headers.get("accept") ?? "application/json");
|
|
22951
23914
|
if (config.key) {
|
|
22952
23915
|
headers.set("x-api-key", config.key);
|
|
22953
23916
|
headers.set("authorization", `Bearer ${config.key}`);
|
|
22954
23917
|
}
|
|
22955
|
-
return await fetcher(`http://127.0.0.1:${config.port}${
|
|
23918
|
+
return await fetcher(`http://127.0.0.1:${config.port}${path38}`, {
|
|
22956
23919
|
...init,
|
|
22957
23920
|
headers
|
|
22958
23921
|
}).catch((error) => {
|
|
@@ -22961,10 +23924,10 @@ async function fetchHermesApi(fetcher, config, path37, init, options) {
|
|
|
22961
23924
|
}
|
|
22962
23925
|
void options.logger?.warn("hermes_api_server_connect_failed", {
|
|
22963
23926
|
method: String(init.method ?? "GET").toUpperCase(),
|
|
22964
|
-
path:
|
|
23927
|
+
path: path38,
|
|
22965
23928
|
profile: options.profileName ?? "default",
|
|
22966
23929
|
port: config.port ?? null,
|
|
22967
|
-
url: `http://127.0.0.1:${config.port}${
|
|
23930
|
+
url: `http://127.0.0.1:${config.port}${path38}`,
|
|
22968
23931
|
error: error instanceof Error ? error.message : String(error)
|
|
22969
23932
|
});
|
|
22970
23933
|
throw new LinkHttpError(
|
|
@@ -22974,10 +23937,10 @@ async function fetchHermesApi(fetcher, config, path37, init, options) {
|
|
|
22974
23937
|
);
|
|
22975
23938
|
});
|
|
22976
23939
|
}
|
|
22977
|
-
function logHermesApiResponse(logger, method,
|
|
23940
|
+
function logHermesApiResponse(logger, method, path38, profileName, startedAt, response) {
|
|
22978
23941
|
const fields = {
|
|
22979
23942
|
method,
|
|
22980
|
-
path:
|
|
23943
|
+
path: path38,
|
|
22981
23944
|
profile: profileName ?? "default",
|
|
22982
23945
|
status: response.status,
|
|
22983
23946
|
duration_ms: Date.now() - startedAt
|
|
@@ -22998,10 +23961,10 @@ async function logHermesApiFailureResponse(logger, fields, response) {
|
|
|
22998
23961
|
...upstreamError ? { upstream_error: upstreamError } : {}
|
|
22999
23962
|
});
|
|
23000
23963
|
}
|
|
23001
|
-
function logHermesApiError(logger, method,
|
|
23964
|
+
function logHermesApiError(logger, method, path38, profileName, startedAt, error) {
|
|
23002
23965
|
void logger?.warn("hermes_api_request_failed", {
|
|
23003
23966
|
method,
|
|
23004
|
-
path:
|
|
23967
|
+
path: path38,
|
|
23005
23968
|
profile: profileName ?? "default",
|
|
23006
23969
|
duration_ms: Date.now() - startedAt,
|
|
23007
23970
|
...error instanceof LinkHttpError ? { status: error.status, code: error.code } : {},
|
|
@@ -23062,18 +24025,18 @@ function readString13(payload, key) {
|
|
|
23062
24025
|
const value = payload[key];
|
|
23063
24026
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
23064
24027
|
}
|
|
23065
|
-
function
|
|
24028
|
+
function readBoolean3(payload, key) {
|
|
23066
24029
|
const value = payload[key];
|
|
23067
24030
|
return typeof value === "boolean" ? value : null;
|
|
23068
24031
|
}
|
|
23069
24032
|
|
|
23070
24033
|
// src/hermes/stt.ts
|
|
23071
|
-
import { execFile as
|
|
24034
|
+
import { execFile as execFile4 } from "child_process";
|
|
23072
24035
|
import { access as access2, readFile as readFile12, stat as stat14 } from "fs/promises";
|
|
23073
24036
|
import os6 from "os";
|
|
23074
|
-
import
|
|
23075
|
-
import { promisify as
|
|
23076
|
-
var
|
|
24037
|
+
import path23 from "path";
|
|
24038
|
+
import { promisify as promisify4 } from "util";
|
|
24039
|
+
var execFileAsync4 = promisify4(execFile4);
|
|
23077
24040
|
var STT_RESULT_PREFIX = "__HERMES_LINK_STT__";
|
|
23078
24041
|
var STT_TIMEOUT_MS = 18e4;
|
|
23079
24042
|
var STT_MAX_BUFFER_BYTES = 2 * 1024 * 1024;
|
|
@@ -23106,7 +24069,7 @@ async function transcribeAudioWithHermesProfile(input) {
|
|
|
23106
24069
|
let stdout = "";
|
|
23107
24070
|
let stderr = "";
|
|
23108
24071
|
try {
|
|
23109
|
-
const output = await
|
|
24072
|
+
const output = await execFileAsync4(
|
|
23110
24073
|
python.command,
|
|
23111
24074
|
[...python.args, "-c", script, input.audioPath],
|
|
23112
24075
|
{
|
|
@@ -23166,7 +24129,7 @@ async function buildHermesSttEnv(profileName, hermesSourceRoot) {
|
|
|
23166
24129
|
};
|
|
23167
24130
|
const sourceRoot = hermesSourceRoot ?? await findDevHermesAgentSource();
|
|
23168
24131
|
if (sourceRoot) {
|
|
23169
|
-
env.PYTHONPATH = [sourceRoot, env.PYTHONPATH].filter(Boolean).join(
|
|
24132
|
+
env.PYTHONPATH = [sourceRoot, env.PYTHONPATH].filter(Boolean).join(path23.delimiter);
|
|
23170
24133
|
}
|
|
23171
24134
|
return env;
|
|
23172
24135
|
}
|
|
@@ -23261,14 +24224,14 @@ async function resolveHermesPythonRuntime() {
|
|
|
23261
24224
|
};
|
|
23262
24225
|
}
|
|
23263
24226
|
async function resolveExecutablePath(command) {
|
|
23264
|
-
if (
|
|
24227
|
+
if (path23.isAbsolute(command)) {
|
|
23265
24228
|
return await isExecutableFile2(command) ? command : null;
|
|
23266
24229
|
}
|
|
23267
24230
|
const pathEnv = process.env.PATH ?? "";
|
|
23268
24231
|
const extensions = process.platform === "win32" ? (process.env.PATHEXT ?? ".EXE;.CMD;.BAT").split(";") : [""];
|
|
23269
|
-
for (const dir of pathEnv.split(
|
|
24232
|
+
for (const dir of pathEnv.split(path23.delimiter)) {
|
|
23270
24233
|
for (const extension of extensions) {
|
|
23271
|
-
const candidate =
|
|
24234
|
+
const candidate = path23.join(dir, `${command}${extension}`);
|
|
23272
24235
|
if (await isExecutableFile2(candidate)) {
|
|
23273
24236
|
return candidate;
|
|
23274
24237
|
}
|
|
@@ -23290,11 +24253,11 @@ async function isExecutableFile2(filePath) {
|
|
|
23290
24253
|
}
|
|
23291
24254
|
async function findHermesVenvPython(sourceRoot) {
|
|
23292
24255
|
const candidates = process.platform === "win32" ? [
|
|
23293
|
-
|
|
23294
|
-
|
|
24256
|
+
path23.join(sourceRoot, "venv", "Scripts", "python.exe"),
|
|
24257
|
+
path23.join(sourceRoot, ".venv", "Scripts", "python.exe")
|
|
23295
24258
|
] : [
|
|
23296
|
-
|
|
23297
|
-
|
|
24259
|
+
path23.join(sourceRoot, "venv", "bin", "python"),
|
|
24260
|
+
path23.join(sourceRoot, ".venv", "bin", "python")
|
|
23298
24261
|
];
|
|
23299
24262
|
for (const candidate of candidates) {
|
|
23300
24263
|
if (await isExecutableFile2(candidate)) {
|
|
@@ -23323,8 +24286,8 @@ function shebangToPythonCommand(shebang) {
|
|
|
23323
24286
|
}
|
|
23324
24287
|
async function findDevHermesAgentSource() {
|
|
23325
24288
|
const candidates = [
|
|
23326
|
-
|
|
23327
|
-
|
|
24289
|
+
path23.resolve(process.cwd(), "reference/hermes-agent"),
|
|
24290
|
+
path23.resolve(process.cwd(), "../../reference/hermes-agent")
|
|
23328
24291
|
];
|
|
23329
24292
|
for (const candidate of candidates) {
|
|
23330
24293
|
if (await isHermesAgentSourceRoot(candidate)) {
|
|
@@ -23340,7 +24303,7 @@ async function readHermesLauncherTarget(filePath) {
|
|
|
23340
24303
|
line
|
|
23341
24304
|
);
|
|
23342
24305
|
const rawTarget = quoted?.groups?.target ?? /^\s*exec\s+(?<target>\S+)\s+(?:"\$@"|'\$@')/.exec(line)?.groups?.target;
|
|
23343
|
-
if (!rawTarget || !
|
|
24306
|
+
if (!rawTarget || !path23.isAbsolute(rawTarget)) {
|
|
23344
24307
|
continue;
|
|
23345
24308
|
}
|
|
23346
24309
|
if (await isExecutableFile2(rawTarget)) {
|
|
@@ -23370,12 +24333,12 @@ async function resolveHermesEntrypointRuntime(entrypointPath) {
|
|
|
23370
24333
|
return null;
|
|
23371
24334
|
}
|
|
23372
24335
|
async function findHermesSourceRoot(executablePath) {
|
|
23373
|
-
let cursor =
|
|
24336
|
+
let cursor = path23.dirname(path23.resolve(executablePath));
|
|
23374
24337
|
for (let index = 0; index < 6; index += 1) {
|
|
23375
24338
|
if (await isHermesAgentSourceRoot(cursor)) {
|
|
23376
24339
|
return cursor;
|
|
23377
24340
|
}
|
|
23378
|
-
const parent =
|
|
24341
|
+
const parent = path23.dirname(cursor);
|
|
23379
24342
|
if (parent === cursor) {
|
|
23380
24343
|
break;
|
|
23381
24344
|
}
|
|
@@ -23386,14 +24349,14 @@ async function findHermesSourceRoot(executablePath) {
|
|
|
23386
24349
|
function hermesSourceRootCandidates() {
|
|
23387
24350
|
const candidates = [
|
|
23388
24351
|
process.env.HERMES_PYTHON_SRC_ROOT?.trim(),
|
|
23389
|
-
|
|
24352
|
+
path23.join(os6.homedir(), ".hermes", "hermes-agent"),
|
|
23390
24353
|
"/usr/local/lib/hermes-agent",
|
|
23391
24354
|
"/opt/hermes"
|
|
23392
24355
|
].filter((candidate) => Boolean(candidate));
|
|
23393
24356
|
if (process.platform === "win32") {
|
|
23394
24357
|
const localAppData = process.env.LOCALAPPDATA?.trim();
|
|
23395
24358
|
if (localAppData) {
|
|
23396
|
-
candidates.unshift(
|
|
24359
|
+
candidates.unshift(path23.join(localAppData, "hermes", "hermes-agent"));
|
|
23397
24360
|
}
|
|
23398
24361
|
}
|
|
23399
24362
|
return candidates;
|
|
@@ -23403,7 +24366,7 @@ async function findExplicitHermesSourceRoot() {
|
|
|
23403
24366
|
return explicit && await isHermesAgentSourceRoot(explicit) ? explicit : null;
|
|
23404
24367
|
}
|
|
23405
24368
|
async function isHermesAgentSourceRoot(candidate) {
|
|
23406
|
-
return await isDirectory(candidate) && await isDirectory(
|
|
24369
|
+
return await isDirectory(candidate) && await isDirectory(path23.join(candidate, "tools")) && await isDirectory(path23.join(candidate, "hermes_cli"));
|
|
23407
24370
|
}
|
|
23408
24371
|
async function isDirectory(candidate) {
|
|
23409
24372
|
return stat14(candidate).then((info) => info.isDirectory()).catch(() => false);
|
|
@@ -23415,15 +24378,15 @@ function compactProcessOutput(value) {
|
|
|
23415
24378
|
|
|
23416
24379
|
// src/hermes/usage-probe.ts
|
|
23417
24380
|
import { open as open3, readFile as readFile14, rm as rm6, stat as stat16 } from "fs/promises";
|
|
23418
|
-
import
|
|
24381
|
+
import path25 from "path";
|
|
23419
24382
|
import YAML3 from "yaml";
|
|
23420
24383
|
|
|
23421
24384
|
// src/hermes/profiles.ts
|
|
23422
|
-
import { execFile as
|
|
24385
|
+
import { execFile as execFile5 } from "child_process";
|
|
23423
24386
|
import { readdir as readdir8, readFile as readFile13, rename as rename3, stat as stat15 } from "fs/promises";
|
|
23424
|
-
import
|
|
23425
|
-
import { setTimeout as
|
|
23426
|
-
import { promisify as
|
|
24387
|
+
import path24 from "path";
|
|
24388
|
+
import { setTimeout as delay5 } from "timers/promises";
|
|
24389
|
+
import { promisify as promisify5 } from "util";
|
|
23427
24390
|
import YAML2 from "yaml";
|
|
23428
24391
|
var DEFAULT_PROFILE = "default";
|
|
23429
24392
|
var PROFILE_NAME_PATTERN5 = /^[a-zA-Z0-9._-]{1,64}$/;
|
|
@@ -23431,7 +24394,7 @@ var PROFILE_DELETE_TIMEOUT_MS = 3e4;
|
|
|
23431
24394
|
var PROFILE_GATEWAY_STOP_TIMEOUT_MS = 3e3;
|
|
23432
24395
|
var PROFILE_DELETE_STABLE_ABSENCE_MS = 1200;
|
|
23433
24396
|
var PROFILE_DELETE_VERIFY_INTERVAL_MS = 150;
|
|
23434
|
-
var
|
|
24397
|
+
var execFileAsync5 = promisify5(execFile5);
|
|
23435
24398
|
async function listHermesProfiles(paths = resolveRuntimePaths()) {
|
|
23436
24399
|
const profiles = /* @__PURE__ */ new Map();
|
|
23437
24400
|
if (await hasDefaultProfileConfigSource()) {
|
|
@@ -23564,7 +24527,7 @@ async function readHermesProfileCapabilities(name) {
|
|
|
23564
24527
|
return {
|
|
23565
24528
|
defaultModel: listedModels?.defaultModel ?? null,
|
|
23566
24529
|
modelCount: listedModels?.models.length ?? 0,
|
|
23567
|
-
skillCount: await countSkills(
|
|
24530
|
+
skillCount: await countSkills(path24.join(profileDir, "skills")).catch(
|
|
23568
24531
|
() => 0
|
|
23569
24532
|
),
|
|
23570
24533
|
toolCount: await countConfiguredTools(name).catch(() => 0)
|
|
@@ -23625,11 +24588,11 @@ async function hasDefaultProfileConfigSource() {
|
|
|
23625
24588
|
if (!profileStat?.isDirectory()) {
|
|
23626
24589
|
return false;
|
|
23627
24590
|
}
|
|
23628
|
-
return await pathExists(resolveHermesConfigPath(DEFAULT_PROFILE)) || await pathExists(
|
|
24591
|
+
return await pathExists(resolveHermesConfigPath(DEFAULT_PROFILE)) || await pathExists(path24.join(profilePath, ".env"));
|
|
23629
24592
|
}
|
|
23630
24593
|
async function deleteHermesProfileWithCli(name) {
|
|
23631
24594
|
try {
|
|
23632
|
-
await
|
|
24595
|
+
await execFileAsync5(resolveHermesBin(), ["profile", "delete", name, "--yes"], {
|
|
23633
24596
|
timeout: PROFILE_DELETE_TIMEOUT_MS,
|
|
23634
24597
|
windowsHide: true
|
|
23635
24598
|
});
|
|
@@ -23688,7 +24651,7 @@ async function findHermesGatewayProcessIdsForProfile(name) {
|
|
|
23688
24651
|
return [];
|
|
23689
24652
|
}
|
|
23690
24653
|
try {
|
|
23691
|
-
const output = await
|
|
24654
|
+
const output = await execFileAsync5("ps", ["-axo", "pid=,command="], {
|
|
23692
24655
|
timeout: 3e3,
|
|
23693
24656
|
windowsHide: true
|
|
23694
24657
|
});
|
|
@@ -23728,7 +24691,7 @@ async function waitForProcessesToExit(pids, timeoutMs) {
|
|
|
23728
24691
|
const deadline = Date.now() + timeoutMs;
|
|
23729
24692
|
let remaining = pids.filter(isProcessRunning);
|
|
23730
24693
|
while (remaining.length > 0 && Date.now() < deadline) {
|
|
23731
|
-
await
|
|
24694
|
+
await delay5(100);
|
|
23732
24695
|
remaining = remaining.filter(isProcessRunning);
|
|
23733
24696
|
}
|
|
23734
24697
|
return remaining;
|
|
@@ -23747,7 +24710,7 @@ async function waitForProfilePathToRemainAbsent(profilePath) {
|
|
|
23747
24710
|
if (await pathExists(profilePath)) {
|
|
23748
24711
|
return false;
|
|
23749
24712
|
}
|
|
23750
|
-
await
|
|
24713
|
+
await delay5(PROFILE_DELETE_VERIFY_INTERVAL_MS);
|
|
23751
24714
|
}
|
|
23752
24715
|
return !await pathExists(profilePath);
|
|
23753
24716
|
}
|
|
@@ -23795,7 +24758,7 @@ async function countSkills(root) {
|
|
|
23795
24758
|
);
|
|
23796
24759
|
let count = 0;
|
|
23797
24760
|
for (const entry of entries) {
|
|
23798
|
-
const entryPath =
|
|
24761
|
+
const entryPath = path24.join(root, entry.name);
|
|
23799
24762
|
if (entry.name === ".git" || entry.name === ".hub") {
|
|
23800
24763
|
continue;
|
|
23801
24764
|
}
|
|
@@ -23888,7 +24851,7 @@ async function ensureHermesUsageProbeForProfile(profileName, options = {}) {
|
|
|
23888
24851
|
const normalizedProfile = normalizeProfileName6(profileName);
|
|
23889
24852
|
const profilePath = resolveHermesProfileDir(normalizedProfile);
|
|
23890
24853
|
const configPath = resolveHermesConfigPath(normalizedProfile);
|
|
23891
|
-
const pluginPath =
|
|
24854
|
+
const pluginPath = path25.join(
|
|
23892
24855
|
profilePath,
|
|
23893
24856
|
"plugins",
|
|
23894
24857
|
HERMES_USAGE_PROBE_PLUGIN_KEY
|
|
@@ -23914,7 +24877,7 @@ async function ensureHermesUsageProbeForProfile(profileName, options = {}) {
|
|
|
23914
24877
|
return { ...base(), skipped: true };
|
|
23915
24878
|
}
|
|
23916
24879
|
try {
|
|
23917
|
-
await ensureDirectoryWithInheritedMetadata(
|
|
24880
|
+
await ensureDirectoryWithInheritedMetadata(path25.dirname(eventsPath), 448);
|
|
23918
24881
|
const state = await readUsageProbeState(paths);
|
|
23919
24882
|
const pluginState = state.profiles[normalizedProfile];
|
|
23920
24883
|
const currentConfig = await readUsageProbeConfigStatus({
|
|
@@ -24047,7 +25010,7 @@ async function findHermesUsageProbeEventForRun(input) {
|
|
|
24047
25010
|
return aggregateUsageProbeEvents(events.sort(compareUsageProbeEventsByTime));
|
|
24048
25011
|
}
|
|
24049
25012
|
function resolveUsageProbeEventsPath(paths = resolveRuntimePaths(), profileName = "default") {
|
|
24050
|
-
return
|
|
25013
|
+
return path25.join(
|
|
24051
25014
|
paths.homeDir,
|
|
24052
25015
|
USAGE_PROBE_DIR,
|
|
24053
25016
|
safeProfileSegment(profileName),
|
|
@@ -24071,8 +25034,8 @@ function summarizeUsageProbeEnsure(result) {
|
|
|
24071
25034
|
};
|
|
24072
25035
|
}
|
|
24073
25036
|
async function writeUsageProbePlugin(input) {
|
|
24074
|
-
const manifestPath =
|
|
24075
|
-
const initPath =
|
|
25037
|
+
const manifestPath = path25.join(input.pluginPath, "plugin.yaml");
|
|
25038
|
+
const initPath = path25.join(input.pluginPath, "__init__.py");
|
|
24076
25039
|
const manifest = usageProbeManifest();
|
|
24077
25040
|
const source = usageProbePythonSource(
|
|
24078
25041
|
input.profileName,
|
|
@@ -24419,9 +25382,9 @@ async function disableUsageProbeAfterActivationFailure(input) {
|
|
|
24419
25382
|
});
|
|
24420
25383
|
}
|
|
24421
25384
|
async function cleanupLegacyUsageProbePlugin(profilePath) {
|
|
24422
|
-
const legacyPath =
|
|
25385
|
+
const legacyPath = path25.join(profilePath, "plugins", LEGACY_PLUGIN_KEY);
|
|
24423
25386
|
const [manifest, init] = await Promise.all([
|
|
24424
|
-
readFile14(
|
|
25387
|
+
readFile14(path25.join(legacyPath, "plugin.yaml"), "utf8").catch(
|
|
24425
25388
|
(error) => {
|
|
24426
25389
|
if (isNodeError16(error, "ENOENT")) {
|
|
24427
25390
|
return null;
|
|
@@ -24429,7 +25392,7 @@ async function cleanupLegacyUsageProbePlugin(profilePath) {
|
|
|
24429
25392
|
throw error;
|
|
24430
25393
|
}
|
|
24431
25394
|
),
|
|
24432
|
-
readFile14(
|
|
25395
|
+
readFile14(path25.join(legacyPath, "__init__.py"), "utf8").catch(
|
|
24433
25396
|
(error) => {
|
|
24434
25397
|
if (isNodeError16(error, "ENOENT")) {
|
|
24435
25398
|
return null;
|
|
@@ -24478,7 +25441,7 @@ async function rememberUsageProbeState(paths, profileName, patch) {
|
|
|
24478
25441
|
}));
|
|
24479
25442
|
}
|
|
24480
25443
|
function usageProbeStatePath(paths) {
|
|
24481
|
-
return
|
|
25444
|
+
return path25.join(paths.homeDir, USAGE_PROBE_DIR, USAGE_PROBE_STATE_FILE);
|
|
24482
25445
|
}
|
|
24483
25446
|
async function readHermesConfigDocument2(configPath, language) {
|
|
24484
25447
|
const existingRaw = await readFile14(configPath, "utf8").catch(
|
|
@@ -25046,7 +26009,7 @@ function toRecord14(value) {
|
|
|
25046
26009
|
|
|
25047
26010
|
// src/conversations/run-transcript-enrichment.ts
|
|
25048
26011
|
import { readFile as readFile15, stat as stat17 } from "fs/promises";
|
|
25049
|
-
import
|
|
26012
|
+
import path26 from "path";
|
|
25050
26013
|
var MESSAGE_COLUMNS2 = [
|
|
25051
26014
|
"id",
|
|
25052
26015
|
"session_id",
|
|
@@ -25122,8 +26085,8 @@ async function readRunFinalAssistantText(input) {
|
|
|
25122
26085
|
}
|
|
25123
26086
|
async function readHermesTranscriptRows(profileName, sessionId) {
|
|
25124
26087
|
const profileDir = resolveHermesProfileDir(profileName);
|
|
25125
|
-
const dbPath =
|
|
25126
|
-
const sessionsDir = await readHermesSessionsDir(profileName).then((value) => value.sessionsDir).catch(() =>
|
|
26088
|
+
const dbPath = path26.join(profileDir, "state.db");
|
|
26089
|
+
const sessionsDir = await readHermesSessionsDir(profileName).then((value) => value.sessionsDir).catch(() => path26.join(profileDir, "sessions"));
|
|
25127
26090
|
const [dbRows, jsonlRows] = await Promise.all([
|
|
25128
26091
|
readStateDbMessages2(dbPath, sessionId),
|
|
25129
26092
|
readJsonlMessages2(sessionsDir, sessionId)
|
|
@@ -25186,7 +26149,7 @@ async function readJsonlMessages2(sessionsDir, sessionId) {
|
|
|
25186
26149
|
if (!/^[A-Za-z0-9._:-]{1,160}$/u.test(sessionId)) {
|
|
25187
26150
|
return [];
|
|
25188
26151
|
}
|
|
25189
|
-
const transcriptPath =
|
|
26152
|
+
const transcriptPath = path26.join(sessionsDir, `${sessionId}.jsonl`);
|
|
25190
26153
|
const raw = await readFile15(transcriptPath, "utf8").catch((error) => {
|
|
25191
26154
|
if (isNodeError17(error, "ENOENT")) {
|
|
25192
26155
|
return "";
|
|
@@ -25438,7 +26401,7 @@ function isNodeError17(error, code) {
|
|
|
25438
26401
|
}
|
|
25439
26402
|
|
|
25440
26403
|
// src/conversations/run-tool-event-ids.ts
|
|
25441
|
-
import { createHash as
|
|
26404
|
+
import { createHash as createHash7 } from "crypto";
|
|
25442
26405
|
var RunToolEventIdCoalescer = class {
|
|
25443
26406
|
scope;
|
|
25444
26407
|
ordinal = 0;
|
|
@@ -25453,7 +26416,7 @@ var RunToolEventIdCoalescer = class {
|
|
|
25453
26416
|
if (!type || hasStableToolEventId(event.payload)) {
|
|
25454
26417
|
return event;
|
|
25455
26418
|
}
|
|
25456
|
-
const toolKey = normalizeToolKey(
|
|
26419
|
+
const toolKey = normalizeToolKey(readToolName2(event.payload));
|
|
25457
26420
|
if (type === "tool.started") {
|
|
25458
26421
|
return withGeneratedToolEventId(
|
|
25459
26422
|
event,
|
|
@@ -25544,7 +26507,7 @@ function hasStableToolEventId(payload) {
|
|
|
25544
26507
|
readString17(payload, "tool_call_id") ?? readString17(payload, "toolCallId") ?? readString17(payload, "tool_id") ?? readString17(payload, "call_id") ?? readString17(payload, "id") ?? readString17(tool, "id") ?? readString17(call, "id") ?? readString17(fn, "id")
|
|
25545
26508
|
);
|
|
25546
26509
|
}
|
|
25547
|
-
function
|
|
26510
|
+
function readToolName2(payload) {
|
|
25548
26511
|
const tool = toRecord16(payload.tool);
|
|
25549
26512
|
const call = toRecord16(payload.tool_call ?? payload.toolCall);
|
|
25550
26513
|
const fn = toRecord16(call.function ?? payload.function);
|
|
@@ -25603,7 +26566,7 @@ function stableStringify2(value) {
|
|
|
25603
26566
|
}
|
|
25604
26567
|
}
|
|
25605
26568
|
function hashStableValue(value) {
|
|
25606
|
-
return
|
|
26569
|
+
return createHash7("sha256").update(value).digest("hex").slice(0, 16);
|
|
25607
26570
|
}
|
|
25608
26571
|
function readString17(payload, key) {
|
|
25609
26572
|
const value = payload[key];
|
|
@@ -26014,6 +26977,96 @@ function toRecord17(value) {
|
|
|
26014
26977
|
return typeof value === "object" && value !== null ? value : {};
|
|
26015
26978
|
}
|
|
26016
26979
|
|
|
26980
|
+
// src/conversations/goal-lifecycle-marker.ts
|
|
26981
|
+
var GOAL_LIFECYCLE_MARKER_FORMAT = "hermes-link-lifecycle-marker";
|
|
26982
|
+
var GOAL_COMPLETION_MARKER_KIND = "goal_completion";
|
|
26983
|
+
function createGoalCompletionMarker(input) {
|
|
26984
|
+
const text = goalCompletionMarkerText(input.goal, input.language);
|
|
26985
|
+
return {
|
|
26986
|
+
id: goalCompletionMarkerId(input.runId),
|
|
26987
|
+
schema_version: 1,
|
|
26988
|
+
conversation_id: input.conversationId,
|
|
26989
|
+
role: "system",
|
|
26990
|
+
status: "completed",
|
|
26991
|
+
run_id: input.runId,
|
|
26992
|
+
created_at: input.completedAt,
|
|
26993
|
+
updated_at: input.completedAt,
|
|
26994
|
+
sender: {
|
|
26995
|
+
id: "hermes_link",
|
|
26996
|
+
type: "system",
|
|
26997
|
+
display_name: "Hermes Link"
|
|
26998
|
+
},
|
|
26999
|
+
parts: [{ type: "text", text }],
|
|
27000
|
+
attachments: [],
|
|
27001
|
+
raw: {
|
|
27002
|
+
format: GOAL_LIFECYCLE_MARKER_FORMAT,
|
|
27003
|
+
payload: {
|
|
27004
|
+
kind: GOAL_COMPLETION_MARKER_KIND,
|
|
27005
|
+
status: "completed",
|
|
27006
|
+
completed_at: input.completedAt,
|
|
27007
|
+
run_id: input.runId,
|
|
27008
|
+
text,
|
|
27009
|
+
...Number.isFinite(input.goal.turns_used) ? { turns_used: input.goal.turns_used } : {},
|
|
27010
|
+
...Number.isFinite(input.goal.max_turns) ? { max_turns: input.goal.max_turns } : {},
|
|
27011
|
+
...input.goal.usage ? { usage: input.goal.usage } : {}
|
|
27012
|
+
}
|
|
27013
|
+
}
|
|
27014
|
+
};
|
|
27015
|
+
}
|
|
27016
|
+
function goalCompletionMarkerText(goal, language) {
|
|
27017
|
+
const parts = [localizedText2(language, "\u76EE\u6807\u5DF2\u5B8C\u6210", "Goal completed")];
|
|
27018
|
+
const turns = goalTurnsLabel(goal, language);
|
|
27019
|
+
if (turns) {
|
|
27020
|
+
parts.push(turns);
|
|
27021
|
+
}
|
|
27022
|
+
const totalTokens = finiteNonNegativeNumber(goal.usage?.total_tokens);
|
|
27023
|
+
if (totalTokens !== void 0) {
|
|
27024
|
+
parts.push(`${formatCompactNumber(totalTokens)} tokens`);
|
|
27025
|
+
}
|
|
27026
|
+
return parts.join(" ");
|
|
27027
|
+
}
|
|
27028
|
+
function goalCompletionMarkerId(runId) {
|
|
27029
|
+
const safeRunId = runId.trim().replace(/[^a-zA-Z0-9_:-]/gu, "_");
|
|
27030
|
+
return `msg_goal_completed_${safeRunId || "run"}`;
|
|
27031
|
+
}
|
|
27032
|
+
function goalTurnsLabel(goal, language) {
|
|
27033
|
+
const turnsUsed = finiteNonNegativeNumber(goal.turns_used);
|
|
27034
|
+
const maxTurns = finiteNonNegativeNumber(goal.max_turns);
|
|
27035
|
+
if (turnsUsed !== void 0 && maxTurns !== void 0) {
|
|
27036
|
+
return localizedText2(
|
|
27037
|
+
language,
|
|
27038
|
+
`${turnsUsed}/${maxTurns} \u8F6E`,
|
|
27039
|
+
`${turnsUsed}/${maxTurns} turns`
|
|
27040
|
+
);
|
|
27041
|
+
}
|
|
27042
|
+
if (turnsUsed !== void 0) {
|
|
27043
|
+
return localizedText2(language, `${turnsUsed} \u8F6E`, `${turnsUsed} turns`);
|
|
27044
|
+
}
|
|
27045
|
+
return null;
|
|
27046
|
+
}
|
|
27047
|
+
function finiteNonNegativeNumber(value) {
|
|
27048
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
|
|
27049
|
+
return void 0;
|
|
27050
|
+
}
|
|
27051
|
+
return Math.floor(value);
|
|
27052
|
+
}
|
|
27053
|
+
function formatCompactNumber(value) {
|
|
27054
|
+
if (value >= 1e6) {
|
|
27055
|
+
return `${trimTrailingDecimal(value / 1e6)}m`;
|
|
27056
|
+
}
|
|
27057
|
+
if (value >= 1e3) {
|
|
27058
|
+
return `${trimTrailingDecimal(value / 1e3)}k`;
|
|
27059
|
+
}
|
|
27060
|
+
return value.toLocaleString("en-US");
|
|
27061
|
+
}
|
|
27062
|
+
function trimTrailingDecimal(value) {
|
|
27063
|
+
const rounded = Math.round(value * 10) / 10;
|
|
27064
|
+
return Number.isInteger(rounded) ? String(rounded) : rounded.toFixed(1);
|
|
27065
|
+
}
|
|
27066
|
+
function localizedText2(language, zh, en) {
|
|
27067
|
+
return language === "en" ? en : zh;
|
|
27068
|
+
}
|
|
27069
|
+
|
|
26017
27070
|
// src/conversations/run-lifecycle.ts
|
|
26018
27071
|
var RUN_STATUS_RECOVERY_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
26019
27072
|
var RUN_STATUS_RECOVERY_INITIAL_DELAY_MS = 500;
|
|
@@ -26030,7 +27083,7 @@ var ConversationRunLifecycle = class {
|
|
|
26030
27083
|
const snapshot = await this.deps.readSnapshot(conversationId);
|
|
26031
27084
|
const run = snapshot.runs.find((item) => item.id === runId);
|
|
26032
27085
|
if (!run || run.status !== "running") {
|
|
26033
|
-
return;
|
|
27086
|
+
return { shouldDrainQueue: true };
|
|
26034
27087
|
}
|
|
26035
27088
|
const controller = new AbortController();
|
|
26036
27089
|
this.deps.activeRunControllers.set(runId, { conversationId, controller });
|
|
@@ -26384,7 +27437,7 @@ var ConversationRunLifecycle = class {
|
|
|
26384
27437
|
return null;
|
|
26385
27438
|
});
|
|
26386
27439
|
if (!response) {
|
|
26387
|
-
return;
|
|
27440
|
+
return { shouldDrainQueue: true };
|
|
26388
27441
|
}
|
|
26389
27442
|
await this.consumeHermesEventStream({
|
|
26390
27443
|
backend,
|
|
@@ -26406,7 +27459,15 @@ var ConversationRunLifecycle = class {
|
|
|
26406
27459
|
reason: "cancelled by app"
|
|
26407
27460
|
})
|
|
26408
27461
|
);
|
|
26409
|
-
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 };
|
|
26410
27471
|
}
|
|
26411
27472
|
throw error;
|
|
26412
27473
|
} finally {
|
|
@@ -26415,6 +27476,148 @@ var ConversationRunLifecycle = class {
|
|
|
26415
27476
|
}
|
|
26416
27477
|
unregisterDeliveryContext(runId);
|
|
26417
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
|
+
});
|
|
26418
27621
|
}
|
|
26419
27622
|
async ensureUsageProbeBeforeRun(input) {
|
|
26420
27623
|
if (!shouldUseHermesUsageProbe(input.backend)) {
|
|
@@ -26640,6 +27843,9 @@ var ConversationRunLifecycle = class {
|
|
|
26640
27843
|
await this.cancelRunAfterAbort(input.conversationId, input.runId);
|
|
26641
27844
|
return;
|
|
26642
27845
|
}
|
|
27846
|
+
if (isTuiGatewaySessionBusyError(error)) {
|
|
27847
|
+
throw error;
|
|
27848
|
+
}
|
|
26643
27849
|
streamError = error;
|
|
26644
27850
|
await this.deps.logger.warn("tui_gateway_event_stream_interrupted", {
|
|
26645
27851
|
backend: input.backend,
|
|
@@ -27957,6 +29163,13 @@ ${details.join("\n")}` : localizedEmptyHermesResponseMessage(language);
|
|
|
27957
29163
|
completedAt,
|
|
27958
29164
|
"expired"
|
|
27959
29165
|
);
|
|
29166
|
+
const goalCompletionMarker = run.mode === "goal" ? await this.appendGoalCompletionMarkerAfterAssistant({
|
|
29167
|
+
conversationId,
|
|
29168
|
+
snapshot,
|
|
29169
|
+
run,
|
|
29170
|
+
goal: goalUpdate ?? void 0,
|
|
29171
|
+
completedAt
|
|
29172
|
+
}) : null;
|
|
27960
29173
|
await this.deps.writeSnapshot(conversationId, snapshot);
|
|
27961
29174
|
if (goalUpdate) {
|
|
27962
29175
|
await this.deps.appendEvent(conversationId, {
|
|
@@ -27968,6 +29181,14 @@ ${details.join("\n")}` : localizedEmptyHermesResponseMessage(language);
|
|
|
27968
29181
|
}
|
|
27969
29182
|
});
|
|
27970
29183
|
}
|
|
29184
|
+
if (goalCompletionMarker) {
|
|
29185
|
+
await this.deps.appendEvent(conversationId, {
|
|
29186
|
+
type: "message.created",
|
|
29187
|
+
message_id: goalCompletionMarker.id,
|
|
29188
|
+
run_id: runId,
|
|
29189
|
+
payload: { message: goalCompletionMarker }
|
|
29190
|
+
});
|
|
29191
|
+
}
|
|
27971
29192
|
await this.appendExpiredApprovalEvents(
|
|
27972
29193
|
conversationId,
|
|
27973
29194
|
runId,
|
|
@@ -28008,6 +29229,24 @@ ${details.join("\n")}` : localizedEmptyHermesResponseMessage(language);
|
|
|
28008
29229
|
occurredAt: completedAt
|
|
28009
29230
|
});
|
|
28010
29231
|
}
|
|
29232
|
+
async appendGoalCompletionMarkerAfterAssistant(input) {
|
|
29233
|
+
const goal = input.goal ?? (await this.deps.readRunnableManifest(input.conversationId)).command_state?.goal;
|
|
29234
|
+
if (goal?.status !== "done") {
|
|
29235
|
+
return null;
|
|
29236
|
+
}
|
|
29237
|
+
if (findGoalCompletionMarker(input.snapshot, input.run.id)) {
|
|
29238
|
+
return null;
|
|
29239
|
+
}
|
|
29240
|
+
const marker = createGoalCompletionMarker({
|
|
29241
|
+
conversationId: input.conversationId,
|
|
29242
|
+
runId: input.run.id,
|
|
29243
|
+
goal,
|
|
29244
|
+
completedAt: input.completedAt,
|
|
29245
|
+
language: input.run.language === "en" ? "en" : "zh-CN"
|
|
29246
|
+
});
|
|
29247
|
+
insertLifecycleMarkerAfterAssistant(input.snapshot, marker, input.run);
|
|
29248
|
+
return marker;
|
|
29249
|
+
}
|
|
28011
29250
|
async persistGoalUsageFromRunLocked(conversationId, run) {
|
|
28012
29251
|
const manifest = await this.deps.readRunnableManifest(conversationId);
|
|
28013
29252
|
const previousGoal = manifest.command_state?.goal;
|
|
@@ -28391,7 +29630,7 @@ ${details.join("\n")}` : localizedEmptyHermesResponseMessage(language);
|
|
|
28391
29630
|
if (raw.length <= 200) {
|
|
28392
29631
|
return raw;
|
|
28393
29632
|
}
|
|
28394
|
-
return `hermespilot:${
|
|
29633
|
+
return `hermespilot:${createHash8("sha256").update(raw).digest("hex")}`;
|
|
28395
29634
|
}
|
|
28396
29635
|
async assistantMessageIdForRun(conversationId, runId) {
|
|
28397
29636
|
const snapshot = await this.deps.readSnapshot(conversationId).catch(() => null);
|
|
@@ -28536,6 +29775,23 @@ function insertLifecycleMarkerBeforeAssistant(snapshot, marker, run) {
|
|
|
28536
29775
|
}
|
|
28537
29776
|
snapshot.messages.push(marker);
|
|
28538
29777
|
}
|
|
29778
|
+
function insertLifecycleMarkerAfterAssistant(snapshot, marker, run) {
|
|
29779
|
+
const existingIndex = snapshot.messages.findIndex(
|
|
29780
|
+
(message) => message.id === marker.id
|
|
29781
|
+
);
|
|
29782
|
+
if (existingIndex >= 0) {
|
|
29783
|
+
snapshot.messages[existingIndex] = marker;
|
|
29784
|
+
return;
|
|
29785
|
+
}
|
|
29786
|
+
const assistantIndex = snapshot.messages.findIndex(
|
|
29787
|
+
(message) => message.id === run.assistant_message_id
|
|
29788
|
+
);
|
|
29789
|
+
if (assistantIndex >= 0) {
|
|
29790
|
+
snapshot.messages.splice(assistantIndex + 1, 0, marker);
|
|
29791
|
+
return;
|
|
29792
|
+
}
|
|
29793
|
+
snapshot.messages.push(marker);
|
|
29794
|
+
}
|
|
28539
29795
|
function timestampBeforeAssistantForRun(snapshot, run, fallback) {
|
|
28540
29796
|
const user = snapshot.messages.find(
|
|
28541
29797
|
(message) => message.id === run.trigger_message_id
|
|
@@ -28561,6 +29817,12 @@ function findContextCompressionMarker(snapshot, operationId) {
|
|
|
28561
29817
|
return message.raw?.format === CONTEXT_COMPRESSION_MARKER_FORMAT && payload.kind === CONTEXT_COMPRESSION_MARKER_KIND && payload.operation_id === operationId;
|
|
28562
29818
|
});
|
|
28563
29819
|
}
|
|
29820
|
+
function findGoalCompletionMarker(snapshot, runId) {
|
|
29821
|
+
return snapshot.messages.find((message) => {
|
|
29822
|
+
const payload = toRecord18(message.raw?.payload);
|
|
29823
|
+
return message.raw?.format === GOAL_LIFECYCLE_MARKER_FORMAT && payload.kind === GOAL_COMPLETION_MARKER_KIND && payload.run_id === runId;
|
|
29824
|
+
});
|
|
29825
|
+
}
|
|
28564
29826
|
function nextContextCompressionGeneration(snapshot) {
|
|
28565
29827
|
let maxGeneration = 0;
|
|
28566
29828
|
for (const message of snapshot.messages) {
|
|
@@ -29000,6 +30262,21 @@ function expirePendingMessageApprovals(message, resolvedAt) {
|
|
|
29000
30262
|
}
|
|
29001
30263
|
return expired;
|
|
29002
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
|
+
}
|
|
29003
30280
|
function previewText2(message) {
|
|
29004
30281
|
if (!message) {
|
|
29005
30282
|
return null;
|
|
@@ -29008,15 +30285,15 @@ function previewText2(message) {
|
|
|
29008
30285
|
return text ? text.slice(0, 512) : null;
|
|
29009
30286
|
}
|
|
29010
30287
|
function runNotificationSourceEventId(conversationId, runId, eventKind) {
|
|
29011
|
-
const digest =
|
|
30288
|
+
const digest = createHash8("sha256").update(`${conversationId}:${runId}:${eventKind}`).digest("hex").slice(0, 24);
|
|
29012
30289
|
return `${conversationId}:${eventKind}:${digest}`;
|
|
29013
30290
|
}
|
|
29014
30291
|
function approvalNotificationSourceEventId(conversationId, runId, approvalId) {
|
|
29015
|
-
const digest =
|
|
30292
|
+
const digest = createHash8("sha256").update(`${conversationId}:${runId}:${approvalId}:approval_required`).digest("hex").slice(0, 24);
|
|
29016
30293
|
return `${conversationId}:approval_required:${digest}`;
|
|
29017
30294
|
}
|
|
29018
30295
|
function inputRequestNotificationSourceEventId(conversationId, runId, inputRequestId) {
|
|
29019
|
-
const digest =
|
|
30296
|
+
const digest = createHash8("sha256").update(`${conversationId}:${runId}:${inputRequestId}:input_required`).digest("hex").slice(0, 24);
|
|
29020
30297
|
return `${conversationId}:input_required:${digest}`;
|
|
29021
30298
|
}
|
|
29022
30299
|
async function closeSseIterator(iterator) {
|
|
@@ -29127,6 +30404,7 @@ var ConversationService = class {
|
|
|
29127
30404
|
isConversationRunnable: (conversationId) => this.store.isConversationRunnable(conversationId),
|
|
29128
30405
|
writeBlob: (conversationId, input) => this.maintenance.writeBlob(conversationId, input),
|
|
29129
30406
|
syncCronDeliveries: () => this.syncCronDeliveries(),
|
|
30407
|
+
syncConversationMessages: (conversationId) => this.syncHermesConversationMessages(conversationId),
|
|
29130
30408
|
scheduleTitleRefresh: (conversationId) => this.metadata.scheduleGeneratedTitleRefresh(conversationId)
|
|
29131
30409
|
});
|
|
29132
30410
|
this.orchestration = new ConversationOrchestrationCoordinator({
|
|
@@ -29705,6 +30983,16 @@ var ConversationService = class {
|
|
|
29705
30983
|
}
|
|
29706
30984
|
}
|
|
29707
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
|
+
}
|
|
29708
30996
|
async deliverStagedFiles(stagingDir) {
|
|
29709
30997
|
const target = resolveDeliveryStagingTarget(this.paths, stagingDir);
|
|
29710
30998
|
return this.withConversationLock(target.conversationId, async () => {
|
|
@@ -29774,6 +31062,16 @@ var ConversationService = class {
|
|
|
29774
31062
|
};
|
|
29775
31063
|
}
|
|
29776
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
|
+
}
|
|
29777
31075
|
return this.queries.getMessages(conversationId, options);
|
|
29778
31076
|
}
|
|
29779
31077
|
async setConversationModel(conversationId, input) {
|
|
@@ -30659,7 +31957,7 @@ var ConversationService = class {
|
|
|
30659
31957
|
}
|
|
30660
31958
|
}
|
|
30661
31959
|
hermesArchiveStateSyncMarkerPath() {
|
|
30662
|
-
return
|
|
31960
|
+
return path27.join(this.paths.indexesDir, "hermes-archive-state-sync.json");
|
|
30663
31961
|
}
|
|
30664
31962
|
prepareClearAllConversationPlan(targetStatus) {
|
|
30665
31963
|
return this.maintenance.prepareClearAllConversationPlan(targetStatus);
|
|
@@ -30818,7 +32116,7 @@ function findApproval(snapshot, approvalId) {
|
|
|
30818
32116
|
return null;
|
|
30819
32117
|
}
|
|
30820
32118
|
function cronNotificationSourceEventId(conversationId, jobId, outputPath, eventKind) {
|
|
30821
|
-
const digest =
|
|
32119
|
+
const digest = createHash9("sha256").update(`${conversationId}:${jobId}:${outputPath}:${eventKind}`).digest("hex").slice(0, 24);
|
|
30822
32120
|
return `${conversationId}:${eventKind}:${digest}`;
|
|
30823
32121
|
}
|
|
30824
32122
|
function conversationHermesSessionIds(manifest) {
|
|
@@ -30896,6 +32194,7 @@ function buildLiveActivityEvent(input) {
|
|
|
30896
32194
|
return null;
|
|
30897
32195
|
}
|
|
30898
32196
|
const language = run?.language === "en" ? "en" : "zh";
|
|
32197
|
+
const todoProgress = liveActivityTodoProgressForEvent(input.event);
|
|
30899
32198
|
const text = liveActivityTextForEvent({
|
|
30900
32199
|
event: input.event,
|
|
30901
32200
|
snapshot: input.snapshot,
|
|
@@ -30919,11 +32218,56 @@ function buildLiveActivityEvent(input) {
|
|
|
30919
32218
|
statusLabel: text.statusLabel,
|
|
30920
32219
|
progressText: text.progressText,
|
|
30921
32220
|
detailText: text.detailText,
|
|
32221
|
+
todoDoneCount: todoProgress?.doneCount,
|
|
32222
|
+
todoTotalCount: todoProgress?.totalCount,
|
|
32223
|
+
todoProgressText: todoProgress?.progressText,
|
|
30922
32224
|
requiresUserAction: phase === "needs_input" || phase === "needs_approval",
|
|
30923
32225
|
terminal: isLiveActivityTerminalEvent(phase, target.kind),
|
|
30924
32226
|
occurredAt: input.event.created_at
|
|
30925
32227
|
};
|
|
30926
32228
|
}
|
|
32229
|
+
function liveActivityTodoProgressForEvent(event) {
|
|
32230
|
+
if (event.type.toLowerCase() !== "tool.completed") {
|
|
32231
|
+
return null;
|
|
32232
|
+
}
|
|
32233
|
+
const payload = readRecord2(event.payload);
|
|
32234
|
+
const toolName = readToolName3(payload)?.toLowerCase();
|
|
32235
|
+
if (toolName !== "todo") {
|
|
32236
|
+
return null;
|
|
32237
|
+
}
|
|
32238
|
+
const todos = readTodoItems2(payload.todos) ?? readTodoItems2(readRecord2(payload.result).todos);
|
|
32239
|
+
if (todos) {
|
|
32240
|
+
const totalCount2 = todos.length;
|
|
32241
|
+
const doneCount2 = todos.filter((item) => {
|
|
32242
|
+
const status = readString20(item, "status")?.toLowerCase();
|
|
32243
|
+
return status === "completed" || status === "cancelled" || status === "canceled";
|
|
32244
|
+
}).length;
|
|
32245
|
+
return {
|
|
32246
|
+
doneCount: doneCount2,
|
|
32247
|
+
totalCount: totalCount2,
|
|
32248
|
+
progressText: totalCount2 > 0 ? `${doneCount2}/${totalCount2}` : void 0
|
|
32249
|
+
};
|
|
32250
|
+
}
|
|
32251
|
+
const summary = readRecord2(payload.summary).total === void 0 ? readRecord2(readRecord2(payload.result).summary) : readRecord2(payload.summary);
|
|
32252
|
+
const totalCount = readInteger4(summary, "total");
|
|
32253
|
+
if (totalCount === null) {
|
|
32254
|
+
return null;
|
|
32255
|
+
}
|
|
32256
|
+
const completedCount = readInteger4(summary, "completed") ?? 0;
|
|
32257
|
+
const cancelledCount = readInteger4(summary, "cancelled") ?? readInteger4(summary, "canceled") ?? 0;
|
|
32258
|
+
const doneCount = Math.min(totalCount, completedCount + cancelledCount);
|
|
32259
|
+
return {
|
|
32260
|
+
doneCount,
|
|
32261
|
+
totalCount,
|
|
32262
|
+
progressText: totalCount > 0 ? `${doneCount}/${totalCount}` : void 0
|
|
32263
|
+
};
|
|
32264
|
+
}
|
|
32265
|
+
function readTodoItems2(value) {
|
|
32266
|
+
if (!Array.isArray(value)) {
|
|
32267
|
+
return null;
|
|
32268
|
+
}
|
|
32269
|
+
return value.map((item) => readRecord2(item)).filter((item) => Object.keys(item).length > 0);
|
|
32270
|
+
}
|
|
30927
32271
|
function liveActivityConversationTitle(manifest, event, phase, language) {
|
|
30928
32272
|
if (phase === "goal_running" || phase === "goal_paused") {
|
|
30929
32273
|
const goal = manifest.command_state?.goal;
|
|
@@ -31009,7 +32353,7 @@ function liveActivityPhaseForEvent(event, run, contextOperation) {
|
|
|
31009
32353
|
return run?.mode === "goal" ? "goal_running" : "accepted";
|
|
31010
32354
|
}
|
|
31011
32355
|
if (type === "conversation.goal.updated") {
|
|
31012
|
-
const goal =
|
|
32356
|
+
const goal = readRecord2(event.payload).goal;
|
|
31013
32357
|
const status = readString20(goal, "status");
|
|
31014
32358
|
return status === "paused" ? "goal_paused" : "goal_running";
|
|
31015
32359
|
}
|
|
@@ -31055,7 +32399,7 @@ function liveActivityPhaseForEvent(event, run, contextOperation) {
|
|
|
31055
32399
|
return null;
|
|
31056
32400
|
}
|
|
31057
32401
|
function liveActivityTextForEvent(input) {
|
|
31058
|
-
const toolName =
|
|
32402
|
+
const toolName = readToolName3(input.event.payload);
|
|
31059
32403
|
const assistantText = input.event.message_id ? input.snapshot.messages.find((message) => message.id === input.event.message_id) : null;
|
|
31060
32404
|
const preview = input.event.type.toLowerCase() === "reasoning.available" ? liveActivityReasoningPreview(input.event.payload) : isLiveActivityTerminalEvent(input.phase, input.run?.kind === "compression" ? "context_compression" : "run") && assistantText ? notificationPreviewText(assistantText) : null;
|
|
31061
32405
|
if (input.language === "en") {
|
|
@@ -31159,7 +32503,7 @@ function isLiveActivityTerminalEvent(phase, targetKind) {
|
|
|
31159
32503
|
return targetKind === "context_compression" && (phase === "context_compressed" || phase === "context_compression_failed" || phase === "context_compression_timed_out");
|
|
31160
32504
|
}
|
|
31161
32505
|
function readContextCompressionOperation(payload) {
|
|
31162
|
-
const operation =
|
|
32506
|
+
const operation = readRecord2(payload).operation;
|
|
31163
32507
|
if (!operation || typeof operation !== "object") {
|
|
31164
32508
|
return null;
|
|
31165
32509
|
}
|
|
@@ -31176,23 +32520,31 @@ function readContextCompressionOperation(payload) {
|
|
|
31176
32520
|
source: readString20(record, "source") === "manual" ? "manual" : "auto"
|
|
31177
32521
|
};
|
|
31178
32522
|
}
|
|
31179
|
-
function
|
|
31180
|
-
const record =
|
|
31181
|
-
return readString20(record, "tool_name") ?? readString20(record, "tool") ?? readString20(record, "name") ?? readString20(
|
|
32523
|
+
function readToolName3(payload) {
|
|
32524
|
+
const record = readRecord2(payload);
|
|
32525
|
+
return readString20(record, "tool_name") ?? readString20(record, "tool") ?? readString20(record, "name") ?? readString20(readRecord2(record.tool), "name");
|
|
31182
32526
|
}
|
|
31183
|
-
function
|
|
32527
|
+
function readRecord2(value) {
|
|
31184
32528
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
|
|
31185
32529
|
}
|
|
31186
32530
|
function readString20(value, key) {
|
|
31187
|
-
const raw =
|
|
32531
|
+
const raw = readRecord2(value)[key];
|
|
31188
32532
|
return typeof raw === "string" && raw.trim() ? raw.trim() : null;
|
|
31189
32533
|
}
|
|
32534
|
+
function readInteger4(value, key) {
|
|
32535
|
+
const raw = readRecord2(value)[key];
|
|
32536
|
+
const parsed = typeof raw === "number" ? raw : typeof raw === "string" && raw.trim() ? Number(raw) : Number.NaN;
|
|
32537
|
+
if (!Number.isFinite(parsed)) {
|
|
32538
|
+
return null;
|
|
32539
|
+
}
|
|
32540
|
+
return Math.max(0, Math.trunc(parsed));
|
|
32541
|
+
}
|
|
31190
32542
|
function approvalRestartText(language, zh, en) {
|
|
31191
32543
|
return language === "en" ? en : zh;
|
|
31192
32544
|
}
|
|
31193
32545
|
|
|
31194
32546
|
// src/security/devices.ts
|
|
31195
|
-
import { randomBytes as randomBytes3, randomUUID as randomUUID13, timingSafeEqual, createHash as
|
|
32547
|
+
import { randomBytes as randomBytes3, randomUUID as randomUUID13, timingSafeEqual, createHash as createHash10 } from "crypto";
|
|
31196
32548
|
var ACCESS_TOKEN_TTL_MS = 15 * 60 * 1e3;
|
|
31197
32549
|
var REFRESH_TOKEN_TTL_MS = 90 * 24 * 60 * 60 * 1e3;
|
|
31198
32550
|
var DEVICE_SEEN_WRITE_INTERVAL_MS = 60 * 60 * 1e3;
|
|
@@ -31488,7 +32840,7 @@ function randomToken(prefix) {
|
|
|
31488
32840
|
return `${prefix}${randomBytes3(24).toString("base64url")}`;
|
|
31489
32841
|
}
|
|
31490
32842
|
function sha256(value) {
|
|
31491
|
-
return
|
|
32843
|
+
return createHash10("sha256").update(value).digest("hex");
|
|
31492
32844
|
}
|
|
31493
32845
|
function safeEqual(left, right) {
|
|
31494
32846
|
const leftBytes = Buffer.from(left);
|
|
@@ -31768,7 +33120,7 @@ function readPositiveInteger2(value) {
|
|
|
31768
33120
|
}
|
|
31769
33121
|
return void 0;
|
|
31770
33122
|
}
|
|
31771
|
-
function
|
|
33123
|
+
function readBoolean4(value) {
|
|
31772
33124
|
if (typeof value === "boolean") {
|
|
31773
33125
|
return value;
|
|
31774
33126
|
}
|
|
@@ -31806,7 +33158,7 @@ function readQueryString(value) {
|
|
|
31806
33158
|
const trimmed = raw.trim();
|
|
31807
33159
|
return trimmed ? trimmed : void 0;
|
|
31808
33160
|
}
|
|
31809
|
-
function
|
|
33161
|
+
function readInteger5(value) {
|
|
31810
33162
|
const raw = Array.isArray(value) ? value[0] : value;
|
|
31811
33163
|
if (typeof raw !== "string") {
|
|
31812
33164
|
return void 0;
|
|
@@ -31889,7 +33241,7 @@ function readMessageAttachments(value) {
|
|
|
31889
33241
|
}
|
|
31890
33242
|
const kind = readAttachmentString(record.kind);
|
|
31891
33243
|
const type = readAttachmentString(record.type);
|
|
31892
|
-
const isVoiceNote =
|
|
33244
|
+
const isVoiceNote = readBoolean4(record.is_voice_note) ?? readBoolean4(record.isVoiceNote);
|
|
31893
33245
|
const durationMs = readPositiveInteger2(record.duration_ms) ?? readPositiveInteger2(record.durationMs);
|
|
31894
33246
|
const waveform = readAttachmentWaveform2(
|
|
31895
33247
|
record.waveform ?? record.waveform_samples ?? record.waveformSamples
|
|
@@ -32183,7 +33535,8 @@ function registerConversationRoutes(router, options) {
|
|
|
32183
33535
|
ctx.set("cache-control", "no-store");
|
|
32184
33536
|
const result = await conversations.getMessages(ctx.params.conversationId, {
|
|
32185
33537
|
limit: readLimit(ctx.query.limit),
|
|
32186
|
-
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)
|
|
32187
33540
|
});
|
|
32188
33541
|
ctx.body = {
|
|
32189
33542
|
ok: true,
|
|
@@ -32770,6 +34123,13 @@ function registerConversationRoutes(router, options) {
|
|
|
32770
34123
|
}
|
|
32771
34124
|
);
|
|
32772
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
|
+
}
|
|
32773
34133
|
async function prepareConversationListRead(conversations, logger, auth, options = {}) {
|
|
32774
34134
|
if (options.syncHermesSessions) {
|
|
32775
34135
|
await conversations.syncHermesSessions().catch((error) => {
|
|
@@ -32799,7 +34159,7 @@ function readConversationListCursor(query) {
|
|
|
32799
34159
|
}
|
|
32800
34160
|
function readConversationListForce(query) {
|
|
32801
34161
|
const raw = Array.isArray(query.force) ? query.force[0] : query.force;
|
|
32802
|
-
return
|
|
34162
|
+
return readBoolean4(raw) === true;
|
|
32803
34163
|
}
|
|
32804
34164
|
function readConversationWorkspaceFilter(query) {
|
|
32805
34165
|
const workspace = readQueryString(query.workspace) ?? readQueryString(query.workspace_id);
|
|
@@ -32905,7 +34265,7 @@ function toConversationSummary(value) {
|
|
|
32905
34265
|
return candidate;
|
|
32906
34266
|
}
|
|
32907
34267
|
function resolveConversationEventCursor(input) {
|
|
32908
|
-
const queryAfter =
|
|
34268
|
+
const queryAfter = readInteger5(input.queryAfter) ?? 0;
|
|
32909
34269
|
const headerAfter = readNonNegativeIntegerHeader(input.lastEventIdHeader) ?? 0;
|
|
32910
34270
|
return Math.max(queryAfter, headerAfter);
|
|
32911
34271
|
}
|
|
@@ -33051,11 +34411,11 @@ function isSseRequestContext(ctx) {
|
|
|
33051
34411
|
}
|
|
33052
34412
|
return isSseRequestPath(ctx.path) || isActiveSseSocket(ctx.req.socket);
|
|
33053
34413
|
}
|
|
33054
|
-
function isSseRequestPath(
|
|
33055
|
-
if (!
|
|
34414
|
+
function isSseRequestPath(path38) {
|
|
34415
|
+
if (!path38) {
|
|
33056
34416
|
return false;
|
|
33057
34417
|
}
|
|
33058
|
-
return
|
|
34418
|
+
return path38 === "/api/v1/conversations/events" || path38 === "/api/v1/profile-creation/events" || path38 === "/api/v1/hermes/update/events" || path38 === "/api/v1/link/update/events" || /^\/api\/v1\/conversations\/[^/]+\/events$/u.test(path38) || /^\/api\/v1\/runs\/[^/]+\/events$/u.test(path38);
|
|
33059
34419
|
}
|
|
33060
34420
|
function isExpectedClientDisconnectError2(error, options = {}) {
|
|
33061
34421
|
if (!(error instanceof Error)) {
|
|
@@ -33419,7 +34779,7 @@ function readCronJobUpdateInput(body) {
|
|
|
33419
34779
|
if (repeat !== void 0) {
|
|
33420
34780
|
input.repeat = repeat;
|
|
33421
34781
|
}
|
|
33422
|
-
const enabled =
|
|
34782
|
+
const enabled = readBoolean4(body.enabled);
|
|
33423
34783
|
if (enabled !== void 0) {
|
|
33424
34784
|
input.enabled = enabled;
|
|
33425
34785
|
}
|
|
@@ -33489,12 +34849,12 @@ function assertCronJobId(jobId) {
|
|
|
33489
34849
|
}
|
|
33490
34850
|
|
|
33491
34851
|
// src/http/routes/model-configs.ts
|
|
33492
|
-
import { createHash as
|
|
34852
|
+
import { createHash as createHash11 } from "crypto";
|
|
33493
34853
|
|
|
33494
34854
|
// src/model-catalog/catalog.ts
|
|
33495
34855
|
import { randomInt } from "crypto";
|
|
33496
34856
|
import { mkdir as mkdir12 } from "fs/promises";
|
|
33497
|
-
import
|
|
34857
|
+
import path28 from "path";
|
|
33498
34858
|
import { fileURLToPath } from "url";
|
|
33499
34859
|
var MODEL_CATALOG_CACHE_VERSION = 1;
|
|
33500
34860
|
var MODEL_CATALOG_FETCH_TIMEOUT_MS = 1e4;
|
|
@@ -33698,7 +35058,7 @@ async function readCachedCatalogFile(paths) {
|
|
|
33698
35058
|
return cached;
|
|
33699
35059
|
}
|
|
33700
35060
|
async function writeCachedCatalog(paths, value) {
|
|
33701
|
-
await mkdir12(
|
|
35061
|
+
await mkdir12(path28.dirname(modelCatalogCachePath(paths)), {
|
|
33702
35062
|
recursive: true,
|
|
33703
35063
|
mode: 448
|
|
33704
35064
|
});
|
|
@@ -33714,7 +35074,7 @@ async function readFallbackCatalog() {
|
|
|
33714
35074
|
throw new Error("model capability fallback catalog was not found");
|
|
33715
35075
|
}
|
|
33716
35076
|
function modelCatalogCachePath(paths) {
|
|
33717
|
-
return
|
|
35077
|
+
return path28.join(paths.homeDir, "model-capabilities", "catalog-cache.json");
|
|
33718
35078
|
}
|
|
33719
35079
|
function normalizeModelCapabilityCatalog(value) {
|
|
33720
35080
|
if (!value || typeof value !== "object") {
|
|
@@ -34718,7 +36078,7 @@ function modelProviderIdFromParts(input) {
|
|
|
34718
36078
|
input.keyEnv ?? "",
|
|
34719
36079
|
input.authType ?? ""
|
|
34720
36080
|
].join("");
|
|
34721
|
-
return `mp_${
|
|
36081
|
+
return `mp_${createHash11("sha256").update(identity).digest("hex").slice(0, 18)}`;
|
|
34722
36082
|
}
|
|
34723
36083
|
function mergeCredentialState(left, right) {
|
|
34724
36084
|
if (left === "configured" || right === "configured") {
|
|
@@ -35017,7 +36377,7 @@ function readModelConfigInput(body) {
|
|
|
35017
36377
|
body.context_length ?? body.contextLength
|
|
35018
36378
|
),
|
|
35019
36379
|
keyEnv: readString21(body, "key_env") ?? readString21(body, "keyEnv") ?? void 0,
|
|
35020
|
-
setDefault:
|
|
36380
|
+
setDefault: readBoolean4(body.set_default ?? body.setDefault),
|
|
35021
36381
|
reasoningEffort: readString21(body, "reasoning_effort") ?? readString21(body, "reasoningEffort") ?? void 0,
|
|
35022
36382
|
reasoningSupportPolicy: readString21(body, "reasoning_support_policy") ?? readString21(body, "reasoningSupportPolicy") ?? readString21(body, "reasoning_support") ?? readString21(body, "reasoningSupport") ?? void 0,
|
|
35023
36383
|
supportsVision: readNullableBoolean2(
|
|
@@ -35086,14 +36446,14 @@ function readModelConfigImportInput(body) {
|
|
|
35086
36446
|
provider: readString21(body, "provider") ?? readString21(body, "provider_key") ?? readString21(body, "providerKey") ?? void 0,
|
|
35087
36447
|
baseUrl: readString21(body, "base_url") ?? readString21(body, "baseUrl") ?? void 0,
|
|
35088
36448
|
apiMode: readString21(body, "api_mode") ?? readString21(body, "apiMode") ?? void 0,
|
|
35089
|
-
setDefault:
|
|
36449
|
+
setDefault: readBoolean4(body.set_default ?? body.setDefault)
|
|
35090
36450
|
};
|
|
35091
36451
|
}
|
|
35092
36452
|
function shouldReloadGatewayAfterModelConfigChange(body, options = {}) {
|
|
35093
|
-
if (
|
|
36453
|
+
if (readBoolean4(body.skip_gateway_reload ?? body.skipGatewayReload) === true) {
|
|
35094
36454
|
return false;
|
|
35095
36455
|
}
|
|
35096
|
-
if (
|
|
36456
|
+
if (readBoolean4(body.reload_gateway ?? body.reloadGateway) === true) {
|
|
35097
36457
|
return true;
|
|
35098
36458
|
}
|
|
35099
36459
|
return options.defaultReload === true;
|
|
@@ -35193,7 +36553,7 @@ function errorMessage3(error) {
|
|
|
35193
36553
|
|
|
35194
36554
|
// src/hermes/profile-identity.ts
|
|
35195
36555
|
import { readFile as readFile16, stat as stat18 } from "fs/promises";
|
|
35196
|
-
import
|
|
36556
|
+
import path29 from "path";
|
|
35197
36557
|
var MAX_SOUL_MD_LENGTH = 2e4;
|
|
35198
36558
|
async function readHermesProfileIdentity(profileName, paths) {
|
|
35199
36559
|
await assertProfileExists3(profileName, paths);
|
|
@@ -35250,7 +36610,7 @@ async function assertProfileExists3(profileName, paths) {
|
|
|
35250
36610
|
}
|
|
35251
36611
|
}
|
|
35252
36612
|
function resolveSoulPath(profileName) {
|
|
35253
|
-
return
|
|
36613
|
+
return path29.join(resolveHermesProfileDir(profileName), "SOUL.md");
|
|
35254
36614
|
}
|
|
35255
36615
|
function isNodeError19(error, code) {
|
|
35256
36616
|
return error instanceof Error && "code" in error && error.code === code;
|
|
@@ -35266,13 +36626,13 @@ import {
|
|
|
35266
36626
|
rm as rm7,
|
|
35267
36627
|
stat as stat20
|
|
35268
36628
|
} from "fs/promises";
|
|
35269
|
-
import
|
|
36629
|
+
import path31 from "path";
|
|
35270
36630
|
import YAML5 from "yaml";
|
|
35271
36631
|
|
|
35272
36632
|
// src/hermes/link-skill.ts
|
|
35273
36633
|
import { readFile as readFile17, stat as stat19 } from "fs/promises";
|
|
35274
36634
|
import os7 from "os";
|
|
35275
|
-
import
|
|
36635
|
+
import path30 from "path";
|
|
35276
36636
|
import YAML4 from "yaml";
|
|
35277
36637
|
var HERMES_LINK_SKILL_ROOT_DIR = "hermes-skills";
|
|
35278
36638
|
var HERMES_LINK_SKILL_DIR = "hermes-link";
|
|
@@ -35329,13 +36689,20 @@ hermeslink logs -f
|
|
|
35329
36689
|
hermeslink logs --all --level debug -f
|
|
35330
36690
|
\`\`\`
|
|
35331
36691
|
|
|
35332
|
-
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:
|
|
35333
36694
|
|
|
35334
36695
|
\`\`\`bash
|
|
35335
36696
|
hermeslink restart
|
|
35336
36697
|
\`\`\`
|
|
35337
36698
|
|
|
35338
|
-
|
|
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.
|
|
35339
36706
|
|
|
35340
36707
|
## Troubleshooting Flow
|
|
35341
36708
|
|
|
@@ -35352,12 +36719,17 @@ Never reveal API keys, access tokens, refresh tokens, private keys, or full .env
|
|
|
35352
36719
|
|
|
35353
36720
|
Do not recommend exposing port 52379 directly to the public internet without TLS, VPN, Tailscale, WireGuard, or another access-control layer.
|
|
35354
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
|
+
|
|
35355
36727
|
Do not modify Hermes profiles, delete user data, edit config files, or kill processes unless the user explicitly asks.
|
|
35356
36728
|
`;
|
|
35357
36729
|
async function ensureHermesLinkSkillInstalledForProfiles(options = {}) {
|
|
35358
36730
|
const paths = options.paths ?? resolveRuntimePaths();
|
|
35359
36731
|
const externalDir = resolveHermesLinkSkillExternalDir(paths);
|
|
35360
|
-
const skillPath =
|
|
36732
|
+
const skillPath = path30.join(
|
|
35361
36733
|
externalDir,
|
|
35362
36734
|
HERMES_LINK_SKILL_DIR,
|
|
35363
36735
|
HERMES_LINK_SKILL_FILE
|
|
@@ -35439,7 +36811,7 @@ function withDefaultProfilePlaceholder2(profiles) {
|
|
|
35439
36811
|
];
|
|
35440
36812
|
}
|
|
35441
36813
|
function resolveHermesLinkSkillExternalDir(paths = resolveRuntimePaths()) {
|
|
35442
|
-
return
|
|
36814
|
+
return path30.join(paths.homeDir, HERMES_LINK_SKILL_ROOT_DIR);
|
|
35443
36815
|
}
|
|
35444
36816
|
async function writeHermesLinkSkill(skillPath) {
|
|
35445
36817
|
const existing = await readFile17(skillPath, "utf8").catch((error) => {
|
|
@@ -35534,11 +36906,11 @@ function appendExternalDir(current, externalDir, hermesHome) {
|
|
|
35534
36906
|
const seen = new Set(
|
|
35535
36907
|
entries.map((entry) => resolveExternalDirEntry(entry, hermesHome))
|
|
35536
36908
|
);
|
|
35537
|
-
const normalizedExternalDir =
|
|
36909
|
+
const normalizedExternalDir = path30.resolve(externalDir);
|
|
35538
36910
|
return seen.has(normalizedExternalDir) ? entries : [...entries, normalizedExternalDir];
|
|
35539
36911
|
}
|
|
35540
36912
|
function externalDirsInclude(current, externalDir, hermesHome) {
|
|
35541
|
-
const normalizedExternalDir =
|
|
36913
|
+
const normalizedExternalDir = path30.resolve(externalDir);
|
|
35542
36914
|
return readExternalDirEntries(current).some(
|
|
35543
36915
|
(entry) => resolveExternalDirEntry(entry, hermesHome) === normalizedExternalDir
|
|
35544
36916
|
);
|
|
@@ -35549,14 +36921,14 @@ function readExternalDirEntries(value) {
|
|
|
35549
36921
|
}
|
|
35550
36922
|
function resolveExternalDirEntry(entry, hermesHome) {
|
|
35551
36923
|
const expanded = expandHome(expandEnvVars(entry));
|
|
35552
|
-
return
|
|
36924
|
+
return path30.resolve(path30.isAbsolute(expanded) ? expanded : path30.join(hermesHome, expanded));
|
|
35553
36925
|
}
|
|
35554
36926
|
function expandHome(value) {
|
|
35555
36927
|
if (value === "~") {
|
|
35556
36928
|
return os7.homedir();
|
|
35557
36929
|
}
|
|
35558
|
-
if (value.startsWith(`~${
|
|
35559
|
-
return
|
|
36930
|
+
if (value.startsWith(`~${path30.sep}`) || value.startsWith("~/")) {
|
|
36931
|
+
return path30.join(os7.homedir(), value.slice(2));
|
|
35560
36932
|
}
|
|
35561
36933
|
return value;
|
|
35562
36934
|
}
|
|
@@ -35795,7 +37167,7 @@ async function readHermesProfileCreationStatus(paths) {
|
|
|
35795
37167
|
let state = await readJsonFile(
|
|
35796
37168
|
profileCreationStatePath(paths)
|
|
35797
37169
|
);
|
|
35798
|
-
if (state?.state === "running" && !runningProfileCreation && !isRecentRunningState(state) && !
|
|
37170
|
+
if (state?.state === "running" && !runningProfileCreation && !isRecentRunningState(state) && !isProcessAlive2(state.pid)) {
|
|
35799
37171
|
state = {
|
|
35800
37172
|
...state,
|
|
35801
37173
|
state: "failed",
|
|
@@ -36108,7 +37480,7 @@ function collectEnvKeys(value, keys = /* @__PURE__ */ new Set()) {
|
|
|
36108
37480
|
return keys;
|
|
36109
37481
|
}
|
|
36110
37482
|
async function writeEnvValues(profileName, values) {
|
|
36111
|
-
const envPath =
|
|
37483
|
+
const envPath = path31.join(resolveHermesProfileDir(profileName), ".env");
|
|
36112
37484
|
const existingRaw = await readFile18(envPath, "utf8").catch((error) => {
|
|
36113
37485
|
if (isNodeError21(error, "ENOENT")) {
|
|
36114
37486
|
return "";
|
|
@@ -36145,8 +37517,8 @@ async function writeEnvValues(profileName, values) {
|
|
|
36145
37517
|
await atomicWriteFilePreservingMetadata(envPath, nextRaw);
|
|
36146
37518
|
}
|
|
36147
37519
|
async function copySkills(sourceProfile, targetProfile) {
|
|
36148
|
-
const sourceSkills =
|
|
36149
|
-
const targetSkills =
|
|
37520
|
+
const sourceSkills = path31.join(resolveHermesProfileDir(sourceProfile), "skills");
|
|
37521
|
+
const targetSkills = path31.join(resolveHermesProfileDir(targetProfile), "skills");
|
|
36150
37522
|
if (!await pathExists2(sourceSkills)) {
|
|
36151
37523
|
return;
|
|
36152
37524
|
}
|
|
@@ -36241,10 +37613,10 @@ async function readProfileCreationLogLines(paths) {
|
|
|
36241
37613
|
);
|
|
36242
37614
|
}
|
|
36243
37615
|
function profileCreationStatePath(paths) {
|
|
36244
|
-
return
|
|
37616
|
+
return path31.join(paths.runDir, "profile-create-state.json");
|
|
36245
37617
|
}
|
|
36246
37618
|
function profileCreationLogPath(paths) {
|
|
36247
|
-
return
|
|
37619
|
+
return path31.join(paths.logsDir, PROFILE_CREATE_LOG_FILE);
|
|
36248
37620
|
}
|
|
36249
37621
|
async function clearProfileCreationLogFiles(paths) {
|
|
36250
37622
|
const primary = profileCreationLogPath(paths);
|
|
@@ -36270,7 +37642,7 @@ function isRecentRunningState(state) {
|
|
|
36270
37642
|
}
|
|
36271
37643
|
return Date.now() - Date.parse(state.started_at) < 1e4;
|
|
36272
37644
|
}
|
|
36273
|
-
function
|
|
37645
|
+
function isProcessAlive2(pid) {
|
|
36274
37646
|
if (!pid || pid <= 0) {
|
|
36275
37647
|
return false;
|
|
36276
37648
|
}
|
|
@@ -36409,14 +37781,14 @@ function readProfilePermissionsInput(body) {
|
|
|
36409
37781
|
containerDisk: readPositiveInteger2(
|
|
36410
37782
|
terminal.container_disk ?? terminal.containerDisk
|
|
36411
37783
|
),
|
|
36412
|
-
containerPersistent:
|
|
37784
|
+
containerPersistent: readBoolean4(
|
|
36413
37785
|
terminal.container_persistent ?? terminal.containerPersistent
|
|
36414
37786
|
)
|
|
36415
37787
|
};
|
|
36416
37788
|
}
|
|
36417
37789
|
const sudo = readOptionalObject(body, "sudo");
|
|
36418
37790
|
if (sudo) {
|
|
36419
|
-
const clear =
|
|
37791
|
+
const clear = readBoolean4(sudo.clear ?? sudo.remove ?? sudo.delete);
|
|
36420
37792
|
const sudoInput = {
|
|
36421
37793
|
password: readRawString2(sudo, "password") ?? readRawString2(sudo, "sudo_password") ?? readRawString2(sudo, "sudoPassword") ?? void 0,
|
|
36422
37794
|
clear: clear === true ? true : void 0
|
|
@@ -36432,7 +37804,7 @@ function readProfilePermissionsInput(body) {
|
|
|
36432
37804
|
toolsets.enabled_toolsets ?? toolsets.enabledToolsets ?? toolsets.enabled,
|
|
36433
37805
|
"toolsets.enabled"
|
|
36434
37806
|
) ?? void 0,
|
|
36435
|
-
mcpEnabled:
|
|
37807
|
+
mcpEnabled: readBoolean4(toolsets.mcp_enabled ?? toolsets.mcpEnabled)
|
|
36436
37808
|
};
|
|
36437
37809
|
}
|
|
36438
37810
|
if (Object.keys(input).length === 0) {
|
|
@@ -36533,7 +37905,7 @@ import {
|
|
|
36533
37905
|
readFile as readFile19,
|
|
36534
37906
|
stat as stat21
|
|
36535
37907
|
} from "fs/promises";
|
|
36536
|
-
import
|
|
37908
|
+
import path32 from "path";
|
|
36537
37909
|
import YAML6 from "yaml";
|
|
36538
37910
|
var ENTRY_DELIMITER = "\n\xA7\n";
|
|
36539
37911
|
var DEFAULT_MEMORY_LIMIT = 2200;
|
|
@@ -36864,7 +38236,7 @@ async function saveProviderSettings(profileName, provider, patch) {
|
|
|
36864
38236
|
});
|
|
36865
38237
|
await patchJsonProviderConfig(
|
|
36866
38238
|
profileName,
|
|
36867
|
-
|
|
38239
|
+
path32.join("hindsight", "config.json"),
|
|
36868
38240
|
{
|
|
36869
38241
|
mode: patch.mode,
|
|
36870
38242
|
api_url: patch.apiUrl,
|
|
@@ -37067,7 +38439,7 @@ async function patchHermesMemoryLimits(profileName, patch) {
|
|
|
37067
38439
|
await atomicWriteFilePreservingMetadata(configPath, document.toString());
|
|
37068
38440
|
}
|
|
37069
38441
|
function resolveMemoryDir(profileName) {
|
|
37070
|
-
return
|
|
38442
|
+
return path32.join(resolveHermesProfileDir(profileName), "memories");
|
|
37071
38443
|
}
|
|
37072
38444
|
async function readMemoryStore(profileName, target, limits) {
|
|
37073
38445
|
const filePath = memoryFilePath(profileName, target);
|
|
@@ -37128,7 +38500,7 @@ async function writeMemoryEntries(profileName, target, entries) {
|
|
|
37128
38500
|
);
|
|
37129
38501
|
}
|
|
37130
38502
|
function memoryFilePath(profileName, target) {
|
|
37131
|
-
return
|
|
38503
|
+
return path32.join(
|
|
37132
38504
|
resolveMemoryDir(profileName),
|
|
37133
38505
|
target === "user" ? "USER.md" : "MEMORY.md"
|
|
37134
38506
|
);
|
|
@@ -37188,7 +38560,7 @@ async function readCustomProviderSetupSummary(profileName) {
|
|
|
37188
38560
|
configurable: true,
|
|
37189
38561
|
configured: true,
|
|
37190
38562
|
configurationIssue: null,
|
|
37191
|
-
providerConfigPath:
|
|
38563
|
+
providerConfigPath: path32.join(
|
|
37192
38564
|
resolveHermesProfileDir(profileName),
|
|
37193
38565
|
"<provider>.json"
|
|
37194
38566
|
),
|
|
@@ -37582,7 +38954,7 @@ async function readProviderSettings(profileName, provider) {
|
|
|
37582
38954
|
stringSetting(
|
|
37583
38955
|
"dbPath",
|
|
37584
38956
|
"SQLite \u6570\u636E\u5E93\u8DEF\u5F84",
|
|
37585
|
-
config.db_path ??
|
|
38957
|
+
config.db_path ?? path32.join(resolveHermesProfileDir(profileName), "memory_store.db")
|
|
37586
38958
|
),
|
|
37587
38959
|
booleanSetting("autoExtract", "\u4F1A\u8BDD\u7ED3\u675F\u81EA\u52A8\u62BD\u53D6", config.auto_extract ?? false),
|
|
37588
38960
|
numberSetting("defaultTrust", "\u9ED8\u8BA4\u4FE1\u4EFB\u5206", config.default_trust ?? 0.5),
|
|
@@ -37618,7 +38990,7 @@ async function readProviderSettings(profileName, provider) {
|
|
|
37618
38990
|
stringSetting(
|
|
37619
38991
|
"workingDirectory",
|
|
37620
38992
|
"\u5DE5\u4F5C\u76EE\u5F55",
|
|
37621
|
-
|
|
38993
|
+
path32.join(resolveHermesProfileDir(profileName), "byterover"),
|
|
37622
38994
|
false
|
|
37623
38995
|
)
|
|
37624
38996
|
];
|
|
@@ -37627,16 +38999,16 @@ async function readProviderSettings(profileName, provider) {
|
|
|
37627
38999
|
}
|
|
37628
39000
|
function memoryProviderConfigPath(profileName, provider) {
|
|
37629
39001
|
if (provider === "honcho") {
|
|
37630
|
-
return
|
|
39002
|
+
return path32.join(resolveHermesProfileDir(profileName), "honcho.json");
|
|
37631
39003
|
}
|
|
37632
39004
|
if (provider === "mem0") {
|
|
37633
|
-
return
|
|
39005
|
+
return path32.join(resolveHermesProfileDir(profileName), "mem0.json");
|
|
37634
39006
|
}
|
|
37635
39007
|
if (provider === "supermemory") {
|
|
37636
|
-
return
|
|
39008
|
+
return path32.join(resolveHermesProfileDir(profileName), "supermemory.json");
|
|
37637
39009
|
}
|
|
37638
39010
|
if (provider === "hindsight") {
|
|
37639
|
-
return
|
|
39011
|
+
return path32.join(
|
|
37640
39012
|
resolveHermesProfileDir(profileName),
|
|
37641
39013
|
"hindsight",
|
|
37642
39014
|
"config.json"
|
|
@@ -37645,13 +39017,13 @@ function memoryProviderConfigPath(profileName, provider) {
|
|
|
37645
39017
|
return null;
|
|
37646
39018
|
}
|
|
37647
39019
|
function customProviderConfigPath(profileName, provider) {
|
|
37648
|
-
return
|
|
39020
|
+
return path32.join(
|
|
37649
39021
|
resolveHermesProfileDir(profileName),
|
|
37650
39022
|
`${normalizeCustomProviderId(provider)}.json`
|
|
37651
39023
|
);
|
|
37652
39024
|
}
|
|
37653
39025
|
function customProviderRegistryPath(profileName) {
|
|
37654
|
-
return
|
|
39026
|
+
return path32.join(
|
|
37655
39027
|
resolveHermesProfileDir(profileName),
|
|
37656
39028
|
CUSTOM_PROVIDER_REGISTRY_FILE
|
|
37657
39029
|
);
|
|
@@ -37703,7 +39075,7 @@ async function saveCustomProviderRegistryEntry(profileName, provider) {
|
|
|
37703
39075
|
);
|
|
37704
39076
|
}
|
|
37705
39077
|
async function discoverUserMemoryProviderDescriptors(profileName) {
|
|
37706
|
-
const pluginsDir =
|
|
39078
|
+
const pluginsDir = path32.join(resolveHermesProfileDir(profileName), "plugins");
|
|
37707
39079
|
const entries = await readdir10(pluginsDir, { withFileTypes: true }).catch(
|
|
37708
39080
|
(error) => {
|
|
37709
39081
|
if (isNodeError22(error, "ENOENT")) {
|
|
@@ -37723,7 +39095,7 @@ async function discoverUserMemoryProviderDescriptors(profileName) {
|
|
|
37723
39095
|
} catch {
|
|
37724
39096
|
continue;
|
|
37725
39097
|
}
|
|
37726
|
-
const providerDir =
|
|
39098
|
+
const providerDir = path32.join(pluginsDir, entry.name);
|
|
37727
39099
|
if (!await isMemoryProviderPluginDir(providerDir)) {
|
|
37728
39100
|
continue;
|
|
37729
39101
|
}
|
|
@@ -37737,7 +39109,7 @@ async function discoverUserMemoryProviderDescriptors(profileName) {
|
|
|
37737
39109
|
return descriptors;
|
|
37738
39110
|
}
|
|
37739
39111
|
async function isUserMemoryProviderInstalled(profileName, provider) {
|
|
37740
|
-
const providerDir =
|
|
39112
|
+
const providerDir = path32.join(
|
|
37741
39113
|
resolveHermesProfileDir(profileName),
|
|
37742
39114
|
"plugins",
|
|
37743
39115
|
normalizeCustomProviderId(provider)
|
|
@@ -37745,7 +39117,7 @@ async function isUserMemoryProviderInstalled(profileName, provider) {
|
|
|
37745
39117
|
return isMemoryProviderPluginDir(providerDir);
|
|
37746
39118
|
}
|
|
37747
39119
|
async function isMemoryProviderPluginDir(providerDir) {
|
|
37748
|
-
const source = await readFile19(
|
|
39120
|
+
const source = await readFile19(path32.join(providerDir, "__init__.py"), "utf8").catch(
|
|
37749
39121
|
(error) => {
|
|
37750
39122
|
if (isNodeError22(error, "ENOENT")) {
|
|
37751
39123
|
return "";
|
|
@@ -37757,7 +39129,7 @@ async function isMemoryProviderPluginDir(providerDir) {
|
|
|
37757
39129
|
return sample.includes("register_memory_provider") || sample.includes("MemoryProvider");
|
|
37758
39130
|
}
|
|
37759
39131
|
async function readPluginMetadata(providerDir) {
|
|
37760
|
-
const raw = await readFile19(
|
|
39132
|
+
const raw = await readFile19(path32.join(providerDir, "plugin.yaml"), "utf8").catch(
|
|
37761
39133
|
(error) => {
|
|
37762
39134
|
if (isNodeError22(error, "ENOENT")) {
|
|
37763
39135
|
return "";
|
|
@@ -37769,10 +39141,10 @@ async function readPluginMetadata(providerDir) {
|
|
|
37769
39141
|
}
|
|
37770
39142
|
async function resolveByteRoverCli() {
|
|
37771
39143
|
const candidates = [
|
|
37772
|
-
...(process.env.PATH ?? "").split(
|
|
37773
|
-
|
|
39144
|
+
...(process.env.PATH ?? "").split(path32.delimiter).filter(Boolean).map((dir) => path32.join(dir, "brv")),
|
|
39145
|
+
path32.join(process.env.HOME ?? "", ".brv-cli", "bin", "brv"),
|
|
37774
39146
|
"/usr/local/bin/brv",
|
|
37775
|
-
|
|
39147
|
+
path32.join(process.env.HOME ?? "", ".npm-global", "bin", "brv")
|
|
37776
39148
|
].filter(Boolean);
|
|
37777
39149
|
for (const candidate of candidates) {
|
|
37778
39150
|
const found = await access3(candidate).then(() => true).catch(() => false);
|
|
@@ -37833,7 +39205,7 @@ async function patchHermesMemoryEnv(profileName, patch) {
|
|
|
37833
39205
|
if (entries.length === 0) {
|
|
37834
39206
|
return;
|
|
37835
39207
|
}
|
|
37836
|
-
const envPath =
|
|
39208
|
+
const envPath = path32.join(resolveHermesProfileDir(profileName), ".env");
|
|
37837
39209
|
const existingRaw = await readFile19(envPath, "utf8").catch((error) => {
|
|
37838
39210
|
if (isNodeError22(error, "ENOENT")) {
|
|
37839
39211
|
return "";
|
|
@@ -38004,7 +39376,7 @@ async function readActiveMemoryProvider(profileName) {
|
|
|
38004
39376
|
return provider;
|
|
38005
39377
|
}
|
|
38006
39378
|
async function patchJsonProviderConfig(profileName, relativePath, patch) {
|
|
38007
|
-
const configPath =
|
|
39379
|
+
const configPath = path32.join(
|
|
38008
39380
|
resolveHermesProfileDir(profileName),
|
|
38009
39381
|
relativePath
|
|
38010
39382
|
);
|
|
@@ -38033,7 +39405,7 @@ async function readJsonObject(filePath) {
|
|
|
38033
39405
|
} catch {
|
|
38034
39406
|
throw new HermesMemoryError(
|
|
38035
39407
|
"memory_provider_config_invalid",
|
|
38036
|
-
`${
|
|
39408
|
+
`${path32.basename(filePath)} \u4E0D\u662F\u6709\u6548\u7684 JSON \u914D\u7F6E\u6587\u4EF6\u3002`
|
|
38037
39409
|
);
|
|
38038
39410
|
}
|
|
38039
39411
|
}
|
|
@@ -38041,7 +39413,7 @@ function booleanSetting(key, label, value) {
|
|
|
38041
39413
|
return {
|
|
38042
39414
|
key,
|
|
38043
39415
|
label,
|
|
38044
|
-
value:
|
|
39416
|
+
value: readBoolean5(value) ?? false,
|
|
38045
39417
|
editable: true,
|
|
38046
39418
|
kind: "boolean"
|
|
38047
39419
|
};
|
|
@@ -38159,7 +39531,7 @@ function readPositiveInteger4(value) {
|
|
|
38159
39531
|
const numberValue = typeof value === "number" ? value : typeof value === "string" ? Number(value.trim()) : NaN;
|
|
38160
39532
|
return Number.isFinite(numberValue) && numberValue > 0 ? Math.floor(numberValue) : void 0;
|
|
38161
39533
|
}
|
|
38162
|
-
function
|
|
39534
|
+
function readBoolean5(value) {
|
|
38163
39535
|
if (typeof value === "boolean") {
|
|
38164
39536
|
return value;
|
|
38165
39537
|
}
|
|
@@ -38425,15 +39797,15 @@ function readMemorySettingsPatch(body, options = {}) {
|
|
|
38425
39797
|
if (containerTag !== void 0) {
|
|
38426
39798
|
input.containerTag = containerTag;
|
|
38427
39799
|
}
|
|
38428
|
-
const autoRecall =
|
|
39800
|
+
const autoRecall = readBoolean4(body.auto_recall ?? body.autoRecall);
|
|
38429
39801
|
if (autoRecall !== void 0) {
|
|
38430
39802
|
input.autoRecall = autoRecall;
|
|
38431
39803
|
}
|
|
38432
|
-
const autoCapture =
|
|
39804
|
+
const autoCapture = readBoolean4(body.auto_capture ?? body.autoCapture);
|
|
38433
39805
|
if (autoCapture !== void 0) {
|
|
38434
39806
|
input.autoCapture = autoCapture;
|
|
38435
39807
|
}
|
|
38436
|
-
const autoRetain =
|
|
39808
|
+
const autoRetain = readBoolean4(body.auto_retain ?? body.autoRetain);
|
|
38437
39809
|
if (autoRetain !== void 0) {
|
|
38438
39810
|
input.autoRetain = autoRetain;
|
|
38439
39811
|
}
|
|
@@ -38503,7 +39875,7 @@ function readMemorySettingsPatch(body, options = {}) {
|
|
|
38503
39875
|
if (writeFrequency) {
|
|
38504
39876
|
input.writeFrequency = writeFrequency;
|
|
38505
39877
|
}
|
|
38506
|
-
const saveMessages =
|
|
39878
|
+
const saveMessages = readBoolean4(body.save_messages ?? body.saveMessages);
|
|
38507
39879
|
if (saveMessages !== void 0) {
|
|
38508
39880
|
input.saveMessages = saveMessages;
|
|
38509
39881
|
}
|
|
@@ -38543,7 +39915,7 @@ function readMemorySettingsPatch(body, options = {}) {
|
|
|
38543
39915
|
if (agentId !== void 0) {
|
|
38544
39916
|
input.agentId = agentId;
|
|
38545
39917
|
}
|
|
38546
|
-
const rerank =
|
|
39918
|
+
const rerank = readBoolean4(body.rerank);
|
|
38547
39919
|
if (rerank !== void 0) {
|
|
38548
39920
|
input.rerank = rerank;
|
|
38549
39921
|
}
|
|
@@ -38567,7 +39939,7 @@ function readMemorySettingsPatch(body, options = {}) {
|
|
|
38567
39939
|
if (dbPath !== void 0) {
|
|
38568
39940
|
input.dbPath = dbPath;
|
|
38569
39941
|
}
|
|
38570
|
-
const autoExtract =
|
|
39942
|
+
const autoExtract = readBoolean4(body.auto_extract ?? body.autoExtract);
|
|
38571
39943
|
if (autoExtract !== void 0) {
|
|
38572
39944
|
input.autoExtract = autoExtract;
|
|
38573
39945
|
}
|
|
@@ -38657,7 +40029,7 @@ function toMemoryHttpError(error) {
|
|
|
38657
40029
|
|
|
38658
40030
|
// src/hermes/skills.ts
|
|
38659
40031
|
import { readFile as readFile20, readdir as readdir11 } from "fs/promises";
|
|
38660
|
-
import
|
|
40032
|
+
import path33 from "path";
|
|
38661
40033
|
import YAML7 from "yaml";
|
|
38662
40034
|
var HermesSkillNotFoundError = class extends Error {
|
|
38663
40035
|
constructor(skillName) {
|
|
@@ -38671,7 +40043,7 @@ var EXCLUDED_SKILL_DIRS = /* @__PURE__ */ new Set([".git", ".github", ".hub"]);
|
|
|
38671
40043
|
async function listHermesProfileSkills(profileName, paths = resolveRuntimePaths()) {
|
|
38672
40044
|
const profile = await readExistingProfile(profileName, paths);
|
|
38673
40045
|
const profileDir = resolveHermesProfileDir(profile.name);
|
|
38674
|
-
const skillsRoot =
|
|
40046
|
+
const skillsRoot = path33.join(profileDir, "skills");
|
|
38675
40047
|
const [skillFiles, disabled, provenance] = await Promise.all([
|
|
38676
40048
|
findSkillFiles(skillsRoot),
|
|
38677
40049
|
readDisabledSkillNames(resolveHermesConfigPath(profile.name)),
|
|
@@ -38762,7 +40134,7 @@ async function collectSkillFiles(directory, results) {
|
|
|
38762
40134
|
if (EXCLUDED_SKILL_DIRS.has(entry.name)) {
|
|
38763
40135
|
continue;
|
|
38764
40136
|
}
|
|
38765
|
-
const entryPath =
|
|
40137
|
+
const entryPath = path33.join(directory, entry.name);
|
|
38766
40138
|
if (entry.isDirectory()) {
|
|
38767
40139
|
await collectSkillFiles(entryPath, results);
|
|
38768
40140
|
continue;
|
|
@@ -38784,10 +40156,10 @@ async function readSkillMetadata(input) {
|
|
|
38784
40156
|
if (raw === null) {
|
|
38785
40157
|
return null;
|
|
38786
40158
|
}
|
|
38787
|
-
const skillDir =
|
|
40159
|
+
const skillDir = path33.dirname(input.skillFile);
|
|
38788
40160
|
const { frontmatter, body } = parseSkillDocument(raw.slice(0, 4e3));
|
|
38789
40161
|
const name = normalizeSkillName(
|
|
38790
|
-
readString24(frontmatter.name) ??
|
|
40162
|
+
readString24(frontmatter.name) ?? path33.basename(skillDir)
|
|
38791
40163
|
);
|
|
38792
40164
|
if (!name) {
|
|
38793
40165
|
return null;
|
|
@@ -38806,7 +40178,7 @@ async function readSkillMetadata(input) {
|
|
|
38806
40178
|
enabled: !input.disabled.has(name),
|
|
38807
40179
|
source: provenance.source,
|
|
38808
40180
|
trust: provenance.trust,
|
|
38809
|
-
relativePath:
|
|
40181
|
+
relativePath: path33.relative(input.skillsRoot, skillDir)
|
|
38810
40182
|
};
|
|
38811
40183
|
}
|
|
38812
40184
|
function parseSkillDocument(raw) {
|
|
@@ -38827,8 +40199,8 @@ function parseSkillDocument(raw) {
|
|
|
38827
40199
|
}
|
|
38828
40200
|
}
|
|
38829
40201
|
function categoryFromPath(skillsRoot, skillFile) {
|
|
38830
|
-
const relative =
|
|
38831
|
-
const parts = relative.split(
|
|
40202
|
+
const relative = path33.relative(skillsRoot, skillFile);
|
|
40203
|
+
const parts = relative.split(path33.sep).filter(Boolean);
|
|
38832
40204
|
return parts.length >= 3 ? parts[0] : null;
|
|
38833
40205
|
}
|
|
38834
40206
|
function firstBodyDescription(body) {
|
|
@@ -38875,7 +40247,7 @@ async function readSkillProvenance(root) {
|
|
|
38875
40247
|
return provenance;
|
|
38876
40248
|
}
|
|
38877
40249
|
async function readBundledSkillNames(root) {
|
|
38878
|
-
const raw = await readFile20(
|
|
40250
|
+
const raw = await readFile20(path33.join(root, ".bundled_manifest"), "utf8").catch(
|
|
38879
40251
|
(error) => {
|
|
38880
40252
|
if (isNodeError23(error, "ENOENT")) {
|
|
38881
40253
|
return "";
|
|
@@ -38898,7 +40270,7 @@ async function readBundledSkillNames(root) {
|
|
|
38898
40270
|
return names;
|
|
38899
40271
|
}
|
|
38900
40272
|
async function readHubInstalledSkills(root) {
|
|
38901
|
-
const raw = await readFile20(
|
|
40273
|
+
const raw = await readFile20(path33.join(root, ".hub", "lock.json"), "utf8").catch(
|
|
38902
40274
|
(error) => {
|
|
38903
40275
|
if (isNodeError23(error, "ENOENT")) {
|
|
38904
40276
|
return "";
|
|
@@ -39034,7 +40406,7 @@ function registerProfileSkillRoutes(router, options) {
|
|
|
39034
40406
|
router.patch("/api/v1/profiles/:name/skills/:skillName", async (ctx) => {
|
|
39035
40407
|
await authenticateRequest(ctx, paths);
|
|
39036
40408
|
const body = await readJsonBody(ctx.req);
|
|
39037
|
-
const enabled =
|
|
40409
|
+
const enabled = readBoolean4(body.enabled);
|
|
39038
40410
|
if (enabled === void 0) {
|
|
39039
40411
|
throw new LinkHttpError(
|
|
39040
40412
|
400,
|
|
@@ -39472,7 +40844,7 @@ function registerStatisticsRoutes(router, options) {
|
|
|
39472
40844
|
await authenticateRequest(ctx, paths);
|
|
39473
40845
|
ctx.set("cache-control", "no-store");
|
|
39474
40846
|
const usage = await readLinkUsageStatistics(paths, {
|
|
39475
|
-
days:
|
|
40847
|
+
days: readInteger5(ctx.query.days),
|
|
39476
40848
|
from: readQueryString(ctx.query.from),
|
|
39477
40849
|
to: readQueryString(ctx.query.to)
|
|
39478
40850
|
});
|
|
@@ -39566,7 +40938,7 @@ function readModelList(payload) {
|
|
|
39566
40938
|
import { EventEmitter as EventEmitter3 } from "events";
|
|
39567
40939
|
import { spawn as spawn4 } from "child_process";
|
|
39568
40940
|
import { mkdir as mkdir14, readFile as readFile21, rm as rm8 } from "fs/promises";
|
|
39569
|
-
import
|
|
40941
|
+
import path34 from "path";
|
|
39570
40942
|
var SERVER_HERMES_RELEASES_LATEST_PATH = "/api/v1/hermes-agent/releases/latest";
|
|
39571
40943
|
var RELEASE_CACHE_TTL_MS = 6 * 60 * 60 * 1e3;
|
|
39572
40944
|
var RELEASE_FETCH_TIMEOUT_MS = 5e3;
|
|
@@ -39713,7 +41085,7 @@ async function startHermesUpdate(options) {
|
|
|
39713
41085
|
}
|
|
39714
41086
|
async function readHermesUpdateStatus(paths) {
|
|
39715
41087
|
let state = await readJsonFile(updateStatePath(paths));
|
|
39716
|
-
if (state?.state === "running" && !runningUpdate && !isRecentRunningState2(state) && !
|
|
41088
|
+
if (state?.state === "running" && !runningUpdate && !isRecentRunningState2(state) && !isProcessAlive3(state.pid)) {
|
|
39717
41089
|
state = {
|
|
39718
41090
|
...state,
|
|
39719
41091
|
state: "failed",
|
|
@@ -39844,13 +41216,13 @@ async function readUpdateLogLines(paths) {
|
|
|
39844
41216
|
);
|
|
39845
41217
|
}
|
|
39846
41218
|
function releaseCachePath(paths) {
|
|
39847
|
-
return
|
|
41219
|
+
return path34.join(paths.indexesDir, "hermes-release-check.json");
|
|
39848
41220
|
}
|
|
39849
41221
|
function updateStatePath(paths) {
|
|
39850
|
-
return
|
|
41222
|
+
return path34.join(paths.runDir, "hermes-update-state.json");
|
|
39851
41223
|
}
|
|
39852
41224
|
function updateLogPath(paths) {
|
|
39853
|
-
return
|
|
41225
|
+
return path34.join(paths.logsDir, UPDATE_LOG_FILE);
|
|
39854
41226
|
}
|
|
39855
41227
|
async function clearUpdateLogFiles(paths) {
|
|
39856
41228
|
const primary = updateLogPath(paths);
|
|
@@ -39924,7 +41296,7 @@ async function readHermesReleaseCheckContext(paths) {
|
|
|
39924
41296
|
releaseCheckUrl: url.toString()
|
|
39925
41297
|
};
|
|
39926
41298
|
}
|
|
39927
|
-
function
|
|
41299
|
+
function isProcessAlive3(pid) {
|
|
39928
41300
|
if (!pid || pid <= 0) {
|
|
39929
41301
|
return false;
|
|
39930
41302
|
}
|
|
@@ -39951,12 +41323,12 @@ function readString25(payload, key) {
|
|
|
39951
41323
|
import { spawn as spawn6 } from "child_process";
|
|
39952
41324
|
import { EventEmitter as EventEmitter4 } from "events";
|
|
39953
41325
|
import { mkdir as mkdir17, readFile as readFile23, rm as rm11 } from "fs/promises";
|
|
39954
|
-
import
|
|
41326
|
+
import path36 from "path";
|
|
39955
41327
|
|
|
39956
41328
|
// src/daemon/process.ts
|
|
39957
41329
|
import { spawn as spawn5 } from "child_process";
|
|
39958
41330
|
import { mkdir as mkdir16, readFile as readFile22, rm as rm10, writeFile as writeFile4 } from "fs/promises";
|
|
39959
|
-
import
|
|
41331
|
+
import path35 from "path";
|
|
39960
41332
|
|
|
39961
41333
|
// src/daemon/service.ts
|
|
39962
41334
|
import { createServer } from "http";
|
|
@@ -40117,17 +41489,17 @@ async function fetchRelayStreamBatchPolicy(serverBaseUrl, options = {}) {
|
|
|
40117
41489
|
}
|
|
40118
41490
|
}
|
|
40119
41491
|
function readRelayStreamBatchPolicy(input) {
|
|
40120
|
-
const record =
|
|
40121
|
-
const body =
|
|
41492
|
+
const record = readRecord3(input);
|
|
41493
|
+
const body = readRecord3(record?.policy) ?? readRecord3(record?.stream_batching) ?? record;
|
|
40122
41494
|
return normalizeRelayStreamBatchPolicy(body);
|
|
40123
41495
|
}
|
|
40124
41496
|
function normalizeRelayStreamBatchPolicy(input) {
|
|
40125
|
-
const record =
|
|
41497
|
+
const record = readRecord3(input);
|
|
40126
41498
|
if (!record) {
|
|
40127
41499
|
return null;
|
|
40128
41500
|
}
|
|
40129
|
-
const flushIntervalMs =
|
|
40130
|
-
const flushBytes =
|
|
41501
|
+
const flushIntervalMs = readInteger6(record.flushIntervalMs ?? record.flush_interval_ms);
|
|
41502
|
+
const flushBytes = readInteger6(record.flushBytes ?? record.flush_bytes);
|
|
40131
41503
|
if (flushIntervalMs === null || flushBytes === null || flushIntervalMs < RELAY_STREAM_POLICY_CONSTRAINTS.flushIntervalMs.min || flushIntervalMs > RELAY_STREAM_POLICY_CONSTRAINTS.flushIntervalMs.max || flushBytes < RELAY_STREAM_POLICY_CONSTRAINTS.flushBytes.min || flushBytes > RELAY_STREAM_POLICY_CONSTRAINTS.flushBytes.max) {
|
|
40132
41504
|
return null;
|
|
40133
41505
|
}
|
|
@@ -40136,10 +41508,10 @@ function normalizeRelayStreamBatchPolicy(input) {
|
|
|
40136
41508
|
flushBytes
|
|
40137
41509
|
};
|
|
40138
41510
|
}
|
|
40139
|
-
function
|
|
41511
|
+
function readRecord3(value) {
|
|
40140
41512
|
return value && typeof value === "object" ? value : null;
|
|
40141
41513
|
}
|
|
40142
|
-
function
|
|
41514
|
+
function readInteger6(value) {
|
|
40143
41515
|
return typeof value === "number" && Number.isInteger(value) ? value : null;
|
|
40144
41516
|
}
|
|
40145
41517
|
|
|
@@ -40173,12 +41545,12 @@ function connectRelayControl(options) {
|
|
|
40173
41545
|
onUpdate: options.onStreamBatchPolicy
|
|
40174
41546
|
};
|
|
40175
41547
|
const startConnect = () => {
|
|
40176
|
-
void waitForPersistedCooldown().then((
|
|
41548
|
+
void waitForPersistedCooldown().then((delay6) => {
|
|
40177
41549
|
if (closedByUser) {
|
|
40178
41550
|
return;
|
|
40179
41551
|
}
|
|
40180
|
-
if (
|
|
40181
|
-
scheduleTimer(
|
|
41552
|
+
if (delay6 > 0) {
|
|
41553
|
+
scheduleTimer(delay6, "cooldown", `Relay reconnect cooldown active for ${delay6}ms`);
|
|
40182
41554
|
return;
|
|
40183
41555
|
}
|
|
40184
41556
|
connect();
|
|
@@ -40316,8 +41688,8 @@ function connectRelayControl(options) {
|
|
|
40316
41688
|
}
|
|
40317
41689
|
if (recorded.cooldownUntilMs !== null) {
|
|
40318
41690
|
reconnectAttempts = 0;
|
|
40319
|
-
const
|
|
40320
|
-
scheduleTimer(
|
|
41691
|
+
const delay7 = Math.max(0, recorded.cooldownUntilMs - Date.now());
|
|
41692
|
+
scheduleTimer(delay7, "cooldown", `Relay reconnect storm guard active for ${delay7}ms`);
|
|
40321
41693
|
return;
|
|
40322
41694
|
}
|
|
40323
41695
|
reconnectAttempts += 1;
|
|
@@ -40325,16 +41697,16 @@ function connectRelayControl(options) {
|
|
|
40325
41697
|
baseMs: backoffBaseMs,
|
|
40326
41698
|
maxMs: backoffMaxMs
|
|
40327
41699
|
});
|
|
40328
|
-
const
|
|
40329
|
-
scheduleTimer(
|
|
41700
|
+
const delay6 = Math.max(backoffMs, relayRetryAfterMs ?? 0);
|
|
41701
|
+
scheduleTimer(delay6, "retrying", `Retrying in ${delay6}ms`);
|
|
40330
41702
|
}
|
|
40331
41703
|
async function waitForPersistedCooldown() {
|
|
40332
41704
|
return await readRelayCooldownDelayMs(paths).catch(() => 0);
|
|
40333
41705
|
}
|
|
40334
|
-
function scheduleTimer(
|
|
41706
|
+
function scheduleTimer(delay6, state, message) {
|
|
40335
41707
|
clearRetryTimer();
|
|
40336
41708
|
options.onStatus?.({ state, attempt: reconnectAttempts, message });
|
|
40337
|
-
retryTimer = setTimeout(connect,
|
|
41709
|
+
retryTimer = setTimeout(connect, delay6);
|
|
40338
41710
|
retryTimer.unref?.();
|
|
40339
41711
|
}
|
|
40340
41712
|
function clearRetryTimer() {
|
|
@@ -40838,6 +42210,7 @@ async function observePublicRoute(options) {
|
|
|
40838
42210
|
"content-type": "application/json",
|
|
40839
42211
|
...options.relayBootstrapToken ? { authorization: `Bearer ${options.relayBootstrapToken}` } : {}
|
|
40840
42212
|
},
|
|
42213
|
+
signal: options.signal,
|
|
40841
42214
|
body: JSON.stringify({
|
|
40842
42215
|
install_id: options.installId,
|
|
40843
42216
|
link_id: options.linkId,
|
|
@@ -41123,7 +42496,8 @@ async function reportLinkStatusToServer(options = {}) {
|
|
|
41123
42496
|
publicKeyPem: identity.public_key_pem,
|
|
41124
42497
|
observePublicRoute: true,
|
|
41125
42498
|
configuredLanHost: config.lanHost,
|
|
41126
|
-
fetchImpl: options.fetchImpl
|
|
42499
|
+
fetchImpl: options.fetchImpl,
|
|
42500
|
+
signal: options.signal
|
|
41127
42501
|
});
|
|
41128
42502
|
const routes = await mergeLastReportedPublicRoutes(paths, discoveredRoutes);
|
|
41129
42503
|
const systemInfo = readLinkSystemInfo();
|
|
@@ -41154,7 +42528,8 @@ async function reportLinkStatusToServer(options = {}) {
|
|
|
41154
42528
|
...payload,
|
|
41155
42529
|
public_key_pem: identity.public_key_pem,
|
|
41156
42530
|
signature
|
|
41157
|
-
})
|
|
42531
|
+
}),
|
|
42532
|
+
signal: options.signal
|
|
41158
42533
|
}
|
|
41159
42534
|
);
|
|
41160
42535
|
const body = await response.json().catch(() => null);
|
|
@@ -41202,19 +42577,28 @@ function startLanIpMonitor(options) {
|
|
|
41202
42577
|
let running = false;
|
|
41203
42578
|
let closed = false;
|
|
41204
42579
|
let current = Promise.resolve();
|
|
42580
|
+
let currentAbortController = null;
|
|
41205
42581
|
const check = (context = {}) => {
|
|
41206
42582
|
if (running || closed) {
|
|
41207
42583
|
return current;
|
|
41208
42584
|
}
|
|
41209
42585
|
running = true;
|
|
42586
|
+
const abortController = new AbortController();
|
|
42587
|
+
currentAbortController = abortController;
|
|
41210
42588
|
current = (async () => {
|
|
41211
42589
|
try {
|
|
41212
|
-
await checkLanIpChange(options, context);
|
|
42590
|
+
await checkLanIpChange({ ...options, signal: abortController.signal }, context);
|
|
41213
42591
|
} catch (error) {
|
|
42592
|
+
if (closed && isAbortError3(error)) {
|
|
42593
|
+
return;
|
|
42594
|
+
}
|
|
41214
42595
|
void options.logger.warn("lan_ip_monitor_failed", {
|
|
41215
42596
|
error: error instanceof Error ? error.message : String(error)
|
|
41216
42597
|
});
|
|
41217
42598
|
} finally {
|
|
42599
|
+
if (currentAbortController === abortController) {
|
|
42600
|
+
currentAbortController = null;
|
|
42601
|
+
}
|
|
41218
42602
|
running = false;
|
|
41219
42603
|
}
|
|
41220
42604
|
})();
|
|
@@ -41233,6 +42617,7 @@ function startLanIpMonitor(options) {
|
|
|
41233
42617
|
async close() {
|
|
41234
42618
|
closed = true;
|
|
41235
42619
|
clearInterval(timer);
|
|
42620
|
+
currentAbortController?.abort();
|
|
41236
42621
|
await current.catch(() => void 0);
|
|
41237
42622
|
}
|
|
41238
42623
|
};
|
|
@@ -41253,7 +42638,8 @@ async function checkLanIpChange(options, context = {}) {
|
|
|
41253
42638
|
publicKeyPem: identity.public_key_pem,
|
|
41254
42639
|
observePublicRoute: context.observePublicRoute === true,
|
|
41255
42640
|
configuredLanHost: config.lanHost,
|
|
41256
|
-
fetchImpl: options.fetchImpl
|
|
42641
|
+
fetchImpl: options.fetchImpl,
|
|
42642
|
+
signal: options.signal
|
|
41257
42643
|
});
|
|
41258
42644
|
const routes = await mergeLastReportedPublicRoutes(options.paths, discoveredRoutes);
|
|
41259
42645
|
if (context.publishToRelay) {
|
|
@@ -41278,7 +42664,8 @@ async function checkLanIpChange(options, context = {}) {
|
|
|
41278
42664
|
const result = await reportLinkStatusToServer({
|
|
41279
42665
|
paths: options.paths,
|
|
41280
42666
|
fetchImpl: options.fetchImpl,
|
|
41281
|
-
routes
|
|
42667
|
+
routes,
|
|
42668
|
+
signal: options.signal
|
|
41282
42669
|
});
|
|
41283
42670
|
if (result) {
|
|
41284
42671
|
options.onNetworkRoutes?.(routes);
|
|
@@ -41296,6 +42683,9 @@ async function checkLanIpChange(options, context = {}) {
|
|
|
41296
42683
|
});
|
|
41297
42684
|
}
|
|
41298
42685
|
}
|
|
42686
|
+
function isAbortError3(error) {
|
|
42687
|
+
return typeof error === "object" && error !== null && "name" in error && error.name === "AbortError";
|
|
42688
|
+
}
|
|
41299
42689
|
|
|
41300
42690
|
// src/daemon/process-guard.ts
|
|
41301
42691
|
var installed = false;
|
|
@@ -41609,6 +42999,22 @@ async function startLinkService(options = {}) {
|
|
|
41609
42999
|
await logger.flush();
|
|
41610
43000
|
throw error;
|
|
41611
43001
|
}
|
|
43002
|
+
const tuiGatewayCleanup = await cleanupRegisteredTuiGatewayProcesses({
|
|
43003
|
+
paths,
|
|
43004
|
+
logger
|
|
43005
|
+
}).catch((error) => {
|
|
43006
|
+
void logger.warn("tui_gateway_registry_startup_cleanup_failed", {
|
|
43007
|
+
error: error instanceof Error ? error.message : String(error)
|
|
43008
|
+
});
|
|
43009
|
+
return null;
|
|
43010
|
+
});
|
|
43011
|
+
if (tuiGatewayCleanup) {
|
|
43012
|
+
void logger.info("tui_gateway_registry_startup_cleanup_finished", {
|
|
43013
|
+
stopped: tuiGatewayCleanup.stopped.length,
|
|
43014
|
+
missing: tuiGatewayCleanup.missing.length,
|
|
43015
|
+
skipped: tuiGatewayCleanup.skipped.length
|
|
43016
|
+
});
|
|
43017
|
+
}
|
|
41612
43018
|
server.on("error", (error) => {
|
|
41613
43019
|
void logger.error("service_error", {
|
|
41614
43020
|
port: config.port,
|
|
@@ -41734,6 +43140,7 @@ async function startLinkService(options = {}) {
|
|
|
41734
43140
|
}
|
|
41735
43141
|
closed = true;
|
|
41736
43142
|
relay?.close();
|
|
43143
|
+
await closeTuiGatewayBackends({ paths, logger, cleanupRegistry: true });
|
|
41737
43144
|
await closeServer(server);
|
|
41738
43145
|
await Promise.all([
|
|
41739
43146
|
scheduler.close(),
|
|
@@ -41949,7 +43356,7 @@ async function runDaemonSupervisor(paths = resolveRuntimePaths()) {
|
|
|
41949
43356
|
await mkdir16(paths.logsDir, { recursive: true, mode: 448 });
|
|
41950
43357
|
const log = createRotatingTextLogWriter({
|
|
41951
43358
|
paths,
|
|
41952
|
-
fileName:
|
|
43359
|
+
fileName: path35.basename(daemonLogFile(paths))
|
|
41953
43360
|
});
|
|
41954
43361
|
const scriptPath = currentCliScriptPath();
|
|
41955
43362
|
const write = (chunk) => {
|
|
@@ -41968,7 +43375,7 @@ async function runDaemonSupervisor(paths = resolveRuntimePaths()) {
|
|
|
41968
43375
|
let stopRequested = false;
|
|
41969
43376
|
const forwardStop = () => {
|
|
41970
43377
|
stopRequested = true;
|
|
41971
|
-
if (child?.pid &&
|
|
43378
|
+
if (child?.pid && isProcessAlive4(child.pid)) {
|
|
41972
43379
|
child.kill("SIGTERM");
|
|
41973
43380
|
}
|
|
41974
43381
|
};
|
|
@@ -42082,23 +43489,23 @@ async function stopDaemonProcess(paths = resolveRuntimePaths()) {
|
|
|
42082
43489
|
}
|
|
42083
43490
|
for (let index = 0; index < 20; index += 1) {
|
|
42084
43491
|
await wait2(250);
|
|
42085
|
-
if (!
|
|
43492
|
+
if (!isProcessAlive4(status.pid)) {
|
|
42086
43493
|
break;
|
|
42087
43494
|
}
|
|
42088
43495
|
}
|
|
42089
|
-
if (
|
|
43496
|
+
if (isProcessAlive4(status.pid)) {
|
|
42090
43497
|
try {
|
|
42091
43498
|
process.kill(status.pid, "SIGKILL");
|
|
42092
43499
|
} catch {
|
|
42093
43500
|
}
|
|
42094
43501
|
for (let index = 0; index < 10; index += 1) {
|
|
42095
43502
|
await wait2(250);
|
|
42096
|
-
if (!
|
|
43503
|
+
if (!isProcessAlive4(status.pid)) {
|
|
42097
43504
|
break;
|
|
42098
43505
|
}
|
|
42099
43506
|
}
|
|
42100
43507
|
}
|
|
42101
|
-
if (!
|
|
43508
|
+
if (!isProcessAlive4(status.pid) || !await pidBackedServiceIsReachable(paths)) {
|
|
42102
43509
|
await rm10(pidFilePath(paths), { force: true }).catch(() => void 0);
|
|
42103
43510
|
}
|
|
42104
43511
|
return await getDaemonStatus(paths);
|
|
@@ -42106,7 +43513,7 @@ async function stopDaemonProcess(paths = resolveRuntimePaths()) {
|
|
|
42106
43513
|
async function getDaemonStatus(paths = resolveRuntimePaths()) {
|
|
42107
43514
|
const pidFile = pidFilePath(paths);
|
|
42108
43515
|
const pid = await readPid(pidFile);
|
|
42109
|
-
if (pid && !
|
|
43516
|
+
if (pid && !isProcessAlive4(pid)) {
|
|
42110
43517
|
await rm10(pidFile, { force: true }).catch(() => void 0);
|
|
42111
43518
|
return {
|
|
42112
43519
|
running: false,
|
|
@@ -42136,7 +43543,7 @@ async function readPid(filePath) {
|
|
|
42136
43543
|
const pid = Number.parseInt(raw.trim(), 10);
|
|
42137
43544
|
return Number.isInteger(pid) && pid > 0 ? pid : null;
|
|
42138
43545
|
}
|
|
42139
|
-
function
|
|
43546
|
+
function isProcessAlive4(pid) {
|
|
42140
43547
|
try {
|
|
42141
43548
|
process.kill(pid, 0);
|
|
42142
43549
|
return true;
|
|
@@ -42178,7 +43585,7 @@ function startSupervisorHealthMonitor(paths, child, write) {
|
|
|
42178
43585
|
);
|
|
42179
43586
|
terminateChild(child, forceKillTimer);
|
|
42180
43587
|
forceKillTimer = setTimeout(() => {
|
|
42181
|
-
if (child.pid &&
|
|
43588
|
+
if (child.pid && isProcessAlive4(child.pid)) {
|
|
42182
43589
|
child.kill("SIGKILL");
|
|
42183
43590
|
}
|
|
42184
43591
|
}, SUPERVISOR_CHILD_STOP_TIMEOUT_MS);
|
|
@@ -42196,7 +43603,7 @@ function startSupervisorHealthMonitor(paths, child, write) {
|
|
|
42196
43603
|
closed = true;
|
|
42197
43604
|
terminateChild(child, forceKillTimer);
|
|
42198
43605
|
forceKillTimer = setTimeout(() => {
|
|
42199
|
-
if (child.pid &&
|
|
43606
|
+
if (child.pid && isProcessAlive4(child.pid)) {
|
|
42200
43607
|
child.kill("SIGKILL");
|
|
42201
43608
|
}
|
|
42202
43609
|
}, SUPERVISOR_CHILD_STOP_TIMEOUT_MS);
|
|
@@ -42243,12 +43650,12 @@ function terminateChild(child, previousForceKillTimer) {
|
|
|
42243
43650
|
if (previousForceKillTimer) {
|
|
42244
43651
|
clearTimeout(previousForceKillTimer);
|
|
42245
43652
|
}
|
|
42246
|
-
if (child.pid &&
|
|
43653
|
+
if (child.pid && isProcessAlive4(child.pid)) {
|
|
42247
43654
|
child.kill("SIGTERM");
|
|
42248
43655
|
}
|
|
42249
43656
|
}
|
|
42250
43657
|
function supervisorStopIntentPath(paths) {
|
|
42251
|
-
return
|
|
43658
|
+
return path35.join(paths.runDir, "supervisor-stop-intent.json");
|
|
42252
43659
|
}
|
|
42253
43660
|
async function writeSupervisorStopIntent(paths, pid) {
|
|
42254
43661
|
await mkdir16(paths.runDir, { recursive: true, mode: 448 });
|
|
@@ -42632,7 +44039,7 @@ async function readLinkUpdateStatus(paths) {
|
|
|
42632
44039
|
};
|
|
42633
44040
|
await writeUpdateState2(paths, state);
|
|
42634
44041
|
}
|
|
42635
|
-
if (state?.state === "running" && !runningUpdate2 && !isRecentRunningState3(state) && !
|
|
44042
|
+
if (state?.state === "running" && !runningUpdate2 && !isRecentRunningState3(state) && !isProcessAlive5(state.pid)) {
|
|
42636
44043
|
const reachedTarget = state.target_version && compareSemver3(LINK_VERSION, state.target_version) >= 0;
|
|
42637
44044
|
state = reachedTarget ? {
|
|
42638
44045
|
...state,
|
|
@@ -42811,7 +44218,7 @@ async function buildOfficialInstallCommand(options, targetVersion) {
|
|
|
42811
44218
|
};
|
|
42812
44219
|
}
|
|
42813
44220
|
function buildUnixInstallCommand(installerUrl) {
|
|
42814
|
-
const nodeBinDir =
|
|
44221
|
+
const nodeBinDir = path36.dirname(process.execPath);
|
|
42815
44222
|
const fetchScript = [
|
|
42816
44223
|
quoteShellToken(process.execPath),
|
|
42817
44224
|
"--input-type=module",
|
|
@@ -43081,10 +44488,10 @@ async function readUpdateLogLines2(paths) {
|
|
|
43081
44488
|
);
|
|
43082
44489
|
}
|
|
43083
44490
|
function updateStatePath2(paths) {
|
|
43084
|
-
return
|
|
44491
|
+
return path36.join(paths.runDir, "link-update-state.json");
|
|
43085
44492
|
}
|
|
43086
44493
|
function updateLogPath2(paths) {
|
|
43087
|
-
return
|
|
44494
|
+
return path36.join(paths.logsDir, UPDATE_LOG_FILE2);
|
|
43088
44495
|
}
|
|
43089
44496
|
async function clearUpdateLogFiles2(paths) {
|
|
43090
44497
|
const primary = updateLogPath2(paths);
|
|
@@ -43145,7 +44552,7 @@ function linkUpdateTimeoutError() {
|
|
|
43145
44552
|
LINK_UPDATE_TIMEOUT_MS / 6e4
|
|
43146
44553
|
)} minutes.`;
|
|
43147
44554
|
}
|
|
43148
|
-
function
|
|
44555
|
+
function isProcessAlive5(pid) {
|
|
43149
44556
|
if (!pid || pid <= 0) {
|
|
43150
44557
|
return false;
|
|
43151
44558
|
}
|
|
@@ -43168,7 +44575,7 @@ function readString26(payload, key) {
|
|
|
43168
44575
|
}
|
|
43169
44576
|
|
|
43170
44577
|
// src/pairing/pairing.ts
|
|
43171
|
-
import
|
|
44578
|
+
import path37 from "path";
|
|
43172
44579
|
import { rm as rm12 } from "fs/promises";
|
|
43173
44580
|
|
|
43174
44581
|
// src/relay/bootstrap.ts
|
|
@@ -43508,10 +44915,10 @@ async function loadRequiredIdentity2(paths) {
|
|
|
43508
44915
|
}
|
|
43509
44916
|
return identity;
|
|
43510
44917
|
}
|
|
43511
|
-
async function postServerJson(serverBaseUrl,
|
|
44918
|
+
async function postServerJson(serverBaseUrl, path38, body, options) {
|
|
43512
44919
|
let response;
|
|
43513
44920
|
try {
|
|
43514
|
-
response = await fetch(`${serverBaseUrl.replace(/\/+$/u, "")}${
|
|
44921
|
+
response = await fetch(`${serverBaseUrl.replace(/\/+$/u, "")}${path38}`, {
|
|
43515
44922
|
method: "POST",
|
|
43516
44923
|
headers: {
|
|
43517
44924
|
accept: "application/json",
|
|
@@ -43559,10 +44966,10 @@ function pairingErrorSnapshot(stage, error) {
|
|
|
43559
44966
|
occurred_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
43560
44967
|
};
|
|
43561
44968
|
}
|
|
43562
|
-
async function patchServerJson(serverBaseUrl,
|
|
44969
|
+
async function patchServerJson(serverBaseUrl, path38, token, body, options) {
|
|
43563
44970
|
let response;
|
|
43564
44971
|
try {
|
|
43565
|
-
response = await fetch(`${serverBaseUrl.replace(/\/+$/u, "")}${
|
|
44972
|
+
response = await fetch(`${serverBaseUrl.replace(/\/+$/u, "")}${path38}`, {
|
|
43566
44973
|
method: "PATCH",
|
|
43567
44974
|
headers: {
|
|
43568
44975
|
accept: "application/json",
|
|
@@ -43610,10 +45017,10 @@ function createPairingNetworkError(input) {
|
|
|
43610
45017
|
);
|
|
43611
45018
|
}
|
|
43612
45019
|
function pairingClaimPath(sessionId, paths) {
|
|
43613
|
-
return
|
|
45020
|
+
return path37.join(paths.pairingDir, `${Buffer.from(sessionId).toString("base64url")}.claimed.json`);
|
|
43614
45021
|
}
|
|
43615
45022
|
function pairingSessionPath(sessionId, paths) {
|
|
43616
|
-
return
|
|
45023
|
+
return path37.join(paths.pairingDir, `${Buffer.from(sessionId).toString("base64url")}.json`);
|
|
43617
45024
|
}
|
|
43618
45025
|
function qrPreferredUrls(routes) {
|
|
43619
45026
|
return routes.preferredUrls.filter((url) => !url.includes("/api/v1/relay/links/")).slice(0, 1);
|
|
@@ -44155,7 +45562,7 @@ async function readActiveCronJobCount(profiles, logger) {
|
|
|
44155
45562
|
}, 0);
|
|
44156
45563
|
}
|
|
44157
45564
|
function isActiveCronJob(job) {
|
|
44158
|
-
const enabled =
|
|
45565
|
+
const enabled = readBoolean4(job.enabled) ?? true;
|
|
44159
45566
|
if (!enabled) {
|
|
44160
45567
|
return false;
|
|
44161
45568
|
}
|
|
@@ -44895,6 +46302,9 @@ function registerLiveActivityRoutes(router, options) {
|
|
|
44895
46302
|
status_label: state.statusLabel,
|
|
44896
46303
|
progress_text: state.progressText,
|
|
44897
46304
|
detail_text: state.detailText ?? null,
|
|
46305
|
+
todo_done_count: state.todoDoneCount ?? null,
|
|
46306
|
+
todo_total_count: state.todoTotalCount ?? null,
|
|
46307
|
+
todo_progress_text: state.todoProgressText ?? null,
|
|
44898
46308
|
updated_at: state.updatedAt ?? null
|
|
44899
46309
|
} : null
|
|
44900
46310
|
};
|