@hermespilot/link 0.8.1-beta.1 → 0.8.1-beta.2
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-ZXE3GXSC.js} +1052 -306
- package/dist/cli/index.js +3 -3
- package/dist/http/app.js +1 -1
- package/package.json +1 -1
|
@@ -5,7 +5,7 @@ import Router from "@koa/router";
|
|
|
5
5
|
// src/conversations/conversation-service.ts
|
|
6
6
|
import { EventEmitter } from "events";
|
|
7
7
|
import { createHash as createHash8, randomUUID as randomUUID12 } from "crypto";
|
|
8
|
-
import
|
|
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.2";
|
|
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,6 +10465,227 @@ 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
|
|
10323
10690
|
var CONNECT_TIMEOUT_MS = 15e3;
|
|
10324
10691
|
var REQUEST_TIMEOUT_MS = 12e4;
|
|
@@ -10705,12 +11072,12 @@ async function restartTuiGatewayBackend(input) {
|
|
|
10705
11072
|
const entry = backendEntries.get(profileName);
|
|
10706
11073
|
backendEntries.delete(profileName);
|
|
10707
11074
|
backendStarts.delete(profileName);
|
|
10708
|
-
if (entry
|
|
10709
|
-
entry
|
|
10710
|
-
|
|
10711
|
-
|
|
10712
|
-
|
|
10713
|
-
}
|
|
11075
|
+
if (entry) {
|
|
11076
|
+
await terminateBackendEntry(entry, {
|
|
11077
|
+
paths: input.paths,
|
|
11078
|
+
logger: input.logger,
|
|
11079
|
+
graceMs: BACKEND_RESTART_GRACE_MS
|
|
11080
|
+
});
|
|
10714
11081
|
}
|
|
10715
11082
|
void input.logger?.warn("tui_gateway_backend_restart_requested", {
|
|
10716
11083
|
profile: profileName,
|
|
@@ -10835,23 +11202,58 @@ async function readTuiGatewayStatus(input = {}) {
|
|
|
10835
11202
|
};
|
|
10836
11203
|
}
|
|
10837
11204
|
}
|
|
10838
|
-
function closeTuiGatewayBackends() {
|
|
11205
|
+
async function closeTuiGatewayBackends(input = {}) {
|
|
10839
11206
|
for (const client of clients.values()) {
|
|
10840
11207
|
client.close();
|
|
10841
11208
|
}
|
|
10842
11209
|
clients.clear();
|
|
10843
11210
|
sessionRefs.clear();
|
|
10844
|
-
|
|
10845
|
-
if (!entry.process.killed) {
|
|
10846
|
-
entry.process.kill();
|
|
10847
|
-
}
|
|
10848
|
-
}
|
|
11211
|
+
const entries = [...backendEntries.values()];
|
|
10849
11212
|
backendEntries.clear();
|
|
11213
|
+
await Promise.all(
|
|
11214
|
+
entries.map(
|
|
11215
|
+
(entry) => terminateBackendEntry(entry, {
|
|
11216
|
+
paths: input.paths,
|
|
11217
|
+
logger: input.logger
|
|
11218
|
+
})
|
|
11219
|
+
)
|
|
11220
|
+
);
|
|
11221
|
+
if (input.cleanupRegistry && input.paths) {
|
|
11222
|
+
await cleanupRegisteredTuiGatewayProcesses({
|
|
11223
|
+
paths: input.paths,
|
|
11224
|
+
logger: input.logger
|
|
11225
|
+
});
|
|
11226
|
+
}
|
|
10850
11227
|
if (backendReaper) {
|
|
10851
11228
|
clearInterval(backendReaper);
|
|
10852
11229
|
backendReaper = null;
|
|
10853
11230
|
}
|
|
10854
11231
|
}
|
|
11232
|
+
async function terminateBackendEntry(entry, input = {}) {
|
|
11233
|
+
if (entry.process.exitCode === null && entry.process.signalCode === null) {
|
|
11234
|
+
if (!entry.process.killed) {
|
|
11235
|
+
entry.process.kill("SIGTERM");
|
|
11236
|
+
}
|
|
11237
|
+
await waitForProcessExit(
|
|
11238
|
+
entry.process,
|
|
11239
|
+
input.graceMs ?? BACKEND_RESTART_GRACE_MS
|
|
11240
|
+
);
|
|
11241
|
+
if (entry.process.exitCode === null && entry.process.signalCode === null) {
|
|
11242
|
+
entry.process.kill("SIGKILL");
|
|
11243
|
+
await waitForProcessExit(entry.process, 1e3);
|
|
11244
|
+
}
|
|
11245
|
+
}
|
|
11246
|
+
await unregisterTuiGatewayProcess(input.paths ?? entry.paths, {
|
|
11247
|
+
pid: entry.process.pid ?? void 0,
|
|
11248
|
+
profile: entry.profile
|
|
11249
|
+
}).catch((error) => {
|
|
11250
|
+
void input.logger?.debug("tui_gateway_registry_unregister_failed", {
|
|
11251
|
+
profile: entry.profile,
|
|
11252
|
+
pid: entry.process.pid ?? null,
|
|
11253
|
+
error: error instanceof Error ? error.message : String(error)
|
|
11254
|
+
});
|
|
11255
|
+
});
|
|
11256
|
+
}
|
|
10855
11257
|
function formatSessionModelConfigValue(model, provider, sessionOnly = false) {
|
|
10856
11258
|
const suffix = sessionOnly ? " --session" : "";
|
|
10857
11259
|
const normalizedProvider = provider?.trim();
|
|
@@ -11032,7 +11434,7 @@ async function startTuiGatewayBackend(input) {
|
|
|
11032
11434
|
];
|
|
11033
11435
|
const logWriter = createRotatingTextLogWriter({
|
|
11034
11436
|
paths,
|
|
11035
|
-
fileName:
|
|
11437
|
+
fileName: path10.basename(getGatewayRuntimeLogFile(paths))
|
|
11036
11438
|
});
|
|
11037
11439
|
const hermesBin = resolveHermesBin();
|
|
11038
11440
|
const child = spawn2(hermesBin, args, {
|
|
@@ -11053,6 +11455,16 @@ async function startTuiGatewayBackend(input) {
|
|
|
11053
11455
|
clients.get(profileName)?.close();
|
|
11054
11456
|
clients.delete(profileName);
|
|
11055
11457
|
backendEntries.delete(profileName);
|
|
11458
|
+
void unregisterTuiGatewayProcess(paths, {
|
|
11459
|
+
pid: child.pid ?? void 0,
|
|
11460
|
+
profile: profileName
|
|
11461
|
+
}).catch((error) => {
|
|
11462
|
+
void input.logger?.debug("tui_gateway_registry_unregister_failed", {
|
|
11463
|
+
profile: profileName,
|
|
11464
|
+
pid: child.pid ?? null,
|
|
11465
|
+
error: error instanceof Error ? error.message : String(error)
|
|
11466
|
+
});
|
|
11467
|
+
});
|
|
11056
11468
|
void input.logger?.warn("tui_gateway_backend_exited", {
|
|
11057
11469
|
profile: profileName,
|
|
11058
11470
|
pid: child.pid ?? null,
|
|
@@ -11083,6 +11495,32 @@ async function startTuiGatewayBackend(input) {
|
|
|
11083
11495
|
await logWriter.flush().catch(() => void 0);
|
|
11084
11496
|
throw error;
|
|
11085
11497
|
}
|
|
11498
|
+
if (!child.pid) {
|
|
11499
|
+
child.kill();
|
|
11500
|
+
await logWriter.flush().catch(() => void 0);
|
|
11501
|
+
throw new LinkHttpError(
|
|
11502
|
+
503,
|
|
11503
|
+
"tui_gateway_backend_unavailable",
|
|
11504
|
+
"Hermes dashboard backend started without a process id."
|
|
11505
|
+
);
|
|
11506
|
+
}
|
|
11507
|
+
try {
|
|
11508
|
+
await registerTuiGatewayProcess(paths, {
|
|
11509
|
+
owner: "hermeslink",
|
|
11510
|
+
pid: child.pid,
|
|
11511
|
+
profile: profileName,
|
|
11512
|
+
port,
|
|
11513
|
+
command: hermesBin,
|
|
11514
|
+
args,
|
|
11515
|
+
runtime_home: paths.homeDir,
|
|
11516
|
+
started_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
11517
|
+
});
|
|
11518
|
+
} catch (error) {
|
|
11519
|
+
child.kill();
|
|
11520
|
+
await waitForProcessExit(child, BACKEND_RESTART_GRACE_MS);
|
|
11521
|
+
await logWriter.flush().catch(() => void 0);
|
|
11522
|
+
throw error;
|
|
11523
|
+
}
|
|
11086
11524
|
const entry = {
|
|
11087
11525
|
profile: profileName,
|
|
11088
11526
|
port,
|
|
@@ -11090,6 +11528,7 @@ async function startTuiGatewayBackend(input) {
|
|
|
11090
11528
|
baseUrl,
|
|
11091
11529
|
wsUrl,
|
|
11092
11530
|
process: child,
|
|
11531
|
+
paths,
|
|
11093
11532
|
logPath: logWriter.filePath,
|
|
11094
11533
|
startedAt: Date.now(),
|
|
11095
11534
|
lastActiveAt: Date.now()
|
|
@@ -11527,14 +11966,14 @@ async function readCurrentStoredSessionId(client, runtimeSessionId, logger) {
|
|
|
11527
11966
|
return null;
|
|
11528
11967
|
}
|
|
11529
11968
|
async function waitForProcessExit(child, timeoutMs) {
|
|
11530
|
-
if (child.
|
|
11969
|
+
if (child.exitCode !== null || child.signalCode !== null) {
|
|
11531
11970
|
return;
|
|
11532
11971
|
}
|
|
11533
11972
|
await Promise.race([
|
|
11534
11973
|
new Promise((resolve) => {
|
|
11535
11974
|
child.once("exit", () => resolve());
|
|
11536
11975
|
}),
|
|
11537
|
-
|
|
11976
|
+
delay4(timeoutMs).then(() => void 0)
|
|
11538
11977
|
]);
|
|
11539
11978
|
}
|
|
11540
11979
|
var GatewayEventQueue = class {
|
|
@@ -11969,7 +12408,7 @@ async function waitForDashboardReady(baseUrl, token, logPath, signal) {
|
|
|
11969
12408
|
} catch (error) {
|
|
11970
12409
|
lastError = error;
|
|
11971
12410
|
}
|
|
11972
|
-
await
|
|
12411
|
+
await delay4(250, void 0, { signal }).catch(() => void 0);
|
|
11973
12412
|
}
|
|
11974
12413
|
throw new LinkHttpError(
|
|
11975
12414
|
503,
|
|
@@ -12056,23 +12495,28 @@ function startBackendReaper() {
|
|
|
12056
12495
|
return;
|
|
12057
12496
|
}
|
|
12058
12497
|
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
|
-
}
|
|
12498
|
+
void reapIdleBackends();
|
|
12073
12499
|
}, 6e4);
|
|
12074
12500
|
backendReaper.unref?.();
|
|
12075
12501
|
}
|
|
12502
|
+
async function reapIdleBackends() {
|
|
12503
|
+
const now = Date.now();
|
|
12504
|
+
const staleEntries = [];
|
|
12505
|
+
for (const [profile, entry] of backendEntries) {
|
|
12506
|
+
if (now - entry.lastActiveAt < IDLE_BACKEND_TTL_MS) {
|
|
12507
|
+
continue;
|
|
12508
|
+
}
|
|
12509
|
+
clients.get(profile)?.close();
|
|
12510
|
+
clients.delete(profile);
|
|
12511
|
+
backendEntries.delete(profile);
|
|
12512
|
+
staleEntries.push(entry);
|
|
12513
|
+
}
|
|
12514
|
+
await Promise.all(staleEntries.map((entry) => terminateBackendEntry(entry)));
|
|
12515
|
+
if (backendEntries.size === 0 && backendReaper) {
|
|
12516
|
+
clearInterval(backendReaper);
|
|
12517
|
+
backendReaper = null;
|
|
12518
|
+
}
|
|
12519
|
+
}
|
|
12076
12520
|
function normalizeProfileName2(profileName) {
|
|
12077
12521
|
const value = profileName?.trim() || "default";
|
|
12078
12522
|
if (!PROFILE_NAME_PATTERN2.test(value)) {
|
|
@@ -12867,11 +13311,11 @@ function workspaceNotFound() {
|
|
|
12867
13311
|
// src/conversations/blob-store.ts
|
|
12868
13312
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
12869
13313
|
import { mkdir as mkdir6, readFile as readFile7, readdir as readdir4, rm as rm3, stat as stat6, writeFile } from "fs/promises";
|
|
12870
|
-
import
|
|
13314
|
+
import path12 from "path";
|
|
12871
13315
|
|
|
12872
13316
|
// src/conversations/media.ts
|
|
12873
13317
|
import { createHash as createHash4 } from "crypto";
|
|
12874
|
-
import
|
|
13318
|
+
import path11 from "path";
|
|
12875
13319
|
|
|
12876
13320
|
// src/conversations/delivery-contract.ts
|
|
12877
13321
|
var HERMES_LINK_DELIVERY_TAG_PATTERN = /<hermes_link_delivery>\s*(\{[\s\S]*?\})\s*<\/hermes_link_delivery>/giu;
|
|
@@ -12961,7 +13405,7 @@ function cleanMessageTextParts(message) {
|
|
|
12961
13405
|
}
|
|
12962
13406
|
}
|
|
12963
13407
|
function inferMimeType(filePath) {
|
|
12964
|
-
const extension =
|
|
13408
|
+
const extension = path11.extname(filePath).toLowerCase();
|
|
12965
13409
|
return {
|
|
12966
13410
|
".png": "image/png",
|
|
12967
13411
|
".jpg": "image/jpeg",
|
|
@@ -13085,7 +13529,7 @@ function mediaSourceKey(sourcePath) {
|
|
|
13085
13529
|
return createHash4("sha256").update(resolveMediaSourcePath(sourcePath)).digest("hex").slice(0, 32);
|
|
13086
13530
|
}
|
|
13087
13531
|
function sanitizeFilename(value, fallback) {
|
|
13088
|
-
const base =
|
|
13532
|
+
const base = path11.basename((value ?? "").replace(/[\r\n\t]/gu, " ").trim());
|
|
13089
13533
|
const safe = base.replace(/[/:\\]/gu, "_").slice(0, 200).trim();
|
|
13090
13534
|
return safe || fallback;
|
|
13091
13535
|
}
|
|
@@ -13114,9 +13558,9 @@ function isBlobReferencedByMessages(snapshot, blobId) {
|
|
|
13114
13558
|
}
|
|
13115
13559
|
function resolveMediaSourcePath(sourcePath) {
|
|
13116
13560
|
const trimmed = sourcePath.trim();
|
|
13117
|
-
const expanded = trimmed.startsWith("~/") ?
|
|
13118
|
-
const resolved =
|
|
13119
|
-
if (!
|
|
13561
|
+
const expanded = trimmed.startsWith("~/") ? path11.join(process.env.HOME ?? "", trimmed.slice(2)) : trimmed;
|
|
13562
|
+
const resolved = path11.resolve(expanded);
|
|
13563
|
+
if (!path11.isAbsolute(expanded)) {
|
|
13120
13564
|
throw new LinkHttpError(
|
|
13121
13565
|
400,
|
|
13122
13566
|
"media_source_path_not_absolute",
|
|
@@ -13311,7 +13755,7 @@ function normalizeConversationIds(conversationIds) {
|
|
|
13311
13755
|
}
|
|
13312
13756
|
function blobPath(paths, blobId) {
|
|
13313
13757
|
assertValidBlobId(blobId);
|
|
13314
|
-
return
|
|
13758
|
+
return path12.join(paths.blobsDir, `${blobId}.bin`);
|
|
13315
13759
|
}
|
|
13316
13760
|
async function writeConversationBlob(paths, conversationId, input, options) {
|
|
13317
13761
|
assertValidConversationId(conversationId);
|
|
@@ -13320,7 +13764,7 @@ async function writeConversationBlob(paths, conversationId, input, options) {
|
|
|
13320
13764
|
}
|
|
13321
13765
|
const id = `blob_${randomUUID5().replaceAll("-", "")}`;
|
|
13322
13766
|
const filePath = blobPath(paths, id);
|
|
13323
|
-
await mkdir6(
|
|
13767
|
+
await mkdir6(path12.dirname(filePath), { recursive: true, mode: 448 });
|
|
13324
13768
|
await writeFile(filePath, input.bytes, { mode: 384 });
|
|
13325
13769
|
const blob = {
|
|
13326
13770
|
id,
|
|
@@ -13396,7 +13840,7 @@ async function materializeConversationBlob(paths, conversationId, blobId, manife
|
|
|
13396
13840
|
}
|
|
13397
13841
|
}
|
|
13398
13842
|
const targetDir = conversationAttachmentsDir(paths, conversationId);
|
|
13399
|
-
const targetPath =
|
|
13843
|
+
const targetPath = path12.join(
|
|
13400
13844
|
targetDir,
|
|
13401
13845
|
materializedAttachmentFilename(blobId, manifest.filename ?? blobId)
|
|
13402
13846
|
);
|
|
@@ -13449,7 +13893,7 @@ async function listConversationBlobIds(paths, conversationId) {
|
|
|
13449
13893
|
continue;
|
|
13450
13894
|
}
|
|
13451
13895
|
const manifest = await readJsonFile(
|
|
13452
|
-
|
|
13896
|
+
path12.join(paths.blobsDir, entry.name)
|
|
13453
13897
|
).catch(() => null);
|
|
13454
13898
|
if (manifest?.conversation_ids?.includes(conversationId)) {
|
|
13455
13899
|
blobIds.push(blobId);
|
|
@@ -13459,7 +13903,7 @@ async function listConversationBlobIds(paths, conversationId) {
|
|
|
13459
13903
|
}
|
|
13460
13904
|
function conversationAttachmentsDir(paths, conversationId) {
|
|
13461
13905
|
assertValidConversationId(conversationId);
|
|
13462
|
-
return
|
|
13906
|
+
return path12.join(paths.conversationsDir, conversationId, "attachments");
|
|
13463
13907
|
}
|
|
13464
13908
|
function isNodeError6(error, code) {
|
|
13465
13909
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
@@ -13471,7 +13915,7 @@ import { stat as stat8 } from "fs/promises";
|
|
|
13471
13915
|
// src/hermes/model-provider-disconnects.ts
|
|
13472
13916
|
import { stat as stat7 } from "fs/promises";
|
|
13473
13917
|
import os5 from "os";
|
|
13474
|
-
import
|
|
13918
|
+
import path13 from "path";
|
|
13475
13919
|
async function markAuthBackedModelProviderDisconnected(paths, profileName, providerKey) {
|
|
13476
13920
|
const profileKey = normalizeProfileName3(profileName);
|
|
13477
13921
|
const provider = providerKey.trim();
|
|
@@ -13549,15 +13993,15 @@ function credentialFilesForProvider(profileName, providerKey) {
|
|
|
13549
13993
|
const profileDir = resolveHermesProfileDir(normalizeProfileName3(profileName));
|
|
13550
13994
|
switch (providerKey) {
|
|
13551
13995
|
case "qwen-oauth":
|
|
13552
|
-
return [
|
|
13996
|
+
return [path13.join(os5.homedir(), ".qwen", "oauth_creds.json")];
|
|
13553
13997
|
case "google-gemini-cli":
|
|
13554
|
-
return [
|
|
13998
|
+
return [path13.join(profileDir, "auth", "google_oauth.json")];
|
|
13555
13999
|
default:
|
|
13556
|
-
return [
|
|
14000
|
+
return [path13.join(profileDir, "auth.json")];
|
|
13557
14001
|
}
|
|
13558
14002
|
}
|
|
13559
14003
|
function disconnectedProvidersPath(paths) {
|
|
13560
|
-
return
|
|
14004
|
+
return path13.join(paths.homeDir, "model-provider-disconnects.json");
|
|
13561
14005
|
}
|
|
13562
14006
|
function normalizeStore(value) {
|
|
13563
14007
|
return {
|
|
@@ -14688,7 +15132,7 @@ function commandText(language, zh, en) {
|
|
|
14688
15132
|
|
|
14689
15133
|
// src/conversations/delivery-staging.ts
|
|
14690
15134
|
import { mkdir as mkdir7, rm as rm4 } from "fs/promises";
|
|
14691
|
-
import
|
|
15135
|
+
import path14 from "path";
|
|
14692
15136
|
async function prepareDeliveryStagingRunDir(paths, conversationId, runId) {
|
|
14693
15137
|
const directory = deliveryStagingRunDir(paths, conversationId, runId);
|
|
14694
15138
|
await mkdir7(directory, { recursive: true, mode: 448 });
|
|
@@ -14701,14 +15145,14 @@ async function removeConversationDeliveryStaging(paths, conversationId) {
|
|
|
14701
15145
|
});
|
|
14702
15146
|
}
|
|
14703
15147
|
function deliveryStagingRunDir(paths, conversationId, runId) {
|
|
14704
|
-
return
|
|
15148
|
+
return path14.join(
|
|
14705
15149
|
deliveryStagingConversationDir(paths, conversationId),
|
|
14706
15150
|
safePathSegment(runId, "run")
|
|
14707
15151
|
);
|
|
14708
15152
|
}
|
|
14709
15153
|
function deliveryStagingConversationDir(paths, conversationId) {
|
|
14710
15154
|
assertValidConversationId(conversationId);
|
|
14711
|
-
return
|
|
15155
|
+
return path14.join(paths.conversationsDir, conversationId, "delivery-staging");
|
|
14712
15156
|
}
|
|
14713
15157
|
function safePathSegment(value, fallback) {
|
|
14714
15158
|
const safe = value.trim().replaceAll(/[^a-zA-Z0-9._-]/gu, "_");
|
|
@@ -14718,7 +15162,7 @@ function safePathSegment(value, fallback) {
|
|
|
14718
15162
|
// src/conversations/conversation-archive-plans.ts
|
|
14719
15163
|
import { randomUUID as randomUUID7 } from "crypto";
|
|
14720
15164
|
import { mkdir as mkdir8 } from "fs/promises";
|
|
14721
|
-
import
|
|
15165
|
+
import path15 from "path";
|
|
14722
15166
|
var PLAN_ID_PATTERN = /^archive_[a-f0-9]{32}$/u;
|
|
14723
15167
|
var ConversationArchivePlanStore = class {
|
|
14724
15168
|
constructor(paths) {
|
|
@@ -14761,10 +15205,10 @@ var ConversationArchivePlanStore = class {
|
|
|
14761
15205
|
await writeJsonFile(this.planPath(normalizedPlanId), plan);
|
|
14762
15206
|
}
|
|
14763
15207
|
plansDir() {
|
|
14764
|
-
return
|
|
15208
|
+
return path15.join(this.paths.indexesDir, "conversation-archive-plans");
|
|
14765
15209
|
}
|
|
14766
15210
|
planPath(planId) {
|
|
14767
|
-
return
|
|
15211
|
+
return path15.join(this.plansDir(), `${planId}.json`);
|
|
14768
15212
|
}
|
|
14769
15213
|
};
|
|
14770
15214
|
function normalizePlanId(planId) {
|
|
@@ -14782,7 +15226,7 @@ function normalizePlanId(planId) {
|
|
|
14782
15226
|
// src/conversations/conversation-clear-plans.ts
|
|
14783
15227
|
import { randomUUID as randomUUID8 } from "crypto";
|
|
14784
15228
|
import { mkdir as mkdir9 } from "fs/promises";
|
|
14785
|
-
import
|
|
15229
|
+
import path16 from "path";
|
|
14786
15230
|
var PLAN_ID_PATTERN2 = /^clear_[a-f0-9]{32}$/u;
|
|
14787
15231
|
var ConversationClearPlanStore = class {
|
|
14788
15232
|
constructor(paths) {
|
|
@@ -14826,10 +15270,10 @@ var ConversationClearPlanStore = class {
|
|
|
14826
15270
|
await writeJsonFile(this.planPath(normalizedPlanId), plan);
|
|
14827
15271
|
}
|
|
14828
15272
|
plansDir() {
|
|
14829
|
-
return
|
|
15273
|
+
return path16.join(this.paths.indexesDir, "conversation-clear-plans");
|
|
14830
15274
|
}
|
|
14831
15275
|
planPath(planId) {
|
|
14832
|
-
return
|
|
15276
|
+
return path16.join(this.plansDir(), `${planId}.json`);
|
|
14833
15277
|
}
|
|
14834
15278
|
};
|
|
14835
15279
|
function normalizePlanId2(planId) {
|
|
@@ -15684,14 +16128,14 @@ function readAttachmentWaveform(attachment) {
|
|
|
15684
16128
|
|
|
15685
16129
|
// src/hermes/session-title.ts
|
|
15686
16130
|
import { stat as stat9 } from "fs/promises";
|
|
15687
|
-
import
|
|
16131
|
+
import path17 from "path";
|
|
15688
16132
|
async function readHermesSessionTitle(sessionId, paths, profileName) {
|
|
15689
16133
|
const trimmedSessionId = sessionId.trim();
|
|
15690
16134
|
if (!trimmedSessionId) {
|
|
15691
16135
|
return void 0;
|
|
15692
16136
|
}
|
|
15693
16137
|
const resolvedProfileName = isValidProfileName(profileName) ? profileName : "default";
|
|
15694
|
-
const dbPath =
|
|
16138
|
+
const dbPath = path17.join(
|
|
15695
16139
|
resolveHermesProfileDir(resolvedProfileName),
|
|
15696
16140
|
"state.db"
|
|
15697
16141
|
);
|
|
@@ -15712,7 +16156,7 @@ async function readHermesCompressionTip(sessionId, paths, profileName) {
|
|
|
15712
16156
|
return void 0;
|
|
15713
16157
|
}
|
|
15714
16158
|
const resolvedProfileName = isValidProfileName(profileName) ? profileName : "default";
|
|
15715
|
-
const dbPath =
|
|
16159
|
+
const dbPath = path17.join(
|
|
15716
16160
|
resolveHermesProfileDir(resolvedProfileName),
|
|
15717
16161
|
"state.db"
|
|
15718
16162
|
);
|
|
@@ -15840,12 +16284,12 @@ var ConversationMetadataCoordinator = class {
|
|
|
15840
16284
|
return { ...next, last_event_seq: event.seq, updated_at: event.created_at };
|
|
15841
16285
|
}
|
|
15842
16286
|
scheduleGeneratedTitleRefresh(conversationId) {
|
|
15843
|
-
for (const
|
|
16287
|
+
for (const delay6 of GENERATED_TITLE_RETRY_DELAYS_MS) {
|
|
15844
16288
|
setTimeout(() => {
|
|
15845
16289
|
void this.generateTitleFromFirstRound(conversationId).catch(
|
|
15846
16290
|
() => void 0
|
|
15847
16291
|
);
|
|
15848
|
-
},
|
|
16292
|
+
}, delay6);
|
|
15849
16293
|
}
|
|
15850
16294
|
}
|
|
15851
16295
|
async renameConversation(conversationId, title, input) {
|
|
@@ -16145,7 +16589,7 @@ function stripCompressionTitleSuffix(value) {
|
|
|
16145
16589
|
|
|
16146
16590
|
// src/conversations/history-builder.ts
|
|
16147
16591
|
import { readFile as readFile8, stat as stat10 } from "fs/promises";
|
|
16148
|
-
import
|
|
16592
|
+
import path18 from "path";
|
|
16149
16593
|
var HISTORY_ROLES = /* @__PURE__ */ new Set(["user", "assistant"]);
|
|
16150
16594
|
var HERMES_HISTORY_COLUMNS = [
|
|
16151
16595
|
"role",
|
|
@@ -16217,13 +16661,13 @@ async function readHermesTranscriptHistory(sessionId, profileName) {
|
|
|
16217
16661
|
}
|
|
16218
16662
|
const normalizedProfileName = isValidProfileName2(profileName) ? profileName : "default";
|
|
16219
16663
|
const profileDir = resolveHermesProfileDir(normalizedProfileName);
|
|
16220
|
-
const dbPath =
|
|
16664
|
+
const dbPath = path18.join(profileDir, "state.db");
|
|
16221
16665
|
const sessionsDirConfig = await readHermesSessionsDir(normalizedProfileName).then((value) => ({
|
|
16222
16666
|
sessionsDir: value.sessionsDir,
|
|
16223
16667
|
configured: value.configured,
|
|
16224
16668
|
configError: false
|
|
16225
16669
|
})).catch(() => ({
|
|
16226
|
-
sessionsDir:
|
|
16670
|
+
sessionsDir: path18.join(profileDir, "sessions"),
|
|
16227
16671
|
configured: false,
|
|
16228
16672
|
configError: true
|
|
16229
16673
|
}));
|
|
@@ -16399,8 +16843,8 @@ async function readFirstExistingFile(paths) {
|
|
|
16399
16843
|
}
|
|
16400
16844
|
function candidateTranscriptPaths(sessionsDir, sessionId, extension) {
|
|
16401
16845
|
return [
|
|
16402
|
-
|
|
16403
|
-
|
|
16846
|
+
path18.join(sessionsDir, `session_${sessionId}.${extension}`),
|
|
16847
|
+
path18.join(sessionsDir, `${sessionId}.${extension}`)
|
|
16404
16848
|
];
|
|
16405
16849
|
}
|
|
16406
16850
|
function readHistoryRows(dbPath, sessionId) {
|
|
@@ -18356,6 +18800,10 @@ var APP_TOOL_EVENT_FIELDS_TO_DROP = /* @__PURE__ */ new Set([
|
|
|
18356
18800
|
"message"
|
|
18357
18801
|
]);
|
|
18358
18802
|
var LIFECYCLE_MARKER_FORMAT = "hermes-link-lifecycle-marker";
|
|
18803
|
+
var APP_VISIBLE_LIFECYCLE_MARKER_KINDS = /* @__PURE__ */ new Set([
|
|
18804
|
+
"context_compression",
|
|
18805
|
+
"goal_completion"
|
|
18806
|
+
]);
|
|
18359
18807
|
function projectConversationAgentEvent(event, language = readLanguage(event.payload)) {
|
|
18360
18808
|
if (!isAgentActivityEvent(event.type)) {
|
|
18361
18809
|
return null;
|
|
@@ -18576,7 +19024,7 @@ function retainAppVisibleRaw(value) {
|
|
|
18576
19024
|
return null;
|
|
18577
19025
|
}
|
|
18578
19026
|
const payload = toRecord8(raw.payload);
|
|
18579
|
-
if (payload.kind !== "
|
|
19027
|
+
if (typeof payload.kind !== "string" || !APP_VISIBLE_LIFECYCLE_MARKER_KINDS.has(payload.kind)) {
|
|
18580
19028
|
return null;
|
|
18581
19029
|
}
|
|
18582
19030
|
return {
|
|
@@ -18942,11 +19390,14 @@ var ConversationQueryCoordinator = class {
|
|
|
18942
19390
|
);
|
|
18943
19391
|
}
|
|
18944
19392
|
const startIndex = Math.max(0, endIndex - limit);
|
|
19393
|
+
const events = await this.listEvents(conversationId, 0).catch(() => []);
|
|
18945
19394
|
const messages2 = await this.hydrateAgentEventsForMessages(
|
|
18946
19395
|
conversationId,
|
|
18947
19396
|
snapshot.messages.slice(startIndex, endIndex),
|
|
18948
|
-
snapshot
|
|
19397
|
+
snapshot,
|
|
19398
|
+
events
|
|
18949
19399
|
);
|
|
19400
|
+
const todoSnapshot = latestTodoSnapshotFromEvents(conversationId, events);
|
|
18950
19401
|
return {
|
|
18951
19402
|
messages: messages2,
|
|
18952
19403
|
last_event_seq: manifest.last_event_seq,
|
|
@@ -18964,7 +19415,8 @@ var ConversationQueryCoordinator = class {
|
|
|
18964
19415
|
},
|
|
18965
19416
|
event_stream: buildConversationEventStreamState(snapshot),
|
|
18966
19417
|
input_requests: pendingInputRequests(snapshot),
|
|
18967
|
-
...normalizeConversationGoalState(manifest.command_state?.goal) ? { goal: normalizeConversationGoalState(manifest.command_state?.goal) } : {}
|
|
19418
|
+
...normalizeConversationGoalState(manifest.command_state?.goal) ? { goal: normalizeConversationGoalState(manifest.command_state?.goal) } : {},
|
|
19419
|
+
...todoSnapshot ? { todo_snapshot: todoSnapshot } : {}
|
|
18968
19420
|
};
|
|
18969
19421
|
}
|
|
18970
19422
|
async listEvents(conversationId, after = 0) {
|
|
@@ -18977,12 +19429,11 @@ var ConversationQueryCoordinator = class {
|
|
|
18977
19429
|
profile ?? await readConversationProfileSummary(this.deps.paths, manifest)
|
|
18978
19430
|
);
|
|
18979
19431
|
}
|
|
18980
|
-
async hydrateAgentEventsForMessages(conversationId, messages2, snapshot) {
|
|
19432
|
+
async hydrateAgentEventsForMessages(conversationId, messages2, snapshot, events) {
|
|
18981
19433
|
if (!messages2.some((message) => message.role === "assistant")) {
|
|
18982
19434
|
return messages2;
|
|
18983
19435
|
}
|
|
18984
19436
|
const eventsByMessageId = /* @__PURE__ */ new Map();
|
|
18985
|
-
const events = await this.listEvents(conversationId, 0).catch(() => []);
|
|
18986
19437
|
for (const event of events) {
|
|
18987
19438
|
if (!event.message_id) {
|
|
18988
19439
|
continue;
|
|
@@ -19018,6 +19469,81 @@ var ConversationQueryCoordinator = class {
|
|
|
19018
19469
|
});
|
|
19019
19470
|
}
|
|
19020
19471
|
};
|
|
19472
|
+
function latestTodoSnapshotFromEvents(conversationId, events) {
|
|
19473
|
+
for (let index = events.length - 1; index >= 0; index -= 1) {
|
|
19474
|
+
const snapshot = todoSnapshotFromEvent(conversationId, events[index]);
|
|
19475
|
+
if (snapshot) {
|
|
19476
|
+
return snapshot;
|
|
19477
|
+
}
|
|
19478
|
+
}
|
|
19479
|
+
return null;
|
|
19480
|
+
}
|
|
19481
|
+
function todoSnapshotFromEvent(conversationId, event) {
|
|
19482
|
+
const type = event.type.toLowerCase();
|
|
19483
|
+
if (type !== "tool.completed" && type !== "tool.complete") {
|
|
19484
|
+
return null;
|
|
19485
|
+
}
|
|
19486
|
+
const payload = readRecord(event.payload);
|
|
19487
|
+
const toolName = readToolName(payload)?.toLowerCase();
|
|
19488
|
+
if (toolName !== "todo") {
|
|
19489
|
+
return null;
|
|
19490
|
+
}
|
|
19491
|
+
const todos = readTodoItems(payload.todos) ?? readTodoItems(readRecord(payload.result).todos) ?? readTodoItems(readRecord(payload.output).todos) ?? readTodoItems(readRecord(payload.content).todos);
|
|
19492
|
+
if (!todos || todos.length === 0) {
|
|
19493
|
+
return null;
|
|
19494
|
+
}
|
|
19495
|
+
return {
|
|
19496
|
+
conversation_id: conversationId,
|
|
19497
|
+
...event.run_id ? { run_id: event.run_id } : {},
|
|
19498
|
+
updated_at: event.created_at,
|
|
19499
|
+
items: todos
|
|
19500
|
+
};
|
|
19501
|
+
}
|
|
19502
|
+
function readTodoItems(value) {
|
|
19503
|
+
if (!Array.isArray(value)) {
|
|
19504
|
+
return null;
|
|
19505
|
+
}
|
|
19506
|
+
const items = value.map((item, index) => readTodoItem(item, index)).filter((item) => item !== null);
|
|
19507
|
+
return items.length > 0 ? items : null;
|
|
19508
|
+
}
|
|
19509
|
+
function readTodoItem(value, index) {
|
|
19510
|
+
const record = readRecord(value);
|
|
19511
|
+
const content = readNonEmptyString(record.content) ?? readNonEmptyString(record.title);
|
|
19512
|
+
const status = readTodoStatus(readNonEmptyString(record.status));
|
|
19513
|
+
if (!content || !status) {
|
|
19514
|
+
return null;
|
|
19515
|
+
}
|
|
19516
|
+
return {
|
|
19517
|
+
id: readNonEmptyString(record.id) ?? `${index + 1}`,
|
|
19518
|
+
content,
|
|
19519
|
+
status
|
|
19520
|
+
};
|
|
19521
|
+
}
|
|
19522
|
+
function readTodoStatus(value) {
|
|
19523
|
+
switch (value?.trim().toLowerCase()) {
|
|
19524
|
+
case "pending":
|
|
19525
|
+
return "pending";
|
|
19526
|
+
case "in_progress":
|
|
19527
|
+
case "in-progress":
|
|
19528
|
+
case "running":
|
|
19529
|
+
return "in_progress";
|
|
19530
|
+
case "completed":
|
|
19531
|
+
case "complete":
|
|
19532
|
+
case "done":
|
|
19533
|
+
return "completed";
|
|
19534
|
+
case "cancelled":
|
|
19535
|
+
case "canceled":
|
|
19536
|
+
return "cancelled";
|
|
19537
|
+
default:
|
|
19538
|
+
return null;
|
|
19539
|
+
}
|
|
19540
|
+
}
|
|
19541
|
+
function readToolName(record) {
|
|
19542
|
+
return readNonEmptyString(record.tool_name) ?? readNonEmptyString(record.toolName) ?? readNonEmptyString(record.name) ?? readNonEmptyString(record.tool) ?? readNonEmptyString(readRecord(record.tool).name);
|
|
19543
|
+
}
|
|
19544
|
+
function readRecord(value) {
|
|
19545
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
|
|
19546
|
+
}
|
|
19021
19547
|
function readRunLanguage(snapshot, runId) {
|
|
19022
19548
|
if (!runId) {
|
|
19023
19549
|
return null;
|
|
@@ -19830,7 +20356,7 @@ import {
|
|
|
19830
20356
|
rm as rm5,
|
|
19831
20357
|
writeFile as writeFile2
|
|
19832
20358
|
} from "fs/promises";
|
|
19833
|
-
import
|
|
20359
|
+
import path19 from "path";
|
|
19834
20360
|
var ConversationStore = class {
|
|
19835
20361
|
constructor(paths) {
|
|
19836
20362
|
this.paths = paths;
|
|
@@ -19961,23 +20487,23 @@ var ConversationStore = class {
|
|
|
19961
20487
|
return manifest != null && manifest.status !== "deleted_soft";
|
|
19962
20488
|
}
|
|
19963
20489
|
removeConversationAttachments(conversationId) {
|
|
19964
|
-
return rm5(
|
|
20490
|
+
return rm5(path19.join(this.conversationDir(conversationId), "attachments"), {
|
|
19965
20491
|
recursive: true,
|
|
19966
20492
|
force: true
|
|
19967
20493
|
});
|
|
19968
20494
|
}
|
|
19969
20495
|
conversationDir(conversationId) {
|
|
19970
20496
|
assertValidConversationId(conversationId);
|
|
19971
|
-
return
|
|
20497
|
+
return path19.join(this.paths.conversationsDir, conversationId);
|
|
19972
20498
|
}
|
|
19973
20499
|
manifestPath(conversationId) {
|
|
19974
|
-
return
|
|
20500
|
+
return path19.join(this.conversationDir(conversationId), "manifest.json");
|
|
19975
20501
|
}
|
|
19976
20502
|
snapshotPath(conversationId) {
|
|
19977
|
-
return
|
|
20503
|
+
return path19.join(this.conversationDir(conversationId), "snapshot.json");
|
|
19978
20504
|
}
|
|
19979
20505
|
eventsPath(conversationId) {
|
|
19980
|
-
return
|
|
20506
|
+
return path19.join(this.conversationDir(conversationId), "events.ndjson");
|
|
19981
20507
|
}
|
|
19982
20508
|
};
|
|
19983
20509
|
function createEmptySnapshot2() {
|
|
@@ -19990,11 +20516,11 @@ function isNodeError11(error, code) {
|
|
|
19990
20516
|
// src/conversations/hermes-session-sync.ts
|
|
19991
20517
|
import { randomUUID as randomUUID11 } from "crypto";
|
|
19992
20518
|
import { readdir as readdir7, readFile as readFile11, stat as stat12 } from "fs/promises";
|
|
19993
|
-
import
|
|
20519
|
+
import path21 from "path";
|
|
19994
20520
|
|
|
19995
20521
|
// src/conversations/delivery-import.ts
|
|
19996
20522
|
import { lstat as lstat2, readFile as readFile10, readdir as readdir6, stat as stat11 } from "fs/promises";
|
|
19997
|
-
import
|
|
20523
|
+
import path20 from "path";
|
|
19998
20524
|
var MAX_IMPORTED_BLOB_BYTES = 100 * 1024 * 1024;
|
|
19999
20525
|
var MAX_MEDIA_IMPORT_FAILURES = 20;
|
|
20000
20526
|
var MAX_DELIVERY_FILES = 50;
|
|
@@ -20065,16 +20591,16 @@ var SUPPORTED_DELIVERY_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
20065
20591
|
".m4a"
|
|
20066
20592
|
]);
|
|
20067
20593
|
function resolveDeliveryStagingTarget(paths, stagingDir) {
|
|
20068
|
-
const resolvedDir =
|
|
20069
|
-
const relative =
|
|
20070
|
-
if (!relative || relative.startsWith("..") ||
|
|
20594
|
+
const resolvedDir = path20.resolve(stagingDir);
|
|
20595
|
+
const relative = path20.relative(path20.resolve(paths.conversationsDir), resolvedDir);
|
|
20596
|
+
if (!relative || relative.startsWith("..") || path20.isAbsolute(relative)) {
|
|
20071
20597
|
throw new LinkHttpError(
|
|
20072
20598
|
400,
|
|
20073
20599
|
"delivery_staging_invalid",
|
|
20074
20600
|
"delivery staging directory must be inside Hermes Link conversations"
|
|
20075
20601
|
);
|
|
20076
20602
|
}
|
|
20077
|
-
const segments = relative.split(
|
|
20603
|
+
const segments = relative.split(path20.sep);
|
|
20078
20604
|
if (segments.length !== 3 || segments[1] !== DELIVERY_STAGING_SEGMENT || !segments[0] || !segments[2]) {
|
|
20079
20605
|
throw new LinkHttpError(
|
|
20080
20606
|
400,
|
|
@@ -20110,7 +20636,7 @@ async function collectStagedDeliveryReferences(stagingDir) {
|
|
|
20110
20636
|
return entries.filter((entry) => entry.isFile() && !entry.name.startsWith(".")).filter((entry) => isSupportedDeliveryFilename(entry.name)).sort(
|
|
20111
20637
|
(left, right) => left.name.localeCompare(right.name, "en", { numeric: true })
|
|
20112
20638
|
).slice(0, MAX_DELIVERY_FILES).map((entry) => {
|
|
20113
|
-
const sourcePath =
|
|
20639
|
+
const sourcePath = path20.join(stagingDir, entry.name);
|
|
20114
20640
|
const mime = inferMimeType(sourcePath);
|
|
20115
20641
|
return {
|
|
20116
20642
|
path: sourcePath,
|
|
@@ -20296,7 +20822,7 @@ async function writeBlobFromFile(deps, conversationId, source) {
|
|
|
20296
20822
|
}
|
|
20297
20823
|
return deps.writeBlob(conversationId, {
|
|
20298
20824
|
bytes: await readFile10(sourcePath),
|
|
20299
|
-
filename:
|
|
20825
|
+
filename: path20.basename(sourcePath),
|
|
20300
20826
|
mime: source.mime ?? inferMimeType(sourcePath)
|
|
20301
20827
|
});
|
|
20302
20828
|
}
|
|
@@ -20309,7 +20835,7 @@ function describeMediaImportFailure(reference, sourceKey, error) {
|
|
|
20309
20835
|
};
|
|
20310
20836
|
}
|
|
20311
20837
|
function isSupportedDeliveryFilename(filename) {
|
|
20312
|
-
return SUPPORTED_DELIVERY_EXTENSIONS.has(
|
|
20838
|
+
return SUPPORTED_DELIVERY_EXTENSIONS.has(path20.extname(filename).toLowerCase());
|
|
20313
20839
|
}
|
|
20314
20840
|
function readString10(payload, key) {
|
|
20315
20841
|
const value = payload[key];
|
|
@@ -20367,7 +20893,7 @@ async function syncHermesSessionsIntoConversations(paths, logger, options = {})
|
|
|
20367
20893
|
const candidates = [];
|
|
20368
20894
|
for (const profileName of profileNames) {
|
|
20369
20895
|
const profileDir = resolveHermesProfileDir(profileName);
|
|
20370
|
-
const dbPath =
|
|
20896
|
+
const dbPath = path21.join(profileDir, "state.db");
|
|
20371
20897
|
const sessions = await listProfileSessions(dbPath).catch((error) => {
|
|
20372
20898
|
result.errors.push({
|
|
20373
20899
|
profile: profileName,
|
|
@@ -20483,7 +21009,7 @@ async function syncHermesCronSessionIntoConversations(paths, logger, input) {
|
|
|
20483
21009
|
const knownHermesSessions = await readKnownHermesSessions(store);
|
|
20484
21010
|
const profileName = input.profileName.trim() || DEFAULT_PROFILE_NAME;
|
|
20485
21011
|
const profileDir = resolveHermesProfileDir(profileName);
|
|
20486
|
-
const dbPath =
|
|
21012
|
+
const dbPath = path21.join(profileDir, "state.db");
|
|
20487
21013
|
const sessions = await listProfileSessionsByIdPrefix(
|
|
20488
21014
|
dbPath,
|
|
20489
21015
|
`cron_${jobId}_`
|
|
@@ -21794,8 +22320,8 @@ async function readJsonlMessages(profileName, sessionId) {
|
|
|
21794
22320
|
return [];
|
|
21795
22321
|
}
|
|
21796
22322
|
const profileDir = resolveHermesProfileDir(profileName);
|
|
21797
|
-
const sessionsDir = await readHermesSessionsDir(profileName).then((value) => value.sessionsDir).catch(() =>
|
|
21798
|
-
const transcriptPath =
|
|
22323
|
+
const sessionsDir = await readHermesSessionsDir(profileName).then((value) => value.sessionsDir).catch(() => path21.join(profileDir, "sessions"));
|
|
22324
|
+
const transcriptPath = path21.join(sessionsDir, `${sessionId}.jsonl`);
|
|
21799
22325
|
const raw = await readFile11(transcriptPath, "utf8").catch((error) => {
|
|
21800
22326
|
if (isNodeError13(error, "ENOENT")) {
|
|
21801
22327
|
return "";
|
|
@@ -22165,7 +22691,7 @@ function isNodeError13(error, code) {
|
|
|
22165
22691
|
|
|
22166
22692
|
// src/conversations/delivery-context.ts
|
|
22167
22693
|
import { copyFile, mkdir as mkdir11, stat as stat13 } from "fs/promises";
|
|
22168
|
-
import
|
|
22694
|
+
import path22 from "path";
|
|
22169
22695
|
var ACTIVE_CONTEXTS = /* @__PURE__ */ new Map();
|
|
22170
22696
|
var CONTEXT_TTL_MS = 2 * 60 * 60 * 1e3;
|
|
22171
22697
|
var MAX_DELIVERY_TOOL_FILES = 50;
|
|
@@ -22237,10 +22763,10 @@ function findDeliveryContext(input) {
|
|
|
22237
22763
|
);
|
|
22238
22764
|
}
|
|
22239
22765
|
async function copyFilesIntoDeliveryContext(paths, context, files) {
|
|
22240
|
-
const stagingDir =
|
|
22241
|
-
const conversationsRoot =
|
|
22242
|
-
const relative =
|
|
22243
|
-
if (!relative || relative.startsWith("..") ||
|
|
22766
|
+
const stagingDir = path22.resolve(context.stagingDir);
|
|
22767
|
+
const conversationsRoot = path22.resolve(paths.conversationsDir);
|
|
22768
|
+
const relative = path22.relative(conversationsRoot, stagingDir);
|
|
22769
|
+
if (!relative || relative.startsWith("..") || path22.isAbsolute(relative)) {
|
|
22244
22770
|
throw new LinkHttpError(
|
|
22245
22771
|
400,
|
|
22246
22772
|
"delivery_staging_invalid",
|
|
@@ -22251,13 +22777,13 @@ async function copyFilesIntoDeliveryContext(paths, context, files) {
|
|
|
22251
22777
|
const result = { staged: [], skipped: [] };
|
|
22252
22778
|
const usedNames = /* @__PURE__ */ new Set();
|
|
22253
22779
|
for (const [index, file] of files.slice(0, MAX_DELIVERY_TOOL_FILES).entries()) {
|
|
22254
|
-
const sourcePath =
|
|
22780
|
+
const sourcePath = path22.resolve(file.path);
|
|
22255
22781
|
let baseName = sanitizeFilename(
|
|
22256
|
-
file.caption ||
|
|
22782
|
+
file.caption || path22.basename(sourcePath),
|
|
22257
22783
|
`attachment-${index + 1}`
|
|
22258
22784
|
);
|
|
22259
|
-
const sourceExtension =
|
|
22260
|
-
if (!
|
|
22785
|
+
const sourceExtension = path22.extname(sourcePath);
|
|
22786
|
+
if (!path22.extname(baseName) && sourceExtension) {
|
|
22261
22787
|
baseName = `${baseName}${sourceExtension}`;
|
|
22262
22788
|
}
|
|
22263
22789
|
const filename = uniqueStagingFilename(
|
|
@@ -22284,7 +22810,7 @@ async function copyFilesIntoDeliveryContext(paths, context, files) {
|
|
|
22284
22810
|
});
|
|
22285
22811
|
continue;
|
|
22286
22812
|
}
|
|
22287
|
-
const targetPath =
|
|
22813
|
+
const targetPath = path22.join(stagingDir, filename);
|
|
22288
22814
|
await copyFile(sourcePath, targetPath);
|
|
22289
22815
|
result.staged.push({
|
|
22290
22816
|
source_path: sourcePath,
|
|
@@ -22327,7 +22853,7 @@ function normalizeDeliveryFileInputs(value) {
|
|
|
22327
22853
|
});
|
|
22328
22854
|
}
|
|
22329
22855
|
function uniqueStagingFilename(filename, usedNames) {
|
|
22330
|
-
const extension =
|
|
22856
|
+
const extension = path22.extname(filename);
|
|
22331
22857
|
const stem = filename.slice(0, filename.length - extension.length);
|
|
22332
22858
|
let candidate = filename;
|
|
22333
22859
|
let suffix = 2;
|
|
@@ -22828,10 +23354,10 @@ function parseHermesApiCapabilities(payload) {
|
|
|
22828
23354
|
sessionKeyHeader: readString13(features, "session_key_header")
|
|
22829
23355
|
};
|
|
22830
23356
|
}
|
|
22831
|
-
async function callHermesApi(
|
|
23357
|
+
async function callHermesApi(path38, init, options) {
|
|
22832
23358
|
const method = init.method ?? "GET";
|
|
22833
23359
|
const startedAt = Date.now();
|
|
22834
|
-
void options.logger?.debug("hermes_api_request_started", { method, path:
|
|
23360
|
+
void options.logger?.debug("hermes_api_request_started", { method, path: path38 });
|
|
22835
23361
|
const availability = await ensureHermesApiServerAvailable({
|
|
22836
23362
|
fetchImpl: options.fetchImpl,
|
|
22837
23363
|
logger: options.logger,
|
|
@@ -22840,7 +23366,7 @@ async function callHermesApi(path37, init, options) {
|
|
|
22840
23366
|
});
|
|
22841
23367
|
let config = availability.configResult.apiServer;
|
|
22842
23368
|
const fetcher = options.fetchImpl ?? fetch;
|
|
22843
|
-
const request = () => fetchHermesApi(fetcher, config,
|
|
23369
|
+
const request = () => fetchHermesApi(fetcher, config, path38, init, options);
|
|
22844
23370
|
let response;
|
|
22845
23371
|
try {
|
|
22846
23372
|
response = await request();
|
|
@@ -22848,7 +23374,7 @@ async function callHermesApi(path37, init, options) {
|
|
|
22848
23374
|
logHermesApiError(
|
|
22849
23375
|
options.logger,
|
|
22850
23376
|
method,
|
|
22851
|
-
|
|
23377
|
+
path38,
|
|
22852
23378
|
options.profileName,
|
|
22853
23379
|
startedAt,
|
|
22854
23380
|
error
|
|
@@ -22859,7 +23385,7 @@ async function callHermesApi(path37, init, options) {
|
|
|
22859
23385
|
logHermesApiResponse(
|
|
22860
23386
|
options.logger,
|
|
22861
23387
|
method,
|
|
22862
|
-
|
|
23388
|
+
path38,
|
|
22863
23389
|
options.profileName,
|
|
22864
23390
|
startedAt,
|
|
22865
23391
|
response
|
|
@@ -22868,7 +23394,7 @@ async function callHermesApi(path37, init, options) {
|
|
|
22868
23394
|
}
|
|
22869
23395
|
void options.logger?.warn("hermes_api_request_retrying_after_401", {
|
|
22870
23396
|
method,
|
|
22871
|
-
path:
|
|
23397
|
+
path: path38,
|
|
22872
23398
|
profile: options.profileName ?? "default",
|
|
22873
23399
|
port: config.port ?? null,
|
|
22874
23400
|
duration_ms: Date.now() - startedAt
|
|
@@ -22887,7 +23413,7 @@ async function callHermesApi(path37, init, options) {
|
|
|
22887
23413
|
logHermesApiError(
|
|
22888
23414
|
options.logger,
|
|
22889
23415
|
method,
|
|
22890
|
-
|
|
23416
|
+
path38,
|
|
22891
23417
|
options.profileName,
|
|
22892
23418
|
startedAt,
|
|
22893
23419
|
error
|
|
@@ -22897,7 +23423,7 @@ async function callHermesApi(path37, init, options) {
|
|
|
22897
23423
|
logHermesApiResponse(
|
|
22898
23424
|
options.logger,
|
|
22899
23425
|
method,
|
|
22900
|
-
|
|
23426
|
+
path38,
|
|
22901
23427
|
options.profileName,
|
|
22902
23428
|
startedAt,
|
|
22903
23429
|
response
|
|
@@ -22907,7 +23433,7 @@ async function callHermesApi(path37, init, options) {
|
|
|
22907
23433
|
}
|
|
22908
23434
|
void options.logger?.warn("hermes_api_request_repairing_after_401", {
|
|
22909
23435
|
method,
|
|
22910
|
-
path:
|
|
23436
|
+
path: path38,
|
|
22911
23437
|
profile: options.profileName ?? "default",
|
|
22912
23438
|
port: config.port ?? null,
|
|
22913
23439
|
duration_ms: Date.now() - startedAt
|
|
@@ -22928,7 +23454,7 @@ async function callHermesApi(path37, init, options) {
|
|
|
22928
23454
|
logHermesApiError(
|
|
22929
23455
|
options.logger,
|
|
22930
23456
|
method,
|
|
22931
|
-
|
|
23457
|
+
path38,
|
|
22932
23458
|
options.profileName,
|
|
22933
23459
|
startedAt,
|
|
22934
23460
|
error
|
|
@@ -22938,21 +23464,21 @@ async function callHermesApi(path37, init, options) {
|
|
|
22938
23464
|
logHermesApiResponse(
|
|
22939
23465
|
options.logger,
|
|
22940
23466
|
method,
|
|
22941
|
-
|
|
23467
|
+
path38,
|
|
22942
23468
|
options.profileName,
|
|
22943
23469
|
startedAt,
|
|
22944
23470
|
response
|
|
22945
23471
|
);
|
|
22946
23472
|
return response;
|
|
22947
23473
|
}
|
|
22948
|
-
async function fetchHermesApi(fetcher, config,
|
|
23474
|
+
async function fetchHermesApi(fetcher, config, path38, init, options) {
|
|
22949
23475
|
const headers = new Headers(init.headers);
|
|
22950
23476
|
headers.set("accept", headers.get("accept") ?? "application/json");
|
|
22951
23477
|
if (config.key) {
|
|
22952
23478
|
headers.set("x-api-key", config.key);
|
|
22953
23479
|
headers.set("authorization", `Bearer ${config.key}`);
|
|
22954
23480
|
}
|
|
22955
|
-
return await fetcher(`http://127.0.0.1:${config.port}${
|
|
23481
|
+
return await fetcher(`http://127.0.0.1:${config.port}${path38}`, {
|
|
22956
23482
|
...init,
|
|
22957
23483
|
headers
|
|
22958
23484
|
}).catch((error) => {
|
|
@@ -22961,10 +23487,10 @@ async function fetchHermesApi(fetcher, config, path37, init, options) {
|
|
|
22961
23487
|
}
|
|
22962
23488
|
void options.logger?.warn("hermes_api_server_connect_failed", {
|
|
22963
23489
|
method: String(init.method ?? "GET").toUpperCase(),
|
|
22964
|
-
path:
|
|
23490
|
+
path: path38,
|
|
22965
23491
|
profile: options.profileName ?? "default",
|
|
22966
23492
|
port: config.port ?? null,
|
|
22967
|
-
url: `http://127.0.0.1:${config.port}${
|
|
23493
|
+
url: `http://127.0.0.1:${config.port}${path38}`,
|
|
22968
23494
|
error: error instanceof Error ? error.message : String(error)
|
|
22969
23495
|
});
|
|
22970
23496
|
throw new LinkHttpError(
|
|
@@ -22974,10 +23500,10 @@ async function fetchHermesApi(fetcher, config, path37, init, options) {
|
|
|
22974
23500
|
);
|
|
22975
23501
|
});
|
|
22976
23502
|
}
|
|
22977
|
-
function logHermesApiResponse(logger, method,
|
|
23503
|
+
function logHermesApiResponse(logger, method, path38, profileName, startedAt, response) {
|
|
22978
23504
|
const fields = {
|
|
22979
23505
|
method,
|
|
22980
|
-
path:
|
|
23506
|
+
path: path38,
|
|
22981
23507
|
profile: profileName ?? "default",
|
|
22982
23508
|
status: response.status,
|
|
22983
23509
|
duration_ms: Date.now() - startedAt
|
|
@@ -22998,10 +23524,10 @@ async function logHermesApiFailureResponse(logger, fields, response) {
|
|
|
22998
23524
|
...upstreamError ? { upstream_error: upstreamError } : {}
|
|
22999
23525
|
});
|
|
23000
23526
|
}
|
|
23001
|
-
function logHermesApiError(logger, method,
|
|
23527
|
+
function logHermesApiError(logger, method, path38, profileName, startedAt, error) {
|
|
23002
23528
|
void logger?.warn("hermes_api_request_failed", {
|
|
23003
23529
|
method,
|
|
23004
|
-
path:
|
|
23530
|
+
path: path38,
|
|
23005
23531
|
profile: profileName ?? "default",
|
|
23006
23532
|
duration_ms: Date.now() - startedAt,
|
|
23007
23533
|
...error instanceof LinkHttpError ? { status: error.status, code: error.code } : {},
|
|
@@ -23068,12 +23594,12 @@ function readBoolean2(payload, key) {
|
|
|
23068
23594
|
}
|
|
23069
23595
|
|
|
23070
23596
|
// src/hermes/stt.ts
|
|
23071
|
-
import { execFile as
|
|
23597
|
+
import { execFile as execFile4 } from "child_process";
|
|
23072
23598
|
import { access as access2, readFile as readFile12, stat as stat14 } from "fs/promises";
|
|
23073
23599
|
import os6 from "os";
|
|
23074
|
-
import
|
|
23075
|
-
import { promisify as
|
|
23076
|
-
var
|
|
23600
|
+
import path23 from "path";
|
|
23601
|
+
import { promisify as promisify4 } from "util";
|
|
23602
|
+
var execFileAsync4 = promisify4(execFile4);
|
|
23077
23603
|
var STT_RESULT_PREFIX = "__HERMES_LINK_STT__";
|
|
23078
23604
|
var STT_TIMEOUT_MS = 18e4;
|
|
23079
23605
|
var STT_MAX_BUFFER_BYTES = 2 * 1024 * 1024;
|
|
@@ -23106,7 +23632,7 @@ async function transcribeAudioWithHermesProfile(input) {
|
|
|
23106
23632
|
let stdout = "";
|
|
23107
23633
|
let stderr = "";
|
|
23108
23634
|
try {
|
|
23109
|
-
const output = await
|
|
23635
|
+
const output = await execFileAsync4(
|
|
23110
23636
|
python.command,
|
|
23111
23637
|
[...python.args, "-c", script, input.audioPath],
|
|
23112
23638
|
{
|
|
@@ -23166,7 +23692,7 @@ async function buildHermesSttEnv(profileName, hermesSourceRoot) {
|
|
|
23166
23692
|
};
|
|
23167
23693
|
const sourceRoot = hermesSourceRoot ?? await findDevHermesAgentSource();
|
|
23168
23694
|
if (sourceRoot) {
|
|
23169
|
-
env.PYTHONPATH = [sourceRoot, env.PYTHONPATH].filter(Boolean).join(
|
|
23695
|
+
env.PYTHONPATH = [sourceRoot, env.PYTHONPATH].filter(Boolean).join(path23.delimiter);
|
|
23170
23696
|
}
|
|
23171
23697
|
return env;
|
|
23172
23698
|
}
|
|
@@ -23261,14 +23787,14 @@ async function resolveHermesPythonRuntime() {
|
|
|
23261
23787
|
};
|
|
23262
23788
|
}
|
|
23263
23789
|
async function resolveExecutablePath(command) {
|
|
23264
|
-
if (
|
|
23790
|
+
if (path23.isAbsolute(command)) {
|
|
23265
23791
|
return await isExecutableFile2(command) ? command : null;
|
|
23266
23792
|
}
|
|
23267
23793
|
const pathEnv = process.env.PATH ?? "";
|
|
23268
23794
|
const extensions = process.platform === "win32" ? (process.env.PATHEXT ?? ".EXE;.CMD;.BAT").split(";") : [""];
|
|
23269
|
-
for (const dir of pathEnv.split(
|
|
23795
|
+
for (const dir of pathEnv.split(path23.delimiter)) {
|
|
23270
23796
|
for (const extension of extensions) {
|
|
23271
|
-
const candidate =
|
|
23797
|
+
const candidate = path23.join(dir, `${command}${extension}`);
|
|
23272
23798
|
if (await isExecutableFile2(candidate)) {
|
|
23273
23799
|
return candidate;
|
|
23274
23800
|
}
|
|
@@ -23290,11 +23816,11 @@ async function isExecutableFile2(filePath) {
|
|
|
23290
23816
|
}
|
|
23291
23817
|
async function findHermesVenvPython(sourceRoot) {
|
|
23292
23818
|
const candidates = process.platform === "win32" ? [
|
|
23293
|
-
|
|
23294
|
-
|
|
23819
|
+
path23.join(sourceRoot, "venv", "Scripts", "python.exe"),
|
|
23820
|
+
path23.join(sourceRoot, ".venv", "Scripts", "python.exe")
|
|
23295
23821
|
] : [
|
|
23296
|
-
|
|
23297
|
-
|
|
23822
|
+
path23.join(sourceRoot, "venv", "bin", "python"),
|
|
23823
|
+
path23.join(sourceRoot, ".venv", "bin", "python")
|
|
23298
23824
|
];
|
|
23299
23825
|
for (const candidate of candidates) {
|
|
23300
23826
|
if (await isExecutableFile2(candidate)) {
|
|
@@ -23323,8 +23849,8 @@ function shebangToPythonCommand(shebang) {
|
|
|
23323
23849
|
}
|
|
23324
23850
|
async function findDevHermesAgentSource() {
|
|
23325
23851
|
const candidates = [
|
|
23326
|
-
|
|
23327
|
-
|
|
23852
|
+
path23.resolve(process.cwd(), "reference/hermes-agent"),
|
|
23853
|
+
path23.resolve(process.cwd(), "../../reference/hermes-agent")
|
|
23328
23854
|
];
|
|
23329
23855
|
for (const candidate of candidates) {
|
|
23330
23856
|
if (await isHermesAgentSourceRoot(candidate)) {
|
|
@@ -23340,7 +23866,7 @@ async function readHermesLauncherTarget(filePath) {
|
|
|
23340
23866
|
line
|
|
23341
23867
|
);
|
|
23342
23868
|
const rawTarget = quoted?.groups?.target ?? /^\s*exec\s+(?<target>\S+)\s+(?:"\$@"|'\$@')/.exec(line)?.groups?.target;
|
|
23343
|
-
if (!rawTarget || !
|
|
23869
|
+
if (!rawTarget || !path23.isAbsolute(rawTarget)) {
|
|
23344
23870
|
continue;
|
|
23345
23871
|
}
|
|
23346
23872
|
if (await isExecutableFile2(rawTarget)) {
|
|
@@ -23370,12 +23896,12 @@ async function resolveHermesEntrypointRuntime(entrypointPath) {
|
|
|
23370
23896
|
return null;
|
|
23371
23897
|
}
|
|
23372
23898
|
async function findHermesSourceRoot(executablePath) {
|
|
23373
|
-
let cursor =
|
|
23899
|
+
let cursor = path23.dirname(path23.resolve(executablePath));
|
|
23374
23900
|
for (let index = 0; index < 6; index += 1) {
|
|
23375
23901
|
if (await isHermesAgentSourceRoot(cursor)) {
|
|
23376
23902
|
return cursor;
|
|
23377
23903
|
}
|
|
23378
|
-
const parent =
|
|
23904
|
+
const parent = path23.dirname(cursor);
|
|
23379
23905
|
if (parent === cursor) {
|
|
23380
23906
|
break;
|
|
23381
23907
|
}
|
|
@@ -23386,14 +23912,14 @@ async function findHermesSourceRoot(executablePath) {
|
|
|
23386
23912
|
function hermesSourceRootCandidates() {
|
|
23387
23913
|
const candidates = [
|
|
23388
23914
|
process.env.HERMES_PYTHON_SRC_ROOT?.trim(),
|
|
23389
|
-
|
|
23915
|
+
path23.join(os6.homedir(), ".hermes", "hermes-agent"),
|
|
23390
23916
|
"/usr/local/lib/hermes-agent",
|
|
23391
23917
|
"/opt/hermes"
|
|
23392
23918
|
].filter((candidate) => Boolean(candidate));
|
|
23393
23919
|
if (process.platform === "win32") {
|
|
23394
23920
|
const localAppData = process.env.LOCALAPPDATA?.trim();
|
|
23395
23921
|
if (localAppData) {
|
|
23396
|
-
candidates.unshift(
|
|
23922
|
+
candidates.unshift(path23.join(localAppData, "hermes", "hermes-agent"));
|
|
23397
23923
|
}
|
|
23398
23924
|
}
|
|
23399
23925
|
return candidates;
|
|
@@ -23403,7 +23929,7 @@ async function findExplicitHermesSourceRoot() {
|
|
|
23403
23929
|
return explicit && await isHermesAgentSourceRoot(explicit) ? explicit : null;
|
|
23404
23930
|
}
|
|
23405
23931
|
async function isHermesAgentSourceRoot(candidate) {
|
|
23406
|
-
return await isDirectory(candidate) && await isDirectory(
|
|
23932
|
+
return await isDirectory(candidate) && await isDirectory(path23.join(candidate, "tools")) && await isDirectory(path23.join(candidate, "hermes_cli"));
|
|
23407
23933
|
}
|
|
23408
23934
|
async function isDirectory(candidate) {
|
|
23409
23935
|
return stat14(candidate).then((info) => info.isDirectory()).catch(() => false);
|
|
@@ -23415,15 +23941,15 @@ function compactProcessOutput(value) {
|
|
|
23415
23941
|
|
|
23416
23942
|
// src/hermes/usage-probe.ts
|
|
23417
23943
|
import { open as open3, readFile as readFile14, rm as rm6, stat as stat16 } from "fs/promises";
|
|
23418
|
-
import
|
|
23944
|
+
import path25 from "path";
|
|
23419
23945
|
import YAML3 from "yaml";
|
|
23420
23946
|
|
|
23421
23947
|
// src/hermes/profiles.ts
|
|
23422
|
-
import { execFile as
|
|
23948
|
+
import { execFile as execFile5 } from "child_process";
|
|
23423
23949
|
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
|
|
23950
|
+
import path24 from "path";
|
|
23951
|
+
import { setTimeout as delay5 } from "timers/promises";
|
|
23952
|
+
import { promisify as promisify5 } from "util";
|
|
23427
23953
|
import YAML2 from "yaml";
|
|
23428
23954
|
var DEFAULT_PROFILE = "default";
|
|
23429
23955
|
var PROFILE_NAME_PATTERN5 = /^[a-zA-Z0-9._-]{1,64}$/;
|
|
@@ -23431,7 +23957,7 @@ var PROFILE_DELETE_TIMEOUT_MS = 3e4;
|
|
|
23431
23957
|
var PROFILE_GATEWAY_STOP_TIMEOUT_MS = 3e3;
|
|
23432
23958
|
var PROFILE_DELETE_STABLE_ABSENCE_MS = 1200;
|
|
23433
23959
|
var PROFILE_DELETE_VERIFY_INTERVAL_MS = 150;
|
|
23434
|
-
var
|
|
23960
|
+
var execFileAsync5 = promisify5(execFile5);
|
|
23435
23961
|
async function listHermesProfiles(paths = resolveRuntimePaths()) {
|
|
23436
23962
|
const profiles = /* @__PURE__ */ new Map();
|
|
23437
23963
|
if (await hasDefaultProfileConfigSource()) {
|
|
@@ -23564,7 +24090,7 @@ async function readHermesProfileCapabilities(name) {
|
|
|
23564
24090
|
return {
|
|
23565
24091
|
defaultModel: listedModels?.defaultModel ?? null,
|
|
23566
24092
|
modelCount: listedModels?.models.length ?? 0,
|
|
23567
|
-
skillCount: await countSkills(
|
|
24093
|
+
skillCount: await countSkills(path24.join(profileDir, "skills")).catch(
|
|
23568
24094
|
() => 0
|
|
23569
24095
|
),
|
|
23570
24096
|
toolCount: await countConfiguredTools(name).catch(() => 0)
|
|
@@ -23625,11 +24151,11 @@ async function hasDefaultProfileConfigSource() {
|
|
|
23625
24151
|
if (!profileStat?.isDirectory()) {
|
|
23626
24152
|
return false;
|
|
23627
24153
|
}
|
|
23628
|
-
return await pathExists(resolveHermesConfigPath(DEFAULT_PROFILE)) || await pathExists(
|
|
24154
|
+
return await pathExists(resolveHermesConfigPath(DEFAULT_PROFILE)) || await pathExists(path24.join(profilePath, ".env"));
|
|
23629
24155
|
}
|
|
23630
24156
|
async function deleteHermesProfileWithCli(name) {
|
|
23631
24157
|
try {
|
|
23632
|
-
await
|
|
24158
|
+
await execFileAsync5(resolveHermesBin(), ["profile", "delete", name, "--yes"], {
|
|
23633
24159
|
timeout: PROFILE_DELETE_TIMEOUT_MS,
|
|
23634
24160
|
windowsHide: true
|
|
23635
24161
|
});
|
|
@@ -23688,7 +24214,7 @@ async function findHermesGatewayProcessIdsForProfile(name) {
|
|
|
23688
24214
|
return [];
|
|
23689
24215
|
}
|
|
23690
24216
|
try {
|
|
23691
|
-
const output = await
|
|
24217
|
+
const output = await execFileAsync5("ps", ["-axo", "pid=,command="], {
|
|
23692
24218
|
timeout: 3e3,
|
|
23693
24219
|
windowsHide: true
|
|
23694
24220
|
});
|
|
@@ -23728,7 +24254,7 @@ async function waitForProcessesToExit(pids, timeoutMs) {
|
|
|
23728
24254
|
const deadline = Date.now() + timeoutMs;
|
|
23729
24255
|
let remaining = pids.filter(isProcessRunning);
|
|
23730
24256
|
while (remaining.length > 0 && Date.now() < deadline) {
|
|
23731
|
-
await
|
|
24257
|
+
await delay5(100);
|
|
23732
24258
|
remaining = remaining.filter(isProcessRunning);
|
|
23733
24259
|
}
|
|
23734
24260
|
return remaining;
|
|
@@ -23747,7 +24273,7 @@ async function waitForProfilePathToRemainAbsent(profilePath) {
|
|
|
23747
24273
|
if (await pathExists(profilePath)) {
|
|
23748
24274
|
return false;
|
|
23749
24275
|
}
|
|
23750
|
-
await
|
|
24276
|
+
await delay5(PROFILE_DELETE_VERIFY_INTERVAL_MS);
|
|
23751
24277
|
}
|
|
23752
24278
|
return !await pathExists(profilePath);
|
|
23753
24279
|
}
|
|
@@ -23795,7 +24321,7 @@ async function countSkills(root) {
|
|
|
23795
24321
|
);
|
|
23796
24322
|
let count = 0;
|
|
23797
24323
|
for (const entry of entries) {
|
|
23798
|
-
const entryPath =
|
|
24324
|
+
const entryPath = path24.join(root, entry.name);
|
|
23799
24325
|
if (entry.name === ".git" || entry.name === ".hub") {
|
|
23800
24326
|
continue;
|
|
23801
24327
|
}
|
|
@@ -23888,7 +24414,7 @@ async function ensureHermesUsageProbeForProfile(profileName, options = {}) {
|
|
|
23888
24414
|
const normalizedProfile = normalizeProfileName6(profileName);
|
|
23889
24415
|
const profilePath = resolveHermesProfileDir(normalizedProfile);
|
|
23890
24416
|
const configPath = resolveHermesConfigPath(normalizedProfile);
|
|
23891
|
-
const pluginPath =
|
|
24417
|
+
const pluginPath = path25.join(
|
|
23892
24418
|
profilePath,
|
|
23893
24419
|
"plugins",
|
|
23894
24420
|
HERMES_USAGE_PROBE_PLUGIN_KEY
|
|
@@ -23914,7 +24440,7 @@ async function ensureHermesUsageProbeForProfile(profileName, options = {}) {
|
|
|
23914
24440
|
return { ...base(), skipped: true };
|
|
23915
24441
|
}
|
|
23916
24442
|
try {
|
|
23917
|
-
await ensureDirectoryWithInheritedMetadata(
|
|
24443
|
+
await ensureDirectoryWithInheritedMetadata(path25.dirname(eventsPath), 448);
|
|
23918
24444
|
const state = await readUsageProbeState(paths);
|
|
23919
24445
|
const pluginState = state.profiles[normalizedProfile];
|
|
23920
24446
|
const currentConfig = await readUsageProbeConfigStatus({
|
|
@@ -24047,7 +24573,7 @@ async function findHermesUsageProbeEventForRun(input) {
|
|
|
24047
24573
|
return aggregateUsageProbeEvents(events.sort(compareUsageProbeEventsByTime));
|
|
24048
24574
|
}
|
|
24049
24575
|
function resolveUsageProbeEventsPath(paths = resolveRuntimePaths(), profileName = "default") {
|
|
24050
|
-
return
|
|
24576
|
+
return path25.join(
|
|
24051
24577
|
paths.homeDir,
|
|
24052
24578
|
USAGE_PROBE_DIR,
|
|
24053
24579
|
safeProfileSegment(profileName),
|
|
@@ -24071,8 +24597,8 @@ function summarizeUsageProbeEnsure(result) {
|
|
|
24071
24597
|
};
|
|
24072
24598
|
}
|
|
24073
24599
|
async function writeUsageProbePlugin(input) {
|
|
24074
|
-
const manifestPath =
|
|
24075
|
-
const initPath =
|
|
24600
|
+
const manifestPath = path25.join(input.pluginPath, "plugin.yaml");
|
|
24601
|
+
const initPath = path25.join(input.pluginPath, "__init__.py");
|
|
24076
24602
|
const manifest = usageProbeManifest();
|
|
24077
24603
|
const source = usageProbePythonSource(
|
|
24078
24604
|
input.profileName,
|
|
@@ -24419,9 +24945,9 @@ async function disableUsageProbeAfterActivationFailure(input) {
|
|
|
24419
24945
|
});
|
|
24420
24946
|
}
|
|
24421
24947
|
async function cleanupLegacyUsageProbePlugin(profilePath) {
|
|
24422
|
-
const legacyPath =
|
|
24948
|
+
const legacyPath = path25.join(profilePath, "plugins", LEGACY_PLUGIN_KEY);
|
|
24423
24949
|
const [manifest, init] = await Promise.all([
|
|
24424
|
-
readFile14(
|
|
24950
|
+
readFile14(path25.join(legacyPath, "plugin.yaml"), "utf8").catch(
|
|
24425
24951
|
(error) => {
|
|
24426
24952
|
if (isNodeError16(error, "ENOENT")) {
|
|
24427
24953
|
return null;
|
|
@@ -24429,7 +24955,7 @@ async function cleanupLegacyUsageProbePlugin(profilePath) {
|
|
|
24429
24955
|
throw error;
|
|
24430
24956
|
}
|
|
24431
24957
|
),
|
|
24432
|
-
readFile14(
|
|
24958
|
+
readFile14(path25.join(legacyPath, "__init__.py"), "utf8").catch(
|
|
24433
24959
|
(error) => {
|
|
24434
24960
|
if (isNodeError16(error, "ENOENT")) {
|
|
24435
24961
|
return null;
|
|
@@ -24478,7 +25004,7 @@ async function rememberUsageProbeState(paths, profileName, patch) {
|
|
|
24478
25004
|
}));
|
|
24479
25005
|
}
|
|
24480
25006
|
function usageProbeStatePath(paths) {
|
|
24481
|
-
return
|
|
25007
|
+
return path25.join(paths.homeDir, USAGE_PROBE_DIR, USAGE_PROBE_STATE_FILE);
|
|
24482
25008
|
}
|
|
24483
25009
|
async function readHermesConfigDocument2(configPath, language) {
|
|
24484
25010
|
const existingRaw = await readFile14(configPath, "utf8").catch(
|
|
@@ -25046,7 +25572,7 @@ function toRecord14(value) {
|
|
|
25046
25572
|
|
|
25047
25573
|
// src/conversations/run-transcript-enrichment.ts
|
|
25048
25574
|
import { readFile as readFile15, stat as stat17 } from "fs/promises";
|
|
25049
|
-
import
|
|
25575
|
+
import path26 from "path";
|
|
25050
25576
|
var MESSAGE_COLUMNS2 = [
|
|
25051
25577
|
"id",
|
|
25052
25578
|
"session_id",
|
|
@@ -25122,8 +25648,8 @@ async function readRunFinalAssistantText(input) {
|
|
|
25122
25648
|
}
|
|
25123
25649
|
async function readHermesTranscriptRows(profileName, sessionId) {
|
|
25124
25650
|
const profileDir = resolveHermesProfileDir(profileName);
|
|
25125
|
-
const dbPath =
|
|
25126
|
-
const sessionsDir = await readHermesSessionsDir(profileName).then((value) => value.sessionsDir).catch(() =>
|
|
25651
|
+
const dbPath = path26.join(profileDir, "state.db");
|
|
25652
|
+
const sessionsDir = await readHermesSessionsDir(profileName).then((value) => value.sessionsDir).catch(() => path26.join(profileDir, "sessions"));
|
|
25127
25653
|
const [dbRows, jsonlRows] = await Promise.all([
|
|
25128
25654
|
readStateDbMessages2(dbPath, sessionId),
|
|
25129
25655
|
readJsonlMessages2(sessionsDir, sessionId)
|
|
@@ -25186,7 +25712,7 @@ async function readJsonlMessages2(sessionsDir, sessionId) {
|
|
|
25186
25712
|
if (!/^[A-Za-z0-9._:-]{1,160}$/u.test(sessionId)) {
|
|
25187
25713
|
return [];
|
|
25188
25714
|
}
|
|
25189
|
-
const transcriptPath =
|
|
25715
|
+
const transcriptPath = path26.join(sessionsDir, `${sessionId}.jsonl`);
|
|
25190
25716
|
const raw = await readFile15(transcriptPath, "utf8").catch((error) => {
|
|
25191
25717
|
if (isNodeError17(error, "ENOENT")) {
|
|
25192
25718
|
return "";
|
|
@@ -25453,7 +25979,7 @@ var RunToolEventIdCoalescer = class {
|
|
|
25453
25979
|
if (!type || hasStableToolEventId(event.payload)) {
|
|
25454
25980
|
return event;
|
|
25455
25981
|
}
|
|
25456
|
-
const toolKey = normalizeToolKey(
|
|
25982
|
+
const toolKey = normalizeToolKey(readToolName2(event.payload));
|
|
25457
25983
|
if (type === "tool.started") {
|
|
25458
25984
|
return withGeneratedToolEventId(
|
|
25459
25985
|
event,
|
|
@@ -25544,7 +26070,7 @@ function hasStableToolEventId(payload) {
|
|
|
25544
26070
|
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
26071
|
);
|
|
25546
26072
|
}
|
|
25547
|
-
function
|
|
26073
|
+
function readToolName2(payload) {
|
|
25548
26074
|
const tool = toRecord16(payload.tool);
|
|
25549
26075
|
const call = toRecord16(payload.tool_call ?? payload.toolCall);
|
|
25550
26076
|
const fn = toRecord16(call.function ?? payload.function);
|
|
@@ -26014,6 +26540,96 @@ function toRecord17(value) {
|
|
|
26014
26540
|
return typeof value === "object" && value !== null ? value : {};
|
|
26015
26541
|
}
|
|
26016
26542
|
|
|
26543
|
+
// src/conversations/goal-lifecycle-marker.ts
|
|
26544
|
+
var GOAL_LIFECYCLE_MARKER_FORMAT = "hermes-link-lifecycle-marker";
|
|
26545
|
+
var GOAL_COMPLETION_MARKER_KIND = "goal_completion";
|
|
26546
|
+
function createGoalCompletionMarker(input) {
|
|
26547
|
+
const text = goalCompletionMarkerText(input.goal, input.language);
|
|
26548
|
+
return {
|
|
26549
|
+
id: goalCompletionMarkerId(input.runId),
|
|
26550
|
+
schema_version: 1,
|
|
26551
|
+
conversation_id: input.conversationId,
|
|
26552
|
+
role: "system",
|
|
26553
|
+
status: "completed",
|
|
26554
|
+
run_id: input.runId,
|
|
26555
|
+
created_at: input.completedAt,
|
|
26556
|
+
updated_at: input.completedAt,
|
|
26557
|
+
sender: {
|
|
26558
|
+
id: "hermes_link",
|
|
26559
|
+
type: "system",
|
|
26560
|
+
display_name: "Hermes Link"
|
|
26561
|
+
},
|
|
26562
|
+
parts: [{ type: "text", text }],
|
|
26563
|
+
attachments: [],
|
|
26564
|
+
raw: {
|
|
26565
|
+
format: GOAL_LIFECYCLE_MARKER_FORMAT,
|
|
26566
|
+
payload: {
|
|
26567
|
+
kind: GOAL_COMPLETION_MARKER_KIND,
|
|
26568
|
+
status: "completed",
|
|
26569
|
+
completed_at: input.completedAt,
|
|
26570
|
+
run_id: input.runId,
|
|
26571
|
+
text,
|
|
26572
|
+
...Number.isFinite(input.goal.turns_used) ? { turns_used: input.goal.turns_used } : {},
|
|
26573
|
+
...Number.isFinite(input.goal.max_turns) ? { max_turns: input.goal.max_turns } : {},
|
|
26574
|
+
...input.goal.usage ? { usage: input.goal.usage } : {}
|
|
26575
|
+
}
|
|
26576
|
+
}
|
|
26577
|
+
};
|
|
26578
|
+
}
|
|
26579
|
+
function goalCompletionMarkerText(goal, language) {
|
|
26580
|
+
const parts = [localizedText2(language, "\u76EE\u6807\u5DF2\u5B8C\u6210", "Goal completed")];
|
|
26581
|
+
const turns = goalTurnsLabel(goal, language);
|
|
26582
|
+
if (turns) {
|
|
26583
|
+
parts.push(turns);
|
|
26584
|
+
}
|
|
26585
|
+
const totalTokens = finiteNonNegativeNumber(goal.usage?.total_tokens);
|
|
26586
|
+
if (totalTokens !== void 0) {
|
|
26587
|
+
parts.push(`${formatCompactNumber(totalTokens)} tokens`);
|
|
26588
|
+
}
|
|
26589
|
+
return parts.join(" ");
|
|
26590
|
+
}
|
|
26591
|
+
function goalCompletionMarkerId(runId) {
|
|
26592
|
+
const safeRunId = runId.trim().replace(/[^a-zA-Z0-9_:-]/gu, "_");
|
|
26593
|
+
return `msg_goal_completed_${safeRunId || "run"}`;
|
|
26594
|
+
}
|
|
26595
|
+
function goalTurnsLabel(goal, language) {
|
|
26596
|
+
const turnsUsed = finiteNonNegativeNumber(goal.turns_used);
|
|
26597
|
+
const maxTurns = finiteNonNegativeNumber(goal.max_turns);
|
|
26598
|
+
if (turnsUsed !== void 0 && maxTurns !== void 0) {
|
|
26599
|
+
return localizedText2(
|
|
26600
|
+
language,
|
|
26601
|
+
`${turnsUsed}/${maxTurns} \u8F6E`,
|
|
26602
|
+
`${turnsUsed}/${maxTurns} turns`
|
|
26603
|
+
);
|
|
26604
|
+
}
|
|
26605
|
+
if (turnsUsed !== void 0) {
|
|
26606
|
+
return localizedText2(language, `${turnsUsed} \u8F6E`, `${turnsUsed} turns`);
|
|
26607
|
+
}
|
|
26608
|
+
return null;
|
|
26609
|
+
}
|
|
26610
|
+
function finiteNonNegativeNumber(value) {
|
|
26611
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
|
|
26612
|
+
return void 0;
|
|
26613
|
+
}
|
|
26614
|
+
return Math.floor(value);
|
|
26615
|
+
}
|
|
26616
|
+
function formatCompactNumber(value) {
|
|
26617
|
+
if (value >= 1e6) {
|
|
26618
|
+
return `${trimTrailingDecimal(value / 1e6)}m`;
|
|
26619
|
+
}
|
|
26620
|
+
if (value >= 1e3) {
|
|
26621
|
+
return `${trimTrailingDecimal(value / 1e3)}k`;
|
|
26622
|
+
}
|
|
26623
|
+
return value.toLocaleString("en-US");
|
|
26624
|
+
}
|
|
26625
|
+
function trimTrailingDecimal(value) {
|
|
26626
|
+
const rounded = Math.round(value * 10) / 10;
|
|
26627
|
+
return Number.isInteger(rounded) ? String(rounded) : rounded.toFixed(1);
|
|
26628
|
+
}
|
|
26629
|
+
function localizedText2(language, zh, en) {
|
|
26630
|
+
return language === "en" ? en : zh;
|
|
26631
|
+
}
|
|
26632
|
+
|
|
26017
26633
|
// src/conversations/run-lifecycle.ts
|
|
26018
26634
|
var RUN_STATUS_RECOVERY_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
26019
26635
|
var RUN_STATUS_RECOVERY_INITIAL_DELAY_MS = 500;
|
|
@@ -27957,6 +28573,13 @@ ${details.join("\n")}` : localizedEmptyHermesResponseMessage(language);
|
|
|
27957
28573
|
completedAt,
|
|
27958
28574
|
"expired"
|
|
27959
28575
|
);
|
|
28576
|
+
const goalCompletionMarker = run.mode === "goal" ? await this.appendGoalCompletionMarkerAfterAssistant({
|
|
28577
|
+
conversationId,
|
|
28578
|
+
snapshot,
|
|
28579
|
+
run,
|
|
28580
|
+
goal: goalUpdate ?? void 0,
|
|
28581
|
+
completedAt
|
|
28582
|
+
}) : null;
|
|
27960
28583
|
await this.deps.writeSnapshot(conversationId, snapshot);
|
|
27961
28584
|
if (goalUpdate) {
|
|
27962
28585
|
await this.deps.appendEvent(conversationId, {
|
|
@@ -27968,6 +28591,14 @@ ${details.join("\n")}` : localizedEmptyHermesResponseMessage(language);
|
|
|
27968
28591
|
}
|
|
27969
28592
|
});
|
|
27970
28593
|
}
|
|
28594
|
+
if (goalCompletionMarker) {
|
|
28595
|
+
await this.deps.appendEvent(conversationId, {
|
|
28596
|
+
type: "message.created",
|
|
28597
|
+
message_id: goalCompletionMarker.id,
|
|
28598
|
+
run_id: runId,
|
|
28599
|
+
payload: { message: goalCompletionMarker }
|
|
28600
|
+
});
|
|
28601
|
+
}
|
|
27971
28602
|
await this.appendExpiredApprovalEvents(
|
|
27972
28603
|
conversationId,
|
|
27973
28604
|
runId,
|
|
@@ -28008,6 +28639,24 @@ ${details.join("\n")}` : localizedEmptyHermesResponseMessage(language);
|
|
|
28008
28639
|
occurredAt: completedAt
|
|
28009
28640
|
});
|
|
28010
28641
|
}
|
|
28642
|
+
async appendGoalCompletionMarkerAfterAssistant(input) {
|
|
28643
|
+
const goal = input.goal ?? (await this.deps.readRunnableManifest(input.conversationId)).command_state?.goal;
|
|
28644
|
+
if (goal?.status !== "done") {
|
|
28645
|
+
return null;
|
|
28646
|
+
}
|
|
28647
|
+
if (findGoalCompletionMarker(input.snapshot, input.run.id)) {
|
|
28648
|
+
return null;
|
|
28649
|
+
}
|
|
28650
|
+
const marker = createGoalCompletionMarker({
|
|
28651
|
+
conversationId: input.conversationId,
|
|
28652
|
+
runId: input.run.id,
|
|
28653
|
+
goal,
|
|
28654
|
+
completedAt: input.completedAt,
|
|
28655
|
+
language: input.run.language === "en" ? "en" : "zh-CN"
|
|
28656
|
+
});
|
|
28657
|
+
insertLifecycleMarkerAfterAssistant(input.snapshot, marker, input.run);
|
|
28658
|
+
return marker;
|
|
28659
|
+
}
|
|
28011
28660
|
async persistGoalUsageFromRunLocked(conversationId, run) {
|
|
28012
28661
|
const manifest = await this.deps.readRunnableManifest(conversationId);
|
|
28013
28662
|
const previousGoal = manifest.command_state?.goal;
|
|
@@ -28536,6 +29185,23 @@ function insertLifecycleMarkerBeforeAssistant(snapshot, marker, run) {
|
|
|
28536
29185
|
}
|
|
28537
29186
|
snapshot.messages.push(marker);
|
|
28538
29187
|
}
|
|
29188
|
+
function insertLifecycleMarkerAfterAssistant(snapshot, marker, run) {
|
|
29189
|
+
const existingIndex = snapshot.messages.findIndex(
|
|
29190
|
+
(message) => message.id === marker.id
|
|
29191
|
+
);
|
|
29192
|
+
if (existingIndex >= 0) {
|
|
29193
|
+
snapshot.messages[existingIndex] = marker;
|
|
29194
|
+
return;
|
|
29195
|
+
}
|
|
29196
|
+
const assistantIndex = snapshot.messages.findIndex(
|
|
29197
|
+
(message) => message.id === run.assistant_message_id
|
|
29198
|
+
);
|
|
29199
|
+
if (assistantIndex >= 0) {
|
|
29200
|
+
snapshot.messages.splice(assistantIndex + 1, 0, marker);
|
|
29201
|
+
return;
|
|
29202
|
+
}
|
|
29203
|
+
snapshot.messages.push(marker);
|
|
29204
|
+
}
|
|
28539
29205
|
function timestampBeforeAssistantForRun(snapshot, run, fallback) {
|
|
28540
29206
|
const user = snapshot.messages.find(
|
|
28541
29207
|
(message) => message.id === run.trigger_message_id
|
|
@@ -28561,6 +29227,12 @@ function findContextCompressionMarker(snapshot, operationId) {
|
|
|
28561
29227
|
return message.raw?.format === CONTEXT_COMPRESSION_MARKER_FORMAT && payload.kind === CONTEXT_COMPRESSION_MARKER_KIND && payload.operation_id === operationId;
|
|
28562
29228
|
});
|
|
28563
29229
|
}
|
|
29230
|
+
function findGoalCompletionMarker(snapshot, runId) {
|
|
29231
|
+
return snapshot.messages.find((message) => {
|
|
29232
|
+
const payload = toRecord18(message.raw?.payload);
|
|
29233
|
+
return message.raw?.format === GOAL_LIFECYCLE_MARKER_FORMAT && payload.kind === GOAL_COMPLETION_MARKER_KIND && payload.run_id === runId;
|
|
29234
|
+
});
|
|
29235
|
+
}
|
|
28564
29236
|
function nextContextCompressionGeneration(snapshot) {
|
|
28565
29237
|
let maxGeneration = 0;
|
|
28566
29238
|
for (const message of snapshot.messages) {
|
|
@@ -30659,7 +31331,7 @@ var ConversationService = class {
|
|
|
30659
31331
|
}
|
|
30660
31332
|
}
|
|
30661
31333
|
hermesArchiveStateSyncMarkerPath() {
|
|
30662
|
-
return
|
|
31334
|
+
return path27.join(this.paths.indexesDir, "hermes-archive-state-sync.json");
|
|
30663
31335
|
}
|
|
30664
31336
|
prepareClearAllConversationPlan(targetStatus) {
|
|
30665
31337
|
return this.maintenance.prepareClearAllConversationPlan(targetStatus);
|
|
@@ -30896,6 +31568,7 @@ function buildLiveActivityEvent(input) {
|
|
|
30896
31568
|
return null;
|
|
30897
31569
|
}
|
|
30898
31570
|
const language = run?.language === "en" ? "en" : "zh";
|
|
31571
|
+
const todoProgress = liveActivityTodoProgressForEvent(input.event);
|
|
30899
31572
|
const text = liveActivityTextForEvent({
|
|
30900
31573
|
event: input.event,
|
|
30901
31574
|
snapshot: input.snapshot,
|
|
@@ -30919,11 +31592,56 @@ function buildLiveActivityEvent(input) {
|
|
|
30919
31592
|
statusLabel: text.statusLabel,
|
|
30920
31593
|
progressText: text.progressText,
|
|
30921
31594
|
detailText: text.detailText,
|
|
31595
|
+
todoDoneCount: todoProgress?.doneCount,
|
|
31596
|
+
todoTotalCount: todoProgress?.totalCount,
|
|
31597
|
+
todoProgressText: todoProgress?.progressText,
|
|
30922
31598
|
requiresUserAction: phase === "needs_input" || phase === "needs_approval",
|
|
30923
31599
|
terminal: isLiveActivityTerminalEvent(phase, target.kind),
|
|
30924
31600
|
occurredAt: input.event.created_at
|
|
30925
31601
|
};
|
|
30926
31602
|
}
|
|
31603
|
+
function liveActivityTodoProgressForEvent(event) {
|
|
31604
|
+
if (event.type.toLowerCase() !== "tool.completed") {
|
|
31605
|
+
return null;
|
|
31606
|
+
}
|
|
31607
|
+
const payload = readRecord2(event.payload);
|
|
31608
|
+
const toolName = readToolName3(payload)?.toLowerCase();
|
|
31609
|
+
if (toolName !== "todo") {
|
|
31610
|
+
return null;
|
|
31611
|
+
}
|
|
31612
|
+
const todos = readTodoItems2(payload.todos) ?? readTodoItems2(readRecord2(payload.result).todos);
|
|
31613
|
+
if (todos) {
|
|
31614
|
+
const totalCount2 = todos.length;
|
|
31615
|
+
const doneCount2 = todos.filter((item) => {
|
|
31616
|
+
const status = readString20(item, "status")?.toLowerCase();
|
|
31617
|
+
return status === "completed" || status === "cancelled" || status === "canceled";
|
|
31618
|
+
}).length;
|
|
31619
|
+
return {
|
|
31620
|
+
doneCount: doneCount2,
|
|
31621
|
+
totalCount: totalCount2,
|
|
31622
|
+
progressText: totalCount2 > 0 ? `${doneCount2}/${totalCount2}` : void 0
|
|
31623
|
+
};
|
|
31624
|
+
}
|
|
31625
|
+
const summary = readRecord2(payload.summary).total === void 0 ? readRecord2(readRecord2(payload.result).summary) : readRecord2(payload.summary);
|
|
31626
|
+
const totalCount = readInteger4(summary, "total");
|
|
31627
|
+
if (totalCount === null) {
|
|
31628
|
+
return null;
|
|
31629
|
+
}
|
|
31630
|
+
const completedCount = readInteger4(summary, "completed") ?? 0;
|
|
31631
|
+
const cancelledCount = readInteger4(summary, "cancelled") ?? readInteger4(summary, "canceled") ?? 0;
|
|
31632
|
+
const doneCount = Math.min(totalCount, completedCount + cancelledCount);
|
|
31633
|
+
return {
|
|
31634
|
+
doneCount,
|
|
31635
|
+
totalCount,
|
|
31636
|
+
progressText: totalCount > 0 ? `${doneCount}/${totalCount}` : void 0
|
|
31637
|
+
};
|
|
31638
|
+
}
|
|
31639
|
+
function readTodoItems2(value) {
|
|
31640
|
+
if (!Array.isArray(value)) {
|
|
31641
|
+
return null;
|
|
31642
|
+
}
|
|
31643
|
+
return value.map((item) => readRecord2(item)).filter((item) => Object.keys(item).length > 0);
|
|
31644
|
+
}
|
|
30927
31645
|
function liveActivityConversationTitle(manifest, event, phase, language) {
|
|
30928
31646
|
if (phase === "goal_running" || phase === "goal_paused") {
|
|
30929
31647
|
const goal = manifest.command_state?.goal;
|
|
@@ -31009,7 +31727,7 @@ function liveActivityPhaseForEvent(event, run, contextOperation) {
|
|
|
31009
31727
|
return run?.mode === "goal" ? "goal_running" : "accepted";
|
|
31010
31728
|
}
|
|
31011
31729
|
if (type === "conversation.goal.updated") {
|
|
31012
|
-
const goal =
|
|
31730
|
+
const goal = readRecord2(event.payload).goal;
|
|
31013
31731
|
const status = readString20(goal, "status");
|
|
31014
31732
|
return status === "paused" ? "goal_paused" : "goal_running";
|
|
31015
31733
|
}
|
|
@@ -31055,7 +31773,7 @@ function liveActivityPhaseForEvent(event, run, contextOperation) {
|
|
|
31055
31773
|
return null;
|
|
31056
31774
|
}
|
|
31057
31775
|
function liveActivityTextForEvent(input) {
|
|
31058
|
-
const toolName =
|
|
31776
|
+
const toolName = readToolName3(input.event.payload);
|
|
31059
31777
|
const assistantText = input.event.message_id ? input.snapshot.messages.find((message) => message.id === input.event.message_id) : null;
|
|
31060
31778
|
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
31779
|
if (input.language === "en") {
|
|
@@ -31159,7 +31877,7 @@ function isLiveActivityTerminalEvent(phase, targetKind) {
|
|
|
31159
31877
|
return targetKind === "context_compression" && (phase === "context_compressed" || phase === "context_compression_failed" || phase === "context_compression_timed_out");
|
|
31160
31878
|
}
|
|
31161
31879
|
function readContextCompressionOperation(payload) {
|
|
31162
|
-
const operation =
|
|
31880
|
+
const operation = readRecord2(payload).operation;
|
|
31163
31881
|
if (!operation || typeof operation !== "object") {
|
|
31164
31882
|
return null;
|
|
31165
31883
|
}
|
|
@@ -31176,17 +31894,25 @@ function readContextCompressionOperation(payload) {
|
|
|
31176
31894
|
source: readString20(record, "source") === "manual" ? "manual" : "auto"
|
|
31177
31895
|
};
|
|
31178
31896
|
}
|
|
31179
|
-
function
|
|
31180
|
-
const record =
|
|
31181
|
-
return readString20(record, "tool_name") ?? readString20(record, "tool") ?? readString20(record, "name") ?? readString20(
|
|
31897
|
+
function readToolName3(payload) {
|
|
31898
|
+
const record = readRecord2(payload);
|
|
31899
|
+
return readString20(record, "tool_name") ?? readString20(record, "tool") ?? readString20(record, "name") ?? readString20(readRecord2(record.tool), "name");
|
|
31182
31900
|
}
|
|
31183
|
-
function
|
|
31901
|
+
function readRecord2(value) {
|
|
31184
31902
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
|
|
31185
31903
|
}
|
|
31186
31904
|
function readString20(value, key) {
|
|
31187
|
-
const raw =
|
|
31905
|
+
const raw = readRecord2(value)[key];
|
|
31188
31906
|
return typeof raw === "string" && raw.trim() ? raw.trim() : null;
|
|
31189
31907
|
}
|
|
31908
|
+
function readInteger4(value, key) {
|
|
31909
|
+
const raw = readRecord2(value)[key];
|
|
31910
|
+
const parsed = typeof raw === "number" ? raw : typeof raw === "string" && raw.trim() ? Number(raw) : Number.NaN;
|
|
31911
|
+
if (!Number.isFinite(parsed)) {
|
|
31912
|
+
return null;
|
|
31913
|
+
}
|
|
31914
|
+
return Math.max(0, Math.trunc(parsed));
|
|
31915
|
+
}
|
|
31190
31916
|
function approvalRestartText(language, zh, en) {
|
|
31191
31917
|
return language === "en" ? en : zh;
|
|
31192
31918
|
}
|
|
@@ -31806,7 +32532,7 @@ function readQueryString(value) {
|
|
|
31806
32532
|
const trimmed = raw.trim();
|
|
31807
32533
|
return trimmed ? trimmed : void 0;
|
|
31808
32534
|
}
|
|
31809
|
-
function
|
|
32535
|
+
function readInteger5(value) {
|
|
31810
32536
|
const raw = Array.isArray(value) ? value[0] : value;
|
|
31811
32537
|
if (typeof raw !== "string") {
|
|
31812
32538
|
return void 0;
|
|
@@ -32905,7 +33631,7 @@ function toConversationSummary(value) {
|
|
|
32905
33631
|
return candidate;
|
|
32906
33632
|
}
|
|
32907
33633
|
function resolveConversationEventCursor(input) {
|
|
32908
|
-
const queryAfter =
|
|
33634
|
+
const queryAfter = readInteger5(input.queryAfter) ?? 0;
|
|
32909
33635
|
const headerAfter = readNonNegativeIntegerHeader(input.lastEventIdHeader) ?? 0;
|
|
32910
33636
|
return Math.max(queryAfter, headerAfter);
|
|
32911
33637
|
}
|
|
@@ -33051,11 +33777,11 @@ function isSseRequestContext(ctx) {
|
|
|
33051
33777
|
}
|
|
33052
33778
|
return isSseRequestPath(ctx.path) || isActiveSseSocket(ctx.req.socket);
|
|
33053
33779
|
}
|
|
33054
|
-
function isSseRequestPath(
|
|
33055
|
-
if (!
|
|
33780
|
+
function isSseRequestPath(path38) {
|
|
33781
|
+
if (!path38) {
|
|
33056
33782
|
return false;
|
|
33057
33783
|
}
|
|
33058
|
-
return
|
|
33784
|
+
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
33785
|
}
|
|
33060
33786
|
function isExpectedClientDisconnectError2(error, options = {}) {
|
|
33061
33787
|
if (!(error instanceof Error)) {
|
|
@@ -33494,7 +34220,7 @@ import { createHash as createHash10 } from "crypto";
|
|
|
33494
34220
|
// src/model-catalog/catalog.ts
|
|
33495
34221
|
import { randomInt } from "crypto";
|
|
33496
34222
|
import { mkdir as mkdir12 } from "fs/promises";
|
|
33497
|
-
import
|
|
34223
|
+
import path28 from "path";
|
|
33498
34224
|
import { fileURLToPath } from "url";
|
|
33499
34225
|
var MODEL_CATALOG_CACHE_VERSION = 1;
|
|
33500
34226
|
var MODEL_CATALOG_FETCH_TIMEOUT_MS = 1e4;
|
|
@@ -33698,7 +34424,7 @@ async function readCachedCatalogFile(paths) {
|
|
|
33698
34424
|
return cached;
|
|
33699
34425
|
}
|
|
33700
34426
|
async function writeCachedCatalog(paths, value) {
|
|
33701
|
-
await mkdir12(
|
|
34427
|
+
await mkdir12(path28.dirname(modelCatalogCachePath(paths)), {
|
|
33702
34428
|
recursive: true,
|
|
33703
34429
|
mode: 448
|
|
33704
34430
|
});
|
|
@@ -33714,7 +34440,7 @@ async function readFallbackCatalog() {
|
|
|
33714
34440
|
throw new Error("model capability fallback catalog was not found");
|
|
33715
34441
|
}
|
|
33716
34442
|
function modelCatalogCachePath(paths) {
|
|
33717
|
-
return
|
|
34443
|
+
return path28.join(paths.homeDir, "model-capabilities", "catalog-cache.json");
|
|
33718
34444
|
}
|
|
33719
34445
|
function normalizeModelCapabilityCatalog(value) {
|
|
33720
34446
|
if (!value || typeof value !== "object") {
|
|
@@ -35193,7 +35919,7 @@ function errorMessage3(error) {
|
|
|
35193
35919
|
|
|
35194
35920
|
// src/hermes/profile-identity.ts
|
|
35195
35921
|
import { readFile as readFile16, stat as stat18 } from "fs/promises";
|
|
35196
|
-
import
|
|
35922
|
+
import path29 from "path";
|
|
35197
35923
|
var MAX_SOUL_MD_LENGTH = 2e4;
|
|
35198
35924
|
async function readHermesProfileIdentity(profileName, paths) {
|
|
35199
35925
|
await assertProfileExists3(profileName, paths);
|
|
@@ -35250,7 +35976,7 @@ async function assertProfileExists3(profileName, paths) {
|
|
|
35250
35976
|
}
|
|
35251
35977
|
}
|
|
35252
35978
|
function resolveSoulPath(profileName) {
|
|
35253
|
-
return
|
|
35979
|
+
return path29.join(resolveHermesProfileDir(profileName), "SOUL.md");
|
|
35254
35980
|
}
|
|
35255
35981
|
function isNodeError19(error, code) {
|
|
35256
35982
|
return error instanceof Error && "code" in error && error.code === code;
|
|
@@ -35266,13 +35992,13 @@ import {
|
|
|
35266
35992
|
rm as rm7,
|
|
35267
35993
|
stat as stat20
|
|
35268
35994
|
} from "fs/promises";
|
|
35269
|
-
import
|
|
35995
|
+
import path31 from "path";
|
|
35270
35996
|
import YAML5 from "yaml";
|
|
35271
35997
|
|
|
35272
35998
|
// src/hermes/link-skill.ts
|
|
35273
35999
|
import { readFile as readFile17, stat as stat19 } from "fs/promises";
|
|
35274
36000
|
import os7 from "os";
|
|
35275
|
-
import
|
|
36001
|
+
import path30 from "path";
|
|
35276
36002
|
import YAML4 from "yaml";
|
|
35277
36003
|
var HERMES_LINK_SKILL_ROOT_DIR = "hermes-skills";
|
|
35278
36004
|
var HERMES_LINK_SKILL_DIR = "hermes-link";
|
|
@@ -35357,7 +36083,7 @@ Do not modify Hermes profiles, delete user data, edit config files, or kill proc
|
|
|
35357
36083
|
async function ensureHermesLinkSkillInstalledForProfiles(options = {}) {
|
|
35358
36084
|
const paths = options.paths ?? resolveRuntimePaths();
|
|
35359
36085
|
const externalDir = resolveHermesLinkSkillExternalDir(paths);
|
|
35360
|
-
const skillPath =
|
|
36086
|
+
const skillPath = path30.join(
|
|
35361
36087
|
externalDir,
|
|
35362
36088
|
HERMES_LINK_SKILL_DIR,
|
|
35363
36089
|
HERMES_LINK_SKILL_FILE
|
|
@@ -35439,7 +36165,7 @@ function withDefaultProfilePlaceholder2(profiles) {
|
|
|
35439
36165
|
];
|
|
35440
36166
|
}
|
|
35441
36167
|
function resolveHermesLinkSkillExternalDir(paths = resolveRuntimePaths()) {
|
|
35442
|
-
return
|
|
36168
|
+
return path30.join(paths.homeDir, HERMES_LINK_SKILL_ROOT_DIR);
|
|
35443
36169
|
}
|
|
35444
36170
|
async function writeHermesLinkSkill(skillPath) {
|
|
35445
36171
|
const existing = await readFile17(skillPath, "utf8").catch((error) => {
|
|
@@ -35534,11 +36260,11 @@ function appendExternalDir(current, externalDir, hermesHome) {
|
|
|
35534
36260
|
const seen = new Set(
|
|
35535
36261
|
entries.map((entry) => resolveExternalDirEntry(entry, hermesHome))
|
|
35536
36262
|
);
|
|
35537
|
-
const normalizedExternalDir =
|
|
36263
|
+
const normalizedExternalDir = path30.resolve(externalDir);
|
|
35538
36264
|
return seen.has(normalizedExternalDir) ? entries : [...entries, normalizedExternalDir];
|
|
35539
36265
|
}
|
|
35540
36266
|
function externalDirsInclude(current, externalDir, hermesHome) {
|
|
35541
|
-
const normalizedExternalDir =
|
|
36267
|
+
const normalizedExternalDir = path30.resolve(externalDir);
|
|
35542
36268
|
return readExternalDirEntries(current).some(
|
|
35543
36269
|
(entry) => resolveExternalDirEntry(entry, hermesHome) === normalizedExternalDir
|
|
35544
36270
|
);
|
|
@@ -35549,14 +36275,14 @@ function readExternalDirEntries(value) {
|
|
|
35549
36275
|
}
|
|
35550
36276
|
function resolveExternalDirEntry(entry, hermesHome) {
|
|
35551
36277
|
const expanded = expandHome(expandEnvVars(entry));
|
|
35552
|
-
return
|
|
36278
|
+
return path30.resolve(path30.isAbsolute(expanded) ? expanded : path30.join(hermesHome, expanded));
|
|
35553
36279
|
}
|
|
35554
36280
|
function expandHome(value) {
|
|
35555
36281
|
if (value === "~") {
|
|
35556
36282
|
return os7.homedir();
|
|
35557
36283
|
}
|
|
35558
|
-
if (value.startsWith(`~${
|
|
35559
|
-
return
|
|
36284
|
+
if (value.startsWith(`~${path30.sep}`) || value.startsWith("~/")) {
|
|
36285
|
+
return path30.join(os7.homedir(), value.slice(2));
|
|
35560
36286
|
}
|
|
35561
36287
|
return value;
|
|
35562
36288
|
}
|
|
@@ -35795,7 +36521,7 @@ async function readHermesProfileCreationStatus(paths) {
|
|
|
35795
36521
|
let state = await readJsonFile(
|
|
35796
36522
|
profileCreationStatePath(paths)
|
|
35797
36523
|
);
|
|
35798
|
-
if (state?.state === "running" && !runningProfileCreation && !isRecentRunningState(state) && !
|
|
36524
|
+
if (state?.state === "running" && !runningProfileCreation && !isRecentRunningState(state) && !isProcessAlive2(state.pid)) {
|
|
35799
36525
|
state = {
|
|
35800
36526
|
...state,
|
|
35801
36527
|
state: "failed",
|
|
@@ -36108,7 +36834,7 @@ function collectEnvKeys(value, keys = /* @__PURE__ */ new Set()) {
|
|
|
36108
36834
|
return keys;
|
|
36109
36835
|
}
|
|
36110
36836
|
async function writeEnvValues(profileName, values) {
|
|
36111
|
-
const envPath =
|
|
36837
|
+
const envPath = path31.join(resolveHermesProfileDir(profileName), ".env");
|
|
36112
36838
|
const existingRaw = await readFile18(envPath, "utf8").catch((error) => {
|
|
36113
36839
|
if (isNodeError21(error, "ENOENT")) {
|
|
36114
36840
|
return "";
|
|
@@ -36145,8 +36871,8 @@ async function writeEnvValues(profileName, values) {
|
|
|
36145
36871
|
await atomicWriteFilePreservingMetadata(envPath, nextRaw);
|
|
36146
36872
|
}
|
|
36147
36873
|
async function copySkills(sourceProfile, targetProfile) {
|
|
36148
|
-
const sourceSkills =
|
|
36149
|
-
const targetSkills =
|
|
36874
|
+
const sourceSkills = path31.join(resolveHermesProfileDir(sourceProfile), "skills");
|
|
36875
|
+
const targetSkills = path31.join(resolveHermesProfileDir(targetProfile), "skills");
|
|
36150
36876
|
if (!await pathExists2(sourceSkills)) {
|
|
36151
36877
|
return;
|
|
36152
36878
|
}
|
|
@@ -36241,10 +36967,10 @@ async function readProfileCreationLogLines(paths) {
|
|
|
36241
36967
|
);
|
|
36242
36968
|
}
|
|
36243
36969
|
function profileCreationStatePath(paths) {
|
|
36244
|
-
return
|
|
36970
|
+
return path31.join(paths.runDir, "profile-create-state.json");
|
|
36245
36971
|
}
|
|
36246
36972
|
function profileCreationLogPath(paths) {
|
|
36247
|
-
return
|
|
36973
|
+
return path31.join(paths.logsDir, PROFILE_CREATE_LOG_FILE);
|
|
36248
36974
|
}
|
|
36249
36975
|
async function clearProfileCreationLogFiles(paths) {
|
|
36250
36976
|
const primary = profileCreationLogPath(paths);
|
|
@@ -36270,7 +36996,7 @@ function isRecentRunningState(state) {
|
|
|
36270
36996
|
}
|
|
36271
36997
|
return Date.now() - Date.parse(state.started_at) < 1e4;
|
|
36272
36998
|
}
|
|
36273
|
-
function
|
|
36999
|
+
function isProcessAlive2(pid) {
|
|
36274
37000
|
if (!pid || pid <= 0) {
|
|
36275
37001
|
return false;
|
|
36276
37002
|
}
|
|
@@ -36533,7 +37259,7 @@ import {
|
|
|
36533
37259
|
readFile as readFile19,
|
|
36534
37260
|
stat as stat21
|
|
36535
37261
|
} from "fs/promises";
|
|
36536
|
-
import
|
|
37262
|
+
import path32 from "path";
|
|
36537
37263
|
import YAML6 from "yaml";
|
|
36538
37264
|
var ENTRY_DELIMITER = "\n\xA7\n";
|
|
36539
37265
|
var DEFAULT_MEMORY_LIMIT = 2200;
|
|
@@ -36864,7 +37590,7 @@ async function saveProviderSettings(profileName, provider, patch) {
|
|
|
36864
37590
|
});
|
|
36865
37591
|
await patchJsonProviderConfig(
|
|
36866
37592
|
profileName,
|
|
36867
|
-
|
|
37593
|
+
path32.join("hindsight", "config.json"),
|
|
36868
37594
|
{
|
|
36869
37595
|
mode: patch.mode,
|
|
36870
37596
|
api_url: patch.apiUrl,
|
|
@@ -37067,7 +37793,7 @@ async function patchHermesMemoryLimits(profileName, patch) {
|
|
|
37067
37793
|
await atomicWriteFilePreservingMetadata(configPath, document.toString());
|
|
37068
37794
|
}
|
|
37069
37795
|
function resolveMemoryDir(profileName) {
|
|
37070
|
-
return
|
|
37796
|
+
return path32.join(resolveHermesProfileDir(profileName), "memories");
|
|
37071
37797
|
}
|
|
37072
37798
|
async function readMemoryStore(profileName, target, limits) {
|
|
37073
37799
|
const filePath = memoryFilePath(profileName, target);
|
|
@@ -37128,7 +37854,7 @@ async function writeMemoryEntries(profileName, target, entries) {
|
|
|
37128
37854
|
);
|
|
37129
37855
|
}
|
|
37130
37856
|
function memoryFilePath(profileName, target) {
|
|
37131
|
-
return
|
|
37857
|
+
return path32.join(
|
|
37132
37858
|
resolveMemoryDir(profileName),
|
|
37133
37859
|
target === "user" ? "USER.md" : "MEMORY.md"
|
|
37134
37860
|
);
|
|
@@ -37188,7 +37914,7 @@ async function readCustomProviderSetupSummary(profileName) {
|
|
|
37188
37914
|
configurable: true,
|
|
37189
37915
|
configured: true,
|
|
37190
37916
|
configurationIssue: null,
|
|
37191
|
-
providerConfigPath:
|
|
37917
|
+
providerConfigPath: path32.join(
|
|
37192
37918
|
resolveHermesProfileDir(profileName),
|
|
37193
37919
|
"<provider>.json"
|
|
37194
37920
|
),
|
|
@@ -37582,7 +38308,7 @@ async function readProviderSettings(profileName, provider) {
|
|
|
37582
38308
|
stringSetting(
|
|
37583
38309
|
"dbPath",
|
|
37584
38310
|
"SQLite \u6570\u636E\u5E93\u8DEF\u5F84",
|
|
37585
|
-
config.db_path ??
|
|
38311
|
+
config.db_path ?? path32.join(resolveHermesProfileDir(profileName), "memory_store.db")
|
|
37586
38312
|
),
|
|
37587
38313
|
booleanSetting("autoExtract", "\u4F1A\u8BDD\u7ED3\u675F\u81EA\u52A8\u62BD\u53D6", config.auto_extract ?? false),
|
|
37588
38314
|
numberSetting("defaultTrust", "\u9ED8\u8BA4\u4FE1\u4EFB\u5206", config.default_trust ?? 0.5),
|
|
@@ -37618,7 +38344,7 @@ async function readProviderSettings(profileName, provider) {
|
|
|
37618
38344
|
stringSetting(
|
|
37619
38345
|
"workingDirectory",
|
|
37620
38346
|
"\u5DE5\u4F5C\u76EE\u5F55",
|
|
37621
|
-
|
|
38347
|
+
path32.join(resolveHermesProfileDir(profileName), "byterover"),
|
|
37622
38348
|
false
|
|
37623
38349
|
)
|
|
37624
38350
|
];
|
|
@@ -37627,16 +38353,16 @@ async function readProviderSettings(profileName, provider) {
|
|
|
37627
38353
|
}
|
|
37628
38354
|
function memoryProviderConfigPath(profileName, provider) {
|
|
37629
38355
|
if (provider === "honcho") {
|
|
37630
|
-
return
|
|
38356
|
+
return path32.join(resolveHermesProfileDir(profileName), "honcho.json");
|
|
37631
38357
|
}
|
|
37632
38358
|
if (provider === "mem0") {
|
|
37633
|
-
return
|
|
38359
|
+
return path32.join(resolveHermesProfileDir(profileName), "mem0.json");
|
|
37634
38360
|
}
|
|
37635
38361
|
if (provider === "supermemory") {
|
|
37636
|
-
return
|
|
38362
|
+
return path32.join(resolveHermesProfileDir(profileName), "supermemory.json");
|
|
37637
38363
|
}
|
|
37638
38364
|
if (provider === "hindsight") {
|
|
37639
|
-
return
|
|
38365
|
+
return path32.join(
|
|
37640
38366
|
resolveHermesProfileDir(profileName),
|
|
37641
38367
|
"hindsight",
|
|
37642
38368
|
"config.json"
|
|
@@ -37645,13 +38371,13 @@ function memoryProviderConfigPath(profileName, provider) {
|
|
|
37645
38371
|
return null;
|
|
37646
38372
|
}
|
|
37647
38373
|
function customProviderConfigPath(profileName, provider) {
|
|
37648
|
-
return
|
|
38374
|
+
return path32.join(
|
|
37649
38375
|
resolveHermesProfileDir(profileName),
|
|
37650
38376
|
`${normalizeCustomProviderId(provider)}.json`
|
|
37651
38377
|
);
|
|
37652
38378
|
}
|
|
37653
38379
|
function customProviderRegistryPath(profileName) {
|
|
37654
|
-
return
|
|
38380
|
+
return path32.join(
|
|
37655
38381
|
resolveHermesProfileDir(profileName),
|
|
37656
38382
|
CUSTOM_PROVIDER_REGISTRY_FILE
|
|
37657
38383
|
);
|
|
@@ -37703,7 +38429,7 @@ async function saveCustomProviderRegistryEntry(profileName, provider) {
|
|
|
37703
38429
|
);
|
|
37704
38430
|
}
|
|
37705
38431
|
async function discoverUserMemoryProviderDescriptors(profileName) {
|
|
37706
|
-
const pluginsDir =
|
|
38432
|
+
const pluginsDir = path32.join(resolveHermesProfileDir(profileName), "plugins");
|
|
37707
38433
|
const entries = await readdir10(pluginsDir, { withFileTypes: true }).catch(
|
|
37708
38434
|
(error) => {
|
|
37709
38435
|
if (isNodeError22(error, "ENOENT")) {
|
|
@@ -37723,7 +38449,7 @@ async function discoverUserMemoryProviderDescriptors(profileName) {
|
|
|
37723
38449
|
} catch {
|
|
37724
38450
|
continue;
|
|
37725
38451
|
}
|
|
37726
|
-
const providerDir =
|
|
38452
|
+
const providerDir = path32.join(pluginsDir, entry.name);
|
|
37727
38453
|
if (!await isMemoryProviderPluginDir(providerDir)) {
|
|
37728
38454
|
continue;
|
|
37729
38455
|
}
|
|
@@ -37737,7 +38463,7 @@ async function discoverUserMemoryProviderDescriptors(profileName) {
|
|
|
37737
38463
|
return descriptors;
|
|
37738
38464
|
}
|
|
37739
38465
|
async function isUserMemoryProviderInstalled(profileName, provider) {
|
|
37740
|
-
const providerDir =
|
|
38466
|
+
const providerDir = path32.join(
|
|
37741
38467
|
resolveHermesProfileDir(profileName),
|
|
37742
38468
|
"plugins",
|
|
37743
38469
|
normalizeCustomProviderId(provider)
|
|
@@ -37745,7 +38471,7 @@ async function isUserMemoryProviderInstalled(profileName, provider) {
|
|
|
37745
38471
|
return isMemoryProviderPluginDir(providerDir);
|
|
37746
38472
|
}
|
|
37747
38473
|
async function isMemoryProviderPluginDir(providerDir) {
|
|
37748
|
-
const source = await readFile19(
|
|
38474
|
+
const source = await readFile19(path32.join(providerDir, "__init__.py"), "utf8").catch(
|
|
37749
38475
|
(error) => {
|
|
37750
38476
|
if (isNodeError22(error, "ENOENT")) {
|
|
37751
38477
|
return "";
|
|
@@ -37757,7 +38483,7 @@ async function isMemoryProviderPluginDir(providerDir) {
|
|
|
37757
38483
|
return sample.includes("register_memory_provider") || sample.includes("MemoryProvider");
|
|
37758
38484
|
}
|
|
37759
38485
|
async function readPluginMetadata(providerDir) {
|
|
37760
|
-
const raw = await readFile19(
|
|
38486
|
+
const raw = await readFile19(path32.join(providerDir, "plugin.yaml"), "utf8").catch(
|
|
37761
38487
|
(error) => {
|
|
37762
38488
|
if (isNodeError22(error, "ENOENT")) {
|
|
37763
38489
|
return "";
|
|
@@ -37769,10 +38495,10 @@ async function readPluginMetadata(providerDir) {
|
|
|
37769
38495
|
}
|
|
37770
38496
|
async function resolveByteRoverCli() {
|
|
37771
38497
|
const candidates = [
|
|
37772
|
-
...(process.env.PATH ?? "").split(
|
|
37773
|
-
|
|
38498
|
+
...(process.env.PATH ?? "").split(path32.delimiter).filter(Boolean).map((dir) => path32.join(dir, "brv")),
|
|
38499
|
+
path32.join(process.env.HOME ?? "", ".brv-cli", "bin", "brv"),
|
|
37774
38500
|
"/usr/local/bin/brv",
|
|
37775
|
-
|
|
38501
|
+
path32.join(process.env.HOME ?? "", ".npm-global", "bin", "brv")
|
|
37776
38502
|
].filter(Boolean);
|
|
37777
38503
|
for (const candidate of candidates) {
|
|
37778
38504
|
const found = await access3(candidate).then(() => true).catch(() => false);
|
|
@@ -37833,7 +38559,7 @@ async function patchHermesMemoryEnv(profileName, patch) {
|
|
|
37833
38559
|
if (entries.length === 0) {
|
|
37834
38560
|
return;
|
|
37835
38561
|
}
|
|
37836
|
-
const envPath =
|
|
38562
|
+
const envPath = path32.join(resolveHermesProfileDir(profileName), ".env");
|
|
37837
38563
|
const existingRaw = await readFile19(envPath, "utf8").catch((error) => {
|
|
37838
38564
|
if (isNodeError22(error, "ENOENT")) {
|
|
37839
38565
|
return "";
|
|
@@ -38004,7 +38730,7 @@ async function readActiveMemoryProvider(profileName) {
|
|
|
38004
38730
|
return provider;
|
|
38005
38731
|
}
|
|
38006
38732
|
async function patchJsonProviderConfig(profileName, relativePath, patch) {
|
|
38007
|
-
const configPath =
|
|
38733
|
+
const configPath = path32.join(
|
|
38008
38734
|
resolveHermesProfileDir(profileName),
|
|
38009
38735
|
relativePath
|
|
38010
38736
|
);
|
|
@@ -38033,7 +38759,7 @@ async function readJsonObject(filePath) {
|
|
|
38033
38759
|
} catch {
|
|
38034
38760
|
throw new HermesMemoryError(
|
|
38035
38761
|
"memory_provider_config_invalid",
|
|
38036
|
-
`${
|
|
38762
|
+
`${path32.basename(filePath)} \u4E0D\u662F\u6709\u6548\u7684 JSON \u914D\u7F6E\u6587\u4EF6\u3002`
|
|
38037
38763
|
);
|
|
38038
38764
|
}
|
|
38039
38765
|
}
|
|
@@ -38657,7 +39383,7 @@ function toMemoryHttpError(error) {
|
|
|
38657
39383
|
|
|
38658
39384
|
// src/hermes/skills.ts
|
|
38659
39385
|
import { readFile as readFile20, readdir as readdir11 } from "fs/promises";
|
|
38660
|
-
import
|
|
39386
|
+
import path33 from "path";
|
|
38661
39387
|
import YAML7 from "yaml";
|
|
38662
39388
|
var HermesSkillNotFoundError = class extends Error {
|
|
38663
39389
|
constructor(skillName) {
|
|
@@ -38671,7 +39397,7 @@ var EXCLUDED_SKILL_DIRS = /* @__PURE__ */ new Set([".git", ".github", ".hub"]);
|
|
|
38671
39397
|
async function listHermesProfileSkills(profileName, paths = resolveRuntimePaths()) {
|
|
38672
39398
|
const profile = await readExistingProfile(profileName, paths);
|
|
38673
39399
|
const profileDir = resolveHermesProfileDir(profile.name);
|
|
38674
|
-
const skillsRoot =
|
|
39400
|
+
const skillsRoot = path33.join(profileDir, "skills");
|
|
38675
39401
|
const [skillFiles, disabled, provenance] = await Promise.all([
|
|
38676
39402
|
findSkillFiles(skillsRoot),
|
|
38677
39403
|
readDisabledSkillNames(resolveHermesConfigPath(profile.name)),
|
|
@@ -38762,7 +39488,7 @@ async function collectSkillFiles(directory, results) {
|
|
|
38762
39488
|
if (EXCLUDED_SKILL_DIRS.has(entry.name)) {
|
|
38763
39489
|
continue;
|
|
38764
39490
|
}
|
|
38765
|
-
const entryPath =
|
|
39491
|
+
const entryPath = path33.join(directory, entry.name);
|
|
38766
39492
|
if (entry.isDirectory()) {
|
|
38767
39493
|
await collectSkillFiles(entryPath, results);
|
|
38768
39494
|
continue;
|
|
@@ -38784,10 +39510,10 @@ async function readSkillMetadata(input) {
|
|
|
38784
39510
|
if (raw === null) {
|
|
38785
39511
|
return null;
|
|
38786
39512
|
}
|
|
38787
|
-
const skillDir =
|
|
39513
|
+
const skillDir = path33.dirname(input.skillFile);
|
|
38788
39514
|
const { frontmatter, body } = parseSkillDocument(raw.slice(0, 4e3));
|
|
38789
39515
|
const name = normalizeSkillName(
|
|
38790
|
-
readString24(frontmatter.name) ??
|
|
39516
|
+
readString24(frontmatter.name) ?? path33.basename(skillDir)
|
|
38791
39517
|
);
|
|
38792
39518
|
if (!name) {
|
|
38793
39519
|
return null;
|
|
@@ -38806,7 +39532,7 @@ async function readSkillMetadata(input) {
|
|
|
38806
39532
|
enabled: !input.disabled.has(name),
|
|
38807
39533
|
source: provenance.source,
|
|
38808
39534
|
trust: provenance.trust,
|
|
38809
|
-
relativePath:
|
|
39535
|
+
relativePath: path33.relative(input.skillsRoot, skillDir)
|
|
38810
39536
|
};
|
|
38811
39537
|
}
|
|
38812
39538
|
function parseSkillDocument(raw) {
|
|
@@ -38827,8 +39553,8 @@ function parseSkillDocument(raw) {
|
|
|
38827
39553
|
}
|
|
38828
39554
|
}
|
|
38829
39555
|
function categoryFromPath(skillsRoot, skillFile) {
|
|
38830
|
-
const relative =
|
|
38831
|
-
const parts = relative.split(
|
|
39556
|
+
const relative = path33.relative(skillsRoot, skillFile);
|
|
39557
|
+
const parts = relative.split(path33.sep).filter(Boolean);
|
|
38832
39558
|
return parts.length >= 3 ? parts[0] : null;
|
|
38833
39559
|
}
|
|
38834
39560
|
function firstBodyDescription(body) {
|
|
@@ -38875,7 +39601,7 @@ async function readSkillProvenance(root) {
|
|
|
38875
39601
|
return provenance;
|
|
38876
39602
|
}
|
|
38877
39603
|
async function readBundledSkillNames(root) {
|
|
38878
|
-
const raw = await readFile20(
|
|
39604
|
+
const raw = await readFile20(path33.join(root, ".bundled_manifest"), "utf8").catch(
|
|
38879
39605
|
(error) => {
|
|
38880
39606
|
if (isNodeError23(error, "ENOENT")) {
|
|
38881
39607
|
return "";
|
|
@@ -38898,7 +39624,7 @@ async function readBundledSkillNames(root) {
|
|
|
38898
39624
|
return names;
|
|
38899
39625
|
}
|
|
38900
39626
|
async function readHubInstalledSkills(root) {
|
|
38901
|
-
const raw = await readFile20(
|
|
39627
|
+
const raw = await readFile20(path33.join(root, ".hub", "lock.json"), "utf8").catch(
|
|
38902
39628
|
(error) => {
|
|
38903
39629
|
if (isNodeError23(error, "ENOENT")) {
|
|
38904
39630
|
return "";
|
|
@@ -39472,7 +40198,7 @@ function registerStatisticsRoutes(router, options) {
|
|
|
39472
40198
|
await authenticateRequest(ctx, paths);
|
|
39473
40199
|
ctx.set("cache-control", "no-store");
|
|
39474
40200
|
const usage = await readLinkUsageStatistics(paths, {
|
|
39475
|
-
days:
|
|
40201
|
+
days: readInteger5(ctx.query.days),
|
|
39476
40202
|
from: readQueryString(ctx.query.from),
|
|
39477
40203
|
to: readQueryString(ctx.query.to)
|
|
39478
40204
|
});
|
|
@@ -39566,7 +40292,7 @@ function readModelList(payload) {
|
|
|
39566
40292
|
import { EventEmitter as EventEmitter3 } from "events";
|
|
39567
40293
|
import { spawn as spawn4 } from "child_process";
|
|
39568
40294
|
import { mkdir as mkdir14, readFile as readFile21, rm as rm8 } from "fs/promises";
|
|
39569
|
-
import
|
|
40295
|
+
import path34 from "path";
|
|
39570
40296
|
var SERVER_HERMES_RELEASES_LATEST_PATH = "/api/v1/hermes-agent/releases/latest";
|
|
39571
40297
|
var RELEASE_CACHE_TTL_MS = 6 * 60 * 60 * 1e3;
|
|
39572
40298
|
var RELEASE_FETCH_TIMEOUT_MS = 5e3;
|
|
@@ -39713,7 +40439,7 @@ async function startHermesUpdate(options) {
|
|
|
39713
40439
|
}
|
|
39714
40440
|
async function readHermesUpdateStatus(paths) {
|
|
39715
40441
|
let state = await readJsonFile(updateStatePath(paths));
|
|
39716
|
-
if (state?.state === "running" && !runningUpdate && !isRecentRunningState2(state) && !
|
|
40442
|
+
if (state?.state === "running" && !runningUpdate && !isRecentRunningState2(state) && !isProcessAlive3(state.pid)) {
|
|
39717
40443
|
state = {
|
|
39718
40444
|
...state,
|
|
39719
40445
|
state: "failed",
|
|
@@ -39844,13 +40570,13 @@ async function readUpdateLogLines(paths) {
|
|
|
39844
40570
|
);
|
|
39845
40571
|
}
|
|
39846
40572
|
function releaseCachePath(paths) {
|
|
39847
|
-
return
|
|
40573
|
+
return path34.join(paths.indexesDir, "hermes-release-check.json");
|
|
39848
40574
|
}
|
|
39849
40575
|
function updateStatePath(paths) {
|
|
39850
|
-
return
|
|
40576
|
+
return path34.join(paths.runDir, "hermes-update-state.json");
|
|
39851
40577
|
}
|
|
39852
40578
|
function updateLogPath(paths) {
|
|
39853
|
-
return
|
|
40579
|
+
return path34.join(paths.logsDir, UPDATE_LOG_FILE);
|
|
39854
40580
|
}
|
|
39855
40581
|
async function clearUpdateLogFiles(paths) {
|
|
39856
40582
|
const primary = updateLogPath(paths);
|
|
@@ -39924,7 +40650,7 @@ async function readHermesReleaseCheckContext(paths) {
|
|
|
39924
40650
|
releaseCheckUrl: url.toString()
|
|
39925
40651
|
};
|
|
39926
40652
|
}
|
|
39927
|
-
function
|
|
40653
|
+
function isProcessAlive3(pid) {
|
|
39928
40654
|
if (!pid || pid <= 0) {
|
|
39929
40655
|
return false;
|
|
39930
40656
|
}
|
|
@@ -39951,12 +40677,12 @@ function readString25(payload, key) {
|
|
|
39951
40677
|
import { spawn as spawn6 } from "child_process";
|
|
39952
40678
|
import { EventEmitter as EventEmitter4 } from "events";
|
|
39953
40679
|
import { mkdir as mkdir17, readFile as readFile23, rm as rm11 } from "fs/promises";
|
|
39954
|
-
import
|
|
40680
|
+
import path36 from "path";
|
|
39955
40681
|
|
|
39956
40682
|
// src/daemon/process.ts
|
|
39957
40683
|
import { spawn as spawn5 } from "child_process";
|
|
39958
40684
|
import { mkdir as mkdir16, readFile as readFile22, rm as rm10, writeFile as writeFile4 } from "fs/promises";
|
|
39959
|
-
import
|
|
40685
|
+
import path35 from "path";
|
|
39960
40686
|
|
|
39961
40687
|
// src/daemon/service.ts
|
|
39962
40688
|
import { createServer } from "http";
|
|
@@ -40117,17 +40843,17 @@ async function fetchRelayStreamBatchPolicy(serverBaseUrl, options = {}) {
|
|
|
40117
40843
|
}
|
|
40118
40844
|
}
|
|
40119
40845
|
function readRelayStreamBatchPolicy(input) {
|
|
40120
|
-
const record =
|
|
40121
|
-
const body =
|
|
40846
|
+
const record = readRecord3(input);
|
|
40847
|
+
const body = readRecord3(record?.policy) ?? readRecord3(record?.stream_batching) ?? record;
|
|
40122
40848
|
return normalizeRelayStreamBatchPolicy(body);
|
|
40123
40849
|
}
|
|
40124
40850
|
function normalizeRelayStreamBatchPolicy(input) {
|
|
40125
|
-
const record =
|
|
40851
|
+
const record = readRecord3(input);
|
|
40126
40852
|
if (!record) {
|
|
40127
40853
|
return null;
|
|
40128
40854
|
}
|
|
40129
|
-
const flushIntervalMs =
|
|
40130
|
-
const flushBytes =
|
|
40855
|
+
const flushIntervalMs = readInteger6(record.flushIntervalMs ?? record.flush_interval_ms);
|
|
40856
|
+
const flushBytes = readInteger6(record.flushBytes ?? record.flush_bytes);
|
|
40131
40857
|
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
40858
|
return null;
|
|
40133
40859
|
}
|
|
@@ -40136,10 +40862,10 @@ function normalizeRelayStreamBatchPolicy(input) {
|
|
|
40136
40862
|
flushBytes
|
|
40137
40863
|
};
|
|
40138
40864
|
}
|
|
40139
|
-
function
|
|
40865
|
+
function readRecord3(value) {
|
|
40140
40866
|
return value && typeof value === "object" ? value : null;
|
|
40141
40867
|
}
|
|
40142
|
-
function
|
|
40868
|
+
function readInteger6(value) {
|
|
40143
40869
|
return typeof value === "number" && Number.isInteger(value) ? value : null;
|
|
40144
40870
|
}
|
|
40145
40871
|
|
|
@@ -40173,12 +40899,12 @@ function connectRelayControl(options) {
|
|
|
40173
40899
|
onUpdate: options.onStreamBatchPolicy
|
|
40174
40900
|
};
|
|
40175
40901
|
const startConnect = () => {
|
|
40176
|
-
void waitForPersistedCooldown().then((
|
|
40902
|
+
void waitForPersistedCooldown().then((delay6) => {
|
|
40177
40903
|
if (closedByUser) {
|
|
40178
40904
|
return;
|
|
40179
40905
|
}
|
|
40180
|
-
if (
|
|
40181
|
-
scheduleTimer(
|
|
40906
|
+
if (delay6 > 0) {
|
|
40907
|
+
scheduleTimer(delay6, "cooldown", `Relay reconnect cooldown active for ${delay6}ms`);
|
|
40182
40908
|
return;
|
|
40183
40909
|
}
|
|
40184
40910
|
connect();
|
|
@@ -40316,8 +41042,8 @@ function connectRelayControl(options) {
|
|
|
40316
41042
|
}
|
|
40317
41043
|
if (recorded.cooldownUntilMs !== null) {
|
|
40318
41044
|
reconnectAttempts = 0;
|
|
40319
|
-
const
|
|
40320
|
-
scheduleTimer(
|
|
41045
|
+
const delay7 = Math.max(0, recorded.cooldownUntilMs - Date.now());
|
|
41046
|
+
scheduleTimer(delay7, "cooldown", `Relay reconnect storm guard active for ${delay7}ms`);
|
|
40321
41047
|
return;
|
|
40322
41048
|
}
|
|
40323
41049
|
reconnectAttempts += 1;
|
|
@@ -40325,16 +41051,16 @@ function connectRelayControl(options) {
|
|
|
40325
41051
|
baseMs: backoffBaseMs,
|
|
40326
41052
|
maxMs: backoffMaxMs
|
|
40327
41053
|
});
|
|
40328
|
-
const
|
|
40329
|
-
scheduleTimer(
|
|
41054
|
+
const delay6 = Math.max(backoffMs, relayRetryAfterMs ?? 0);
|
|
41055
|
+
scheduleTimer(delay6, "retrying", `Retrying in ${delay6}ms`);
|
|
40330
41056
|
}
|
|
40331
41057
|
async function waitForPersistedCooldown() {
|
|
40332
41058
|
return await readRelayCooldownDelayMs(paths).catch(() => 0);
|
|
40333
41059
|
}
|
|
40334
|
-
function scheduleTimer(
|
|
41060
|
+
function scheduleTimer(delay6, state, message) {
|
|
40335
41061
|
clearRetryTimer();
|
|
40336
41062
|
options.onStatus?.({ state, attempt: reconnectAttempts, message });
|
|
40337
|
-
retryTimer = setTimeout(connect,
|
|
41063
|
+
retryTimer = setTimeout(connect, delay6);
|
|
40338
41064
|
retryTimer.unref?.();
|
|
40339
41065
|
}
|
|
40340
41066
|
function clearRetryTimer() {
|
|
@@ -41609,6 +42335,22 @@ async function startLinkService(options = {}) {
|
|
|
41609
42335
|
await logger.flush();
|
|
41610
42336
|
throw error;
|
|
41611
42337
|
}
|
|
42338
|
+
const tuiGatewayCleanup = await cleanupRegisteredTuiGatewayProcesses({
|
|
42339
|
+
paths,
|
|
42340
|
+
logger
|
|
42341
|
+
}).catch((error) => {
|
|
42342
|
+
void logger.warn("tui_gateway_registry_startup_cleanup_failed", {
|
|
42343
|
+
error: error instanceof Error ? error.message : String(error)
|
|
42344
|
+
});
|
|
42345
|
+
return null;
|
|
42346
|
+
});
|
|
42347
|
+
if (tuiGatewayCleanup) {
|
|
42348
|
+
void logger.info("tui_gateway_registry_startup_cleanup_finished", {
|
|
42349
|
+
stopped: tuiGatewayCleanup.stopped.length,
|
|
42350
|
+
missing: tuiGatewayCleanup.missing.length,
|
|
42351
|
+
skipped: tuiGatewayCleanup.skipped.length
|
|
42352
|
+
});
|
|
42353
|
+
}
|
|
41612
42354
|
server.on("error", (error) => {
|
|
41613
42355
|
void logger.error("service_error", {
|
|
41614
42356
|
port: config.port,
|
|
@@ -41734,6 +42476,7 @@ async function startLinkService(options = {}) {
|
|
|
41734
42476
|
}
|
|
41735
42477
|
closed = true;
|
|
41736
42478
|
relay?.close();
|
|
42479
|
+
await closeTuiGatewayBackends({ paths, logger, cleanupRegistry: true });
|
|
41737
42480
|
await closeServer(server);
|
|
41738
42481
|
await Promise.all([
|
|
41739
42482
|
scheduler.close(),
|
|
@@ -41949,7 +42692,7 @@ async function runDaemonSupervisor(paths = resolveRuntimePaths()) {
|
|
|
41949
42692
|
await mkdir16(paths.logsDir, { recursive: true, mode: 448 });
|
|
41950
42693
|
const log = createRotatingTextLogWriter({
|
|
41951
42694
|
paths,
|
|
41952
|
-
fileName:
|
|
42695
|
+
fileName: path35.basename(daemonLogFile(paths))
|
|
41953
42696
|
});
|
|
41954
42697
|
const scriptPath = currentCliScriptPath();
|
|
41955
42698
|
const write = (chunk) => {
|
|
@@ -41968,7 +42711,7 @@ async function runDaemonSupervisor(paths = resolveRuntimePaths()) {
|
|
|
41968
42711
|
let stopRequested = false;
|
|
41969
42712
|
const forwardStop = () => {
|
|
41970
42713
|
stopRequested = true;
|
|
41971
|
-
if (child?.pid &&
|
|
42714
|
+
if (child?.pid && isProcessAlive4(child.pid)) {
|
|
41972
42715
|
child.kill("SIGTERM");
|
|
41973
42716
|
}
|
|
41974
42717
|
};
|
|
@@ -42082,23 +42825,23 @@ async function stopDaemonProcess(paths = resolveRuntimePaths()) {
|
|
|
42082
42825
|
}
|
|
42083
42826
|
for (let index = 0; index < 20; index += 1) {
|
|
42084
42827
|
await wait2(250);
|
|
42085
|
-
if (!
|
|
42828
|
+
if (!isProcessAlive4(status.pid)) {
|
|
42086
42829
|
break;
|
|
42087
42830
|
}
|
|
42088
42831
|
}
|
|
42089
|
-
if (
|
|
42832
|
+
if (isProcessAlive4(status.pid)) {
|
|
42090
42833
|
try {
|
|
42091
42834
|
process.kill(status.pid, "SIGKILL");
|
|
42092
42835
|
} catch {
|
|
42093
42836
|
}
|
|
42094
42837
|
for (let index = 0; index < 10; index += 1) {
|
|
42095
42838
|
await wait2(250);
|
|
42096
|
-
if (!
|
|
42839
|
+
if (!isProcessAlive4(status.pid)) {
|
|
42097
42840
|
break;
|
|
42098
42841
|
}
|
|
42099
42842
|
}
|
|
42100
42843
|
}
|
|
42101
|
-
if (!
|
|
42844
|
+
if (!isProcessAlive4(status.pid) || !await pidBackedServiceIsReachable(paths)) {
|
|
42102
42845
|
await rm10(pidFilePath(paths), { force: true }).catch(() => void 0);
|
|
42103
42846
|
}
|
|
42104
42847
|
return await getDaemonStatus(paths);
|
|
@@ -42106,7 +42849,7 @@ async function stopDaemonProcess(paths = resolveRuntimePaths()) {
|
|
|
42106
42849
|
async function getDaemonStatus(paths = resolveRuntimePaths()) {
|
|
42107
42850
|
const pidFile = pidFilePath(paths);
|
|
42108
42851
|
const pid = await readPid(pidFile);
|
|
42109
|
-
if (pid && !
|
|
42852
|
+
if (pid && !isProcessAlive4(pid)) {
|
|
42110
42853
|
await rm10(pidFile, { force: true }).catch(() => void 0);
|
|
42111
42854
|
return {
|
|
42112
42855
|
running: false,
|
|
@@ -42136,7 +42879,7 @@ async function readPid(filePath) {
|
|
|
42136
42879
|
const pid = Number.parseInt(raw.trim(), 10);
|
|
42137
42880
|
return Number.isInteger(pid) && pid > 0 ? pid : null;
|
|
42138
42881
|
}
|
|
42139
|
-
function
|
|
42882
|
+
function isProcessAlive4(pid) {
|
|
42140
42883
|
try {
|
|
42141
42884
|
process.kill(pid, 0);
|
|
42142
42885
|
return true;
|
|
@@ -42178,7 +42921,7 @@ function startSupervisorHealthMonitor(paths, child, write) {
|
|
|
42178
42921
|
);
|
|
42179
42922
|
terminateChild(child, forceKillTimer);
|
|
42180
42923
|
forceKillTimer = setTimeout(() => {
|
|
42181
|
-
if (child.pid &&
|
|
42924
|
+
if (child.pid && isProcessAlive4(child.pid)) {
|
|
42182
42925
|
child.kill("SIGKILL");
|
|
42183
42926
|
}
|
|
42184
42927
|
}, SUPERVISOR_CHILD_STOP_TIMEOUT_MS);
|
|
@@ -42196,7 +42939,7 @@ function startSupervisorHealthMonitor(paths, child, write) {
|
|
|
42196
42939
|
closed = true;
|
|
42197
42940
|
terminateChild(child, forceKillTimer);
|
|
42198
42941
|
forceKillTimer = setTimeout(() => {
|
|
42199
|
-
if (child.pid &&
|
|
42942
|
+
if (child.pid && isProcessAlive4(child.pid)) {
|
|
42200
42943
|
child.kill("SIGKILL");
|
|
42201
42944
|
}
|
|
42202
42945
|
}, SUPERVISOR_CHILD_STOP_TIMEOUT_MS);
|
|
@@ -42243,12 +42986,12 @@ function terminateChild(child, previousForceKillTimer) {
|
|
|
42243
42986
|
if (previousForceKillTimer) {
|
|
42244
42987
|
clearTimeout(previousForceKillTimer);
|
|
42245
42988
|
}
|
|
42246
|
-
if (child.pid &&
|
|
42989
|
+
if (child.pid && isProcessAlive4(child.pid)) {
|
|
42247
42990
|
child.kill("SIGTERM");
|
|
42248
42991
|
}
|
|
42249
42992
|
}
|
|
42250
42993
|
function supervisorStopIntentPath(paths) {
|
|
42251
|
-
return
|
|
42994
|
+
return path35.join(paths.runDir, "supervisor-stop-intent.json");
|
|
42252
42995
|
}
|
|
42253
42996
|
async function writeSupervisorStopIntent(paths, pid) {
|
|
42254
42997
|
await mkdir16(paths.runDir, { recursive: true, mode: 448 });
|
|
@@ -42632,7 +43375,7 @@ async function readLinkUpdateStatus(paths) {
|
|
|
42632
43375
|
};
|
|
42633
43376
|
await writeUpdateState2(paths, state);
|
|
42634
43377
|
}
|
|
42635
|
-
if (state?.state === "running" && !runningUpdate2 && !isRecentRunningState3(state) && !
|
|
43378
|
+
if (state?.state === "running" && !runningUpdate2 && !isRecentRunningState3(state) && !isProcessAlive5(state.pid)) {
|
|
42636
43379
|
const reachedTarget = state.target_version && compareSemver3(LINK_VERSION, state.target_version) >= 0;
|
|
42637
43380
|
state = reachedTarget ? {
|
|
42638
43381
|
...state,
|
|
@@ -42811,7 +43554,7 @@ async function buildOfficialInstallCommand(options, targetVersion) {
|
|
|
42811
43554
|
};
|
|
42812
43555
|
}
|
|
42813
43556
|
function buildUnixInstallCommand(installerUrl) {
|
|
42814
|
-
const nodeBinDir =
|
|
43557
|
+
const nodeBinDir = path36.dirname(process.execPath);
|
|
42815
43558
|
const fetchScript = [
|
|
42816
43559
|
quoteShellToken(process.execPath),
|
|
42817
43560
|
"--input-type=module",
|
|
@@ -43081,10 +43824,10 @@ async function readUpdateLogLines2(paths) {
|
|
|
43081
43824
|
);
|
|
43082
43825
|
}
|
|
43083
43826
|
function updateStatePath2(paths) {
|
|
43084
|
-
return
|
|
43827
|
+
return path36.join(paths.runDir, "link-update-state.json");
|
|
43085
43828
|
}
|
|
43086
43829
|
function updateLogPath2(paths) {
|
|
43087
|
-
return
|
|
43830
|
+
return path36.join(paths.logsDir, UPDATE_LOG_FILE2);
|
|
43088
43831
|
}
|
|
43089
43832
|
async function clearUpdateLogFiles2(paths) {
|
|
43090
43833
|
const primary = updateLogPath2(paths);
|
|
@@ -43145,7 +43888,7 @@ function linkUpdateTimeoutError() {
|
|
|
43145
43888
|
LINK_UPDATE_TIMEOUT_MS / 6e4
|
|
43146
43889
|
)} minutes.`;
|
|
43147
43890
|
}
|
|
43148
|
-
function
|
|
43891
|
+
function isProcessAlive5(pid) {
|
|
43149
43892
|
if (!pid || pid <= 0) {
|
|
43150
43893
|
return false;
|
|
43151
43894
|
}
|
|
@@ -43168,7 +43911,7 @@ function readString26(payload, key) {
|
|
|
43168
43911
|
}
|
|
43169
43912
|
|
|
43170
43913
|
// src/pairing/pairing.ts
|
|
43171
|
-
import
|
|
43914
|
+
import path37 from "path";
|
|
43172
43915
|
import { rm as rm12 } from "fs/promises";
|
|
43173
43916
|
|
|
43174
43917
|
// src/relay/bootstrap.ts
|
|
@@ -43508,10 +44251,10 @@ async function loadRequiredIdentity2(paths) {
|
|
|
43508
44251
|
}
|
|
43509
44252
|
return identity;
|
|
43510
44253
|
}
|
|
43511
|
-
async function postServerJson(serverBaseUrl,
|
|
44254
|
+
async function postServerJson(serverBaseUrl, path38, body, options) {
|
|
43512
44255
|
let response;
|
|
43513
44256
|
try {
|
|
43514
|
-
response = await fetch(`${serverBaseUrl.replace(/\/+$/u, "")}${
|
|
44257
|
+
response = await fetch(`${serverBaseUrl.replace(/\/+$/u, "")}${path38}`, {
|
|
43515
44258
|
method: "POST",
|
|
43516
44259
|
headers: {
|
|
43517
44260
|
accept: "application/json",
|
|
@@ -43559,10 +44302,10 @@ function pairingErrorSnapshot(stage, error) {
|
|
|
43559
44302
|
occurred_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
43560
44303
|
};
|
|
43561
44304
|
}
|
|
43562
|
-
async function patchServerJson(serverBaseUrl,
|
|
44305
|
+
async function patchServerJson(serverBaseUrl, path38, token, body, options) {
|
|
43563
44306
|
let response;
|
|
43564
44307
|
try {
|
|
43565
|
-
response = await fetch(`${serverBaseUrl.replace(/\/+$/u, "")}${
|
|
44308
|
+
response = await fetch(`${serverBaseUrl.replace(/\/+$/u, "")}${path38}`, {
|
|
43566
44309
|
method: "PATCH",
|
|
43567
44310
|
headers: {
|
|
43568
44311
|
accept: "application/json",
|
|
@@ -43610,10 +44353,10 @@ function createPairingNetworkError(input) {
|
|
|
43610
44353
|
);
|
|
43611
44354
|
}
|
|
43612
44355
|
function pairingClaimPath(sessionId, paths) {
|
|
43613
|
-
return
|
|
44356
|
+
return path37.join(paths.pairingDir, `${Buffer.from(sessionId).toString("base64url")}.claimed.json`);
|
|
43614
44357
|
}
|
|
43615
44358
|
function pairingSessionPath(sessionId, paths) {
|
|
43616
|
-
return
|
|
44359
|
+
return path37.join(paths.pairingDir, `${Buffer.from(sessionId).toString("base64url")}.json`);
|
|
43617
44360
|
}
|
|
43618
44361
|
function qrPreferredUrls(routes) {
|
|
43619
44362
|
return routes.preferredUrls.filter((url) => !url.includes("/api/v1/relay/links/")).slice(0, 1);
|
|
@@ -44895,6 +45638,9 @@ function registerLiveActivityRoutes(router, options) {
|
|
|
44895
45638
|
status_label: state.statusLabel,
|
|
44896
45639
|
progress_text: state.progressText,
|
|
44897
45640
|
detail_text: state.detailText ?? null,
|
|
45641
|
+
todo_done_count: state.todoDoneCount ?? null,
|
|
45642
|
+
todo_total_count: state.todoTotalCount ?? null,
|
|
45643
|
+
todo_progress_text: state.todoProgressText ?? null,
|
|
44898
45644
|
updated_at: state.updatedAt ?? null
|
|
44899
45645
|
} : null
|
|
44900
45646
|
};
|