@hermespilot/link 0.7.9 → 0.8.1-beta.0
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-LUGNDJU3.js → chunk-HBRTCPIG.js} +2064 -578
- package/dist/cli/index.js +175 -9
- package/dist/http/app.d.ts +1 -0
- package/dist/http/app.js +1 -1
- package/package.json +1 -1
|
@@ -4,8 +4,8 @@ import Router from "@koa/router";
|
|
|
4
4
|
|
|
5
5
|
// src/conversations/conversation-service.ts
|
|
6
6
|
import { EventEmitter } from "events";
|
|
7
|
-
import { createHash as
|
|
8
|
-
import
|
|
7
|
+
import { createHash as createHash8, randomUUID as randomUUID12 } from "crypto";
|
|
8
|
+
import path26 from "path";
|
|
9
9
|
|
|
10
10
|
// src/database/link-database.ts
|
|
11
11
|
import { mkdir } from "fs/promises";
|
|
@@ -151,6 +151,62 @@ var MIGRATIONS = [
|
|
|
151
151
|
OR message_count > 0
|
|
152
152
|
OR run_count > 0;
|
|
153
153
|
`
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
version: 4,
|
|
157
|
+
name: "live_activity_registry",
|
|
158
|
+
sql: `
|
|
159
|
+
CREATE TABLE IF NOT EXISTS live_activities (
|
|
160
|
+
activity_id TEXT PRIMARY KEY,
|
|
161
|
+
activity_token TEXT NOT NULL,
|
|
162
|
+
apns_environment TEXT NOT NULL CHECK (apns_environment IN ('sandbox', 'production')),
|
|
163
|
+
apns_topic TEXT NOT NULL,
|
|
164
|
+
link_id TEXT NOT NULL,
|
|
165
|
+
account_id TEXT,
|
|
166
|
+
app_instance_id TEXT NOT NULL,
|
|
167
|
+
conversation_id TEXT NOT NULL,
|
|
168
|
+
conversation_title TEXT,
|
|
169
|
+
target_kind TEXT NOT NULL CHECK (target_kind IN ('run', 'context_compression')),
|
|
170
|
+
target_id TEXT NOT NULL,
|
|
171
|
+
run_id TEXT,
|
|
172
|
+
operation_id TEXT,
|
|
173
|
+
status TEXT NOT NULL CHECK (status IN ('active', 'ended', 'expired', 'invalid')),
|
|
174
|
+
language TEXT CHECK (language IS NULL OR language IN ('zh', 'en')),
|
|
175
|
+
privacy_level TEXT NOT NULL DEFAULT 'summary'
|
|
176
|
+
CHECK (privacy_level IN ('minimal', 'summary', 'detailed')),
|
|
177
|
+
last_phase TEXT,
|
|
178
|
+
last_summary_hash TEXT,
|
|
179
|
+
last_pushed_at TEXT,
|
|
180
|
+
push_count INTEGER NOT NULL DEFAULT 0,
|
|
181
|
+
started_at TEXT NOT NULL,
|
|
182
|
+
ended_at TEXT,
|
|
183
|
+
created_at TEXT NOT NULL,
|
|
184
|
+
updated_at TEXT NOT NULL,
|
|
185
|
+
CHECK (
|
|
186
|
+
(target_kind = 'run' AND run_id IS NOT NULL AND operation_id IS NULL)
|
|
187
|
+
OR
|
|
188
|
+
(target_kind = 'context_compression' AND operation_id IS NOT NULL AND run_id IS NULL)
|
|
189
|
+
)
|
|
190
|
+
);
|
|
191
|
+
|
|
192
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_live_activities_target_active
|
|
193
|
+
ON live_activities(app_instance_id, conversation_id, target_kind, target_id)
|
|
194
|
+
WHERE status = 'active';
|
|
195
|
+
CREATE INDEX IF NOT EXISTS idx_live_activities_target_lookup
|
|
196
|
+
ON live_activities(conversation_id, target_kind, target_id, status);
|
|
197
|
+
CREATE INDEX IF NOT EXISTS idx_live_activities_status_updated
|
|
198
|
+
ON live_activities(status, updated_at);
|
|
199
|
+
`
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
version: 5,
|
|
203
|
+
name: "live_activity_visible_state",
|
|
204
|
+
sql: `
|
|
205
|
+
ALTER TABLE live_activities ADD COLUMN last_status_label TEXT;
|
|
206
|
+
ALTER TABLE live_activities ADD COLUMN last_progress_text TEXT;
|
|
207
|
+
ALTER TABLE live_activities ADD COLUMN last_detail_text TEXT;
|
|
208
|
+
ALTER TABLE live_activities ADD COLUMN last_visible_updated_at TEXT;
|
|
209
|
+
`
|
|
154
210
|
}
|
|
155
211
|
];
|
|
156
212
|
async function migrateLinkDatabase(paths) {
|
|
@@ -229,6 +285,245 @@ async function upsertConversationStats(paths, record) {
|
|
|
229
285
|
db.close();
|
|
230
286
|
}
|
|
231
287
|
}
|
|
288
|
+
async function upsertLiveActivity(paths, input) {
|
|
289
|
+
await migrateLinkDatabase(paths);
|
|
290
|
+
const db = openDatabase(paths);
|
|
291
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
292
|
+
try {
|
|
293
|
+
db.exec("BEGIN IMMEDIATE");
|
|
294
|
+
try {
|
|
295
|
+
const restoreState = readLiveActivityRestoreStateForTarget(db, {
|
|
296
|
+
accountId: input.accountId,
|
|
297
|
+
appInstanceId: input.appInstanceId,
|
|
298
|
+
conversationId: input.conversationId,
|
|
299
|
+
targetKind: input.targetKind,
|
|
300
|
+
targetId: input.targetId
|
|
301
|
+
});
|
|
302
|
+
db.prepare(
|
|
303
|
+
`
|
|
304
|
+
UPDATE live_activities
|
|
305
|
+
SET status = 'expired',
|
|
306
|
+
ended_at = COALESCE(ended_at, ?),
|
|
307
|
+
updated_at = ?
|
|
308
|
+
WHERE app_instance_id = ?
|
|
309
|
+
AND activity_id != ?
|
|
310
|
+
AND status = 'active'
|
|
311
|
+
`
|
|
312
|
+
).run(now, now, input.appInstanceId, input.activityId);
|
|
313
|
+
db.prepare(
|
|
314
|
+
`
|
|
315
|
+
INSERT INTO live_activities (
|
|
316
|
+
activity_id,
|
|
317
|
+
activity_token,
|
|
318
|
+
apns_environment,
|
|
319
|
+
apns_topic,
|
|
320
|
+
link_id,
|
|
321
|
+
account_id,
|
|
322
|
+
app_instance_id,
|
|
323
|
+
conversation_id,
|
|
324
|
+
conversation_title,
|
|
325
|
+
target_kind,
|
|
326
|
+
target_id,
|
|
327
|
+
run_id,
|
|
328
|
+
operation_id,
|
|
329
|
+
status,
|
|
330
|
+
language,
|
|
331
|
+
privacy_level,
|
|
332
|
+
last_phase,
|
|
333
|
+
last_summary_hash,
|
|
334
|
+
last_pushed_at,
|
|
335
|
+
last_status_label,
|
|
336
|
+
last_progress_text,
|
|
337
|
+
last_detail_text,
|
|
338
|
+
last_visible_updated_at,
|
|
339
|
+
push_count,
|
|
340
|
+
started_at,
|
|
341
|
+
ended_at,
|
|
342
|
+
created_at,
|
|
343
|
+
updated_at
|
|
344
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'active', ?, ?, ?, NULL, NULL, ?, ?, ?, ?, 0, ?, NULL, ?, ?)
|
|
345
|
+
ON CONFLICT(activity_id) DO UPDATE SET
|
|
346
|
+
activity_token = excluded.activity_token,
|
|
347
|
+
apns_environment = excluded.apns_environment,
|
|
348
|
+
apns_topic = excluded.apns_topic,
|
|
349
|
+
link_id = excluded.link_id,
|
|
350
|
+
account_id = excluded.account_id,
|
|
351
|
+
app_instance_id = excluded.app_instance_id,
|
|
352
|
+
conversation_id = excluded.conversation_id,
|
|
353
|
+
conversation_title = excluded.conversation_title,
|
|
354
|
+
target_kind = excluded.target_kind,
|
|
355
|
+
target_id = excluded.target_id,
|
|
356
|
+
run_id = excluded.run_id,
|
|
357
|
+
operation_id = excluded.operation_id,
|
|
358
|
+
status = 'active',
|
|
359
|
+
language = excluded.language,
|
|
360
|
+
privacy_level = excluded.privacy_level,
|
|
361
|
+
last_phase = excluded.last_phase,
|
|
362
|
+
last_summary_hash = NULL,
|
|
363
|
+
last_pushed_at = NULL,
|
|
364
|
+
last_status_label = excluded.last_status_label,
|
|
365
|
+
last_progress_text = excluded.last_progress_text,
|
|
366
|
+
last_detail_text = excluded.last_detail_text,
|
|
367
|
+
last_visible_updated_at = excluded.last_visible_updated_at,
|
|
368
|
+
push_count = 0,
|
|
369
|
+
started_at = excluded.started_at,
|
|
370
|
+
ended_at = NULL,
|
|
371
|
+
updated_at = excluded.updated_at
|
|
372
|
+
`
|
|
373
|
+
).run(
|
|
374
|
+
input.activityId,
|
|
375
|
+
input.activityToken,
|
|
376
|
+
input.apnsEnvironment,
|
|
377
|
+
input.apnsTopic,
|
|
378
|
+
input.linkId,
|
|
379
|
+
input.accountId ?? null,
|
|
380
|
+
input.appInstanceId,
|
|
381
|
+
input.conversationId,
|
|
382
|
+
input.conversationTitle ?? null,
|
|
383
|
+
input.targetKind,
|
|
384
|
+
input.targetId,
|
|
385
|
+
input.runId ?? null,
|
|
386
|
+
input.operationId ?? null,
|
|
387
|
+
input.language ?? null,
|
|
388
|
+
input.privacyLevel ?? "summary",
|
|
389
|
+
restoreState?.phase ?? "connecting",
|
|
390
|
+
restoreState?.statusLabel ?? null,
|
|
391
|
+
restoreState?.progressText ?? null,
|
|
392
|
+
restoreState?.detailText ?? null,
|
|
393
|
+
restoreState?.updatedAt ?? null,
|
|
394
|
+
input.startedAt,
|
|
395
|
+
now,
|
|
396
|
+
now
|
|
397
|
+
);
|
|
398
|
+
db.exec("COMMIT");
|
|
399
|
+
} catch (error) {
|
|
400
|
+
rollback(db);
|
|
401
|
+
throw error;
|
|
402
|
+
}
|
|
403
|
+
const row = readLiveActivityRow(db, input.activityId);
|
|
404
|
+
if (!row) {
|
|
405
|
+
throw new Error("Live Activity was not persisted");
|
|
406
|
+
}
|
|
407
|
+
return row;
|
|
408
|
+
} finally {
|
|
409
|
+
db.close();
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
async function findActiveLiveActivityForTarget(paths, input) {
|
|
413
|
+
await migrateLinkDatabase(paths);
|
|
414
|
+
const db = openDatabase(paths);
|
|
415
|
+
try {
|
|
416
|
+
const accountFilter = input.accountId ? "AND account_id = ?" : "";
|
|
417
|
+
const appInstanceFilter = input.appInstanceId ? "AND app_instance_id = ?" : "";
|
|
418
|
+
const row = db.prepare(
|
|
419
|
+
`
|
|
420
|
+
SELECT *
|
|
421
|
+
FROM live_activities
|
|
422
|
+
WHERE conversation_id = ?
|
|
423
|
+
AND target_kind = ?
|
|
424
|
+
AND target_id = ?
|
|
425
|
+
AND status = 'active'
|
|
426
|
+
${accountFilter}
|
|
427
|
+
${appInstanceFilter}
|
|
428
|
+
ORDER BY datetime(started_at) DESC
|
|
429
|
+
LIMIT 1
|
|
430
|
+
`
|
|
431
|
+
).get(
|
|
432
|
+
input.conversationId,
|
|
433
|
+
input.targetKind,
|
|
434
|
+
input.targetId,
|
|
435
|
+
...input.accountId ? [input.accountId] : [],
|
|
436
|
+
...input.appInstanceId ? [input.appInstanceId] : []
|
|
437
|
+
);
|
|
438
|
+
return liveActivityFromRow(row);
|
|
439
|
+
} finally {
|
|
440
|
+
db.close();
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
async function findLiveActivityRestoreState(paths, input) {
|
|
444
|
+
await migrateLinkDatabase(paths);
|
|
445
|
+
const db = openDatabase(paths);
|
|
446
|
+
try {
|
|
447
|
+
return readLiveActivityRestoreStateForTarget(db, input);
|
|
448
|
+
} finally {
|
|
449
|
+
db.close();
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
async function endLiveActivity(paths, activityId) {
|
|
453
|
+
await migrateLinkDatabase(paths);
|
|
454
|
+
const db = openDatabase(paths);
|
|
455
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
456
|
+
try {
|
|
457
|
+
const result = db.prepare(
|
|
458
|
+
`
|
|
459
|
+
UPDATE live_activities
|
|
460
|
+
SET status = 'ended',
|
|
461
|
+
ended_at = COALESCE(ended_at, ?),
|
|
462
|
+
updated_at = ?
|
|
463
|
+
WHERE activity_id = ?
|
|
464
|
+
`
|
|
465
|
+
).run(now, now, activityId);
|
|
466
|
+
return result.changes > 0;
|
|
467
|
+
} finally {
|
|
468
|
+
db.close();
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
async function updateLiveActivityPushState(paths, input) {
|
|
472
|
+
await migrateLinkDatabase(paths);
|
|
473
|
+
const db = openDatabase(paths);
|
|
474
|
+
try {
|
|
475
|
+
db.prepare(
|
|
476
|
+
`
|
|
477
|
+
UPDATE live_activities
|
|
478
|
+
SET last_phase = ?,
|
|
479
|
+
last_summary_hash = ?,
|
|
480
|
+
last_pushed_at = ?,
|
|
481
|
+
last_status_label = ?,
|
|
482
|
+
last_progress_text = ?,
|
|
483
|
+
last_detail_text = ?,
|
|
484
|
+
last_visible_updated_at = ?,
|
|
485
|
+
push_count = push_count + 1,
|
|
486
|
+
status = CASE WHEN ? THEN 'ended' ELSE status END,
|
|
487
|
+
ended_at = CASE WHEN ? THEN COALESCE(ended_at, ?) ELSE ended_at END,
|
|
488
|
+
updated_at = ?
|
|
489
|
+
WHERE activity_id = ?
|
|
490
|
+
`
|
|
491
|
+
).run(
|
|
492
|
+
input.phase,
|
|
493
|
+
input.summaryHash,
|
|
494
|
+
input.pushedAt,
|
|
495
|
+
normalizeNullableText(input.statusLabel),
|
|
496
|
+
normalizeNullableText(input.progressText),
|
|
497
|
+
normalizeNullableText(input.detailText),
|
|
498
|
+
input.visibleUpdatedAt,
|
|
499
|
+
input.terminal ? 1 : 0,
|
|
500
|
+
input.terminal ? 1 : 0,
|
|
501
|
+
input.pushedAt,
|
|
502
|
+
input.pushedAt,
|
|
503
|
+
input.activityId
|
|
504
|
+
);
|
|
505
|
+
} finally {
|
|
506
|
+
db.close();
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
async function invalidateLiveActivity(paths, activityId) {
|
|
510
|
+
await migrateLinkDatabase(paths);
|
|
511
|
+
const db = openDatabase(paths);
|
|
512
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
513
|
+
try {
|
|
514
|
+
db.prepare(
|
|
515
|
+
`
|
|
516
|
+
UPDATE live_activities
|
|
517
|
+
SET status = 'invalid',
|
|
518
|
+
ended_at = COALESCE(ended_at, ?),
|
|
519
|
+
updated_at = ?
|
|
520
|
+
WHERE activity_id = ?
|
|
521
|
+
`
|
|
522
|
+
).run(now, now, activityId);
|
|
523
|
+
} finally {
|
|
524
|
+
db.close();
|
|
525
|
+
}
|
|
526
|
+
}
|
|
232
527
|
async function listConversationStatsPage(paths, input) {
|
|
233
528
|
await migrateLinkDatabase(paths);
|
|
234
529
|
const rawLimit = Number.isFinite(input.limit) ? Math.trunc(input.limit) : 25;
|
|
@@ -975,6 +1270,109 @@ function runUsageFactParams(record) {
|
|
|
975
1270
|
record.updatedAt
|
|
976
1271
|
];
|
|
977
1272
|
}
|
|
1273
|
+
function readLiveActivityRow(db, activityId) {
|
|
1274
|
+
const row = db.prepare("SELECT * FROM live_activities WHERE activity_id = ? LIMIT 1").get(activityId);
|
|
1275
|
+
return liveActivityFromRow(row);
|
|
1276
|
+
}
|
|
1277
|
+
function readLiveActivityRestoreStateForTarget(db, input) {
|
|
1278
|
+
const accountFilter = input.accountId ? "AND (account_id = ? OR account_id IS NULL)" : "";
|
|
1279
|
+
const row = db.prepare(
|
|
1280
|
+
`
|
|
1281
|
+
SELECT *
|
|
1282
|
+
FROM live_activities
|
|
1283
|
+
WHERE app_instance_id = ?
|
|
1284
|
+
AND conversation_id = ?
|
|
1285
|
+
AND target_kind = ?
|
|
1286
|
+
AND target_id = ?
|
|
1287
|
+
AND status IN ('active', 'ended', 'expired')
|
|
1288
|
+
AND last_phase IS NOT NULL
|
|
1289
|
+
AND last_status_label IS NOT NULL
|
|
1290
|
+
AND last_progress_text IS NOT NULL
|
|
1291
|
+
${accountFilter}
|
|
1292
|
+
ORDER BY datetime(COALESCE(last_visible_updated_at, updated_at)) DESC
|
|
1293
|
+
LIMIT 1
|
|
1294
|
+
`
|
|
1295
|
+
).get(
|
|
1296
|
+
input.appInstanceId,
|
|
1297
|
+
input.conversationId,
|
|
1298
|
+
input.targetKind,
|
|
1299
|
+
input.targetId,
|
|
1300
|
+
...input.accountId ? [input.accountId] : []
|
|
1301
|
+
);
|
|
1302
|
+
return liveActivityRestoreStateFromRow(row);
|
|
1303
|
+
}
|
|
1304
|
+
function liveActivityFromRow(row) {
|
|
1305
|
+
const activityId = readString(row, "activity_id");
|
|
1306
|
+
const activityToken = readString(row, "activity_token");
|
|
1307
|
+
const apnsEnvironment = readString(row, "apns_environment");
|
|
1308
|
+
const apnsTopic = readString(row, "apns_topic");
|
|
1309
|
+
const linkId = readString(row, "link_id");
|
|
1310
|
+
const appInstanceId = readString(row, "app_instance_id");
|
|
1311
|
+
const conversationId = readString(row, "conversation_id");
|
|
1312
|
+
const targetKind = readString(row, "target_kind");
|
|
1313
|
+
const targetId = readString(row, "target_id");
|
|
1314
|
+
const status = readString(row, "status");
|
|
1315
|
+
const startedAt = readString(row, "started_at");
|
|
1316
|
+
const createdAt = readString(row, "created_at");
|
|
1317
|
+
const updatedAt = readString(row, "updated_at");
|
|
1318
|
+
if (!activityId || !activityToken || apnsEnvironment !== "sandbox" && apnsEnvironment !== "production" || !apnsTopic || !linkId || !appInstanceId || !conversationId || targetKind !== "run" && targetKind !== "context_compression" || !targetId || !isLiveActivityStatus(status) || !startedAt || !createdAt || !updatedAt) {
|
|
1319
|
+
return null;
|
|
1320
|
+
}
|
|
1321
|
+
const language = readString(row, "language");
|
|
1322
|
+
const privacyLevel = readString(row, "privacy_level");
|
|
1323
|
+
return {
|
|
1324
|
+
activityId,
|
|
1325
|
+
activityToken,
|
|
1326
|
+
apnsEnvironment,
|
|
1327
|
+
apnsTopic,
|
|
1328
|
+
linkId,
|
|
1329
|
+
accountId: readNullableString(row, "account_id"),
|
|
1330
|
+
appInstanceId,
|
|
1331
|
+
conversationId,
|
|
1332
|
+
conversationTitle: readNullableString(row, "conversation_title"),
|
|
1333
|
+
targetKind,
|
|
1334
|
+
targetId,
|
|
1335
|
+
runId: readNullableString(row, "run_id"),
|
|
1336
|
+
operationId: readNullableString(row, "operation_id"),
|
|
1337
|
+
status,
|
|
1338
|
+
language: language === "zh" || language === "en" ? language : null,
|
|
1339
|
+
privacyLevel: privacyLevel === "minimal" || privacyLevel === "summary" || privacyLevel === "detailed" ? privacyLevel : "summary",
|
|
1340
|
+
lastPhase: readNullableString(row, "last_phase"),
|
|
1341
|
+
lastSummaryHash: readNullableString(row, "last_summary_hash"),
|
|
1342
|
+
lastPushedAt: readNullableString(row, "last_pushed_at"),
|
|
1343
|
+
lastStatusLabel: readNullableString(row, "last_status_label"),
|
|
1344
|
+
lastProgressText: readNullableString(row, "last_progress_text"),
|
|
1345
|
+
lastDetailText: readNullableString(row, "last_detail_text"),
|
|
1346
|
+
lastVisibleUpdatedAt: readNullableString(row, "last_visible_updated_at"),
|
|
1347
|
+
pushCount: readNumber(row, "push_count"),
|
|
1348
|
+
startedAt,
|
|
1349
|
+
endedAt: readNullableString(row, "ended_at"),
|
|
1350
|
+
createdAt,
|
|
1351
|
+
updatedAt
|
|
1352
|
+
};
|
|
1353
|
+
}
|
|
1354
|
+
function liveActivityRestoreStateFromRow(row) {
|
|
1355
|
+
const phase = readString(row, "last_phase");
|
|
1356
|
+
const statusLabel = readString(row, "last_status_label");
|
|
1357
|
+
const progressText = readString(row, "last_progress_text");
|
|
1358
|
+
if (!phase || !statusLabel || !progressText) {
|
|
1359
|
+
return null;
|
|
1360
|
+
}
|
|
1361
|
+
return {
|
|
1362
|
+
phase,
|
|
1363
|
+
statusLabel,
|
|
1364
|
+
progressText,
|
|
1365
|
+
detailText: readNullableString(row, "last_detail_text"),
|
|
1366
|
+
updatedAt: readNullableString(row, "last_visible_updated_at") ?? readNullableString(row, "updated_at")
|
|
1367
|
+
};
|
|
1368
|
+
}
|
|
1369
|
+
function isLiveActivityStatus(value) {
|
|
1370
|
+
return value === "active" || value === "ended" || value === "expired" || value === "invalid";
|
|
1371
|
+
}
|
|
1372
|
+
function normalizeNullableText(value) {
|
|
1373
|
+
const trimmed = value?.trim();
|
|
1374
|
+
return trimmed ? trimmed : null;
|
|
1375
|
+
}
|
|
978
1376
|
function rollback(db) {
|
|
979
1377
|
try {
|
|
980
1378
|
db.exec("ROLLBACK");
|
|
@@ -1544,7 +1942,7 @@ var messages = {
|
|
|
1544
1942
|
"autostart.enabled": "Boot autostart enabled via {method}: {path}",
|
|
1545
1943
|
"autostart.disabled": "Boot autostart disabled.",
|
|
1546
1944
|
"autostart.status.enabled": "Boot autostart: enabled via {method}: {path}",
|
|
1547
|
-
"autostart.status.disabled": "Boot autostart: disabled. Method: {method}.
|
|
1945
|
+
"autostart.status.disabled": "Boot autostart: disabled. Method: {method}. Target: {path}",
|
|
1548
1946
|
"autostart.unsupported": "Boot autostart is not supported on this platform yet.",
|
|
1549
1947
|
"autostart.alreadyEnabled": "Boot autostart is already enabled via {method}: {path}",
|
|
1550
1948
|
"pair.description": "Create a Hermes Link pairing session",
|
|
@@ -1787,12 +2185,12 @@ var messages = {
|
|
|
1787
2185
|
"autostart.on.description": "\u542F\u7528\u5F00\u673A\u81EA\u542F",
|
|
1788
2186
|
"autostart.off.description": "\u5173\u95ED\u5F00\u673A\u81EA\u542F",
|
|
1789
2187
|
"autostart.status.description": "\u67E5\u770B\u5F00\u673A\u81EA\u542F\u72B6\u6001",
|
|
1790
|
-
"autostart.enabled": "\u5DF2\u542F\u7528\u5F00\u673A\u81EA\u542F\uFF0C\u65B9\u5F0F\uFF1A{method}\uFF0C\
|
|
2188
|
+
"autostart.enabled": "\u5DF2\u542F\u7528\u5F00\u673A\u81EA\u542F\uFF0C\u65B9\u5F0F\uFF1A{method}\uFF0C\u4F4D\u7F6E\uFF1A{path}",
|
|
1791
2189
|
"autostart.disabled": "\u5DF2\u5173\u95ED\u5F00\u673A\u81EA\u542F\u3002",
|
|
1792
|
-
"autostart.status.enabled": "\u5F00\u673A\u81EA\u542F\uFF1A\u5DF2\u542F\u7528\uFF0C\u65B9\u5F0F\uFF1A{method}\uFF0C\
|
|
1793
|
-
"autostart.status.disabled": "\u5F00\u673A\u81EA\u542F\uFF1A\u672A\u542F\u7528\u3002\u65B9\u5F0F\uFF1A{method}\uFF0C\
|
|
2190
|
+
"autostart.status.enabled": "\u5F00\u673A\u81EA\u542F\uFF1A\u5DF2\u542F\u7528\uFF0C\u65B9\u5F0F\uFF1A{method}\uFF0C\u4F4D\u7F6E\uFF1A{path}",
|
|
2191
|
+
"autostart.status.disabled": "\u5F00\u673A\u81EA\u542F\uFF1A\u672A\u542F\u7528\u3002\u65B9\u5F0F\uFF1A{method}\uFF0C\u76EE\u6807\uFF1A{path}",
|
|
1794
2192
|
"autostart.unsupported": "\u5F53\u524D\u5E73\u53F0\u6682\u4E0D\u652F\u6301\u5F00\u673A\u81EA\u542F\u3002",
|
|
1795
|
-
"autostart.alreadyEnabled": "\u5F00\u673A\u81EA\u542F\u5DF2\u542F\u7528\uFF0C\u65B9\u5F0F\uFF1A{method}\uFF0C\
|
|
2193
|
+
"autostart.alreadyEnabled": "\u5F00\u673A\u81EA\u542F\u5DF2\u542F\u7528\uFF0C\u65B9\u5F0F\uFF1A{method}\uFF0C\u4F4D\u7F6E\uFF1A{path}",
|
|
1796
2194
|
"pair.description": "\u521B\u5EFA Hermes Link \u914D\u5BF9\u4F1A\u8BDD",
|
|
1797
2195
|
"pair.preflight": "\u6B63\u5728\u914D\u5BF9\u524D\u68C0\u67E5\u672C\u673A Hermes \u914D\u7F6E...",
|
|
1798
2196
|
"pair.preflight.hermesFiles": "\u6B63\u5728\u68C0\u67E5 Hermes \u6570\u636E\u76EE\u5F55\u3001\u914D\u7F6E\u6587\u4EF6\u548C\u73AF\u5883\u6587\u4EF6...",
|
|
@@ -2068,6 +2466,22 @@ function parseLanguage(value) {
|
|
|
2068
2466
|
}
|
|
2069
2467
|
|
|
2070
2468
|
// src/hermes/config.ts
|
|
2469
|
+
function filterHermesModelConfigsByProviderKeys(result, hiddenProviderKeys) {
|
|
2470
|
+
if (hiddenProviderKeys.size === 0) {
|
|
2471
|
+
return result;
|
|
2472
|
+
}
|
|
2473
|
+
const models = result.models.filter(
|
|
2474
|
+
(model) => !hiddenProviderKeys.has(model.provider)
|
|
2475
|
+
);
|
|
2476
|
+
const compressionModel = result.compressionModel && !hiddenProviderKeys.has(result.compressionModel.provider) ? result.compressionModel : null;
|
|
2477
|
+
return {
|
|
2478
|
+
...result,
|
|
2479
|
+
defaultModel: models.some((model) => model.isDefault) ? result.defaultModel : null,
|
|
2480
|
+
compressionModelId: compressionModel ? result.compressionModelId : null,
|
|
2481
|
+
compressionModel,
|
|
2482
|
+
models
|
|
2483
|
+
};
|
|
2484
|
+
}
|
|
2071
2485
|
var DEFAULT_HERMES_API_SERVER_HOST = "127.0.0.1";
|
|
2072
2486
|
var DEFAULT_HERMES_API_SERVER_PORT = 8642;
|
|
2073
2487
|
var PROFILE_API_SERVER_PORT_START = DEFAULT_HERMES_API_SERVER_PORT + 1;
|
|
@@ -2628,7 +3042,12 @@ async function importHermesModelConfig(input, targetProfileName = "default", tar
|
|
|
2628
3042
|
const sourceApiKey = sourceKeyEnv ? source.env[sourceKeyEnv]?.trim() : source.inlineApiKey;
|
|
2629
3043
|
let keyEnv = existingKeyEnv;
|
|
2630
3044
|
if (!keyEnv && !existingInlineApiKey) {
|
|
2631
|
-
keyEnv = sourceKeyEnv
|
|
3045
|
+
keyEnv = (sourceKeyEnv && !reservedHermesEnvKeys(targetEnv, config).has(sourceKeyEnv) ? sourceKeyEnv : void 0) ?? (sourceApiKey ? await buildUniqueApiKeyEnvName(targetProfileName, config, targetEnv, {
|
|
3046
|
+
providerName: source.model.providerName,
|
|
3047
|
+
modelId: source.model.id,
|
|
3048
|
+
providerKey: targetProviderKey,
|
|
3049
|
+
apiKey: sourceApiKey
|
|
3050
|
+
}) : void 0);
|
|
2632
3051
|
}
|
|
2633
3052
|
if (sourceApiKey && keyEnv && !existingInlineApiKey && (!existingKeyEnv || !targetEnv[keyEnv]?.trim())) {
|
|
2634
3053
|
await writeHermesEnvValue(targetProfileName, keyEnv, sourceApiKey);
|
|
@@ -2771,7 +3190,12 @@ async function saveHermesModelConfig(input, profileName = "default", configPath
|
|
|
2771
3190
|
);
|
|
2772
3191
|
const entry = toRecord(providers[providerKey]);
|
|
2773
3192
|
const existingKeyEnv = readString2(entry.key_env) ?? parseEnvReference(readString2(entry.api_key));
|
|
2774
|
-
const keyEnv = normalized.keyEnv ?? existingKeyEnv ?? (normalized.apiKey ?
|
|
3193
|
+
const keyEnv = normalized.keyEnv ?? existingKeyEnv ?? (normalized.apiKey ? await buildUniqueApiKeyEnvName(profileName, config, void 0, {
|
|
3194
|
+
providerName: normalized.providerName,
|
|
3195
|
+
modelId: normalized.id,
|
|
3196
|
+
providerKey,
|
|
3197
|
+
apiKey: normalized.apiKey
|
|
3198
|
+
}) : void 0);
|
|
2775
3199
|
if (normalized.apiKey && keyEnv) {
|
|
2776
3200
|
await writeHermesEnvValue(profileName, keyEnv, normalized.apiKey);
|
|
2777
3201
|
}
|
|
@@ -2963,22 +3387,28 @@ async function saveHermesModelProviderConfig(input, profileName = "default", con
|
|
|
2963
3387
|
const { document, config, existingRaw } = await readHermesConfigDocument(configPath);
|
|
2964
3388
|
const providers = ensureProvidersRecordWithLegacyMigration(config);
|
|
2965
3389
|
const requestedProviderKey = input.providerKey?.trim();
|
|
3390
|
+
const apiKey = input.apiKey?.trim();
|
|
2966
3391
|
if (requestedProviderKey && !Object.prototype.hasOwnProperty.call(providers, requestedProviderKey)) {
|
|
2967
3392
|
throw new Error(`provider "${requestedProviderKey}" is not configured`);
|
|
2968
3393
|
}
|
|
2969
|
-
const
|
|
3394
|
+
const canReuseVisibleEndpoint = Boolean(requestedProviderKey) || !apiKey || Boolean(input.keyEnv?.trim());
|
|
3395
|
+
const providerKey = requestedProviderKey ?? (canReuseVisibleEndpoint ? findProviderConfigKeyByVisibleEndpoint(providers, {
|
|
2970
3396
|
providerName,
|
|
2971
3397
|
baseUrl,
|
|
2972
3398
|
apiMode,
|
|
2973
3399
|
keyEnv: input.keyEnv
|
|
2974
|
-
}) ?? uniqueProviderConfigKey(
|
|
3400
|
+
}) : null) ?? uniqueProviderConfigKey(
|
|
2975
3401
|
providerConfigKeyBase(void 0, providerName, baseUrl),
|
|
2976
3402
|
providers
|
|
2977
3403
|
);
|
|
2978
3404
|
const entry = toRecord(providers[providerKey]);
|
|
2979
3405
|
const existingKeyEnv = readString2(entry.key_env) ?? parseEnvReference(readString2(entry.api_key));
|
|
2980
|
-
const
|
|
2981
|
-
|
|
3406
|
+
const keyEnv = input.keyEnv?.trim() || existingKeyEnv || (apiKey ? await buildUniqueApiKeyEnvName(profileName, config, void 0, {
|
|
3407
|
+
providerName,
|
|
3408
|
+
modelId: "PROVIDER",
|
|
3409
|
+
providerKey,
|
|
3410
|
+
apiKey
|
|
3411
|
+
}) : void 0);
|
|
2982
3412
|
if (apiKey && keyEnv) {
|
|
2983
3413
|
await writeHermesEnvValue(profileName, keyEnv, apiKey);
|
|
2984
3414
|
}
|
|
@@ -3020,6 +3450,9 @@ async function deleteHermesModelProviderConfig(providerKey, profileName = "defau
|
|
|
3020
3450
|
if (!Object.prototype.hasOwnProperty.call(providers, normalizedProviderKey)) {
|
|
3021
3451
|
throw new Error(`provider "${normalizedProviderKey}" is not configured`);
|
|
3022
3452
|
}
|
|
3453
|
+
const removedKeyEnv = providerCredentialEnvKey(
|
|
3454
|
+
toRecord(providers[normalizedProviderKey])
|
|
3455
|
+
);
|
|
3023
3456
|
delete providers[normalizedProviderKey];
|
|
3024
3457
|
const backupPath = await writeHermesConfigDocument({
|
|
3025
3458
|
configPath,
|
|
@@ -3027,6 +3460,7 @@ async function deleteHermesModelProviderConfig(providerKey, profileName = "defau
|
|
|
3027
3460
|
config,
|
|
3028
3461
|
existingRaw
|
|
3029
3462
|
});
|
|
3463
|
+
await deleteHermesEnvValueIfUnreferenced(profileName, config, removedKeyEnv);
|
|
3030
3464
|
return {
|
|
3031
3465
|
...await listHermesModelConfigs(profileName, configPath),
|
|
3032
3466
|
backupPath,
|
|
@@ -3146,6 +3580,7 @@ async function deleteHermesModelConfig(input, profileName = "default", configPat
|
|
|
3146
3580
|
);
|
|
3147
3581
|
}
|
|
3148
3582
|
const providers = ensureProvidersRecordWithLegacyMigration(config);
|
|
3583
|
+
const removedProviderKeyEnvs = /* @__PURE__ */ new Set();
|
|
3149
3584
|
for (const [key, rawEntry] of Object.entries(providers)) {
|
|
3150
3585
|
const entry = providerConfigToLegacyEntry(key, toRecord(rawEntry));
|
|
3151
3586
|
if (!entry || !providerEntryMatchesManagedModel(entry, deletingModel)) {
|
|
@@ -3155,6 +3590,10 @@ async function deleteHermesModelConfig(input, profileName = "default", configPat
|
|
|
3155
3590
|
if (nextEntry) {
|
|
3156
3591
|
providers[key] = nextEntry;
|
|
3157
3592
|
} else {
|
|
3593
|
+
const keyEnv = providerCredentialEnvKey(toRecord(rawEntry));
|
|
3594
|
+
if (keyEnv) {
|
|
3595
|
+
removedProviderKeyEnvs.add(keyEnv);
|
|
3596
|
+
}
|
|
3158
3597
|
delete providers[key];
|
|
3159
3598
|
}
|
|
3160
3599
|
}
|
|
@@ -3200,6 +3639,9 @@ async function deleteHermesModelConfig(input, profileName = "default", configPat
|
|
|
3200
3639
|
config,
|
|
3201
3640
|
existingRaw
|
|
3202
3641
|
});
|
|
3642
|
+
for (const keyEnv of removedProviderKeyEnvs) {
|
|
3643
|
+
await deleteHermesEnvValueIfUnreferenced(profileName, config, keyEnv);
|
|
3644
|
+
}
|
|
3203
3645
|
const listed = await listHermesModelConfigs(profileName, configPath);
|
|
3204
3646
|
return {
|
|
3205
3647
|
...listed,
|
|
@@ -6694,10 +7136,72 @@ function parseEnvReference(value) {
|
|
|
6694
7136
|
const match = /^\$\{(?<key>[A-Za-z_][A-Za-z0-9_]*)\}$/u.exec(value.trim());
|
|
6695
7137
|
return match?.groups?.key;
|
|
6696
7138
|
}
|
|
7139
|
+
function providerCredentialEnvKey(entry) {
|
|
7140
|
+
return readString2(entry.key_env) ?? parseEnvReference(readString2(entry.api_key));
|
|
7141
|
+
}
|
|
6697
7142
|
function buildApiKeyEnvName(providerName, modelId) {
|
|
6698
7143
|
const base = `${providerName || modelId}`.trim().toUpperCase().replace(/[^A-Z0-9]+/gu, "_").replace(/^_+|_+$/gu, "");
|
|
6699
7144
|
return `HERMES_${base || "MODEL"}_API_KEY`;
|
|
6700
7145
|
}
|
|
7146
|
+
async function buildUniqueApiKeyEnvName(profileName, config, env, input) {
|
|
7147
|
+
const envValues = env ?? await readHermesEnvFile(profileName);
|
|
7148
|
+
const configReferences = collectEnvReferenceKeys(config);
|
|
7149
|
+
const reserved = /* @__PURE__ */ new Set([...Object.keys(envValues), ...configReferences]);
|
|
7150
|
+
const candidates = [
|
|
7151
|
+
buildApiKeyEnvName(input.providerName, input.modelId),
|
|
7152
|
+
buildApiKeyEnvName(input.providerKey, input.modelId)
|
|
7153
|
+
];
|
|
7154
|
+
for (const candidate of uniqueStrings(candidates)) {
|
|
7155
|
+
if (input.apiKey && !configReferences.has(candidate) && envValues[candidate]?.trim() === input.apiKey.trim()) {
|
|
7156
|
+
return candidate;
|
|
7157
|
+
}
|
|
7158
|
+
if (!reserved.has(candidate)) {
|
|
7159
|
+
return candidate;
|
|
7160
|
+
}
|
|
7161
|
+
}
|
|
7162
|
+
const base = buildApiKeyEnvName(input.providerKey, input.modelId);
|
|
7163
|
+
let index = 2;
|
|
7164
|
+
while (reserved.has(apiKeyEnvNameWithSuffix(base, index))) {
|
|
7165
|
+
index += 1;
|
|
7166
|
+
}
|
|
7167
|
+
return apiKeyEnvNameWithSuffix(base, index);
|
|
7168
|
+
}
|
|
7169
|
+
function reservedHermesEnvKeys(env, config) {
|
|
7170
|
+
return /* @__PURE__ */ new Set([...Object.keys(env), ...collectEnvReferenceKeys(config)]);
|
|
7171
|
+
}
|
|
7172
|
+
function collectEnvReferenceKeys(value, keys = /* @__PURE__ */ new Set()) {
|
|
7173
|
+
if (typeof value === "string") {
|
|
7174
|
+
const key = parseEnvReference(value);
|
|
7175
|
+
if (key) {
|
|
7176
|
+
keys.add(key);
|
|
7177
|
+
}
|
|
7178
|
+
return keys;
|
|
7179
|
+
}
|
|
7180
|
+
if (Array.isArray(value)) {
|
|
7181
|
+
for (const item of value) {
|
|
7182
|
+
collectEnvReferenceKeys(item, keys);
|
|
7183
|
+
}
|
|
7184
|
+
return keys;
|
|
7185
|
+
}
|
|
7186
|
+
if (value && typeof value === "object") {
|
|
7187
|
+
for (const item of Object.values(value)) {
|
|
7188
|
+
collectEnvReferenceKeys(item, keys);
|
|
7189
|
+
}
|
|
7190
|
+
}
|
|
7191
|
+
return keys;
|
|
7192
|
+
}
|
|
7193
|
+
function uniqueStrings(values) {
|
|
7194
|
+
return [...new Set(values.filter((value) => value.trim()))];
|
|
7195
|
+
}
|
|
7196
|
+
function apiKeyEnvNameWithSuffix(base, index) {
|
|
7197
|
+
return base.endsWith("_API_KEY") ? `${base.slice(0, -"_API_KEY".length)}_${index}_API_KEY` : `${base}_${index}`;
|
|
7198
|
+
}
|
|
7199
|
+
async function deleteHermesEnvValueIfUnreferenced(profileName, config, key) {
|
|
7200
|
+
if (!key || collectEnvReferenceKeys(config).has(key)) {
|
|
7201
|
+
return;
|
|
7202
|
+
}
|
|
7203
|
+
await deleteHermesEnvValue(profileName, key);
|
|
7204
|
+
}
|
|
6701
7205
|
function formatEnvValue(value) {
|
|
6702
7206
|
return `"${value.replace(/\\/gu, "\\\\").replace(/"/gu, '\\"').replace(/\n/gu, "\\n")}"`;
|
|
6703
7207
|
}
|
|
@@ -7109,7 +7613,7 @@ function isConversationMissingError(error) {
|
|
|
7109
7613
|
}
|
|
7110
7614
|
|
|
7111
7615
|
// src/constants.ts
|
|
7112
|
-
var LINK_VERSION = "0.
|
|
7616
|
+
var LINK_VERSION = "0.8.1-beta.0";
|
|
7113
7617
|
var LINK_COMMAND = "hermeslink";
|
|
7114
7618
|
var LINK_DEFAULT_PORT = 52379;
|
|
7115
7619
|
var LINK_RUNTIME_DIR_NAME = ".hermeslink";
|
|
@@ -7420,6 +7924,271 @@ function readErrorMessage(payload) {
|
|
|
7420
7924
|
return typeof message === "string" ? message : null;
|
|
7421
7925
|
}
|
|
7422
7926
|
|
|
7927
|
+
// src/link/live-activity-events.ts
|
|
7928
|
+
import { createHash } from "crypto";
|
|
7929
|
+
var MIN_LIVE_ACTIVITY_PUSH_INTERVAL_MS = 1e3;
|
|
7930
|
+
var liveActivityReportQueues = /* @__PURE__ */ new Map();
|
|
7931
|
+
async function reportLiveActivityEventToServer(options) {
|
|
7932
|
+
const queueKey = liveActivityReportQueueKey(options.event);
|
|
7933
|
+
const previous = liveActivityReportQueues.get(queueKey) ?? Promise.resolve();
|
|
7934
|
+
const next = previous.catch(() => void 0).then(() => reportLiveActivityEventToServerNow(options));
|
|
7935
|
+
liveActivityReportQueues.set(queueKey, next);
|
|
7936
|
+
try {
|
|
7937
|
+
await next;
|
|
7938
|
+
} finally {
|
|
7939
|
+
if (liveActivityReportQueues.get(queueKey) === next) {
|
|
7940
|
+
liveActivityReportQueues.delete(queueKey);
|
|
7941
|
+
}
|
|
7942
|
+
}
|
|
7943
|
+
}
|
|
7944
|
+
async function reportLiveActivityEventToServerNow(options) {
|
|
7945
|
+
const [identity, config] = await Promise.all([
|
|
7946
|
+
loadIdentity(options.paths),
|
|
7947
|
+
loadConfig(options.paths)
|
|
7948
|
+
]);
|
|
7949
|
+
if (!identity?.link_id) {
|
|
7950
|
+
return;
|
|
7951
|
+
}
|
|
7952
|
+
const activity = await findActiveLiveActivityForTarget(options.paths, {
|
|
7953
|
+
conversationId: options.event.conversationId,
|
|
7954
|
+
targetKind: options.event.targetKind,
|
|
7955
|
+
targetId: options.event.targetId,
|
|
7956
|
+
accountId: options.event.accountId,
|
|
7957
|
+
appInstanceId: options.event.appInstanceId
|
|
7958
|
+
});
|
|
7959
|
+
if (!activity) {
|
|
7960
|
+
return;
|
|
7961
|
+
}
|
|
7962
|
+
const event = liveActivityEventWithInheritedDetail(activity, options.event);
|
|
7963
|
+
const occurredAt = normalizeIso2(options.event.occurredAt) ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
7964
|
+
const summaryHash = hashLiveActivitySummary(event);
|
|
7965
|
+
const skipReason = resolveSkipReason(activity, event, summaryHash);
|
|
7966
|
+
if (skipReason) {
|
|
7967
|
+
await options.logger.debug("live_activity_event_skipped", {
|
|
7968
|
+
activity_id: activity.activityId,
|
|
7969
|
+
conversation_id: event.conversationId,
|
|
7970
|
+
target_kind: event.targetKind,
|
|
7971
|
+
target_id: event.targetId,
|
|
7972
|
+
phase: event.phase,
|
|
7973
|
+
reason: skipReason
|
|
7974
|
+
});
|
|
7975
|
+
return;
|
|
7976
|
+
}
|
|
7977
|
+
const reportedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
7978
|
+
const payload = {
|
|
7979
|
+
type: "hermes_link_live_activity_push",
|
|
7980
|
+
link_id: identity.link_id,
|
|
7981
|
+
install_id: identity.install_id,
|
|
7982
|
+
source_event_id: event.sourceEventId,
|
|
7983
|
+
event_kind: event.eventKind,
|
|
7984
|
+
activity_id: activity.activityId,
|
|
7985
|
+
activity_token: activity.activityToken,
|
|
7986
|
+
apns_environment: activity.apnsEnvironment,
|
|
7987
|
+
apns_topic: activity.apnsTopic,
|
|
7988
|
+
conversation_id: event.conversationId,
|
|
7989
|
+
target_kind: event.targetKind,
|
|
7990
|
+
target_id: event.targetId,
|
|
7991
|
+
phase: event.phase,
|
|
7992
|
+
terminal: event.terminal === true,
|
|
7993
|
+
occurred_at: occurredAt,
|
|
7994
|
+
reported_at: reportedAt
|
|
7995
|
+
};
|
|
7996
|
+
addOptional2(payload, "account_id", activity.accountId ?? event.accountId, 128);
|
|
7997
|
+
addOptional2(payload, "app_instance_id", activity.appInstanceId ?? event.appInstanceId, 128);
|
|
7998
|
+
addOptional2(payload, "run_id", event.runId ?? activity.runId, 128);
|
|
7999
|
+
addOptional2(payload, "operation_id", event.operationId ?? activity.operationId, 128);
|
|
8000
|
+
addOptional2(payload, "conversation_title", event.conversationTitle ?? activity.conversationTitle, 80);
|
|
8001
|
+
addOptional2(payload, "status_label", event.statusLabel, 24);
|
|
8002
|
+
addOptional2(payload, "progress_text", event.progressText, 120);
|
|
8003
|
+
addOptional2(payload, "detail_text", event.detailText, 240);
|
|
8004
|
+
addOptional2(payload, "language", activity.language, 16);
|
|
8005
|
+
addOptional2(payload, "privacy_level", activity.privacyLevel, 32);
|
|
8006
|
+
if (typeof event.requiresUserAction === "boolean") {
|
|
8007
|
+
payload.requires_user_action = event.requiresUserAction;
|
|
8008
|
+
}
|
|
8009
|
+
const signature = signIdentityPayload(identity, canonicalJson2(payload));
|
|
8010
|
+
const fetcher = options.fetchImpl ?? fetch;
|
|
8011
|
+
const response = await fetcher(
|
|
8012
|
+
`${config.serverBaseUrl.replace(/\/+$/u, "")}/api/v1/links/${encodeURIComponent(identity.link_id)}/live-activity-pushes`,
|
|
8013
|
+
{
|
|
8014
|
+
method: "POST",
|
|
8015
|
+
headers: {
|
|
8016
|
+
accept: "application/json",
|
|
8017
|
+
"content-type": "application/json"
|
|
8018
|
+
},
|
|
8019
|
+
body: JSON.stringify({
|
|
8020
|
+
...payload,
|
|
8021
|
+
public_key_pem: identity.public_key_pem,
|
|
8022
|
+
signature
|
|
8023
|
+
})
|
|
8024
|
+
}
|
|
8025
|
+
);
|
|
8026
|
+
const body = await response.json().catch(() => null);
|
|
8027
|
+
if (!response.ok) {
|
|
8028
|
+
const message = readErrorMessage2(body) ?? `HermesPilot Server request failed with HTTP ${response.status}`;
|
|
8029
|
+
throw new LinkHttpError(response.status, "server_request_failed", message);
|
|
8030
|
+
}
|
|
8031
|
+
await updateLiveActivityPushState(options.paths, {
|
|
8032
|
+
activityId: activity.activityId,
|
|
8033
|
+
phase: event.phase,
|
|
8034
|
+
summaryHash,
|
|
8035
|
+
pushedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8036
|
+
visibleUpdatedAt: occurredAt,
|
|
8037
|
+
terminal: event.terminal === true,
|
|
8038
|
+
statusLabel: event.statusLabel,
|
|
8039
|
+
progressText: event.progressText,
|
|
8040
|
+
detailText: event.detailText
|
|
8041
|
+
});
|
|
8042
|
+
const apnsReason = readApnsReason(body);
|
|
8043
|
+
if (apnsReason && shouldInvalidateLiveActivity(apnsReason)) {
|
|
8044
|
+
await invalidateLiveActivity(options.paths, activity.activityId);
|
|
8045
|
+
}
|
|
8046
|
+
}
|
|
8047
|
+
function liveActivityEventWithInheritedDetail(activity, event) {
|
|
8048
|
+
if (isLiveActivityTitleUpdateEvent(event)) {
|
|
8049
|
+
const phase = isLiveActivityPhase(activity.lastPhase) ? activity.lastPhase : event.phase;
|
|
8050
|
+
return {
|
|
8051
|
+
...event,
|
|
8052
|
+
phase,
|
|
8053
|
+
statusLabel: activity.lastStatusLabel?.trim() || event.statusLabel,
|
|
8054
|
+
progressText: activity.lastProgressText?.trim() || event.progressText,
|
|
8055
|
+
detailText: activity.lastDetailText?.trim() || event.detailText
|
|
8056
|
+
};
|
|
8057
|
+
}
|
|
8058
|
+
if (event.detailText?.trim() || !isToolPhase(event.phase)) {
|
|
8059
|
+
return event;
|
|
8060
|
+
}
|
|
8061
|
+
const detailText = activity.lastDetailText?.trim();
|
|
8062
|
+
return detailText ? { ...event, detailText } : event;
|
|
8063
|
+
}
|
|
8064
|
+
function resolveSkipReason(activity, event, summaryHash) {
|
|
8065
|
+
if (shouldThrottleLiveActivityEvent(activity, event)) {
|
|
8066
|
+
return "push_interval_throttled";
|
|
8067
|
+
}
|
|
8068
|
+
if (activity.pushCount >= 60 && event.terminal !== true) {
|
|
8069
|
+
return "push_count_exceeded";
|
|
8070
|
+
}
|
|
8071
|
+
if (activity.lastSummaryHash === summaryHash && event.terminal !== true) {
|
|
8072
|
+
return "duplicate_summary";
|
|
8073
|
+
}
|
|
8074
|
+
if (activity.lastPhase === "context_compression_timed_out" && event.phase === "context_compressed" && activity.targetKind === "context_compression") {
|
|
8075
|
+
return "late_context_compression_completed_after_timeout";
|
|
8076
|
+
}
|
|
8077
|
+
return null;
|
|
8078
|
+
}
|
|
8079
|
+
function shouldThrottleLiveActivityEvent(activity, event) {
|
|
8080
|
+
if (event.terminal === true || event.requiresUserAction === true || event.phase === "needs_input" || event.phase === "needs_approval") {
|
|
8081
|
+
return false;
|
|
8082
|
+
}
|
|
8083
|
+
if (isLiveActivityTitleUpdateEvent(event)) {
|
|
8084
|
+
return false;
|
|
8085
|
+
}
|
|
8086
|
+
const lastVisibleAt = liveActivityLastVisibleTimeMs(activity);
|
|
8087
|
+
if (!Number.isFinite(lastVisibleAt)) {
|
|
8088
|
+
return false;
|
|
8089
|
+
}
|
|
8090
|
+
if (isAssistantProgressEvent(event) && isToolPhase(activity.lastPhase)) {
|
|
8091
|
+
return false;
|
|
8092
|
+
}
|
|
8093
|
+
return liveActivityEventTimeMs(event) - lastVisibleAt < MIN_LIVE_ACTIVITY_PUSH_INTERVAL_MS;
|
|
8094
|
+
}
|
|
8095
|
+
function liveActivityEventTimeMs(event) {
|
|
8096
|
+
const parsed = Date.parse(event.occurredAt ?? "");
|
|
8097
|
+
return Number.isFinite(parsed) ? parsed : Date.now();
|
|
8098
|
+
}
|
|
8099
|
+
function liveActivityLastVisibleTimeMs(activity) {
|
|
8100
|
+
const visible = Date.parse(activity.lastVisibleUpdatedAt ?? "");
|
|
8101
|
+
if (Number.isFinite(visible)) {
|
|
8102
|
+
return visible;
|
|
8103
|
+
}
|
|
8104
|
+
return Date.parse(activity.lastPushedAt ?? "");
|
|
8105
|
+
}
|
|
8106
|
+
function liveActivityReportQueueKey(event) {
|
|
8107
|
+
return [
|
|
8108
|
+
event.accountId?.trim() ?? "",
|
|
8109
|
+
event.appInstanceId?.trim() ?? "",
|
|
8110
|
+
event.conversationId,
|
|
8111
|
+
event.targetKind,
|
|
8112
|
+
event.targetId
|
|
8113
|
+
].join(":");
|
|
8114
|
+
}
|
|
8115
|
+
function isAssistantProgressEvent(event) {
|
|
8116
|
+
return event.eventKind === "reasoning_available";
|
|
8117
|
+
}
|
|
8118
|
+
function isLiveActivityTitleUpdateEvent(event) {
|
|
8119
|
+
return event.eventKind === "conversation_updated" && Boolean(event.conversationTitle?.trim());
|
|
8120
|
+
}
|
|
8121
|
+
function isToolPhase(phase) {
|
|
8122
|
+
return phase === "using_tool" || phase === "tool_completed";
|
|
8123
|
+
}
|
|
8124
|
+
function isLiveActivityPhase(value) {
|
|
8125
|
+
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
|
+
}
|
|
8127
|
+
function hashLiveActivitySummary(event) {
|
|
8128
|
+
return createHash("sha256").update(
|
|
8129
|
+
[
|
|
8130
|
+
event.phase,
|
|
8131
|
+
event.conversationTitle?.trim() ?? "",
|
|
8132
|
+
event.statusLabel?.trim() ?? "",
|
|
8133
|
+
event.progressText?.trim() ?? "",
|
|
8134
|
+
event.detailText?.trim() ?? "",
|
|
8135
|
+
event.terminal === true ? "terminal" : "update"
|
|
8136
|
+
].join("|")
|
|
8137
|
+
).digest("hex");
|
|
8138
|
+
}
|
|
8139
|
+
function addOptional2(payload, key, value, maxLength) {
|
|
8140
|
+
const normalized = value?.trim();
|
|
8141
|
+
if (normalized) {
|
|
8142
|
+
payload[key] = normalized.slice(0, maxLength);
|
|
8143
|
+
}
|
|
8144
|
+
}
|
|
8145
|
+
function normalizeIso2(value) {
|
|
8146
|
+
const normalized = value?.trim();
|
|
8147
|
+
if (!normalized) {
|
|
8148
|
+
return null;
|
|
8149
|
+
}
|
|
8150
|
+
const date = new Date(normalized);
|
|
8151
|
+
return Number.isNaN(date.getTime()) ? null : date.toISOString();
|
|
8152
|
+
}
|
|
8153
|
+
function canonicalJson2(value) {
|
|
8154
|
+
return JSON.stringify(sortJsonValue2(value));
|
|
8155
|
+
}
|
|
8156
|
+
function sortJsonValue2(value) {
|
|
8157
|
+
if (Array.isArray(value)) {
|
|
8158
|
+
return value.map(sortJsonValue2);
|
|
8159
|
+
}
|
|
8160
|
+
if (value && typeof value === "object") {
|
|
8161
|
+
const record = value;
|
|
8162
|
+
const sorted = {};
|
|
8163
|
+
for (const key of Object.keys(record).sort()) {
|
|
8164
|
+
sorted[key] = sortJsonValue2(record[key]);
|
|
8165
|
+
}
|
|
8166
|
+
return sorted;
|
|
8167
|
+
}
|
|
8168
|
+
return value;
|
|
8169
|
+
}
|
|
8170
|
+
function readErrorMessage2(payload) {
|
|
8171
|
+
if (typeof payload !== "object" || payload === null) {
|
|
8172
|
+
return null;
|
|
8173
|
+
}
|
|
8174
|
+
const error = payload.error;
|
|
8175
|
+
if (typeof error !== "object" || error === null) {
|
|
8176
|
+
return null;
|
|
8177
|
+
}
|
|
8178
|
+
const message = error.message;
|
|
8179
|
+
return typeof message === "string" ? message : null;
|
|
8180
|
+
}
|
|
8181
|
+
function readApnsReason(payload) {
|
|
8182
|
+
if (typeof payload !== "object" || payload === null) {
|
|
8183
|
+
return null;
|
|
8184
|
+
}
|
|
8185
|
+
const reason = payload.apnsReason ?? payload.apns_reason;
|
|
8186
|
+
return typeof reason === "string" ? reason : null;
|
|
8187
|
+
}
|
|
8188
|
+
function shouldInvalidateLiveActivity(reason) {
|
|
8189
|
+
return ["BadDeviceToken", "Unregistered", "DeviceTokenNotForTopic", "TopicDisallowed"].includes(reason);
|
|
8190
|
+
}
|
|
8191
|
+
|
|
7423
8192
|
// src/hermes/gateway.ts
|
|
7424
8193
|
import { execFile as execFile2, spawn } from "child_process";
|
|
7425
8194
|
import { access, readFile as readFile5, stat as stat4 } from "fs/promises";
|
|
@@ -11359,7 +12128,7 @@ function toRecord3(value) {
|
|
|
11359
12128
|
}
|
|
11360
12129
|
|
|
11361
12130
|
// src/conversations/approvals.ts
|
|
11362
|
-
import { createHash } from "crypto";
|
|
12131
|
+
import { createHash as createHash2 } from "crypto";
|
|
11363
12132
|
var LINK_APPROVAL_RESUME_HINT_ZH = "Hermes \u5F53\u524D\u8FD8\u672A\u63D0\u4F9B\u53EF\u8C03\u7528\u7684\u5BA1\u6279\u7EED\u8DD1\u63A5\u53E3\uFF0C\u60A8\u53EF\u4EE5\u70B9\u51FB\u201C\u59CB\u7EC8\u5141\u8BB8\u201D\uFF0C\u7136\u540E\u91CD\u65B0\u53D1\u51FA\u547D\u4EE4\uFF0C\u6216\u5728\u201CProfile \u6743\u9650\u201D\u8BBE\u7F6E\u9875\u9762\u4FEE\u6539\u5BA1\u6279\u7B56\u7565\u3002";
|
|
11364
12133
|
var LINK_APPROVAL_RESUME_HINT_EN = "Hermes does not yet provide a callable approval-resume API. Tap \u201CAlways allow\u201D, then send the command again, or change the approval policy on the \u201CProfile permissions\u201D settings page.";
|
|
11365
12134
|
var LINK_APPROVAL_CONFIRM_HINT_ZH = "\u8BF7\u786E\u8BA4\u662F\u5426\u5141\u8BB8 Hermes \u7EE7\u7EED\u6267\u884C\u8FD9\u6761\u547D\u4EE4\u3002";
|
|
@@ -11459,7 +12228,7 @@ function coerceRecord(value) {
|
|
|
11459
12228
|
return toRecord4(value);
|
|
11460
12229
|
}
|
|
11461
12230
|
function createApprovalId(input) {
|
|
11462
|
-
const digest =
|
|
12231
|
+
const digest = createHash2("sha256").update(input.runId).update("\0").update(input.messageId).update("\0").update(input.command).update("\0").update(input.patternKey ?? "").digest("hex").slice(0, 24);
|
|
11463
12232
|
return `approval_${digest}`;
|
|
11464
12233
|
}
|
|
11465
12234
|
function readString6(payload, key) {
|
|
@@ -11477,7 +12246,7 @@ function toRecord4(value) {
|
|
|
11477
12246
|
}
|
|
11478
12247
|
|
|
11479
12248
|
// src/conversations/input-requests.ts
|
|
11480
|
-
import { createHash as
|
|
12249
|
+
import { createHash as createHash3 } from "crypto";
|
|
11481
12250
|
function extractInputRequestFromEvent(input) {
|
|
11482
12251
|
const kind = readInputRequestKind(input.event.payloadType);
|
|
11483
12252
|
if (!kind) {
|
|
@@ -11625,7 +12394,7 @@ function readInputRequestKind(payloadType) {
|
|
|
11625
12394
|
return null;
|
|
11626
12395
|
}
|
|
11627
12396
|
function createInputRequestId(input) {
|
|
11628
|
-
const digest =
|
|
12397
|
+
const digest = createHash3("sha256").update(input.runId).update("\0").update(input.messageId).update("\0").update(input.kind).update("\0").update(input.hermesRequestId).digest("hex").slice(0, 24);
|
|
11629
12398
|
return `input_${digest}`;
|
|
11630
12399
|
}
|
|
11631
12400
|
function readString7(payload, key) {
|
|
@@ -12101,7 +12870,7 @@ import { mkdir as mkdir6, readFile as readFile7, readdir as readdir4, rm as rm3,
|
|
|
12101
12870
|
import path11 from "path";
|
|
12102
12871
|
|
|
12103
12872
|
// src/conversations/media.ts
|
|
12104
|
-
import { createHash as
|
|
12873
|
+
import { createHash as createHash4 } from "crypto";
|
|
12105
12874
|
import path10 from "path";
|
|
12106
12875
|
|
|
12107
12876
|
// src/conversations/delivery-contract.ts
|
|
@@ -12313,7 +13082,7 @@ function readImportedMediaSourceKeys(message) {
|
|
|
12313
13082
|
);
|
|
12314
13083
|
}
|
|
12315
13084
|
function mediaSourceKey(sourcePath) {
|
|
12316
|
-
return
|
|
13085
|
+
return createHash4("sha256").update(resolveMediaSourcePath(sourcePath)).digest("hex").slice(0, 32);
|
|
12317
13086
|
}
|
|
12318
13087
|
function sanitizeFilename(value, fallback) {
|
|
12319
13088
|
const base = path10.basename((value ?? "").replace(/[\r\n\t]/gu, " ").trim());
|
|
@@ -12697,9 +13466,115 @@ function isNodeError6(error, code) {
|
|
|
12697
13466
|
}
|
|
12698
13467
|
|
|
12699
13468
|
// src/conversations/profile-runtime.ts
|
|
13469
|
+
import { stat as stat8 } from "fs/promises";
|
|
13470
|
+
|
|
13471
|
+
// src/hermes/model-provider-disconnects.ts
|
|
12700
13472
|
import { stat as stat7 } from "fs/promises";
|
|
12701
|
-
|
|
13473
|
+
import os5 from "os";
|
|
13474
|
+
import path12 from "path";
|
|
13475
|
+
async function markAuthBackedModelProviderDisconnected(paths, profileName, providerKey) {
|
|
13476
|
+
const profileKey = normalizeProfileName3(profileName);
|
|
13477
|
+
const provider = providerKey.trim();
|
|
13478
|
+
if (!provider) {
|
|
13479
|
+
return;
|
|
13480
|
+
}
|
|
13481
|
+
const credentialMtimeMs = await readProviderCredentialMtimeMs(
|
|
13482
|
+
profileKey,
|
|
13483
|
+
provider
|
|
13484
|
+
);
|
|
13485
|
+
await updateJsonFile(
|
|
13486
|
+
disconnectedProvidersPath(paths),
|
|
13487
|
+
(current) => {
|
|
13488
|
+
const store = normalizeStore(current);
|
|
13489
|
+
const profiles = { ...store.profiles ?? {} };
|
|
13490
|
+
const profile = profiles[profileKey] ?? {};
|
|
13491
|
+
profiles[profileKey] = {
|
|
13492
|
+
...profile,
|
|
13493
|
+
providers: {
|
|
13494
|
+
...profile.providers ?? {},
|
|
13495
|
+
[provider]: {
|
|
13496
|
+
disconnectedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
13497
|
+
credentialMtimeMs
|
|
13498
|
+
}
|
|
13499
|
+
}
|
|
13500
|
+
};
|
|
13501
|
+
return { ...store, profiles };
|
|
13502
|
+
}
|
|
13503
|
+
);
|
|
13504
|
+
}
|
|
13505
|
+
async function readDisconnectedAuthBackedModelProviderKeys(paths, profileName) {
|
|
13506
|
+
const store = normalizeStore(
|
|
13507
|
+
await readJsonFile(
|
|
13508
|
+
disconnectedProvidersPath(paths)
|
|
13509
|
+
).catch(() => null)
|
|
13510
|
+
);
|
|
13511
|
+
const profile = store.profiles?.[normalizeProfileName3(profileName)];
|
|
13512
|
+
const providers = profile?.providers ?? {};
|
|
13513
|
+
const active = /* @__PURE__ */ new Set();
|
|
13514
|
+
for (const [provider, entry] of Object.entries(providers)) {
|
|
13515
|
+
if (await isDisconnectMarkerActive(profileName, provider, entry)) {
|
|
13516
|
+
active.add(provider);
|
|
13517
|
+
}
|
|
13518
|
+
}
|
|
13519
|
+
return active;
|
|
13520
|
+
}
|
|
13521
|
+
async function isDisconnectMarkerActive(profileName, providerKey, entry) {
|
|
13522
|
+
const currentMtimeMs = await readProviderCredentialMtimeMs(
|
|
13523
|
+
profileName,
|
|
13524
|
+
providerKey
|
|
13525
|
+
);
|
|
13526
|
+
if (entry.credentialMtimeMs === null || !Number.isFinite(entry.credentialMtimeMs)) {
|
|
13527
|
+
return currentMtimeMs === null;
|
|
13528
|
+
}
|
|
13529
|
+
if (currentMtimeMs === null || !Number.isFinite(currentMtimeMs)) {
|
|
13530
|
+
return true;
|
|
13531
|
+
}
|
|
13532
|
+
return currentMtimeMs <= entry.credentialMtimeMs + 1e3;
|
|
13533
|
+
}
|
|
13534
|
+
async function readProviderCredentialMtimeMs(profileName, providerKey) {
|
|
13535
|
+
for (const filePath of credentialFilesForProvider(profileName, providerKey)) {
|
|
13536
|
+
const stats = await stat7(filePath).catch((error) => {
|
|
13537
|
+
if (isNodeError7(error, "ENOENT")) {
|
|
13538
|
+
return null;
|
|
13539
|
+
}
|
|
13540
|
+
throw error;
|
|
13541
|
+
});
|
|
13542
|
+
if (stats) {
|
|
13543
|
+
return stats.mtimeMs;
|
|
13544
|
+
}
|
|
13545
|
+
}
|
|
13546
|
+
return null;
|
|
13547
|
+
}
|
|
13548
|
+
function credentialFilesForProvider(profileName, providerKey) {
|
|
13549
|
+
const profileDir = resolveHermesProfileDir(normalizeProfileName3(profileName));
|
|
13550
|
+
switch (providerKey) {
|
|
13551
|
+
case "qwen-oauth":
|
|
13552
|
+
return [path12.join(os5.homedir(), ".qwen", "oauth_creds.json")];
|
|
13553
|
+
case "google-gemini-cli":
|
|
13554
|
+
return [path12.join(profileDir, "auth", "google_oauth.json")];
|
|
13555
|
+
default:
|
|
13556
|
+
return [path12.join(profileDir, "auth.json")];
|
|
13557
|
+
}
|
|
13558
|
+
}
|
|
13559
|
+
function disconnectedProvidersPath(paths) {
|
|
13560
|
+
return path12.join(paths.homeDir, "model-provider-disconnects.json");
|
|
13561
|
+
}
|
|
13562
|
+
function normalizeStore(value) {
|
|
13563
|
+
return {
|
|
13564
|
+
version: 1,
|
|
13565
|
+
profiles: value?.profiles && typeof value.profiles === "object" ? value.profiles : {}
|
|
13566
|
+
};
|
|
13567
|
+
}
|
|
12702
13568
|
function normalizeProfileName3(profileName) {
|
|
13569
|
+
return profileName.trim() || "default";
|
|
13570
|
+
}
|
|
13571
|
+
function isNodeError7(error, code) {
|
|
13572
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
13573
|
+
}
|
|
13574
|
+
|
|
13575
|
+
// src/conversations/profile-runtime.ts
|
|
13576
|
+
var PROFILE_NAME_PATTERN3 = /^[a-zA-Z0-9._-]{1,64}$/u;
|
|
13577
|
+
function normalizeProfileName4(profileName) {
|
|
12703
13578
|
const value = profileName?.trim() || "default";
|
|
12704
13579
|
if (!PROFILE_NAME_PATTERN3.test(value)) {
|
|
12705
13580
|
throw new Error("invalid profile name");
|
|
@@ -12710,7 +13585,7 @@ function fallbackProfileDisplayName(profileName) {
|
|
|
12710
13585
|
return profileName === "default" ? "Default" : profileName;
|
|
12711
13586
|
}
|
|
12712
13587
|
async function resolveConversationProfileTarget(paths, profileName) {
|
|
12713
|
-
const normalized =
|
|
13588
|
+
const normalized = normalizeProfileName4(profileName);
|
|
12714
13589
|
const identity = await ensureProfileIdentityForRuntime(paths, normalized);
|
|
12715
13590
|
return {
|
|
12716
13591
|
profileUid: identity.profileUid,
|
|
@@ -12720,7 +13595,7 @@ async function resolveConversationProfileTarget(paths, profileName) {
|
|
|
12720
13595
|
};
|
|
12721
13596
|
}
|
|
12722
13597
|
async function readCurrentConversationRuntime(paths, manifest) {
|
|
12723
|
-
const profileName =
|
|
13598
|
+
const profileName = normalizeProfileName4(
|
|
12724
13599
|
manifest?.profile_name_snapshot ?? manifest?.profile
|
|
12725
13600
|
);
|
|
12726
13601
|
const identity = await ensureProfileIdentityForRuntime(paths, profileName);
|
|
@@ -12732,7 +13607,7 @@ async function readCurrentConversationRuntime(paths, manifest) {
|
|
|
12732
13607
|
contextLength: void 0,
|
|
12733
13608
|
reasoningEffort: void 0
|
|
12734
13609
|
}));
|
|
12735
|
-
const configuredDefaultModel = modelConfig.model ? await findConfiguredModel(profileName, modelConfig.model) : void 0;
|
|
13610
|
+
const configuredDefaultModel = modelConfig.model ? await findConfiguredModel(profileName, modelConfig.model, {}, paths) : void 0;
|
|
12736
13611
|
const override = manifest?.model_override;
|
|
12737
13612
|
const overrideModel = override?.id?.trim();
|
|
12738
13613
|
const overrideProvider = override?.provider_name?.trim() || override?.provider?.trim();
|
|
@@ -12760,7 +13635,7 @@ async function readProfilePresentation(paths, profileName) {
|
|
|
12760
13635
|
};
|
|
12761
13636
|
}
|
|
12762
13637
|
async function readConversationProfileSummary(paths, manifest) {
|
|
12763
|
-
const profileName =
|
|
13638
|
+
const profileName = normalizeProfileName4(
|
|
12764
13639
|
manifest.profile_name_snapshot ?? manifest.profile
|
|
12765
13640
|
);
|
|
12766
13641
|
const presentation = await readProfilePresentation(paths, profileName).catch(
|
|
@@ -12831,8 +13706,8 @@ async function buildConversationRuntimeMetadata(paths, manifest, snapshot) {
|
|
|
12831
13706
|
async function ensureProfileIdentityForRuntime(paths, profileName) {
|
|
12832
13707
|
const profilePath = resolveHermesProfileDir(profileName);
|
|
12833
13708
|
if (profileName !== "default") {
|
|
12834
|
-
const exists = await
|
|
12835
|
-
if (
|
|
13709
|
+
const exists = await stat8(profilePath).then((value) => value.isDirectory()).catch((error) => {
|
|
13710
|
+
if (isNodeError8(error, "ENOENT")) {
|
|
12836
13711
|
return false;
|
|
12837
13712
|
}
|
|
12838
13713
|
throw error;
|
|
@@ -12850,12 +13725,18 @@ async function ensureProfileIdentityForRuntime(paths, profileName) {
|
|
|
12850
13725
|
profilePath
|
|
12851
13726
|
});
|
|
12852
13727
|
}
|
|
12853
|
-
async function findConfiguredModel(profileName, modelId, selector = {}) {
|
|
13728
|
+
async function findConfiguredModel(profileName, modelId, selector = {}, paths) {
|
|
12854
13729
|
const result = await listHermesModelConfigs(profileName).catch(() => null);
|
|
13730
|
+
const disconnectedProviders = paths ? await readDisconnectedAuthBackedModelProviderKeys(paths, profileName).catch(
|
|
13731
|
+
() => /* @__PURE__ */ new Set()
|
|
13732
|
+
) : /* @__PURE__ */ new Set();
|
|
12855
13733
|
return result?.models.find((model) => {
|
|
12856
13734
|
if (model.id !== modelId) {
|
|
12857
13735
|
return false;
|
|
12858
13736
|
}
|
|
13737
|
+
if (disconnectedProviders.has(model.provider)) {
|
|
13738
|
+
return false;
|
|
13739
|
+
}
|
|
12859
13740
|
if (selector.provider && model.provider !== selector.provider) {
|
|
12860
13741
|
return false;
|
|
12861
13742
|
}
|
|
@@ -12875,7 +13756,7 @@ function displayNameForProfile(profile) {
|
|
|
12875
13756
|
const custom = profile.displayName?.trim();
|
|
12876
13757
|
return custom || fallbackProfileDisplayName(profile.profileName);
|
|
12877
13758
|
}
|
|
12878
|
-
function
|
|
13759
|
+
function isNodeError8(error, code) {
|
|
12879
13760
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
12880
13761
|
}
|
|
12881
13762
|
|
|
@@ -13295,9 +14176,15 @@ var ConversationCommandHandlers = class {
|
|
|
13295
14176
|
}
|
|
13296
14177
|
async modelStatusMessage(manifest, language = "zh-CN") {
|
|
13297
14178
|
const runtime = await readCurrentConversationRuntime(this.deps.paths, manifest);
|
|
13298
|
-
const configured = await listHermesModelConfigs(runtime.profileName).
|
|
13299
|
-
() =>
|
|
13300
|
-
|
|
14179
|
+
const configured = await listHermesModelConfigs(runtime.profileName).then(
|
|
14180
|
+
async (result) => filterHermesModelConfigsByProviderKeys(
|
|
14181
|
+
result,
|
|
14182
|
+
await readDisconnectedAuthBackedModelProviderKeys(
|
|
14183
|
+
this.deps.paths,
|
|
14184
|
+
runtime.profileName
|
|
14185
|
+
)
|
|
14186
|
+
)
|
|
14187
|
+
).catch(() => null);
|
|
13301
14188
|
const lines = [
|
|
13302
14189
|
commandText(
|
|
13303
14190
|
language,
|
|
@@ -13408,7 +14295,12 @@ var ConversationCommandHandlers = class {
|
|
|
13408
14295
|
response: await this.modelStatusMessage(input.manifest, input.language)
|
|
13409
14296
|
});
|
|
13410
14297
|
}
|
|
13411
|
-
const configuredModel = await findConfiguredModel(
|
|
14298
|
+
const configuredModel = await findConfiguredModel(
|
|
14299
|
+
runtime.profileName,
|
|
14300
|
+
model,
|
|
14301
|
+
{},
|
|
14302
|
+
this.deps.paths
|
|
14303
|
+
);
|
|
13412
14304
|
if (!configuredModel) {
|
|
13413
14305
|
return this.deps.appendCommandResult({
|
|
13414
14306
|
manifest: input.manifest,
|
|
@@ -13606,7 +14498,9 @@ var ConversationCommandHandlers = class {
|
|
|
13606
14498
|
const reasoningEffort = arg;
|
|
13607
14499
|
const configuredModel = await findConfiguredModel(
|
|
13608
14500
|
runtime.profileName,
|
|
13609
|
-
runtime.model
|
|
14501
|
+
runtime.model,
|
|
14502
|
+
{},
|
|
14503
|
+
this.deps.paths
|
|
13610
14504
|
);
|
|
13611
14505
|
if (!isReasoningEffortSupported(configuredModel, reasoningEffort)) {
|
|
13612
14506
|
return this.deps.appendCommandResult({
|
|
@@ -13794,7 +14688,7 @@ function commandText(language, zh, en) {
|
|
|
13794
14688
|
|
|
13795
14689
|
// src/conversations/delivery-staging.ts
|
|
13796
14690
|
import { mkdir as mkdir7, rm as rm4 } from "fs/promises";
|
|
13797
|
-
import
|
|
14691
|
+
import path13 from "path";
|
|
13798
14692
|
async function prepareDeliveryStagingRunDir(paths, conversationId, runId) {
|
|
13799
14693
|
const directory = deliveryStagingRunDir(paths, conversationId, runId);
|
|
13800
14694
|
await mkdir7(directory, { recursive: true, mode: 448 });
|
|
@@ -13807,14 +14701,14 @@ async function removeConversationDeliveryStaging(paths, conversationId) {
|
|
|
13807
14701
|
});
|
|
13808
14702
|
}
|
|
13809
14703
|
function deliveryStagingRunDir(paths, conversationId, runId) {
|
|
13810
|
-
return
|
|
14704
|
+
return path13.join(
|
|
13811
14705
|
deliveryStagingConversationDir(paths, conversationId),
|
|
13812
14706
|
safePathSegment(runId, "run")
|
|
13813
14707
|
);
|
|
13814
14708
|
}
|
|
13815
14709
|
function deliveryStagingConversationDir(paths, conversationId) {
|
|
13816
14710
|
assertValidConversationId(conversationId);
|
|
13817
|
-
return
|
|
14711
|
+
return path13.join(paths.conversationsDir, conversationId, "delivery-staging");
|
|
13818
14712
|
}
|
|
13819
14713
|
function safePathSegment(value, fallback) {
|
|
13820
14714
|
const safe = value.trim().replaceAll(/[^a-zA-Z0-9._-]/gu, "_");
|
|
@@ -13824,7 +14718,7 @@ function safePathSegment(value, fallback) {
|
|
|
13824
14718
|
// src/conversations/conversation-archive-plans.ts
|
|
13825
14719
|
import { randomUUID as randomUUID7 } from "crypto";
|
|
13826
14720
|
import { mkdir as mkdir8 } from "fs/promises";
|
|
13827
|
-
import
|
|
14721
|
+
import path14 from "path";
|
|
13828
14722
|
var PLAN_ID_PATTERN = /^archive_[a-f0-9]{32}$/u;
|
|
13829
14723
|
var ConversationArchivePlanStore = class {
|
|
13830
14724
|
constructor(paths) {
|
|
@@ -13867,10 +14761,10 @@ var ConversationArchivePlanStore = class {
|
|
|
13867
14761
|
await writeJsonFile(this.planPath(normalizedPlanId), plan);
|
|
13868
14762
|
}
|
|
13869
14763
|
plansDir() {
|
|
13870
|
-
return
|
|
14764
|
+
return path14.join(this.paths.indexesDir, "conversation-archive-plans");
|
|
13871
14765
|
}
|
|
13872
14766
|
planPath(planId) {
|
|
13873
|
-
return
|
|
14767
|
+
return path14.join(this.plansDir(), `${planId}.json`);
|
|
13874
14768
|
}
|
|
13875
14769
|
};
|
|
13876
14770
|
function normalizePlanId(planId) {
|
|
@@ -13888,7 +14782,7 @@ function normalizePlanId(planId) {
|
|
|
13888
14782
|
// src/conversations/conversation-clear-plans.ts
|
|
13889
14783
|
import { randomUUID as randomUUID8 } from "crypto";
|
|
13890
14784
|
import { mkdir as mkdir9 } from "fs/promises";
|
|
13891
|
-
import
|
|
14785
|
+
import path15 from "path";
|
|
13892
14786
|
var PLAN_ID_PATTERN2 = /^clear_[a-f0-9]{32}$/u;
|
|
13893
14787
|
var ConversationClearPlanStore = class {
|
|
13894
14788
|
constructor(paths) {
|
|
@@ -13932,10 +14826,10 @@ var ConversationClearPlanStore = class {
|
|
|
13932
14826
|
await writeJsonFile(this.planPath(normalizedPlanId), plan);
|
|
13933
14827
|
}
|
|
13934
14828
|
plansDir() {
|
|
13935
|
-
return
|
|
14829
|
+
return path15.join(this.paths.indexesDir, "conversation-clear-plans");
|
|
13936
14830
|
}
|
|
13937
14831
|
planPath(planId) {
|
|
13938
|
-
return
|
|
14832
|
+
return path15.join(this.plansDir(), `${planId}.json`);
|
|
13939
14833
|
}
|
|
13940
14834
|
};
|
|
13941
14835
|
function normalizePlanId2(planId) {
|
|
@@ -14789,20 +15683,20 @@ function readAttachmentWaveform(attachment) {
|
|
|
14789
15683
|
}
|
|
14790
15684
|
|
|
14791
15685
|
// src/hermes/session-title.ts
|
|
14792
|
-
import { stat as
|
|
14793
|
-
import
|
|
15686
|
+
import { stat as stat9 } from "fs/promises";
|
|
15687
|
+
import path16 from "path";
|
|
14794
15688
|
async function readHermesSessionTitle(sessionId, paths, profileName) {
|
|
14795
15689
|
const trimmedSessionId = sessionId.trim();
|
|
14796
15690
|
if (!trimmedSessionId) {
|
|
14797
15691
|
return void 0;
|
|
14798
15692
|
}
|
|
14799
15693
|
const resolvedProfileName = isValidProfileName(profileName) ? profileName : "default";
|
|
14800
|
-
const dbPath =
|
|
15694
|
+
const dbPath = path16.join(
|
|
14801
15695
|
resolveHermesProfileDir(resolvedProfileName),
|
|
14802
15696
|
"state.db"
|
|
14803
15697
|
);
|
|
14804
|
-
const exists = await
|
|
14805
|
-
if (
|
|
15698
|
+
const exists = await stat9(dbPath).then((value) => value.isFile()).catch((error) => {
|
|
15699
|
+
if (isNodeError9(error, "ENOENT")) {
|
|
14806
15700
|
return false;
|
|
14807
15701
|
}
|
|
14808
15702
|
throw error;
|
|
@@ -14818,12 +15712,12 @@ async function readHermesCompressionTip(sessionId, paths, profileName) {
|
|
|
14818
15712
|
return void 0;
|
|
14819
15713
|
}
|
|
14820
15714
|
const resolvedProfileName = isValidProfileName(profileName) ? profileName : "default";
|
|
14821
|
-
const dbPath =
|
|
15715
|
+
const dbPath = path16.join(
|
|
14822
15716
|
resolveHermesProfileDir(resolvedProfileName),
|
|
14823
15717
|
"state.db"
|
|
14824
15718
|
);
|
|
14825
|
-
const exists = await
|
|
14826
|
-
if (
|
|
15719
|
+
const exists = await stat9(dbPath).then((value) => value.isFile()).catch((error) => {
|
|
15720
|
+
if (isNodeError9(error, "ENOENT")) {
|
|
14827
15721
|
return false;
|
|
14828
15722
|
}
|
|
14829
15723
|
throw error;
|
|
@@ -14886,7 +15780,7 @@ function readCompressionTipFromStateDb(dbPath, sessionId) {
|
|
|
14886
15780
|
db?.close();
|
|
14887
15781
|
}
|
|
14888
15782
|
}
|
|
14889
|
-
function
|
|
15783
|
+
function isNodeError9(error, code) {
|
|
14890
15784
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
14891
15785
|
}
|
|
14892
15786
|
function isValidProfileName(value) {
|
|
@@ -15250,8 +16144,8 @@ function stripCompressionTitleSuffix(value) {
|
|
|
15250
16144
|
}
|
|
15251
16145
|
|
|
15252
16146
|
// src/conversations/history-builder.ts
|
|
15253
|
-
import { readFile as readFile8, stat as
|
|
15254
|
-
import
|
|
16147
|
+
import { readFile as readFile8, stat as stat10 } from "fs/promises";
|
|
16148
|
+
import path17 from "path";
|
|
15255
16149
|
var HISTORY_ROLES = /* @__PURE__ */ new Set(["user", "assistant"]);
|
|
15256
16150
|
var HERMES_HISTORY_COLUMNS = [
|
|
15257
16151
|
"role",
|
|
@@ -15323,13 +16217,13 @@ async function readHermesTranscriptHistory(sessionId, profileName) {
|
|
|
15323
16217
|
}
|
|
15324
16218
|
const normalizedProfileName = isValidProfileName2(profileName) ? profileName : "default";
|
|
15325
16219
|
const profileDir = resolveHermesProfileDir(normalizedProfileName);
|
|
15326
|
-
const dbPath =
|
|
16220
|
+
const dbPath = path17.join(profileDir, "state.db");
|
|
15327
16221
|
const sessionsDirConfig = await readHermesSessionsDir(normalizedProfileName).then((value) => ({
|
|
15328
16222
|
sessionsDir: value.sessionsDir,
|
|
15329
16223
|
configured: value.configured,
|
|
15330
16224
|
configError: false
|
|
15331
16225
|
})).catch(() => ({
|
|
15332
|
-
sessionsDir:
|
|
16226
|
+
sessionsDir: path17.join(profileDir, "sessions"),
|
|
15333
16227
|
configured: false,
|
|
15334
16228
|
configError: true
|
|
15335
16229
|
}));
|
|
@@ -15374,8 +16268,8 @@ async function readHermesTranscriptHistory(sessionId, profileName) {
|
|
|
15374
16268
|
};
|
|
15375
16269
|
}
|
|
15376
16270
|
async function readHermesStateDbHistory(dbPath, sessionId) {
|
|
15377
|
-
const exists = await
|
|
15378
|
-
if (
|
|
16271
|
+
const exists = await stat10(dbPath).then((value) => value.isFile()).catch((error) => {
|
|
16272
|
+
if (isNodeError10(error, "ENOENT")) {
|
|
15379
16273
|
return false;
|
|
15380
16274
|
}
|
|
15381
16275
|
throw error;
|
|
@@ -15492,7 +16386,7 @@ async function readHermesTranscriptFilesHistory(sessionsDir, sessionId) {
|
|
|
15492
16386
|
async function readFirstExistingFile(paths) {
|
|
15493
16387
|
for (const filePath of paths) {
|
|
15494
16388
|
const raw = await readFile8(filePath, "utf8").catch((error) => {
|
|
15495
|
-
if (
|
|
16389
|
+
if (isNodeError10(error, "ENOENT")) {
|
|
15496
16390
|
return null;
|
|
15497
16391
|
}
|
|
15498
16392
|
throw error;
|
|
@@ -15505,8 +16399,8 @@ async function readFirstExistingFile(paths) {
|
|
|
15505
16399
|
}
|
|
15506
16400
|
function candidateTranscriptPaths(sessionsDir, sessionId, extension) {
|
|
15507
16401
|
return [
|
|
15508
|
-
|
|
15509
|
-
|
|
16402
|
+
path17.join(sessionsDir, `session_${sessionId}.${extension}`),
|
|
16403
|
+
path17.join(sessionsDir, `${sessionId}.${extension}`)
|
|
15510
16404
|
];
|
|
15511
16405
|
}
|
|
15512
16406
|
function readHistoryRows(dbPath, sessionId) {
|
|
@@ -15706,7 +16600,7 @@ function readTableColumns(db, table) {
|
|
|
15706
16600
|
db.prepare(`PRAGMA table_info(${table})`).all().map((row) => row.name).filter((name) => typeof name === "string")
|
|
15707
16601
|
);
|
|
15708
16602
|
}
|
|
15709
|
-
function
|
|
16603
|
+
function isNodeError10(error, code) {
|
|
15710
16604
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
15711
16605
|
}
|
|
15712
16606
|
function isValidProfileName2(value) {
|
|
@@ -17361,13 +18255,13 @@ var ConversationOrchestrationCoordinator = class {
|
|
|
17361
18255
|
};
|
|
17362
18256
|
}
|
|
17363
18257
|
async ensureManifestProfile(manifest, requestedProfileName) {
|
|
17364
|
-
const existingProfileName =
|
|
18258
|
+
const existingProfileName = normalizeProfileName4(
|
|
17365
18259
|
manifest.profile_name_snapshot ?? manifest.profile
|
|
17366
18260
|
);
|
|
17367
18261
|
const hasExistingProfile = Boolean(
|
|
17368
18262
|
manifest.profile_name_snapshot ?? manifest.profile
|
|
17369
18263
|
);
|
|
17370
|
-
const nextProfileName = requestedProfileName ?
|
|
18264
|
+
const nextProfileName = requestedProfileName ? normalizeProfileName4(requestedProfileName) : existingProfileName;
|
|
17371
18265
|
if ((!requestedProfileName || nextProfileName === existingProfileName) && hasExistingProfile && manifest.profile_uid) {
|
|
17372
18266
|
return manifest;
|
|
17373
18267
|
}
|
|
@@ -17454,7 +18348,7 @@ function isConversationNotFoundError(error) {
|
|
|
17454
18348
|
}
|
|
17455
18349
|
|
|
17456
18350
|
// src/conversations/agent-events.ts
|
|
17457
|
-
import { createHash as
|
|
18351
|
+
import { createHash as createHash5 } from "crypto";
|
|
17458
18352
|
var APP_TOOL_EVENT_FIELDS_TO_DROP = /* @__PURE__ */ new Set([
|
|
17459
18353
|
"output",
|
|
17460
18354
|
"content",
|
|
@@ -17495,7 +18389,7 @@ function projectAgentEvent(input) {
|
|
|
17495
18389
|
summary,
|
|
17496
18390
|
args
|
|
17497
18391
|
});
|
|
17498
|
-
const detail = status === "failed" ?
|
|
18392
|
+
const detail = status === "failed" ? readErrorMessage3(input.payload) ?? actionSummary ?? void 0 : actionSummary ?? void 0;
|
|
17499
18393
|
const subtitle = actionSummary ?? (status === "running" ? agentEventText(input.language, `\u6B63\u5728\u8C03\u7528 ${name}`, `Running ${name}`) : status === "completed" ? agentEventText(input.language, `${name} \u5DF2\u5B8C\u6210`, `${name} done`) : agentEventText(input.language, `${name} \u6267\u884C\u5931\u8D25`, `${name} failed`));
|
|
17500
18394
|
return {
|
|
17501
18395
|
id,
|
|
@@ -17837,9 +18731,9 @@ function stableStringify(value) {
|
|
|
17837
18731
|
}
|
|
17838
18732
|
}
|
|
17839
18733
|
function hashAgentEventKey(value) {
|
|
17840
|
-
return
|
|
18734
|
+
return createHash5("sha256").update(value).digest("hex").slice(0, 16);
|
|
17841
18735
|
}
|
|
17842
|
-
function
|
|
18736
|
+
function readErrorMessage3(payload) {
|
|
17843
18737
|
if (typeof payload.error === "string" && payload.error.trim()) {
|
|
17844
18738
|
return payload.error.trim();
|
|
17845
18739
|
}
|
|
@@ -18697,7 +19591,7 @@ var ContextCompressionCoordinator = class {
|
|
|
18697
19591
|
continue;
|
|
18698
19592
|
}
|
|
18699
19593
|
if (snapshot.runs.some(
|
|
18700
|
-
(run) => run.status === "running" && run.kind !== "command" &&
|
|
19594
|
+
(run) => run.status === "running" && run.kind !== "command" && normalizeProfileName5(run.profile_name_snapshot ?? run.profile) === profileName
|
|
18701
19595
|
)) {
|
|
18702
19596
|
return true;
|
|
18703
19597
|
}
|
|
@@ -18707,7 +19601,7 @@ var ContextCompressionCoordinator = class {
|
|
|
18707
19601
|
async profileNameForRun(conversationId, runId) {
|
|
18708
19602
|
const snapshot = await this.deps.store.readSnapshot(conversationId).catch(() => null);
|
|
18709
19603
|
const run = snapshot?.runs.find((item) => item.id === runId);
|
|
18710
|
-
return
|
|
19604
|
+
return normalizeProfileName5(run?.profile_name_snapshot ?? run?.profile);
|
|
18711
19605
|
}
|
|
18712
19606
|
};
|
|
18713
19607
|
function createContextCompressionMarker(input) {
|
|
@@ -18908,7 +19802,7 @@ function normalizeFocus(value) {
|
|
|
18908
19802
|
function normalizeLanguage2(value) {
|
|
18909
19803
|
return value === "en" ? "en" : "zh-CN";
|
|
18910
19804
|
}
|
|
18911
|
-
function
|
|
19805
|
+
function normalizeProfileName5(value) {
|
|
18912
19806
|
return value?.trim() || "default";
|
|
18913
19807
|
}
|
|
18914
19808
|
function localizedText(language, zh, en) {
|
|
@@ -18936,7 +19830,7 @@ import {
|
|
|
18936
19830
|
rm as rm5,
|
|
18937
19831
|
writeFile as writeFile2
|
|
18938
19832
|
} from "fs/promises";
|
|
18939
|
-
import
|
|
19833
|
+
import path18 from "path";
|
|
18940
19834
|
var ConversationStore = class {
|
|
18941
19835
|
constructor(paths) {
|
|
18942
19836
|
this.paths = paths;
|
|
@@ -18950,7 +19844,7 @@ var ConversationStore = class {
|
|
|
18950
19844
|
const entries = await readdir5(this.paths.conversationsDir, {
|
|
18951
19845
|
withFileTypes: true
|
|
18952
19846
|
}).catch((error) => {
|
|
18953
|
-
if (
|
|
19847
|
+
if (isNodeError11(error, "ENOENT")) {
|
|
18954
19848
|
return [];
|
|
18955
19849
|
}
|
|
18956
19850
|
throw error;
|
|
@@ -19019,7 +19913,7 @@ var ConversationStore = class {
|
|
|
19019
19913
|
await this.readManifest(conversationId);
|
|
19020
19914
|
const raw = await readFile9(this.eventsPath(conversationId), "utf8").catch(
|
|
19021
19915
|
(error) => {
|
|
19022
|
-
if (
|
|
19916
|
+
if (isNodeError11(error, "ENOENT")) {
|
|
19023
19917
|
return "";
|
|
19024
19918
|
}
|
|
19025
19919
|
throw error;
|
|
@@ -19067,40 +19961,40 @@ var ConversationStore = class {
|
|
|
19067
19961
|
return manifest != null && manifest.status !== "deleted_soft";
|
|
19068
19962
|
}
|
|
19069
19963
|
removeConversationAttachments(conversationId) {
|
|
19070
|
-
return rm5(
|
|
19964
|
+
return rm5(path18.join(this.conversationDir(conversationId), "attachments"), {
|
|
19071
19965
|
recursive: true,
|
|
19072
19966
|
force: true
|
|
19073
19967
|
});
|
|
19074
19968
|
}
|
|
19075
19969
|
conversationDir(conversationId) {
|
|
19076
19970
|
assertValidConversationId(conversationId);
|
|
19077
|
-
return
|
|
19971
|
+
return path18.join(this.paths.conversationsDir, conversationId);
|
|
19078
19972
|
}
|
|
19079
19973
|
manifestPath(conversationId) {
|
|
19080
|
-
return
|
|
19974
|
+
return path18.join(this.conversationDir(conversationId), "manifest.json");
|
|
19081
19975
|
}
|
|
19082
19976
|
snapshotPath(conversationId) {
|
|
19083
|
-
return
|
|
19977
|
+
return path18.join(this.conversationDir(conversationId), "snapshot.json");
|
|
19084
19978
|
}
|
|
19085
19979
|
eventsPath(conversationId) {
|
|
19086
|
-
return
|
|
19980
|
+
return path18.join(this.conversationDir(conversationId), "events.ndjson");
|
|
19087
19981
|
}
|
|
19088
19982
|
};
|
|
19089
19983
|
function createEmptySnapshot2() {
|
|
19090
19984
|
return { schema_version: 1, messages: [], runs: [] };
|
|
19091
19985
|
}
|
|
19092
|
-
function
|
|
19986
|
+
function isNodeError11(error, code) {
|
|
19093
19987
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
19094
19988
|
}
|
|
19095
19989
|
|
|
19096
19990
|
// src/conversations/hermes-session-sync.ts
|
|
19097
19991
|
import { randomUUID as randomUUID11 } from "crypto";
|
|
19098
|
-
import { readdir as readdir7, readFile as readFile11, stat as
|
|
19099
|
-
import
|
|
19992
|
+
import { readdir as readdir7, readFile as readFile11, stat as stat12 } from "fs/promises";
|
|
19993
|
+
import path20 from "path";
|
|
19100
19994
|
|
|
19101
19995
|
// src/conversations/delivery-import.ts
|
|
19102
|
-
import { lstat as lstat2, readFile as readFile10, readdir as readdir6, stat as
|
|
19103
|
-
import
|
|
19996
|
+
import { lstat as lstat2, readFile as readFile10, readdir as readdir6, stat as stat11 } from "fs/promises";
|
|
19997
|
+
import path19 from "path";
|
|
19104
19998
|
var MAX_IMPORTED_BLOB_BYTES = 100 * 1024 * 1024;
|
|
19105
19999
|
var MAX_MEDIA_IMPORT_FAILURES = 20;
|
|
19106
20000
|
var MAX_DELIVERY_FILES = 50;
|
|
@@ -19171,16 +20065,16 @@ var SUPPORTED_DELIVERY_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
19171
20065
|
".m4a"
|
|
19172
20066
|
]);
|
|
19173
20067
|
function resolveDeliveryStagingTarget(paths, stagingDir) {
|
|
19174
|
-
const resolvedDir =
|
|
19175
|
-
const relative =
|
|
19176
|
-
if (!relative || relative.startsWith("..") ||
|
|
20068
|
+
const resolvedDir = path19.resolve(stagingDir);
|
|
20069
|
+
const relative = path19.relative(path19.resolve(paths.conversationsDir), resolvedDir);
|
|
20070
|
+
if (!relative || relative.startsWith("..") || path19.isAbsolute(relative)) {
|
|
19177
20071
|
throw new LinkHttpError(
|
|
19178
20072
|
400,
|
|
19179
20073
|
"delivery_staging_invalid",
|
|
19180
20074
|
"delivery staging directory must be inside Hermes Link conversations"
|
|
19181
20075
|
);
|
|
19182
20076
|
}
|
|
19183
|
-
const segments = relative.split(
|
|
20077
|
+
const segments = relative.split(path19.sep);
|
|
19184
20078
|
if (segments.length !== 3 || segments[1] !== DELIVERY_STAGING_SEGMENT || !segments[0] || !segments[2]) {
|
|
19185
20079
|
throw new LinkHttpError(
|
|
19186
20080
|
400,
|
|
@@ -19196,7 +20090,7 @@ function resolveDeliveryStagingTarget(paths, stagingDir) {
|
|
|
19196
20090
|
}
|
|
19197
20091
|
async function collectStagedDeliveryReferences(stagingDir) {
|
|
19198
20092
|
const directoryStat = await lstat2(stagingDir).catch((error) => {
|
|
19199
|
-
if (
|
|
20093
|
+
if (isNodeError12(error, "ENOENT")) {
|
|
19200
20094
|
throw new LinkHttpError(
|
|
19201
20095
|
404,
|
|
19202
20096
|
"delivery_staging_not_found",
|
|
@@ -19216,7 +20110,7 @@ async function collectStagedDeliveryReferences(stagingDir) {
|
|
|
19216
20110
|
return entries.filter((entry) => entry.isFile() && !entry.name.startsWith(".")).filter((entry) => isSupportedDeliveryFilename(entry.name)).sort(
|
|
19217
20111
|
(left, right) => left.name.localeCompare(right.name, "en", { numeric: true })
|
|
19218
20112
|
).slice(0, MAX_DELIVERY_FILES).map((entry) => {
|
|
19219
|
-
const sourcePath =
|
|
20113
|
+
const sourcePath = path19.join(stagingDir, entry.name);
|
|
19220
20114
|
const mime = inferMimeType(sourcePath);
|
|
19221
20115
|
return {
|
|
19222
20116
|
path: sourcePath,
|
|
@@ -19376,8 +20270,8 @@ function emptyImportResult(input) {
|
|
|
19376
20270
|
}
|
|
19377
20271
|
async function writeBlobFromFile(deps, conversationId, source) {
|
|
19378
20272
|
const sourcePath = resolveMediaSourcePath(source.path);
|
|
19379
|
-
const fileStat = await
|
|
19380
|
-
if (
|
|
20273
|
+
const fileStat = await stat11(sourcePath).catch((error) => {
|
|
20274
|
+
if (isNodeError12(error, "ENOENT")) {
|
|
19381
20275
|
throw new LinkHttpError(
|
|
19382
20276
|
404,
|
|
19383
20277
|
"media_source_not_found",
|
|
@@ -19402,7 +20296,7 @@ async function writeBlobFromFile(deps, conversationId, source) {
|
|
|
19402
20296
|
}
|
|
19403
20297
|
return deps.writeBlob(conversationId, {
|
|
19404
20298
|
bytes: await readFile10(sourcePath),
|
|
19405
|
-
filename:
|
|
20299
|
+
filename: path19.basename(sourcePath),
|
|
19406
20300
|
mime: source.mime ?? inferMimeType(sourcePath)
|
|
19407
20301
|
});
|
|
19408
20302
|
}
|
|
@@ -19411,11 +20305,11 @@ function describeMediaImportFailure(reference, sourceKey, error) {
|
|
|
19411
20305
|
key: sourceKey,
|
|
19412
20306
|
filename: sanitizeFilename(reference.path, "attachment"),
|
|
19413
20307
|
reason: error instanceof Error ? error.message : String(error),
|
|
19414
|
-
...
|
|
20308
|
+
...isNodeError12(error) && error.code ? { code: error.code } : {}
|
|
19415
20309
|
};
|
|
19416
20310
|
}
|
|
19417
20311
|
function isSupportedDeliveryFilename(filename) {
|
|
19418
|
-
return SUPPORTED_DELIVERY_EXTENSIONS.has(
|
|
20312
|
+
return SUPPORTED_DELIVERY_EXTENSIONS.has(path19.extname(filename).toLowerCase());
|
|
19419
20313
|
}
|
|
19420
20314
|
function readString10(payload, key) {
|
|
19421
20315
|
const value = payload[key];
|
|
@@ -19424,7 +20318,7 @@ function readString10(payload, key) {
|
|
|
19424
20318
|
function toRecord10(value) {
|
|
19425
20319
|
return typeof value === "object" && value !== null ? value : {};
|
|
19426
20320
|
}
|
|
19427
|
-
function
|
|
20321
|
+
function isNodeError12(error, code) {
|
|
19428
20322
|
return typeof error === "object" && error !== null && "code" in error && (code === void 0 || error.code === code);
|
|
19429
20323
|
}
|
|
19430
20324
|
|
|
@@ -19473,7 +20367,7 @@ async function syncHermesSessionsIntoConversations(paths, logger, options = {})
|
|
|
19473
20367
|
const candidates = [];
|
|
19474
20368
|
for (const profileName of profileNames) {
|
|
19475
20369
|
const profileDir = resolveHermesProfileDir(profileName);
|
|
19476
|
-
const dbPath =
|
|
20370
|
+
const dbPath = path20.join(profileDir, "state.db");
|
|
19477
20371
|
const sessions = await listProfileSessions(dbPath).catch((error) => {
|
|
19478
20372
|
result.errors.push({
|
|
19479
20373
|
profile: profileName,
|
|
@@ -19589,7 +20483,7 @@ async function syncHermesCronSessionIntoConversations(paths, logger, input) {
|
|
|
19589
20483
|
const knownHermesSessions = await readKnownHermesSessions(store);
|
|
19590
20484
|
const profileName = input.profileName.trim() || DEFAULT_PROFILE_NAME;
|
|
19591
20485
|
const profileDir = resolveHermesProfileDir(profileName);
|
|
19592
|
-
const dbPath =
|
|
20486
|
+
const dbPath = path20.join(profileDir, "state.db");
|
|
19593
20487
|
const sessions = await listProfileSessionsByIdPrefix(
|
|
19594
20488
|
dbPath,
|
|
19595
20489
|
`cron_${jobId}_`
|
|
@@ -20604,7 +21498,7 @@ async function discoverHermesProfileNames() {
|
|
|
20604
21498
|
const profilesDir = resolveHermesProfilesDir();
|
|
20605
21499
|
const entries = await readdir7(profilesDir, { withFileTypes: true }).catch(
|
|
20606
21500
|
(error) => {
|
|
20607
|
-
if (
|
|
21501
|
+
if (isNodeError13(error, "ENOENT")) {
|
|
20608
21502
|
return [];
|
|
20609
21503
|
}
|
|
20610
21504
|
throw error;
|
|
@@ -20900,10 +21794,10 @@ async function readJsonlMessages(profileName, sessionId) {
|
|
|
20900
21794
|
return [];
|
|
20901
21795
|
}
|
|
20902
21796
|
const profileDir = resolveHermesProfileDir(profileName);
|
|
20903
|
-
const sessionsDir = await readHermesSessionsDir(profileName).then((value) => value.sessionsDir).catch(() =>
|
|
20904
|
-
const transcriptPath =
|
|
21797
|
+
const sessionsDir = await readHermesSessionsDir(profileName).then((value) => value.sessionsDir).catch(() => path20.join(profileDir, "sessions"));
|
|
21798
|
+
const transcriptPath = path20.join(sessionsDir, `${sessionId}.jsonl`);
|
|
20905
21799
|
const raw = await readFile11(transcriptPath, "utf8").catch((error) => {
|
|
20906
|
-
if (
|
|
21800
|
+
if (isNodeError13(error, "ENOENT")) {
|
|
20907
21801
|
return "";
|
|
20908
21802
|
}
|
|
20909
21803
|
throw error;
|
|
@@ -21231,8 +22125,8 @@ function quoteIdentifier(value) {
|
|
|
21231
22125
|
return `"${value.replaceAll('"', '""')}"`;
|
|
21232
22126
|
}
|
|
21233
22127
|
async function isFile(filePath) {
|
|
21234
|
-
return
|
|
21235
|
-
if (
|
|
22128
|
+
return stat12(filePath).then((value) => value.isFile()).catch((error) => {
|
|
22129
|
+
if (isNodeError13(error, "ENOENT")) {
|
|
21236
22130
|
return false;
|
|
21237
22131
|
}
|
|
21238
22132
|
throw error;
|
|
@@ -21265,13 +22159,13 @@ function readBoolean(value) {
|
|
|
21265
22159
|
}
|
|
21266
22160
|
return false;
|
|
21267
22161
|
}
|
|
21268
|
-
function
|
|
22162
|
+
function isNodeError13(error, code) {
|
|
21269
22163
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
21270
22164
|
}
|
|
21271
22165
|
|
|
21272
22166
|
// src/conversations/delivery-context.ts
|
|
21273
|
-
import { copyFile, mkdir as mkdir11, stat as
|
|
21274
|
-
import
|
|
22167
|
+
import { copyFile, mkdir as mkdir11, stat as stat13 } from "fs/promises";
|
|
22168
|
+
import path21 from "path";
|
|
21275
22169
|
var ACTIVE_CONTEXTS = /* @__PURE__ */ new Map();
|
|
21276
22170
|
var CONTEXT_TTL_MS = 2 * 60 * 60 * 1e3;
|
|
21277
22171
|
var MAX_DELIVERY_TOOL_FILES = 50;
|
|
@@ -21343,10 +22237,10 @@ function findDeliveryContext(input) {
|
|
|
21343
22237
|
);
|
|
21344
22238
|
}
|
|
21345
22239
|
async function copyFilesIntoDeliveryContext(paths, context, files) {
|
|
21346
|
-
const stagingDir =
|
|
21347
|
-
const conversationsRoot =
|
|
21348
|
-
const relative =
|
|
21349
|
-
if (!relative || relative.startsWith("..") ||
|
|
22240
|
+
const stagingDir = path21.resolve(context.stagingDir);
|
|
22241
|
+
const conversationsRoot = path21.resolve(paths.conversationsDir);
|
|
22242
|
+
const relative = path21.relative(conversationsRoot, stagingDir);
|
|
22243
|
+
if (!relative || relative.startsWith("..") || path21.isAbsolute(relative)) {
|
|
21350
22244
|
throw new LinkHttpError(
|
|
21351
22245
|
400,
|
|
21352
22246
|
"delivery_staging_invalid",
|
|
@@ -21357,13 +22251,13 @@ async function copyFilesIntoDeliveryContext(paths, context, files) {
|
|
|
21357
22251
|
const result = { staged: [], skipped: [] };
|
|
21358
22252
|
const usedNames = /* @__PURE__ */ new Set();
|
|
21359
22253
|
for (const [index, file] of files.slice(0, MAX_DELIVERY_TOOL_FILES).entries()) {
|
|
21360
|
-
const sourcePath =
|
|
22254
|
+
const sourcePath = path21.resolve(file.path);
|
|
21361
22255
|
let baseName = sanitizeFilename(
|
|
21362
|
-
file.caption ||
|
|
22256
|
+
file.caption || path21.basename(sourcePath),
|
|
21363
22257
|
`attachment-${index + 1}`
|
|
21364
22258
|
);
|
|
21365
|
-
const sourceExtension =
|
|
21366
|
-
if (!
|
|
22259
|
+
const sourceExtension = path21.extname(sourcePath);
|
|
22260
|
+
if (!path21.extname(baseName) && sourceExtension) {
|
|
21367
22261
|
baseName = `${baseName}${sourceExtension}`;
|
|
21368
22262
|
}
|
|
21369
22263
|
const filename = uniqueStagingFilename(
|
|
@@ -21377,8 +22271,8 @@ async function copyFilesIntoDeliveryContext(paths, context, files) {
|
|
|
21377
22271
|
});
|
|
21378
22272
|
continue;
|
|
21379
22273
|
}
|
|
21380
|
-
const sourceStat = await
|
|
21381
|
-
if (
|
|
22274
|
+
const sourceStat = await stat13(sourcePath).catch((error) => {
|
|
22275
|
+
if (isNodeError14(error, "ENOENT")) {
|
|
21382
22276
|
return null;
|
|
21383
22277
|
}
|
|
21384
22278
|
throw error;
|
|
@@ -21390,7 +22284,7 @@ async function copyFilesIntoDeliveryContext(paths, context, files) {
|
|
|
21390
22284
|
});
|
|
21391
22285
|
continue;
|
|
21392
22286
|
}
|
|
21393
|
-
const targetPath =
|
|
22287
|
+
const targetPath = path21.join(stagingDir, filename);
|
|
21394
22288
|
await copyFile(sourcePath, targetPath);
|
|
21395
22289
|
result.staged.push({
|
|
21396
22290
|
source_path: sourcePath,
|
|
@@ -21433,7 +22327,7 @@ function normalizeDeliveryFileInputs(value) {
|
|
|
21433
22327
|
});
|
|
21434
22328
|
}
|
|
21435
22329
|
function uniqueStagingFilename(filename, usedNames) {
|
|
21436
|
-
const extension =
|
|
22330
|
+
const extension = path21.extname(filename);
|
|
21437
22331
|
const stem = filename.slice(0, filename.length - extension.length);
|
|
21438
22332
|
let candidate = filename;
|
|
21439
22333
|
let suffix = 2;
|
|
@@ -21469,12 +22363,12 @@ function readString12(payload, key) {
|
|
|
21469
22363
|
const value = payload[key];
|
|
21470
22364
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
21471
22365
|
}
|
|
21472
|
-
function
|
|
22366
|
+
function isNodeError14(error, code) {
|
|
21473
22367
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
21474
22368
|
}
|
|
21475
22369
|
|
|
21476
22370
|
// src/conversations/run-lifecycle.ts
|
|
21477
|
-
import { createHash as
|
|
22371
|
+
import { createHash as createHash7 } from "crypto";
|
|
21478
22372
|
import { readdir as readdir9 } from "fs/promises";
|
|
21479
22373
|
|
|
21480
22374
|
// src/hermes/api-server.ts
|
|
@@ -22175,9 +23069,9 @@ function readBoolean2(payload, key) {
|
|
|
22175
23069
|
|
|
22176
23070
|
// src/hermes/stt.ts
|
|
22177
23071
|
import { execFile as execFile3 } from "child_process";
|
|
22178
|
-
import { access as access2, readFile as readFile12, stat as
|
|
22179
|
-
import
|
|
22180
|
-
import
|
|
23072
|
+
import { access as access2, readFile as readFile12, stat as stat14 } from "fs/promises";
|
|
23073
|
+
import os6 from "os";
|
|
23074
|
+
import path22 from "path";
|
|
22181
23075
|
import { promisify as promisify3 } from "util";
|
|
22182
23076
|
var execFileAsync3 = promisify3(execFile3);
|
|
22183
23077
|
var STT_RESULT_PREFIX = "__HERMES_LINK_STT__";
|
|
@@ -22272,7 +23166,7 @@ async function buildHermesSttEnv(profileName, hermesSourceRoot) {
|
|
|
22272
23166
|
};
|
|
22273
23167
|
const sourceRoot = hermesSourceRoot ?? await findDevHermesAgentSource();
|
|
22274
23168
|
if (sourceRoot) {
|
|
22275
|
-
env.PYTHONPATH = [sourceRoot, env.PYTHONPATH].filter(Boolean).join(
|
|
23169
|
+
env.PYTHONPATH = [sourceRoot, env.PYTHONPATH].filter(Boolean).join(path22.delimiter);
|
|
22276
23170
|
}
|
|
22277
23171
|
return env;
|
|
22278
23172
|
}
|
|
@@ -22367,14 +23261,14 @@ async function resolveHermesPythonRuntime() {
|
|
|
22367
23261
|
};
|
|
22368
23262
|
}
|
|
22369
23263
|
async function resolveExecutablePath(command) {
|
|
22370
|
-
if (
|
|
23264
|
+
if (path22.isAbsolute(command)) {
|
|
22371
23265
|
return await isExecutableFile2(command) ? command : null;
|
|
22372
23266
|
}
|
|
22373
23267
|
const pathEnv = process.env.PATH ?? "";
|
|
22374
23268
|
const extensions = process.platform === "win32" ? (process.env.PATHEXT ?? ".EXE;.CMD;.BAT").split(";") : [""];
|
|
22375
|
-
for (const dir of pathEnv.split(
|
|
23269
|
+
for (const dir of pathEnv.split(path22.delimiter)) {
|
|
22376
23270
|
for (const extension of extensions) {
|
|
22377
|
-
const candidate =
|
|
23271
|
+
const candidate = path22.join(dir, `${command}${extension}`);
|
|
22378
23272
|
if (await isExecutableFile2(candidate)) {
|
|
22379
23273
|
return candidate;
|
|
22380
23274
|
}
|
|
@@ -22384,7 +23278,7 @@ async function resolveExecutablePath(command) {
|
|
|
22384
23278
|
}
|
|
22385
23279
|
async function isExecutableFile2(filePath) {
|
|
22386
23280
|
try {
|
|
22387
|
-
const info = await
|
|
23281
|
+
const info = await stat14(filePath);
|
|
22388
23282
|
if (!info.isFile()) {
|
|
22389
23283
|
return false;
|
|
22390
23284
|
}
|
|
@@ -22396,11 +23290,11 @@ async function isExecutableFile2(filePath) {
|
|
|
22396
23290
|
}
|
|
22397
23291
|
async function findHermesVenvPython(sourceRoot) {
|
|
22398
23292
|
const candidates = process.platform === "win32" ? [
|
|
22399
|
-
|
|
22400
|
-
|
|
23293
|
+
path22.join(sourceRoot, "venv", "Scripts", "python.exe"),
|
|
23294
|
+
path22.join(sourceRoot, ".venv", "Scripts", "python.exe")
|
|
22401
23295
|
] : [
|
|
22402
|
-
|
|
22403
|
-
|
|
23296
|
+
path22.join(sourceRoot, "venv", "bin", "python"),
|
|
23297
|
+
path22.join(sourceRoot, ".venv", "bin", "python")
|
|
22404
23298
|
];
|
|
22405
23299
|
for (const candidate of candidates) {
|
|
22406
23300
|
if (await isExecutableFile2(candidate)) {
|
|
@@ -22429,8 +23323,8 @@ function shebangToPythonCommand(shebang) {
|
|
|
22429
23323
|
}
|
|
22430
23324
|
async function findDevHermesAgentSource() {
|
|
22431
23325
|
const candidates = [
|
|
22432
|
-
|
|
22433
|
-
|
|
23326
|
+
path22.resolve(process.cwd(), "reference/hermes-agent"),
|
|
23327
|
+
path22.resolve(process.cwd(), "../../reference/hermes-agent")
|
|
22434
23328
|
];
|
|
22435
23329
|
for (const candidate of candidates) {
|
|
22436
23330
|
if (await isHermesAgentSourceRoot(candidate)) {
|
|
@@ -22446,7 +23340,7 @@ async function readHermesLauncherTarget(filePath) {
|
|
|
22446
23340
|
line
|
|
22447
23341
|
);
|
|
22448
23342
|
const rawTarget = quoted?.groups?.target ?? /^\s*exec\s+(?<target>\S+)\s+(?:"\$@"|'\$@')/.exec(line)?.groups?.target;
|
|
22449
|
-
if (!rawTarget || !
|
|
23343
|
+
if (!rawTarget || !path22.isAbsolute(rawTarget)) {
|
|
22450
23344
|
continue;
|
|
22451
23345
|
}
|
|
22452
23346
|
if (await isExecutableFile2(rawTarget)) {
|
|
@@ -22476,12 +23370,12 @@ async function resolveHermesEntrypointRuntime(entrypointPath) {
|
|
|
22476
23370
|
return null;
|
|
22477
23371
|
}
|
|
22478
23372
|
async function findHermesSourceRoot(executablePath) {
|
|
22479
|
-
let cursor =
|
|
23373
|
+
let cursor = path22.dirname(path22.resolve(executablePath));
|
|
22480
23374
|
for (let index = 0; index < 6; index += 1) {
|
|
22481
23375
|
if (await isHermesAgentSourceRoot(cursor)) {
|
|
22482
23376
|
return cursor;
|
|
22483
23377
|
}
|
|
22484
|
-
const parent =
|
|
23378
|
+
const parent = path22.dirname(cursor);
|
|
22485
23379
|
if (parent === cursor) {
|
|
22486
23380
|
break;
|
|
22487
23381
|
}
|
|
@@ -22492,14 +23386,14 @@ async function findHermesSourceRoot(executablePath) {
|
|
|
22492
23386
|
function hermesSourceRootCandidates() {
|
|
22493
23387
|
const candidates = [
|
|
22494
23388
|
process.env.HERMES_PYTHON_SRC_ROOT?.trim(),
|
|
22495
|
-
|
|
23389
|
+
path22.join(os6.homedir(), ".hermes", "hermes-agent"),
|
|
22496
23390
|
"/usr/local/lib/hermes-agent",
|
|
22497
23391
|
"/opt/hermes"
|
|
22498
23392
|
].filter((candidate) => Boolean(candidate));
|
|
22499
23393
|
if (process.platform === "win32") {
|
|
22500
23394
|
const localAppData = process.env.LOCALAPPDATA?.trim();
|
|
22501
23395
|
if (localAppData) {
|
|
22502
|
-
candidates.unshift(
|
|
23396
|
+
candidates.unshift(path22.join(localAppData, "hermes", "hermes-agent"));
|
|
22503
23397
|
}
|
|
22504
23398
|
}
|
|
22505
23399
|
return candidates;
|
|
@@ -22509,10 +23403,10 @@ async function findExplicitHermesSourceRoot() {
|
|
|
22509
23403
|
return explicit && await isHermesAgentSourceRoot(explicit) ? explicit : null;
|
|
22510
23404
|
}
|
|
22511
23405
|
async function isHermesAgentSourceRoot(candidate) {
|
|
22512
|
-
return await isDirectory(candidate) && await isDirectory(
|
|
23406
|
+
return await isDirectory(candidate) && await isDirectory(path22.join(candidate, "tools")) && await isDirectory(path22.join(candidate, "hermes_cli"));
|
|
22513
23407
|
}
|
|
22514
23408
|
async function isDirectory(candidate) {
|
|
22515
|
-
return
|
|
23409
|
+
return stat14(candidate).then((info) => info.isDirectory()).catch(() => false);
|
|
22516
23410
|
}
|
|
22517
23411
|
function compactProcessOutput(value) {
|
|
22518
23412
|
const compact = value.trim().replace(/\s+/gu, " ").slice(0, 500);
|
|
@@ -22520,14 +23414,14 @@ function compactProcessOutput(value) {
|
|
|
22520
23414
|
}
|
|
22521
23415
|
|
|
22522
23416
|
// src/hermes/usage-probe.ts
|
|
22523
|
-
import { open as open3, readFile as readFile14, rm as rm6, stat as
|
|
22524
|
-
import
|
|
23417
|
+
import { open as open3, readFile as readFile14, rm as rm6, stat as stat16 } from "fs/promises";
|
|
23418
|
+
import path24 from "path";
|
|
22525
23419
|
import YAML3 from "yaml";
|
|
22526
23420
|
|
|
22527
23421
|
// src/hermes/profiles.ts
|
|
22528
23422
|
import { execFile as execFile4 } from "child_process";
|
|
22529
|
-
import { readdir as readdir8, readFile as readFile13, rename as rename3, stat as
|
|
22530
|
-
import
|
|
23423
|
+
import { readdir as readdir8, readFile as readFile13, rename as rename3, stat as stat15 } from "fs/promises";
|
|
23424
|
+
import path23 from "path";
|
|
22531
23425
|
import { setTimeout as delay4 } from "timers/promises";
|
|
22532
23426
|
import { promisify as promisify4 } from "util";
|
|
22533
23427
|
import YAML2 from "yaml";
|
|
@@ -22546,7 +23440,7 @@ async function listHermesProfiles(paths = resolveRuntimePaths()) {
|
|
|
22546
23440
|
const profilesDir = resolveHermesProfilesDir();
|
|
22547
23441
|
const entries = await readdir8(profilesDir, { withFileTypes: true }).catch(
|
|
22548
23442
|
(error) => {
|
|
22549
|
-
if (
|
|
23443
|
+
if (isNodeError15(error, "ENOENT")) {
|
|
22550
23444
|
return [];
|
|
22551
23445
|
}
|
|
22552
23446
|
throw error;
|
|
@@ -22600,8 +23494,8 @@ async function prepareHermesProfilesForUse(paths = resolveRuntimePaths()) {
|
|
|
22600
23494
|
async function getHermesProfileStatus(name, paths = resolveRuntimePaths()) {
|
|
22601
23495
|
assertProfileName(name);
|
|
22602
23496
|
const profile = await profileInfo(name, paths);
|
|
22603
|
-
const exists = await
|
|
22604
|
-
if (
|
|
23497
|
+
const exists = await stat15(profile.path).then((value) => value.isDirectory()).catch((error) => {
|
|
23498
|
+
if (isNodeError15(error, "ENOENT")) {
|
|
22605
23499
|
return false;
|
|
22606
23500
|
}
|
|
22607
23501
|
throw error;
|
|
@@ -22670,7 +23564,7 @@ async function readHermesProfileCapabilities(name) {
|
|
|
22670
23564
|
return {
|
|
22671
23565
|
defaultModel: listedModels?.defaultModel ?? null,
|
|
22672
23566
|
modelCount: listedModels?.models.length ?? 0,
|
|
22673
|
-
skillCount: await countSkills(
|
|
23567
|
+
skillCount: await countSkills(path23.join(profileDir, "skills")).catch(
|
|
22674
23568
|
() => 0
|
|
22675
23569
|
),
|
|
22676
23570
|
toolCount: await countConfiguredTools(name).catch(() => 0)
|
|
@@ -22713,8 +23607,8 @@ function assertProfileName(name) {
|
|
|
22713
23607
|
}
|
|
22714
23608
|
}
|
|
22715
23609
|
async function pathExists(targetPath) {
|
|
22716
|
-
return await
|
|
22717
|
-
if (
|
|
23610
|
+
return await stat15(targetPath).then(() => true).catch((error) => {
|
|
23611
|
+
if (isNodeError15(error, "ENOENT")) {
|
|
22718
23612
|
return false;
|
|
22719
23613
|
}
|
|
22720
23614
|
throw error;
|
|
@@ -22722,8 +23616,8 @@ async function pathExists(targetPath) {
|
|
|
22722
23616
|
}
|
|
22723
23617
|
async function hasDefaultProfileConfigSource() {
|
|
22724
23618
|
const profilePath = resolveHermesProfileDir(DEFAULT_PROFILE);
|
|
22725
|
-
const profileStat = await
|
|
22726
|
-
if (
|
|
23619
|
+
const profileStat = await stat15(profilePath).catch((error) => {
|
|
23620
|
+
if (isNodeError15(error, "ENOENT")) {
|
|
22727
23621
|
return null;
|
|
22728
23622
|
}
|
|
22729
23623
|
throw error;
|
|
@@ -22731,7 +23625,7 @@ async function hasDefaultProfileConfigSource() {
|
|
|
22731
23625
|
if (!profileStat?.isDirectory()) {
|
|
22732
23626
|
return false;
|
|
22733
23627
|
}
|
|
22734
|
-
return await pathExists(resolveHermesConfigPath(DEFAULT_PROFILE)) || await pathExists(
|
|
23628
|
+
return await pathExists(resolveHermesConfigPath(DEFAULT_PROFILE)) || await pathExists(path23.join(profilePath, ".env"));
|
|
22735
23629
|
}
|
|
22736
23630
|
async function deleteHermesProfileWithCli(name) {
|
|
22737
23631
|
try {
|
|
@@ -22844,7 +23738,7 @@ function isProcessRunning(pid) {
|
|
|
22844
23738
|
process.kill(pid, 0);
|
|
22845
23739
|
return true;
|
|
22846
23740
|
} catch (error) {
|
|
22847
|
-
return
|
|
23741
|
+
return isNodeError15(error, "EPERM");
|
|
22848
23742
|
}
|
|
22849
23743
|
}
|
|
22850
23744
|
async function waitForProfilePathToRemainAbsent(profilePath) {
|
|
@@ -22881,7 +23775,7 @@ function readExecErrorOutput2(error) {
|
|
|
22881
23775
|
}
|
|
22882
23776
|
return parts.join("\n");
|
|
22883
23777
|
}
|
|
22884
|
-
function
|
|
23778
|
+
function isNodeError15(error, code) {
|
|
22885
23779
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
22886
23780
|
}
|
|
22887
23781
|
function errorMessage(error) {
|
|
@@ -22893,7 +23787,7 @@ function escapeRegExp2(value) {
|
|
|
22893
23787
|
async function countSkills(root) {
|
|
22894
23788
|
const entries = await readdir8(root, { withFileTypes: true }).catch(
|
|
22895
23789
|
(error) => {
|
|
22896
|
-
if (
|
|
23790
|
+
if (isNodeError15(error, "ENOENT")) {
|
|
22897
23791
|
return [];
|
|
22898
23792
|
}
|
|
22899
23793
|
throw error;
|
|
@@ -22901,7 +23795,7 @@ async function countSkills(root) {
|
|
|
22901
23795
|
);
|
|
22902
23796
|
let count = 0;
|
|
22903
23797
|
for (const entry of entries) {
|
|
22904
|
-
const entryPath =
|
|
23798
|
+
const entryPath = path23.join(root, entry.name);
|
|
22905
23799
|
if (entry.name === ".git" || entry.name === ".hub") {
|
|
22906
23800
|
continue;
|
|
22907
23801
|
}
|
|
@@ -22920,7 +23814,7 @@ async function countConfiguredTools(profileName) {
|
|
|
22920
23814
|
resolveHermesConfigPath(profileName),
|
|
22921
23815
|
"utf8"
|
|
22922
23816
|
).catch((error) => {
|
|
22923
|
-
if (
|
|
23817
|
+
if (isNodeError15(error, "ENOENT")) {
|
|
22924
23818
|
return "";
|
|
22925
23819
|
}
|
|
22926
23820
|
throw error;
|
|
@@ -22991,10 +23885,10 @@ async function ensureHermesUsageProbeForProfiles(options = {}) {
|
|
|
22991
23885
|
}
|
|
22992
23886
|
async function ensureHermesUsageProbeForProfile(profileName, options = {}) {
|
|
22993
23887
|
const paths = options.paths ?? resolveRuntimePaths();
|
|
22994
|
-
const normalizedProfile =
|
|
23888
|
+
const normalizedProfile = normalizeProfileName6(profileName);
|
|
22995
23889
|
const profilePath = resolveHermesProfileDir(normalizedProfile);
|
|
22996
23890
|
const configPath = resolveHermesConfigPath(normalizedProfile);
|
|
22997
|
-
const pluginPath =
|
|
23891
|
+
const pluginPath = path24.join(
|
|
22998
23892
|
profilePath,
|
|
22999
23893
|
"plugins",
|
|
23000
23894
|
HERMES_USAGE_PROBE_PLUGIN_KEY
|
|
@@ -23020,7 +23914,7 @@ async function ensureHermesUsageProbeForProfile(profileName, options = {}) {
|
|
|
23020
23914
|
return { ...base(), skipped: true };
|
|
23021
23915
|
}
|
|
23022
23916
|
try {
|
|
23023
|
-
await ensureDirectoryWithInheritedMetadata(
|
|
23917
|
+
await ensureDirectoryWithInheritedMetadata(path24.dirname(eventsPath), 448);
|
|
23024
23918
|
const state = await readUsageProbeState(paths);
|
|
23025
23919
|
const pluginState = state.profiles[normalizedProfile];
|
|
23026
23920
|
const currentConfig = await readUsageProbeConfigStatus({
|
|
@@ -23116,11 +24010,11 @@ async function ensureHermesUsageProbeForProfile(profileName, options = {}) {
|
|
|
23116
24010
|
}
|
|
23117
24011
|
async function findHermesUsageProbeEventForRun(input) {
|
|
23118
24012
|
const paths = input.paths ?? resolveRuntimePaths();
|
|
23119
|
-
const profileName =
|
|
24013
|
+
const profileName = normalizeProfileName6(input.profileName);
|
|
23120
24014
|
const eventsPath = resolveUsageProbeEventsPath(paths, profileName);
|
|
23121
24015
|
const raw = await readTail2(eventsPath, MAX_EVENT_READ_BYTES).catch(
|
|
23122
24016
|
(error) => {
|
|
23123
|
-
if (
|
|
24017
|
+
if (isNodeError16(error, "ENOENT")) {
|
|
23124
24018
|
return "";
|
|
23125
24019
|
}
|
|
23126
24020
|
throw error;
|
|
@@ -23153,7 +24047,7 @@ async function findHermesUsageProbeEventForRun(input) {
|
|
|
23153
24047
|
return aggregateUsageProbeEvents(events.sort(compareUsageProbeEventsByTime));
|
|
23154
24048
|
}
|
|
23155
24049
|
function resolveUsageProbeEventsPath(paths = resolveRuntimePaths(), profileName = "default") {
|
|
23156
|
-
return
|
|
24050
|
+
return path24.join(
|
|
23157
24051
|
paths.homeDir,
|
|
23158
24052
|
USAGE_PROBE_DIR,
|
|
23159
24053
|
safeProfileSegment(profileName),
|
|
@@ -23177,8 +24071,8 @@ function summarizeUsageProbeEnsure(result) {
|
|
|
23177
24071
|
};
|
|
23178
24072
|
}
|
|
23179
24073
|
async function writeUsageProbePlugin(input) {
|
|
23180
|
-
const manifestPath =
|
|
23181
|
-
const initPath =
|
|
24074
|
+
const manifestPath = path24.join(input.pluginPath, "plugin.yaml");
|
|
24075
|
+
const initPath = path24.join(input.pluginPath, "__init__.py");
|
|
23182
24076
|
const manifest = usageProbeManifest();
|
|
23183
24077
|
const source = usageProbePythonSource(
|
|
23184
24078
|
input.profileName,
|
|
@@ -23191,7 +24085,7 @@ async function writeUsageProbePlugin(input) {
|
|
|
23191
24085
|
}
|
|
23192
24086
|
async function writeManagedFile(filePath, content) {
|
|
23193
24087
|
const existing = await readFile14(filePath, "utf8").catch((error) => {
|
|
23194
|
-
if (
|
|
24088
|
+
if (isNodeError16(error, "ENOENT")) {
|
|
23195
24089
|
return null;
|
|
23196
24090
|
}
|
|
23197
24091
|
throw error;
|
|
@@ -23294,13 +24188,13 @@ function hasManagedPluginEntry(entries) {
|
|
|
23294
24188
|
return entries.includes(HERMES_USAGE_PROBE_PLUGIN_KEY) || entries.includes(LEGACY_PLUGIN_KEY);
|
|
23295
24189
|
}
|
|
23296
24190
|
function normalizeEnabledPluginConfig(plugins, enabledEntries, disabledEntries) {
|
|
23297
|
-
const nextEnabled =
|
|
24191
|
+
const nextEnabled = uniqueStrings2([
|
|
23298
24192
|
...enabledEntries.filter(
|
|
23299
24193
|
(entry) => entry !== LEGACY_PLUGIN_KEY && entry !== HERMES_USAGE_PROBE_PLUGIN_KEY
|
|
23300
24194
|
),
|
|
23301
24195
|
HERMES_USAGE_PROBE_PLUGIN_KEY
|
|
23302
24196
|
]);
|
|
23303
|
-
const nextDisabled =
|
|
24197
|
+
const nextDisabled = uniqueStrings2(
|
|
23304
24198
|
disabledEntries.filter(
|
|
23305
24199
|
(entry) => entry !== LEGACY_PLUGIN_KEY && entry !== HERMES_USAGE_PROBE_PLUGIN_KEY
|
|
23306
24200
|
)
|
|
@@ -23315,12 +24209,12 @@ function normalizeEnabledPluginConfig(plugins, enabledEntries, disabledEntries)
|
|
|
23315
24209
|
return changed;
|
|
23316
24210
|
}
|
|
23317
24211
|
function normalizeDisabledPluginConfig(plugins, enabledEntries, disabledEntries) {
|
|
23318
|
-
const nextEnabled =
|
|
24212
|
+
const nextEnabled = uniqueStrings2(
|
|
23319
24213
|
enabledEntries.filter(
|
|
23320
24214
|
(entry) => entry !== LEGACY_PLUGIN_KEY && entry !== HERMES_USAGE_PROBE_PLUGIN_KEY
|
|
23321
24215
|
)
|
|
23322
24216
|
);
|
|
23323
|
-
const nextDisabled =
|
|
24217
|
+
const nextDisabled = uniqueStrings2([
|
|
23324
24218
|
...disabledEntries.filter(
|
|
23325
24219
|
(entry) => entry !== LEGACY_PLUGIN_KEY && entry !== HERMES_USAGE_PROBE_PLUGIN_KEY
|
|
23326
24220
|
),
|
|
@@ -23341,11 +24235,11 @@ function ensureApiServerToolsetEnabled(config) {
|
|
|
23341
24235
|
if (apiServerToolsets.length === 0 && !Array.isArray(platformToolsets?.api_server) && !knownApiServerToolsets.includes(HERMESPILOT_LINK_TOOLSET)) {
|
|
23342
24236
|
return false;
|
|
23343
24237
|
}
|
|
23344
|
-
const nextToolsets =
|
|
24238
|
+
const nextToolsets = uniqueStrings2([
|
|
23345
24239
|
...Array.isArray(platformToolsets?.api_server) ? apiServerToolsets : ["hermes-api-server"],
|
|
23346
24240
|
HERMESPILOT_LINK_TOOLSET
|
|
23347
24241
|
]);
|
|
23348
|
-
const nextKnownToolsets =
|
|
24242
|
+
const nextKnownToolsets = uniqueStrings2([
|
|
23349
24243
|
...knownApiServerToolsets,
|
|
23350
24244
|
HERMESPILOT_LINK_TOOLSET
|
|
23351
24245
|
]);
|
|
@@ -23525,19 +24419,19 @@ async function disableUsageProbeAfterActivationFailure(input) {
|
|
|
23525
24419
|
});
|
|
23526
24420
|
}
|
|
23527
24421
|
async function cleanupLegacyUsageProbePlugin(profilePath) {
|
|
23528
|
-
const legacyPath =
|
|
24422
|
+
const legacyPath = path24.join(profilePath, "plugins", LEGACY_PLUGIN_KEY);
|
|
23529
24423
|
const [manifest, init] = await Promise.all([
|
|
23530
|
-
readFile14(
|
|
24424
|
+
readFile14(path24.join(legacyPath, "plugin.yaml"), "utf8").catch(
|
|
23531
24425
|
(error) => {
|
|
23532
|
-
if (
|
|
24426
|
+
if (isNodeError16(error, "ENOENT")) {
|
|
23533
24427
|
return null;
|
|
23534
24428
|
}
|
|
23535
24429
|
throw error;
|
|
23536
24430
|
}
|
|
23537
24431
|
),
|
|
23538
|
-
readFile14(
|
|
24432
|
+
readFile14(path24.join(legacyPath, "__init__.py"), "utf8").catch(
|
|
23539
24433
|
(error) => {
|
|
23540
|
-
if (
|
|
24434
|
+
if (isNodeError16(error, "ENOENT")) {
|
|
23541
24435
|
return null;
|
|
23542
24436
|
}
|
|
23543
24437
|
throw error;
|
|
@@ -23584,12 +24478,12 @@ async function rememberUsageProbeState(paths, profileName, patch) {
|
|
|
23584
24478
|
}));
|
|
23585
24479
|
}
|
|
23586
24480
|
function usageProbeStatePath(paths) {
|
|
23587
|
-
return
|
|
24481
|
+
return path24.join(paths.homeDir, USAGE_PROBE_DIR, USAGE_PROBE_STATE_FILE);
|
|
23588
24482
|
}
|
|
23589
24483
|
async function readHermesConfigDocument2(configPath, language) {
|
|
23590
24484
|
const existingRaw = await readFile14(configPath, "utf8").catch(
|
|
23591
24485
|
(error) => {
|
|
23592
|
-
if (
|
|
24486
|
+
if (isNodeError16(error, "ENOENT")) {
|
|
23593
24487
|
return null;
|
|
23594
24488
|
}
|
|
23595
24489
|
throw error;
|
|
@@ -23620,7 +24514,7 @@ async function writeHermesConfigDocument2(input) {
|
|
|
23620
24514
|
);
|
|
23621
24515
|
}
|
|
23622
24516
|
async function readTail2(filePath, maxBytes) {
|
|
23623
|
-
const info = await
|
|
24517
|
+
const info = await stat16(filePath);
|
|
23624
24518
|
const length = Math.min(info.size, maxBytes);
|
|
23625
24519
|
if (length <= 0) {
|
|
23626
24520
|
return "";
|
|
@@ -23996,7 +24890,7 @@ function withDefaultProfilePlaceholder(profiles) {
|
|
|
23996
24890
|
...profiles
|
|
23997
24891
|
];
|
|
23998
24892
|
}
|
|
23999
|
-
function
|
|
24893
|
+
function normalizeProfileName6(profileName) {
|
|
24000
24894
|
const value = profileName?.trim() || "default";
|
|
24001
24895
|
if (!/^[a-zA-Z0-9._-]{1,64}$/u.test(value)) {
|
|
24002
24896
|
throw new Error("invalid profile name");
|
|
@@ -24004,7 +24898,7 @@ function normalizeProfileName5(profileName) {
|
|
|
24004
24898
|
return value;
|
|
24005
24899
|
}
|
|
24006
24900
|
function safeProfileSegment(profileName) {
|
|
24007
|
-
return encodeURIComponent(
|
|
24901
|
+
return encodeURIComponent(normalizeProfileName6(profileName));
|
|
24008
24902
|
}
|
|
24009
24903
|
function ensureRecord2(target, key) {
|
|
24010
24904
|
const existing = target[key];
|
|
@@ -24021,7 +24915,7 @@ function readStringList5(value) {
|
|
|
24021
24915
|
}
|
|
24022
24916
|
return value.filter((entry) => typeof entry === "string").map((entry) => entry.trim()).filter(Boolean);
|
|
24023
24917
|
}
|
|
24024
|
-
function
|
|
24918
|
+
function uniqueStrings2(entries) {
|
|
24025
24919
|
return [...new Set(entries.map((entry) => entry.trim()).filter(Boolean))];
|
|
24026
24920
|
}
|
|
24027
24921
|
function sameStringList(left, right) {
|
|
@@ -24047,8 +24941,8 @@ function isRecord3(value) {
|
|
|
24047
24941
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
24048
24942
|
}
|
|
24049
24943
|
async function pathIsDirectory(filePath) {
|
|
24050
|
-
return
|
|
24051
|
-
if (
|
|
24944
|
+
return stat16(filePath).then((value) => value.isDirectory()).catch((error) => {
|
|
24945
|
+
if (isNodeError16(error, "ENOENT")) {
|
|
24052
24946
|
return false;
|
|
24053
24947
|
}
|
|
24054
24948
|
throw error;
|
|
@@ -24071,7 +24965,7 @@ function logEnsureResult(logger, source, profiles) {
|
|
|
24071
24965
|
function errorMessage2(error) {
|
|
24072
24966
|
return error instanceof Error ? error.message : String(error);
|
|
24073
24967
|
}
|
|
24074
|
-
function
|
|
24968
|
+
function isNodeError16(error, code) {
|
|
24075
24969
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
24076
24970
|
}
|
|
24077
24971
|
|
|
@@ -24151,8 +25045,8 @@ function toRecord14(value) {
|
|
|
24151
25045
|
}
|
|
24152
25046
|
|
|
24153
25047
|
// src/conversations/run-transcript-enrichment.ts
|
|
24154
|
-
import { readFile as readFile15, stat as
|
|
24155
|
-
import
|
|
25048
|
+
import { readFile as readFile15, stat as stat17 } from "fs/promises";
|
|
25049
|
+
import path25 from "path";
|
|
24156
25050
|
var MESSAGE_COLUMNS2 = [
|
|
24157
25051
|
"id",
|
|
24158
25052
|
"session_id",
|
|
@@ -24228,8 +25122,8 @@ async function readRunFinalAssistantText(input) {
|
|
|
24228
25122
|
}
|
|
24229
25123
|
async function readHermesTranscriptRows(profileName, sessionId) {
|
|
24230
25124
|
const profileDir = resolveHermesProfileDir(profileName);
|
|
24231
|
-
const dbPath =
|
|
24232
|
-
const sessionsDir = await readHermesSessionsDir(profileName).then((value) => value.sessionsDir).catch(() =>
|
|
25125
|
+
const dbPath = path25.join(profileDir, "state.db");
|
|
25126
|
+
const sessionsDir = await readHermesSessionsDir(profileName).then((value) => value.sessionsDir).catch(() => path25.join(profileDir, "sessions"));
|
|
24233
25127
|
const [dbRows, jsonlRows] = await Promise.all([
|
|
24234
25128
|
readStateDbMessages2(dbPath, sessionId),
|
|
24235
25129
|
readJsonlMessages2(sessionsDir, sessionId)
|
|
@@ -24292,9 +25186,9 @@ async function readJsonlMessages2(sessionsDir, sessionId) {
|
|
|
24292
25186
|
if (!/^[A-Za-z0-9._:-]{1,160}$/u.test(sessionId)) {
|
|
24293
25187
|
return [];
|
|
24294
25188
|
}
|
|
24295
|
-
const transcriptPath =
|
|
25189
|
+
const transcriptPath = path25.join(sessionsDir, `${sessionId}.jsonl`);
|
|
24296
25190
|
const raw = await readFile15(transcriptPath, "utf8").catch((error) => {
|
|
24297
|
-
if (
|
|
25191
|
+
if (isNodeError17(error, "ENOENT")) {
|
|
24298
25192
|
return "";
|
|
24299
25193
|
}
|
|
24300
25194
|
throw error;
|
|
@@ -24508,8 +25402,8 @@ function quoteIdentifier2(value) {
|
|
|
24508
25402
|
return `"${value.replaceAll('"', '""')}"`;
|
|
24509
25403
|
}
|
|
24510
25404
|
async function isFile2(filePath) {
|
|
24511
|
-
return
|
|
24512
|
-
if (
|
|
25405
|
+
return stat17(filePath).then((value) => value.isFile()).catch((error) => {
|
|
25406
|
+
if (isNodeError17(error, "ENOENT")) {
|
|
24513
25407
|
return false;
|
|
24514
25408
|
}
|
|
24515
25409
|
throw error;
|
|
@@ -24539,12 +25433,12 @@ function readNumber4(value) {
|
|
|
24539
25433
|
function toRecord15(value) {
|
|
24540
25434
|
return typeof value === "object" && value !== null ? value : {};
|
|
24541
25435
|
}
|
|
24542
|
-
function
|
|
25436
|
+
function isNodeError17(error, code) {
|
|
24543
25437
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
24544
25438
|
}
|
|
24545
25439
|
|
|
24546
25440
|
// src/conversations/run-tool-event-ids.ts
|
|
24547
|
-
import { createHash as
|
|
25441
|
+
import { createHash as createHash6 } from "crypto";
|
|
24548
25442
|
var RunToolEventIdCoalescer = class {
|
|
24549
25443
|
scope;
|
|
24550
25444
|
ordinal = 0;
|
|
@@ -24709,7 +25603,7 @@ function stableStringify2(value) {
|
|
|
24709
25603
|
}
|
|
24710
25604
|
}
|
|
24711
25605
|
function hashStableValue(value) {
|
|
24712
|
-
return
|
|
25606
|
+
return createHash6("sha256").update(value).digest("hex").slice(0, 16);
|
|
24713
25607
|
}
|
|
24714
25608
|
function readString17(payload, key) {
|
|
24715
25609
|
const value = payload[key];
|
|
@@ -24733,7 +25627,7 @@ function normalizeHermesStreamEvent(event) {
|
|
|
24733
25627
|
...event.payload,
|
|
24734
25628
|
type: "run.failed",
|
|
24735
25629
|
error: {
|
|
24736
|
-
message:
|
|
25630
|
+
message: readErrorMessage4(event.payload) ?? readDelta(event.payload) ?? "Hermes run failed"
|
|
24737
25631
|
}
|
|
24738
25632
|
}
|
|
24739
25633
|
};
|
|
@@ -24923,7 +25817,7 @@ function normalizeStreamingTextDelta(currentText, nextChunk) {
|
|
|
24923
25817
|
}
|
|
24924
25818
|
return nextChunk;
|
|
24925
25819
|
}
|
|
24926
|
-
function
|
|
25820
|
+
function readErrorMessage4(payload) {
|
|
24927
25821
|
if (typeof payload.error === "string" && payload.error.trim()) {
|
|
24928
25822
|
return payload.error.trim();
|
|
24929
25823
|
}
|
|
@@ -24966,7 +25860,7 @@ function isTopLevelErrorEvent(event) {
|
|
|
24966
25860
|
if (type.startsWith("tool.")) {
|
|
24967
25861
|
return false;
|
|
24968
25862
|
}
|
|
24969
|
-
return type === "error" || type === "run.error" || event.eventName === "error" || Boolean(
|
|
25863
|
+
return type === "error" || type === "run.error" || event.eventName === "error" || Boolean(readErrorMessage4(event.payload));
|
|
24970
25864
|
}
|
|
24971
25865
|
function readChatCompletionDelta(payload) {
|
|
24972
25866
|
const choice = readFirstChoice(payload);
|
|
@@ -25942,7 +26836,7 @@ var ConversationRunLifecycle = class {
|
|
|
25942
26836
|
await this.failRun(
|
|
25943
26837
|
input.conversationId,
|
|
25944
26838
|
input.runId,
|
|
25945
|
-
|
|
26839
|
+
readErrorMessage4(input.event.payload) ?? "Hermes run failed",
|
|
25946
26840
|
input.event
|
|
25947
26841
|
);
|
|
25948
26842
|
return true;
|
|
@@ -27144,7 +28038,7 @@ ${details.join("\n")}` : localizedEmptyHermesResponseMessage(language);
|
|
|
27144
28038
|
run.status = "failed";
|
|
27145
28039
|
run.completed_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
27146
28040
|
run.error_message = message;
|
|
27147
|
-
run.error_detail = source ?
|
|
28041
|
+
run.error_detail = source ? readErrorMessage4(source.payload) ?? void 0 : void 0;
|
|
27148
28042
|
const language = run.language === "en" ? "en" : "zh-CN";
|
|
27149
28043
|
const visibleMessage = formatFailureMessage(
|
|
27150
28044
|
message,
|
|
@@ -27497,7 +28391,7 @@ ${details.join("\n")}` : localizedEmptyHermesResponseMessage(language);
|
|
|
27497
28391
|
if (raw.length <= 200) {
|
|
27498
28392
|
return raw;
|
|
27499
28393
|
}
|
|
27500
|
-
return `hermespilot:${
|
|
28394
|
+
return `hermespilot:${createHash7("sha256").update(raw).digest("hex")}`;
|
|
27501
28395
|
}
|
|
27502
28396
|
async assistantMessageIdForRun(conversationId, runId) {
|
|
27503
28397
|
const snapshot = await this.deps.readSnapshot(conversationId).catch(() => null);
|
|
@@ -27514,7 +28408,7 @@ ${details.join("\n")}` : localizedEmptyHermesResponseMessage(language);
|
|
|
27514
28408
|
async readHermesCronJobIds(profileName) {
|
|
27515
28409
|
const jobs = await listHermesCronJobs({
|
|
27516
28410
|
logger: this.deps.logger,
|
|
27517
|
-
profileName:
|
|
28411
|
+
profileName: normalizeProfileName4(profileName),
|
|
27518
28412
|
includeDisabled: true
|
|
27519
28413
|
});
|
|
27520
28414
|
return new Set(
|
|
@@ -27522,7 +28416,7 @@ ${details.join("\n")}` : localizedEmptyHermesResponseMessage(language);
|
|
|
27522
28416
|
);
|
|
27523
28417
|
}
|
|
27524
28418
|
async bindNewCronJobsCreatedByRun(input) {
|
|
27525
|
-
const profileName =
|
|
28419
|
+
const profileName = normalizeProfileName4(input.profileName);
|
|
27526
28420
|
const jobs = await listHermesCronJobs({
|
|
27527
28421
|
logger: this.deps.logger,
|
|
27528
28422
|
profileName,
|
|
@@ -27612,7 +28506,7 @@ function formatFilenameList(filenames, language = "zh-CN") {
|
|
|
27612
28506
|
}
|
|
27613
28507
|
async function readdirWithDirs(directory) {
|
|
27614
28508
|
return readdir9(directory, { withFileTypes: true }).catch((error) => {
|
|
27615
|
-
if (
|
|
28509
|
+
if (isNodeError18(error, "ENOENT")) {
|
|
27616
28510
|
return [];
|
|
27617
28511
|
}
|
|
27618
28512
|
throw error;
|
|
@@ -28014,7 +28908,7 @@ function findPreviousHermesResponseId(snapshot, run) {
|
|
|
28014
28908
|
}
|
|
28015
28909
|
function normalizeRunProfileForCompare(profileName) {
|
|
28016
28910
|
try {
|
|
28017
|
-
return
|
|
28911
|
+
return normalizeProfileName4(profileName);
|
|
28018
28912
|
} catch {
|
|
28019
28913
|
return null;
|
|
28020
28914
|
}
|
|
@@ -28114,15 +29008,15 @@ function previewText2(message) {
|
|
|
28114
29008
|
return text ? text.slice(0, 512) : null;
|
|
28115
29009
|
}
|
|
28116
29010
|
function runNotificationSourceEventId(conversationId, runId, eventKind) {
|
|
28117
|
-
const digest =
|
|
29011
|
+
const digest = createHash7("sha256").update(`${conversationId}:${runId}:${eventKind}`).digest("hex").slice(0, 24);
|
|
28118
29012
|
return `${conversationId}:${eventKind}:${digest}`;
|
|
28119
29013
|
}
|
|
28120
29014
|
function approvalNotificationSourceEventId(conversationId, runId, approvalId) {
|
|
28121
|
-
const digest =
|
|
29015
|
+
const digest = createHash7("sha256").update(`${conversationId}:${runId}:${approvalId}:approval_required`).digest("hex").slice(0, 24);
|
|
28122
29016
|
return `${conversationId}:approval_required:${digest}`;
|
|
28123
29017
|
}
|
|
28124
29018
|
function inputRequestNotificationSourceEventId(conversationId, runId, inputRequestId) {
|
|
28125
|
-
const digest =
|
|
29019
|
+
const digest = createHash7("sha256").update(`${conversationId}:${runId}:${inputRequestId}:input_required`).digest("hex").slice(0, 24);
|
|
28126
29020
|
return `${conversationId}:input_required:${digest}`;
|
|
28127
29021
|
}
|
|
28128
29022
|
async function closeSseIterator(iterator) {
|
|
@@ -28147,7 +29041,7 @@ async function sleep(ms, signal) {
|
|
|
28147
29041
|
);
|
|
28148
29042
|
});
|
|
28149
29043
|
}
|
|
28150
|
-
function
|
|
29044
|
+
function isNodeError18(error, code) {
|
|
28151
29045
|
if (typeof error !== "object" || error === null || !("code" in error)) {
|
|
28152
29046
|
return false;
|
|
28153
29047
|
}
|
|
@@ -28381,11 +29275,16 @@ var ConversationService = class {
|
|
|
28381
29275
|
"model_id must be a valid model id"
|
|
28382
29276
|
);
|
|
28383
29277
|
}
|
|
28384
|
-
const configuredModel = normalizedModelId ? await findConfiguredModel(
|
|
28385
|
-
|
|
28386
|
-
|
|
28387
|
-
|
|
28388
|
-
|
|
29278
|
+
const configuredModel = normalizedModelId ? await findConfiguredModel(
|
|
29279
|
+
profile.profileName,
|
|
29280
|
+
normalizedModelId,
|
|
29281
|
+
{
|
|
29282
|
+
provider: input.modelProvider,
|
|
29283
|
+
baseUrl: input.modelBaseUrl,
|
|
29284
|
+
apiMode: input.modelApiMode
|
|
29285
|
+
},
|
|
29286
|
+
this.paths
|
|
29287
|
+
) : null;
|
|
28389
29288
|
if (normalizedModelId && !configuredModel) {
|
|
28390
29289
|
throw new LinkHttpError(
|
|
28391
29290
|
404,
|
|
@@ -28475,10 +29374,10 @@ var ConversationService = class {
|
|
|
28475
29374
|
};
|
|
28476
29375
|
}
|
|
28477
29376
|
async ensureCronInboxConversation(input = {}) {
|
|
28478
|
-
const profileName =
|
|
29377
|
+
const profileName = normalizeProfileName4(input.profileName);
|
|
28479
29378
|
for (const conversationId of await this.store.listConversationIds()) {
|
|
28480
29379
|
const manifest = await this.store.readManifest(conversationId).catch(() => null);
|
|
28481
|
-
if (manifest?.status === "active" && manifest.title === "HermesLink \u5B9A\u65F6\u4EFB\u52A1" &&
|
|
29380
|
+
if (manifest?.status === "active" && manifest.title === "HermesLink \u5B9A\u65F6\u4EFB\u52A1" && normalizeProfileName4(
|
|
28482
29381
|
manifest.profile_name_snapshot ?? manifest.profile
|
|
28483
29382
|
) === profileName) {
|
|
28484
29383
|
return manifest.id;
|
|
@@ -28589,7 +29488,7 @@ var ConversationService = class {
|
|
|
28589
29488
|
}
|
|
28590
29489
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
28591
29490
|
const createdAt = input.runAt ?? now;
|
|
28592
|
-
const profileName =
|
|
29491
|
+
const profileName = normalizeProfileName4(
|
|
28593
29492
|
manifest.profile_name_snapshot ?? manifest.profile ?? input.profileName
|
|
28594
29493
|
);
|
|
28595
29494
|
const ownerAccountId = input.accountId ?? manifest.owner_account_id;
|
|
@@ -28656,7 +29555,7 @@ var ConversationService = class {
|
|
|
28656
29555
|
});
|
|
28657
29556
|
}
|
|
28658
29557
|
async findImportedCronConversation(input) {
|
|
28659
|
-
const profileName =
|
|
29558
|
+
const profileName = normalizeProfileName4(input.profileName);
|
|
28660
29559
|
const outputAt = Date.parse(input.runAt ?? "");
|
|
28661
29560
|
const candidates = [];
|
|
28662
29561
|
for (const conversationId of await this.store.listConversationIds()) {
|
|
@@ -28664,7 +29563,7 @@ var ConversationService = class {
|
|
|
28664
29563
|
if (!manifest || manifest.status !== "active") {
|
|
28665
29564
|
continue;
|
|
28666
29565
|
}
|
|
28667
|
-
if (
|
|
29566
|
+
if (normalizeProfileName4(
|
|
28668
29567
|
manifest.profile_name_snapshot ?? manifest.profile
|
|
28669
29568
|
) !== profileName) {
|
|
28670
29569
|
continue;
|
|
@@ -28912,7 +29811,8 @@ var ConversationService = class {
|
|
|
28912
29811
|
const configuredModel = normalizedModelId ? await findConfiguredModel(
|
|
28913
29812
|
currentRuntime.profile.name,
|
|
28914
29813
|
normalizedModelId,
|
|
28915
|
-
modelSelector
|
|
29814
|
+
modelSelector,
|
|
29815
|
+
this.paths
|
|
28916
29816
|
) : null;
|
|
28917
29817
|
if (normalizedModelId && !configuredModel) {
|
|
28918
29818
|
throw new LinkHttpError(
|
|
@@ -29016,7 +29916,7 @@ var ConversationService = class {
|
|
|
29016
29916
|
this.paths,
|
|
29017
29917
|
profileName
|
|
29018
29918
|
);
|
|
29019
|
-
const currentProfile =
|
|
29919
|
+
const currentProfile = normalizeProfileName4(
|
|
29020
29920
|
manifest.profile_name_snapshot ?? manifest.profile
|
|
29021
29921
|
);
|
|
29022
29922
|
if (currentProfile === profile.profileName && (!manifest.profile_uid || manifest.profile_uid === profile.profileUid)) {
|
|
@@ -29306,7 +30206,7 @@ var ConversationService = class {
|
|
|
29306
30206
|
"This approval request cannot be permanently allowed because Hermes did not return a pattern key."
|
|
29307
30207
|
);
|
|
29308
30208
|
}
|
|
29309
|
-
const profileName =
|
|
30209
|
+
const profileName = normalizeProfileName4(
|
|
29310
30210
|
run?.profile ?? match.message.sender.profile ?? manifest.profile_name_snapshot ?? manifest.profile
|
|
29311
30211
|
);
|
|
29312
30212
|
const result = await addHermesCommandAllowlistEntry(
|
|
@@ -29759,7 +30659,7 @@ var ConversationService = class {
|
|
|
29759
30659
|
}
|
|
29760
30660
|
}
|
|
29761
30661
|
hermesArchiveStateSyncMarkerPath() {
|
|
29762
|
-
return
|
|
30662
|
+
return path26.join(this.paths.indexesDir, "hermes-archive-state-sync.json");
|
|
29763
30663
|
}
|
|
29764
30664
|
prepareClearAllConversationPlan(targetStatus) {
|
|
29765
30665
|
return this.maintenance.prepareClearAllConversationPlan(targetStatus);
|
|
@@ -29789,7 +30689,7 @@ var ConversationService = class {
|
|
|
29789
30689
|
return this.maintenance.deleteConversations(conversationIds);
|
|
29790
30690
|
}
|
|
29791
30691
|
async deleteLocalConversationsForProfile(input) {
|
|
29792
|
-
const profileName =
|
|
30692
|
+
const profileName = normalizeProfileName4(input.profileName);
|
|
29793
30693
|
const profileUid = input.profileUid?.trim() || null;
|
|
29794
30694
|
const deletedConversationIds = [];
|
|
29795
30695
|
for (const conversationId of await this.store.listConversationIds()) {
|
|
@@ -29847,8 +30747,40 @@ var ConversationService = class {
|
|
|
29847
30747
|
const event = await this.store.appendEvent(conversationId, input);
|
|
29848
30748
|
this.emitter.emit(this.liveEventName(conversationId), event);
|
|
29849
30749
|
this.emitter.emit(ALL_CONVERSATION_EVENTS, event);
|
|
30750
|
+
void this.reportLiveActivityEvent(conversationId, event);
|
|
29850
30751
|
return event;
|
|
29851
30752
|
}
|
|
30753
|
+
async reportLiveActivityEvent(conversationId, event) {
|
|
30754
|
+
try {
|
|
30755
|
+
const [manifest, snapshot] = await Promise.all([
|
|
30756
|
+
this.store.readManifest(conversationId),
|
|
30757
|
+
this.store.readSnapshot(conversationId)
|
|
30758
|
+
]);
|
|
30759
|
+
if (manifest.status !== "active" || !manifest.owner_account_id) {
|
|
30760
|
+
return;
|
|
30761
|
+
}
|
|
30762
|
+
const liveActivityEvent = buildLiveActivityEvent({
|
|
30763
|
+
manifest,
|
|
30764
|
+
snapshot,
|
|
30765
|
+
event
|
|
30766
|
+
});
|
|
30767
|
+
if (!liveActivityEvent) {
|
|
30768
|
+
return;
|
|
30769
|
+
}
|
|
30770
|
+
await reportLiveActivityEventToServer({
|
|
30771
|
+
paths: this.paths,
|
|
30772
|
+
logger: this.logger,
|
|
30773
|
+
event: liveActivityEvent
|
|
30774
|
+
});
|
|
30775
|
+
} catch (error) {
|
|
30776
|
+
void this.logger.warn("live_activity_event_report_failed", {
|
|
30777
|
+
conversation_id: conversationId,
|
|
30778
|
+
event_type: event.type,
|
|
30779
|
+
event_seq: event.seq,
|
|
30780
|
+
error: error instanceof Error ? error.message : String(error)
|
|
30781
|
+
});
|
|
30782
|
+
}
|
|
30783
|
+
}
|
|
29852
30784
|
abortActiveRunsForConversation(conversationId) {
|
|
29853
30785
|
for (const [runId, active] of this.activeRunControllers) {
|
|
29854
30786
|
if (active.conversationId !== conversationId) {
|
|
@@ -29874,7 +30806,7 @@ function conversationMatchesProfile(manifest, profileName, profileUid) {
|
|
|
29874
30806
|
if (profileUid && manifest.profile_uid === profileUid) {
|
|
29875
30807
|
return true;
|
|
29876
30808
|
}
|
|
29877
|
-
return
|
|
30809
|
+
return normalizeProfileName4(manifest.profile_name_snapshot ?? manifest.profile) === profileName;
|
|
29878
30810
|
}
|
|
29879
30811
|
function findApproval(snapshot, approvalId) {
|
|
29880
30812
|
for (const message of snapshot.messages) {
|
|
@@ -29886,7 +30818,7 @@ function findApproval(snapshot, approvalId) {
|
|
|
29886
30818
|
return null;
|
|
29887
30819
|
}
|
|
29888
30820
|
function cronNotificationSourceEventId(conversationId, jobId, outputPath, eventKind) {
|
|
29889
|
-
const digest =
|
|
30821
|
+
const digest = createHash8("sha256").update(`${conversationId}:${jobId}:${outputPath}:${eventKind}`).digest("hex").slice(0, 24);
|
|
29890
30822
|
return `${conversationId}:${eventKind}:${digest}`;
|
|
29891
30823
|
}
|
|
29892
30824
|
function conversationHermesSessionIds(manifest) {
|
|
@@ -29952,12 +30884,315 @@ function notificationPreviewText(message) {
|
|
|
29952
30884
|
const text = messageText(message).replace(/<[^>]+>/gu, " ").replace(/\s+/gu, " ").trim();
|
|
29953
30885
|
return text ? text.slice(0, 512) : null;
|
|
29954
30886
|
}
|
|
30887
|
+
function buildLiveActivityEvent(input) {
|
|
30888
|
+
const run = liveActivityRunForEvent(input.event, input.snapshot);
|
|
30889
|
+
const contextOperation = readContextCompressionOperation(input.event.payload);
|
|
30890
|
+
const target = resolveLiveActivityTarget({ event: input.event, run, contextOperation });
|
|
30891
|
+
if (!target) {
|
|
30892
|
+
return null;
|
|
30893
|
+
}
|
|
30894
|
+
const phase = liveActivityPhaseForEvent(input.event, run, contextOperation);
|
|
30895
|
+
if (!phase) {
|
|
30896
|
+
return null;
|
|
30897
|
+
}
|
|
30898
|
+
const language = run?.language === "en" ? "en" : "zh";
|
|
30899
|
+
const text = liveActivityTextForEvent({
|
|
30900
|
+
event: input.event,
|
|
30901
|
+
snapshot: input.snapshot,
|
|
30902
|
+
run,
|
|
30903
|
+
phase,
|
|
30904
|
+
language,
|
|
30905
|
+
contextOperation
|
|
30906
|
+
});
|
|
30907
|
+
return {
|
|
30908
|
+
sourceEventId: `${input.manifest.id}:${input.event.seq}:${input.event.type}`,
|
|
30909
|
+
eventKind: input.event.type.replace(/\./gu, "_"),
|
|
30910
|
+
conversationId: input.manifest.id,
|
|
30911
|
+
conversationTitle: liveActivityConversationTitle(input.manifest, input.event, phase, language),
|
|
30912
|
+
accountId: input.manifest.owner_account_id,
|
|
30913
|
+
appInstanceId: input.manifest.owner_app_instance_id,
|
|
30914
|
+
targetKind: target.kind,
|
|
30915
|
+
targetId: target.id,
|
|
30916
|
+
runId: target.kind === "run" ? target.id : void 0,
|
|
30917
|
+
operationId: target.kind === "context_compression" ? target.id : contextOperation?.operation_id,
|
|
30918
|
+
phase,
|
|
30919
|
+
statusLabel: text.statusLabel,
|
|
30920
|
+
progressText: text.progressText,
|
|
30921
|
+
detailText: text.detailText,
|
|
30922
|
+
requiresUserAction: phase === "needs_input" || phase === "needs_approval",
|
|
30923
|
+
terminal: isLiveActivityTerminalEvent(phase, target.kind),
|
|
30924
|
+
occurredAt: input.event.created_at
|
|
30925
|
+
};
|
|
30926
|
+
}
|
|
30927
|
+
function liveActivityConversationTitle(manifest, event, phase, language) {
|
|
30928
|
+
if (phase === "goal_running" || phase === "goal_paused") {
|
|
30929
|
+
const goal = manifest.command_state?.goal;
|
|
30930
|
+
const rawTitle = goal?.title?.trim() || goal?.content?.trim() || "";
|
|
30931
|
+
if (!rawTitle) {
|
|
30932
|
+
return null;
|
|
30933
|
+
}
|
|
30934
|
+
const title = truncateLiveActivityTitle(rawTitle, 18);
|
|
30935
|
+
return language === "en" ? `Goal: ${title}` : `\u76EE\u6807\uFF1A${title}`;
|
|
30936
|
+
}
|
|
30937
|
+
const eventTitle = liveActivityTitleFromConversationUpdate(event);
|
|
30938
|
+
if (eventTitle !== void 0) {
|
|
30939
|
+
return eventTitle;
|
|
30940
|
+
}
|
|
30941
|
+
return liveActivityTitleFromManifest(manifest);
|
|
30942
|
+
}
|
|
30943
|
+
function liveActivityTitleFromConversationUpdate(event) {
|
|
30944
|
+
if (event.type.toLowerCase() !== "conversation.updated") {
|
|
30945
|
+
return void 0;
|
|
30946
|
+
}
|
|
30947
|
+
const titleSource = readString20(event.payload, "title_source");
|
|
30948
|
+
if (titleSource === "default" || titleSource === "temporary_fallback") {
|
|
30949
|
+
return null;
|
|
30950
|
+
}
|
|
30951
|
+
const title = readString20(event.payload, "title");
|
|
30952
|
+
return title ? truncateLiveActivityTitle(title, 24) : null;
|
|
30953
|
+
}
|
|
30954
|
+
function liveActivityTitleFromManifest(manifest) {
|
|
30955
|
+
if (manifest.title_source === "default" || manifest.title_source === "temporary_fallback") {
|
|
30956
|
+
return null;
|
|
30957
|
+
}
|
|
30958
|
+
if (isDefaultConversationTitle(manifest.title)) {
|
|
30959
|
+
return null;
|
|
30960
|
+
}
|
|
30961
|
+
return truncateLiveActivityTitle(manifest.title, 24);
|
|
30962
|
+
}
|
|
30963
|
+
function truncateLiveActivityTitle(value, maxLength) {
|
|
30964
|
+
const normalized = value.replace(/\s+/gu, " ").trim();
|
|
30965
|
+
if (!normalized) {
|
|
30966
|
+
return null;
|
|
30967
|
+
}
|
|
30968
|
+
const chars = Array.from(normalized);
|
|
30969
|
+
if (chars.length <= maxLength) {
|
|
30970
|
+
return normalized;
|
|
30971
|
+
}
|
|
30972
|
+
return `${chars.slice(0, maxLength).join("")}...`;
|
|
30973
|
+
}
|
|
30974
|
+
function liveActivityRunForEvent(event, snapshot) {
|
|
30975
|
+
if (event.run_id) {
|
|
30976
|
+
return snapshot.runs.find((item) => item.id === event.run_id) ?? null;
|
|
30977
|
+
}
|
|
30978
|
+
if (!isConversationTitleUpdateEvent(event)) {
|
|
30979
|
+
return null;
|
|
30980
|
+
}
|
|
30981
|
+
return latestTrackableLiveActivityRun(snapshot);
|
|
30982
|
+
}
|
|
30983
|
+
function latestTrackableLiveActivityRun(snapshot) {
|
|
30984
|
+
const candidates = snapshot.runs.filter((run) => run.status === "running" || run.status === "queued").sort((left, right) => Date.parse(right.started_at) - Date.parse(left.started_at));
|
|
30985
|
+
return candidates[0] ?? null;
|
|
30986
|
+
}
|
|
30987
|
+
function isConversationTitleUpdateEvent(event) {
|
|
30988
|
+
if (event.type.toLowerCase() !== "conversation.updated") {
|
|
30989
|
+
return false;
|
|
30990
|
+
}
|
|
30991
|
+
return Boolean(readString20(event.payload, "title")?.trim());
|
|
30992
|
+
}
|
|
30993
|
+
function resolveLiveActivityTarget(input) {
|
|
30994
|
+
if (input.run?.kind === "compression") {
|
|
30995
|
+
const operationId = input.contextOperation?.operation_id ?? input.run.context_compression?.operation_id;
|
|
30996
|
+
return operationId ? { kind: "context_compression", id: operationId } : null;
|
|
30997
|
+
}
|
|
30998
|
+
if (input.run?.id) {
|
|
30999
|
+
return { kind: "run", id: input.run.id };
|
|
31000
|
+
}
|
|
31001
|
+
if (input.contextOperation?.operation_id) {
|
|
31002
|
+
return { kind: "context_compression", id: input.contextOperation.operation_id };
|
|
31003
|
+
}
|
|
31004
|
+
return null;
|
|
31005
|
+
}
|
|
31006
|
+
function liveActivityPhaseForEvent(event, run, contextOperation) {
|
|
31007
|
+
const type = event.type.toLowerCase();
|
|
31008
|
+
if (type === "run.started" || type === "run.queued") {
|
|
31009
|
+
return run?.mode === "goal" ? "goal_running" : "accepted";
|
|
31010
|
+
}
|
|
31011
|
+
if (type === "conversation.goal.updated") {
|
|
31012
|
+
const goal = readRecord(event.payload).goal;
|
|
31013
|
+
const status = readString20(goal, "status");
|
|
31014
|
+
return status === "paused" ? "goal_paused" : "goal_running";
|
|
31015
|
+
}
|
|
31016
|
+
if (isConversationTitleUpdateEvent(event)) {
|
|
31017
|
+
return run?.mode === "goal" ? "goal_running" : "running";
|
|
31018
|
+
}
|
|
31019
|
+
if (type === "tool.started") {
|
|
31020
|
+
return "using_tool";
|
|
31021
|
+
}
|
|
31022
|
+
if (type === "tool.completed") {
|
|
31023
|
+
return "tool_completed";
|
|
31024
|
+
}
|
|
31025
|
+
if (type === "input_request.created") {
|
|
31026
|
+
return "needs_input";
|
|
31027
|
+
}
|
|
31028
|
+
if (type === "approval.requested") {
|
|
31029
|
+
return "needs_approval";
|
|
31030
|
+
}
|
|
31031
|
+
if (type === "reasoning.available") {
|
|
31032
|
+
return run?.mode === "goal" ? "goal_running" : "running";
|
|
31033
|
+
}
|
|
31034
|
+
if (type === "context_compression.started") {
|
|
31035
|
+
return "context_compressing";
|
|
31036
|
+
}
|
|
31037
|
+
if (type === "context_compression.completed") {
|
|
31038
|
+
return "context_compressed";
|
|
31039
|
+
}
|
|
31040
|
+
if (type === "context_compression.failed") {
|
|
31041
|
+
return contextOperation?.status === "timed_out" ? "context_compression_timed_out" : "context_compression_failed";
|
|
31042
|
+
}
|
|
31043
|
+
if (type === "context_compression.timed_out") {
|
|
31044
|
+
return "context_compression_timed_out";
|
|
31045
|
+
}
|
|
31046
|
+
if (type === "run.completed") {
|
|
31047
|
+
return "completed";
|
|
31048
|
+
}
|
|
31049
|
+
if (type === "run.failed") {
|
|
31050
|
+
return "failed";
|
|
31051
|
+
}
|
|
31052
|
+
if (type === "run.cancelled" || type === "run.canceled") {
|
|
31053
|
+
return "cancelled";
|
|
31054
|
+
}
|
|
31055
|
+
return null;
|
|
31056
|
+
}
|
|
31057
|
+
function liveActivityTextForEvent(input) {
|
|
31058
|
+
const toolName = readToolName2(input.event.payload);
|
|
31059
|
+
const assistantText = input.event.message_id ? input.snapshot.messages.find((message) => message.id === input.event.message_id) : null;
|
|
31060
|
+
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
|
+
if (input.language === "en") {
|
|
31062
|
+
return liveActivityTextEn(input.phase, toolName, preview, input.contextOperation);
|
|
31063
|
+
}
|
|
31064
|
+
return liveActivityTextZh(input.phase, toolName, preview, input.contextOperation);
|
|
31065
|
+
}
|
|
31066
|
+
function liveActivityTextZh(phase, toolName, preview, contextOperation) {
|
|
31067
|
+
switch (phase) {
|
|
31068
|
+
case "using_tool":
|
|
31069
|
+
return { statusLabel: "\u8C03\u7528\u5DE5\u5177", progressText: toolName ? `\u6B63\u5728\u4F7F\u7528 ${toolName}` : "Hermes \u6B63\u5728\u8C03\u7528\u5DE5\u5177", detailText: preview ?? void 0 };
|
|
31070
|
+
case "tool_completed":
|
|
31071
|
+
return { statusLabel: "\u5DE5\u5177\u5B8C\u6210", progressText: toolName ? `${toolName} \u5DF2\u5B8C\u6210` : "\u5DE5\u5177\u8C03\u7528\u5DF2\u5B8C\u6210", detailText: preview ?? void 0 };
|
|
31072
|
+
case "needs_input":
|
|
31073
|
+
return { statusLabel: "\u9700\u56DE\u590D", progressText: "Hermes \u9700\u8981\u4F60\u8865\u5145\u4FE1\u606F", detailText: "\u70B9\u51FB\u56DE\u5230 App \u7EE7\u7EED\u3002" };
|
|
31074
|
+
case "needs_approval":
|
|
31075
|
+
return { statusLabel: "\u9700\u6388\u6743", progressText: "Hermes \u8BF7\u6C42\u6267\u884C\u6743\u9650", detailText: "\u70B9\u51FB\u56DE\u5230 App \u67E5\u770B\u3002" };
|
|
31076
|
+
case "goal_running":
|
|
31077
|
+
return { statusLabel: "\u76EE\u6807\u4E2D", progressText: "Hermes \u6B63\u5728\u63A8\u8FDB\u76EE\u6807", detailText: preview ?? "\u6B63\u5728\u6267\u884C\u4E0B\u4E00\u6B65\u3002" };
|
|
31078
|
+
case "goal_paused":
|
|
31079
|
+
return { statusLabel: "\u5F85\u6307\u5F15", progressText: "\u76EE\u6807\u4EFB\u52A1\u7B49\u5F85\u4F60\u7684\u6307\u5F15", detailText: "\u70B9\u51FB\u56DE\u5230 App \u7EE7\u7EED\u3002" };
|
|
31080
|
+
case "context_compressing":
|
|
31081
|
+
return {
|
|
31082
|
+
statusLabel: contextOperation?.source === "auto" ? "\u6574\u7406\u4E2D" : "\u538B\u7F29\u4E2D",
|
|
31083
|
+
progressText: contextOperation?.source === "auto" ? "Hermes \u6B63\u5728\u81EA\u52A8\u6574\u7406\u4E0A\u4E0B\u6587" : "Hermes \u6B63\u5728\u6574\u7406\u4E0A\u4E0B\u6587",
|
|
31084
|
+
detailText: contextOperation?.source === "auto" ? "\u6211\u5148\u6574\u7406\u4E0A\u4E0B\u6587\uFF0C\u518D\u7EE7\u7EED\u6267\u884C\u3002" : "\u6211\u6B63\u5728\u538B\u7F29\u5F53\u524D\u4F1A\u8BDD\u4E0A\u4E0B\u6587\u3002"
|
|
31085
|
+
};
|
|
31086
|
+
case "context_compressed":
|
|
31087
|
+
return { statusLabel: "\u5DF2\u6574\u7406", progressText: "\u4E0A\u4E0B\u6587\u5DF2\u6574\u7406", detailText: "\u53EF\u4EE5\u7EE7\u7EED\u5F53\u524D\u4EFB\u52A1\u3002" };
|
|
31088
|
+
case "context_compression_failed":
|
|
31089
|
+
return { statusLabel: "\u538B\u7F29\u5931\u8D25", progressText: "\u4E0A\u4E0B\u6587\u538B\u7F29\u5931\u8D25", detailText: "\u70B9\u51FB\u56DE\u5230 App \u67E5\u770B\u8BE6\u60C5\u3002" };
|
|
31090
|
+
case "context_compression_timed_out":
|
|
31091
|
+
return { statusLabel: "\u538B\u7F29\u8D85\u65F6", progressText: "\u6B63\u5728\u6062\u590D Hermes", detailText: "\u6267\u884C\u540E\u7AEF\u6B63\u5728\u6062\u590D\u3002" };
|
|
31092
|
+
case "completed":
|
|
31093
|
+
return { statusLabel: "\u5B8C\u6210", progressText: "\u4EFB\u52A1\u5DF2\u5B8C\u6210", detailText: preview ?? "\u70B9\u51FB\u67E5\u770B\u7ED3\u679C\u3002" };
|
|
31094
|
+
case "failed":
|
|
31095
|
+
return { statusLabel: "\u5931\u8D25", progressText: "\u4EFB\u52A1\u8FD0\u884C\u5931\u8D25", detailText: "\u70B9\u51FB\u67E5\u770B\u5931\u8D25\u8BE6\u60C5\u3002" };
|
|
31096
|
+
case "cancelled":
|
|
31097
|
+
return { statusLabel: "\u5DF2\u53D6\u6D88", progressText: "\u4EFB\u52A1\u5DF2\u53D6\u6D88", detailText: "\u70B9\u51FB\u56DE\u5230 App \u67E5\u770B\u3002" };
|
|
31098
|
+
default:
|
|
31099
|
+
return { statusLabel: "\u6267\u884C\u4E2D", progressText: "\u6B63\u5728\u5904\u7406\u4F60\u7684\u8BF7\u6C42", detailText: preview ?? void 0 };
|
|
31100
|
+
}
|
|
31101
|
+
}
|
|
31102
|
+
function liveActivityTextEn(phase, toolName, preview, contextOperation) {
|
|
31103
|
+
switch (phase) {
|
|
31104
|
+
case "using_tool":
|
|
31105
|
+
return { statusLabel: "Tool", progressText: toolName ? `Using ${toolName}` : "Hermes is using a tool", detailText: preview ?? void 0 };
|
|
31106
|
+
case "tool_completed":
|
|
31107
|
+
return { statusLabel: "Tool done", progressText: toolName ? `${toolName} completed` : "Tool completed", detailText: preview ?? void 0 };
|
|
31108
|
+
case "needs_input":
|
|
31109
|
+
return { statusLabel: "Reply", progressText: "Hermes needs your response", detailText: "Open the app to continue." };
|
|
31110
|
+
case "needs_approval":
|
|
31111
|
+
return { statusLabel: "Approve", progressText: "Hermes needs approval", detailText: "Open the app to review it." };
|
|
31112
|
+
case "goal_running":
|
|
31113
|
+
return { statusLabel: "Goal", progressText: "Hermes is working on the goal", detailText: preview ?? "Working through the next step." };
|
|
31114
|
+
case "goal_paused":
|
|
31115
|
+
return { statusLabel: "Guide", progressText: "Goal needs guidance", detailText: "Open the app to continue." };
|
|
31116
|
+
case "context_compressing":
|
|
31117
|
+
return {
|
|
31118
|
+
statusLabel: contextOperation?.source === "auto" ? "Tidying" : "Compact",
|
|
31119
|
+
progressText: contextOperation?.source === "auto" ? "Hermes is reorganizing context" : "Hermes is compacting context",
|
|
31120
|
+
detailText: contextOperation?.source === "auto" ? "Tidying context before continuing." : "Compacting this conversation context."
|
|
31121
|
+
};
|
|
31122
|
+
case "context_compressed":
|
|
31123
|
+
return { statusLabel: "Ready", progressText: "Context compacted", detailText: "You can continue this task." };
|
|
31124
|
+
case "context_compression_failed":
|
|
31125
|
+
return { statusLabel: "Failed", progressText: "Context compression failed", detailText: "Open the app for details." };
|
|
31126
|
+
case "context_compression_timed_out":
|
|
31127
|
+
return { statusLabel: "Timeout", progressText: "Restoring Hermes", detailText: "The execution backend is recovering." };
|
|
31128
|
+
case "completed":
|
|
31129
|
+
return { statusLabel: "Done", progressText: "Task completed", detailText: preview ?? "Open the app to view the result." };
|
|
31130
|
+
case "failed":
|
|
31131
|
+
return { statusLabel: "Failed", progressText: "Task failed", detailText: "Open the app for details." };
|
|
31132
|
+
case "cancelled":
|
|
31133
|
+
return { statusLabel: "Canceled", progressText: "Task canceled", detailText: "Open the app for details." };
|
|
31134
|
+
default:
|
|
31135
|
+
return { statusLabel: "Running", progressText: "Working on your request", detailText: preview ?? void 0 };
|
|
31136
|
+
}
|
|
31137
|
+
}
|
|
31138
|
+
function liveActivityReasoningPreview(payload) {
|
|
31139
|
+
const text = readString20(payload, "text")?.replace(/<[^>]+>/gu, " ").replace(/\s+/gu, " ").trim();
|
|
31140
|
+
return liveActivityCleanAssistantPreview(text);
|
|
31141
|
+
}
|
|
31142
|
+
function liveActivityCleanAssistantPreview(text) {
|
|
31143
|
+
const normalized = text?.replace(/\s+/gu, " ").trim();
|
|
31144
|
+
if (!normalized || normalized.length < 4) {
|
|
31145
|
+
return null;
|
|
31146
|
+
}
|
|
31147
|
+
if (looksUnsafeForLockScreen(normalized)) {
|
|
31148
|
+
return null;
|
|
31149
|
+
}
|
|
31150
|
+
return normalized.length > 90 ? `${normalized.slice(0, 90)}...` : normalized;
|
|
31151
|
+
}
|
|
31152
|
+
function looksUnsafeForLockScreen(text) {
|
|
31153
|
+
return /(?:^|\s)(?:https?:\/\/|\/[A-Za-z0-9_.-]+\/|[A-Za-z]:\\|[A-Z_]{8,}=)/u.test(text) || /(?:```|<\?xml|BEGIN [A-Z ]+KEY|token|api[_ -]?key|password|secret)/iu.test(text);
|
|
31154
|
+
}
|
|
31155
|
+
function isLiveActivityTerminalEvent(phase, targetKind) {
|
|
31156
|
+
if (phase === "completed" || phase === "failed" || phase === "cancelled") {
|
|
31157
|
+
return true;
|
|
31158
|
+
}
|
|
31159
|
+
return targetKind === "context_compression" && (phase === "context_compressed" || phase === "context_compression_failed" || phase === "context_compression_timed_out");
|
|
31160
|
+
}
|
|
31161
|
+
function readContextCompressionOperation(payload) {
|
|
31162
|
+
const operation = readRecord(payload).operation;
|
|
31163
|
+
if (!operation || typeof operation !== "object") {
|
|
31164
|
+
return null;
|
|
31165
|
+
}
|
|
31166
|
+
const operationId = readString20(operation, "operation_id");
|
|
31167
|
+
if (!operationId) {
|
|
31168
|
+
return null;
|
|
31169
|
+
}
|
|
31170
|
+
const record = operation;
|
|
31171
|
+
return {
|
|
31172
|
+
operation_id: operationId,
|
|
31173
|
+
generation: typeof record.generation === "number" ? record.generation : 0,
|
|
31174
|
+
status: readString20(record, "status") === "completed" ? "completed" : readString20(record, "status") === "failed" ? "failed" : readString20(record, "status") === "timed_out" ? "timed_out" : readString20(record, "status") === "cancelled" ? "cancelled" : "compressing",
|
|
31175
|
+
started_at: readString20(record, "started_at") ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
31176
|
+
source: readString20(record, "source") === "manual" ? "manual" : "auto"
|
|
31177
|
+
};
|
|
31178
|
+
}
|
|
31179
|
+
function readToolName2(payload) {
|
|
31180
|
+
const record = readRecord(payload);
|
|
31181
|
+
return readString20(record, "tool_name") ?? readString20(record, "tool") ?? readString20(record, "name") ?? readString20(readRecord(record.tool), "name");
|
|
31182
|
+
}
|
|
31183
|
+
function readRecord(value) {
|
|
31184
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
|
|
31185
|
+
}
|
|
31186
|
+
function readString20(value, key) {
|
|
31187
|
+
const raw = readRecord(value)[key];
|
|
31188
|
+
return typeof raw === "string" && raw.trim() ? raw.trim() : null;
|
|
31189
|
+
}
|
|
29955
31190
|
function approvalRestartText(language, zh, en) {
|
|
29956
31191
|
return language === "en" ? en : zh;
|
|
29957
31192
|
}
|
|
29958
31193
|
|
|
29959
31194
|
// src/security/devices.ts
|
|
29960
|
-
import { randomBytes as randomBytes3, randomUUID as randomUUID13, timingSafeEqual, createHash as
|
|
31195
|
+
import { randomBytes as randomBytes3, randomUUID as randomUUID13, timingSafeEqual, createHash as createHash9 } from "crypto";
|
|
29961
31196
|
var ACCESS_TOKEN_TTL_MS = 15 * 60 * 1e3;
|
|
29962
31197
|
var REFRESH_TOKEN_TTL_MS = 90 * 24 * 60 * 60 * 1e3;
|
|
29963
31198
|
var DEVICE_SEEN_WRITE_INTERVAL_MS = 60 * 60 * 1e3;
|
|
@@ -30253,7 +31488,7 @@ function randomToken(prefix) {
|
|
|
30253
31488
|
return `${prefix}${randomBytes3(24).toString("base64url")}`;
|
|
30254
31489
|
}
|
|
30255
31490
|
function sha256(value) {
|
|
30256
|
-
return
|
|
31491
|
+
return createHash9("sha256").update(value).digest("hex");
|
|
30257
31492
|
}
|
|
30258
31493
|
function safeEqual(left, right) {
|
|
30259
31494
|
const leftBytes = Buffer.from(left);
|
|
@@ -30460,7 +31695,7 @@ async function readRawBody(request, maxBytes) {
|
|
|
30460
31695
|
}
|
|
30461
31696
|
return Buffer.concat(chunks);
|
|
30462
31697
|
}
|
|
30463
|
-
function
|
|
31698
|
+
function readString21(body, key) {
|
|
30464
31699
|
const value = body[key];
|
|
30465
31700
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
30466
31701
|
}
|
|
@@ -30495,7 +31730,7 @@ function readSupportedLanguage(value) {
|
|
|
30495
31730
|
return null;
|
|
30496
31731
|
}
|
|
30497
31732
|
function readOptionalProfileName(body) {
|
|
30498
|
-
return
|
|
31733
|
+
return readString21(body, "profile") ?? readString21(body, "profile_name") ?? readString21(body, "profileName") ?? void 0;
|
|
30499
31734
|
}
|
|
30500
31735
|
function readStringArray(body, ...keys) {
|
|
30501
31736
|
for (const key of keys) {
|
|
@@ -30915,7 +32150,7 @@ function registerConversationRoutes(router, options) {
|
|
|
30915
32150
|
const language = readPreferredLanguage(ctx);
|
|
30916
32151
|
const body = await readJsonBody(ctx.req);
|
|
30917
32152
|
ctx.status = 201;
|
|
30918
|
-
const rawReasoningEffort =
|
|
32153
|
+
const rawReasoningEffort = readString21(body, "reasoning_effort") ?? readString21(body, "reasoningEffort") ?? readString21(body, "default_reasoning_effort") ?? readString21(body, "defaultReasoningEffort");
|
|
30919
32154
|
const reasoningEffort = normalizeReasoningEffort(rawReasoningEffort);
|
|
30920
32155
|
if (rawReasoningEffort && !reasoningEffort) {
|
|
30921
32156
|
throw new LinkHttpError(
|
|
@@ -30928,15 +32163,15 @@ function registerConversationRoutes(router, options) {
|
|
|
30928
32163
|
ok: true,
|
|
30929
32164
|
conversation: localizeConversationSummary(
|
|
30930
32165
|
await conversations.createConversation({
|
|
30931
|
-
title:
|
|
32166
|
+
title: readString21(body, "title") ?? void 0,
|
|
30932
32167
|
profileName: readOptionalProfileName(body),
|
|
30933
32168
|
accountId: auth.accountId,
|
|
30934
32169
|
appInstanceId: auth.appInstanceId,
|
|
30935
32170
|
workspaceId: readConversationWorkspaceId(body),
|
|
30936
|
-
modelId:
|
|
30937
|
-
modelProvider:
|
|
30938
|
-
modelBaseUrl:
|
|
30939
|
-
modelApiMode:
|
|
32171
|
+
modelId: readString21(body, "model_id") ?? readString21(body, "modelId") ?? readString21(body, "model") ?? void 0,
|
|
32172
|
+
modelProvider: readString21(body, "model_provider") ?? readString21(body, "modelProvider") ?? readString21(body, "provider") ?? void 0,
|
|
32173
|
+
modelBaseUrl: readString21(body, "model_base_url") ?? readString21(body, "modelBaseUrl") ?? readString21(body, "base_url") ?? readString21(body, "baseUrl") ?? void 0,
|
|
32174
|
+
modelApiMode: readString21(body, "model_api_mode") ?? readString21(body, "modelApiMode") ?? readString21(body, "api_mode") ?? readString21(body, "apiMode") ?? void 0,
|
|
30940
32175
|
reasoningEffort
|
|
30941
32176
|
}),
|
|
30942
32177
|
language
|
|
@@ -31013,9 +32248,9 @@ function registerConversationRoutes(router, options) {
|
|
|
31013
32248
|
const auth = await authenticateRequest(ctx, paths);
|
|
31014
32249
|
const language = readPreferredLanguage(ctx);
|
|
31015
32250
|
const body = await readJsonBody(ctx.req);
|
|
31016
|
-
const content =
|
|
32251
|
+
const content = readString21(body, "content") ?? readString21(body, "text") ?? readString21(body, "input") ?? "";
|
|
31017
32252
|
const attachments = readMessageAttachments(body.attachments ?? body.blobs);
|
|
31018
|
-
const mode =
|
|
32253
|
+
const mode = readString21(body, "mode") ?? readString21(body, "send_mode");
|
|
31019
32254
|
if (mode && mode !== "message" && mode !== "goal") {
|
|
31020
32255
|
throw new LinkHttpError(
|
|
31021
32256
|
400,
|
|
@@ -31038,7 +32273,7 @@ function registerConversationRoutes(router, options) {
|
|
|
31038
32273
|
conversationId: ctx.params.conversationId,
|
|
31039
32274
|
content,
|
|
31040
32275
|
attachments,
|
|
31041
|
-
clientMessageId:
|
|
32276
|
+
clientMessageId: readString21(body, "client_message_id") ?? readString21(body, "clientMessageId") ?? void 0,
|
|
31042
32277
|
idempotencyKey: readHeader(ctx, "idempotency-key") ?? void 0,
|
|
31043
32278
|
profileName: readOptionalProfileName(body),
|
|
31044
32279
|
mode: mode === "goal" ? "goal" : "message",
|
|
@@ -31105,8 +32340,8 @@ function registerConversationRoutes(router, options) {
|
|
|
31105
32340
|
...localizeConversationResult(
|
|
31106
32341
|
await conversations.startContextCompression({
|
|
31107
32342
|
conversationId: ctx.params.conversationId,
|
|
31108
|
-
focus:
|
|
31109
|
-
clientOperationId:
|
|
32343
|
+
focus: readString21(body, "focus") ?? readString21(body, "focus_topic") ?? readString21(body, "focusTopic") ?? void 0,
|
|
32344
|
+
clientOperationId: readString21(body, "client_operation_id") ?? readString21(body, "clientOperationId") ?? readHeader(ctx, "idempotency-key") ?? void 0,
|
|
31110
32345
|
createUserMessage: false,
|
|
31111
32346
|
accountId: auth.accountId,
|
|
31112
32347
|
appInstanceId: auth.appInstanceId,
|
|
@@ -31120,11 +32355,11 @@ function registerConversationRoutes(router, options) {
|
|
|
31120
32355
|
router.patch("/api/v1/conversations/:conversationId/model", async (ctx) => {
|
|
31121
32356
|
await authenticateRequest(ctx, paths);
|
|
31122
32357
|
const body = await readJsonBody(ctx.req);
|
|
31123
|
-
const modelId =
|
|
31124
|
-
const modelProvider =
|
|
31125
|
-
const modelBaseUrl =
|
|
31126
|
-
const modelApiMode =
|
|
31127
|
-
const rawReasoningEffort =
|
|
32358
|
+
const modelId = readString21(body, "model_id") ?? readString21(body, "modelId") ?? readString21(body, "model");
|
|
32359
|
+
const modelProvider = readString21(body, "model_provider") ?? readString21(body, "modelProvider") ?? readString21(body, "provider") ?? void 0;
|
|
32360
|
+
const modelBaseUrl = readString21(body, "model_base_url") ?? readString21(body, "modelBaseUrl") ?? readString21(body, "base_url") ?? readString21(body, "baseUrl") ?? void 0;
|
|
32361
|
+
const modelApiMode = readString21(body, "model_api_mode") ?? readString21(body, "modelApiMode") ?? readString21(body, "api_mode") ?? readString21(body, "apiMode") ?? void 0;
|
|
32362
|
+
const rawReasoningEffort = readString21(body, "reasoning_effort") ?? readString21(body, "reasoningEffort") ?? readString21(body, "default_reasoning_effort") ?? readString21(body, "defaultReasoningEffort");
|
|
31128
32363
|
const reasoningEffort = normalizeReasoningEffort(rawReasoningEffort);
|
|
31129
32364
|
if (rawReasoningEffort && !reasoningEffort) {
|
|
31130
32365
|
throw new LinkHttpError(
|
|
@@ -31174,7 +32409,7 @@ function registerConversationRoutes(router, options) {
|
|
|
31174
32409
|
await authenticateRequest(ctx, paths);
|
|
31175
32410
|
const language = readPreferredLanguage(ctx);
|
|
31176
32411
|
const body = await readJsonBody(ctx.req);
|
|
31177
|
-
const title =
|
|
32412
|
+
const title = readString21(body, "title") ?? readString21(body, "name") ?? readString21(body, "display_name");
|
|
31178
32413
|
if (!title) {
|
|
31179
32414
|
throw new LinkHttpError(400, "title_required", "title is required");
|
|
31180
32415
|
}
|
|
@@ -31366,7 +32601,7 @@ function registerConversationRoutes(router, options) {
|
|
|
31366
32601
|
async (ctx) => {
|
|
31367
32602
|
await authenticateRequest(ctx, paths);
|
|
31368
32603
|
const body = await readJsonBody(ctx.req);
|
|
31369
|
-
const scope =
|
|
32604
|
+
const scope = readString21(body, "scope") ?? "always";
|
|
31370
32605
|
ctx.body = {
|
|
31371
32606
|
ok: true,
|
|
31372
32607
|
...await conversations.resolveApproval({
|
|
@@ -31416,7 +32651,7 @@ function registerConversationRoutes(router, options) {
|
|
|
31416
32651
|
conversationId: ctx.params.conversationId,
|
|
31417
32652
|
requestId: ctx.params.requestId,
|
|
31418
32653
|
kind,
|
|
31419
|
-
answer:
|
|
32654
|
+
answer: readString21(body, "answer") ?? void 0,
|
|
31420
32655
|
password: readRawString(body, "password"),
|
|
31421
32656
|
value: readRawString(body, "value")
|
|
31422
32657
|
})
|
|
@@ -31578,10 +32813,10 @@ function readConversationWorkspaceFilter(query) {
|
|
|
31578
32813
|
}
|
|
31579
32814
|
function readConversationWorkspaceId(body) {
|
|
31580
32815
|
if (Object.prototype.hasOwnProperty.call(body, "workspace_id") || Object.prototype.hasOwnProperty.call(body, "workspaceId")) {
|
|
31581
|
-
return
|
|
32816
|
+
return readString21(body, "workspace_id") ?? readString21(body, "workspaceId");
|
|
31582
32817
|
}
|
|
31583
32818
|
if (Object.prototype.hasOwnProperty.call(body, "workspace")) {
|
|
31584
|
-
return
|
|
32819
|
+
return readString21(body, "workspace");
|
|
31585
32820
|
}
|
|
31586
32821
|
return void 0;
|
|
31587
32822
|
}
|
|
@@ -31675,7 +32910,7 @@ function resolveConversationEventCursor(input) {
|
|
|
31675
32910
|
return Math.max(queryAfter, headerAfter);
|
|
31676
32911
|
}
|
|
31677
32912
|
function readConversationClearPlanTargetStatus(body) {
|
|
31678
|
-
const raw =
|
|
32913
|
+
const raw = readString21(body, "target_status") ?? readString21(body, "targetStatus") ?? "active";
|
|
31679
32914
|
if (raw === "active" || raw === "archived") {
|
|
31680
32915
|
return raw;
|
|
31681
32916
|
}
|
|
@@ -31686,7 +32921,7 @@ function readConversationClearPlanTargetStatus(body) {
|
|
|
31686
32921
|
);
|
|
31687
32922
|
}
|
|
31688
32923
|
function readInputRequestKind2(body) {
|
|
31689
|
-
const kind =
|
|
32924
|
+
const kind = readString21(body, "kind") ?? readString21(body, "type");
|
|
31690
32925
|
if (kind === "clarify" || kind === "sudo" || kind === "secret") {
|
|
31691
32926
|
return kind;
|
|
31692
32927
|
}
|
|
@@ -32084,7 +33319,7 @@ function toHermesCronJobInput(input) {
|
|
|
32084
33319
|
};
|
|
32085
33320
|
}
|
|
32086
33321
|
async function bindAndDecorateCronJobForHermesLink(input) {
|
|
32087
|
-
const jobId =
|
|
33322
|
+
const jobId = readString21(input.job, "id") ?? readString21(input.job, "job_id");
|
|
32088
33323
|
if (!jobId) {
|
|
32089
33324
|
return input.job;
|
|
32090
33325
|
}
|
|
@@ -32103,9 +33338,9 @@ async function bindAndDecorateCronJobForHermesLink(input) {
|
|
|
32103
33338
|
}
|
|
32104
33339
|
function readCronJobCreateInput(body) {
|
|
32105
33340
|
const input = {};
|
|
32106
|
-
const name =
|
|
32107
|
-
const prompt =
|
|
32108
|
-
const schedule =
|
|
33341
|
+
const name = readString21(body, "name") ?? readString21(body, "title");
|
|
33342
|
+
const prompt = readString21(body, "prompt") ?? readString21(body, "description") ?? readString21(body, "task");
|
|
33343
|
+
const schedule = readString21(body, "schedule");
|
|
32109
33344
|
if (!name) {
|
|
32110
33345
|
throw new LinkHttpError(400, "cron_job_name_required", "name is required");
|
|
32111
33346
|
}
|
|
@@ -32126,7 +33361,7 @@ function readCronJobCreateInput(body) {
|
|
|
32126
33361
|
input.name = name;
|
|
32127
33362
|
input.prompt = prompt;
|
|
32128
33363
|
input.schedule = schedule;
|
|
32129
|
-
input.deliver =
|
|
33364
|
+
input.deliver = readString21(body, "deliver") ?? HERMES_LINK_CRON_DELIVER;
|
|
32130
33365
|
const skills = readOptionalCronSkills(body);
|
|
32131
33366
|
if (skills) {
|
|
32132
33367
|
input.skills = skills;
|
|
@@ -32254,111 +33489,7 @@ function assertCronJobId(jobId) {
|
|
|
32254
33489
|
}
|
|
32255
33490
|
|
|
32256
33491
|
// src/http/routes/model-configs.ts
|
|
32257
|
-
import { createHash as
|
|
32258
|
-
|
|
32259
|
-
// src/hermes/model-provider-disconnects.ts
|
|
32260
|
-
import { stat as stat17 } from "fs/promises";
|
|
32261
|
-
import os6 from "os";
|
|
32262
|
-
import path26 from "path";
|
|
32263
|
-
async function markAuthBackedModelProviderDisconnected(paths, profileName, providerKey) {
|
|
32264
|
-
const profileKey = normalizeProfileName6(profileName);
|
|
32265
|
-
const provider = providerKey.trim();
|
|
32266
|
-
if (!provider) {
|
|
32267
|
-
return;
|
|
32268
|
-
}
|
|
32269
|
-
const credentialMtimeMs = await readProviderCredentialMtimeMs(
|
|
32270
|
-
profileKey,
|
|
32271
|
-
provider
|
|
32272
|
-
);
|
|
32273
|
-
await updateJsonFile(
|
|
32274
|
-
disconnectedProvidersPath(paths),
|
|
32275
|
-
(current) => {
|
|
32276
|
-
const store = normalizeStore(current);
|
|
32277
|
-
const profiles = { ...store.profiles ?? {} };
|
|
32278
|
-
const profile = profiles[profileKey] ?? {};
|
|
32279
|
-
profiles[profileKey] = {
|
|
32280
|
-
...profile,
|
|
32281
|
-
providers: {
|
|
32282
|
-
...profile.providers ?? {},
|
|
32283
|
-
[provider]: {
|
|
32284
|
-
disconnectedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
32285
|
-
credentialMtimeMs
|
|
32286
|
-
}
|
|
32287
|
-
}
|
|
32288
|
-
};
|
|
32289
|
-
return { ...store, profiles };
|
|
32290
|
-
}
|
|
32291
|
-
);
|
|
32292
|
-
}
|
|
32293
|
-
async function readDisconnectedAuthBackedModelProviderKeys(paths, profileName) {
|
|
32294
|
-
const store = normalizeStore(
|
|
32295
|
-
await readJsonFile(
|
|
32296
|
-
disconnectedProvidersPath(paths)
|
|
32297
|
-
).catch(() => null)
|
|
32298
|
-
);
|
|
32299
|
-
const profile = store.profiles?.[normalizeProfileName6(profileName)];
|
|
32300
|
-
const providers = profile?.providers ?? {};
|
|
32301
|
-
const active = /* @__PURE__ */ new Set();
|
|
32302
|
-
for (const [provider, entry] of Object.entries(providers)) {
|
|
32303
|
-
if (await isDisconnectMarkerActive(profileName, provider, entry)) {
|
|
32304
|
-
active.add(provider);
|
|
32305
|
-
}
|
|
32306
|
-
}
|
|
32307
|
-
return active;
|
|
32308
|
-
}
|
|
32309
|
-
async function isDisconnectMarkerActive(profileName, providerKey, entry) {
|
|
32310
|
-
const currentMtimeMs = await readProviderCredentialMtimeMs(
|
|
32311
|
-
profileName,
|
|
32312
|
-
providerKey
|
|
32313
|
-
);
|
|
32314
|
-
if (entry.credentialMtimeMs === null || !Number.isFinite(entry.credentialMtimeMs)) {
|
|
32315
|
-
return currentMtimeMs === null;
|
|
32316
|
-
}
|
|
32317
|
-
if (currentMtimeMs === null || !Number.isFinite(currentMtimeMs)) {
|
|
32318
|
-
return true;
|
|
32319
|
-
}
|
|
32320
|
-
return currentMtimeMs <= entry.credentialMtimeMs + 1e3;
|
|
32321
|
-
}
|
|
32322
|
-
async function readProviderCredentialMtimeMs(profileName, providerKey) {
|
|
32323
|
-
for (const filePath of credentialFilesForProvider(profileName, providerKey)) {
|
|
32324
|
-
const stats = await stat17(filePath).catch((error) => {
|
|
32325
|
-
if (isNodeError18(error, "ENOENT")) {
|
|
32326
|
-
return null;
|
|
32327
|
-
}
|
|
32328
|
-
throw error;
|
|
32329
|
-
});
|
|
32330
|
-
if (stats) {
|
|
32331
|
-
return stats.mtimeMs;
|
|
32332
|
-
}
|
|
32333
|
-
}
|
|
32334
|
-
return null;
|
|
32335
|
-
}
|
|
32336
|
-
function credentialFilesForProvider(profileName, providerKey) {
|
|
32337
|
-
const profileDir = resolveHermesProfileDir(normalizeProfileName6(profileName));
|
|
32338
|
-
switch (providerKey) {
|
|
32339
|
-
case "qwen-oauth":
|
|
32340
|
-
return [path26.join(os6.homedir(), ".qwen", "oauth_creds.json")];
|
|
32341
|
-
case "google-gemini-cli":
|
|
32342
|
-
return [path26.join(profileDir, "auth", "google_oauth.json")];
|
|
32343
|
-
default:
|
|
32344
|
-
return [path26.join(profileDir, "auth.json")];
|
|
32345
|
-
}
|
|
32346
|
-
}
|
|
32347
|
-
function disconnectedProvidersPath(paths) {
|
|
32348
|
-
return path26.join(paths.homeDir, "model-provider-disconnects.json");
|
|
32349
|
-
}
|
|
32350
|
-
function normalizeStore(value) {
|
|
32351
|
-
return {
|
|
32352
|
-
version: 1,
|
|
32353
|
-
profiles: value?.profiles && typeof value.profiles === "object" ? value.profiles : {}
|
|
32354
|
-
};
|
|
32355
|
-
}
|
|
32356
|
-
function normalizeProfileName6(profileName) {
|
|
32357
|
-
return profileName.trim() || "default";
|
|
32358
|
-
}
|
|
32359
|
-
function isNodeError18(error, code) {
|
|
32360
|
-
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
32361
|
-
}
|
|
33492
|
+
import { createHash as createHash10 } from "crypto";
|
|
32362
33493
|
|
|
32363
33494
|
// src/model-catalog/catalog.ts
|
|
32364
33495
|
import { randomInt } from "crypto";
|
|
@@ -32594,7 +33725,7 @@ function normalizeModelCapabilityCatalog(value) {
|
|
|
32594
33725
|
const models = rawModels.map(normalizeModelCapabilityEntry).filter((entry) => entry !== null);
|
|
32595
33726
|
return {
|
|
32596
33727
|
schemaVersion: readPositiveInteger3(record.schemaVersion) ?? 1,
|
|
32597
|
-
updatedAt:
|
|
33728
|
+
updatedAt: readString22(record.updatedAt),
|
|
32598
33729
|
models
|
|
32599
33730
|
};
|
|
32600
33731
|
}
|
|
@@ -32603,7 +33734,7 @@ function normalizeModelCapabilityEntry(value) {
|
|
|
32603
33734
|
return null;
|
|
32604
33735
|
}
|
|
32605
33736
|
const record = value;
|
|
32606
|
-
const canonicalId =
|
|
33737
|
+
const canonicalId = readString22(record.canonicalId) ?? readString22(record.canonical_id) ?? readString22(record.id);
|
|
32607
33738
|
if (!canonicalId) {
|
|
32608
33739
|
return null;
|
|
32609
33740
|
}
|
|
@@ -32631,12 +33762,12 @@ function normalizeModelCapabilityEntry(value) {
|
|
|
32631
33762
|
outputModalities: readStringArray2(
|
|
32632
33763
|
record.outputModalities ?? record.output_modalities
|
|
32633
33764
|
),
|
|
32634
|
-
source:
|
|
32635
|
-
updatedAt:
|
|
32636
|
-
notes:
|
|
33765
|
+
source: readString22(record.source),
|
|
33766
|
+
updatedAt: readString22(record.updatedAt ?? record.updated_at),
|
|
33767
|
+
notes: readString22(record.notes)
|
|
32637
33768
|
};
|
|
32638
33769
|
}
|
|
32639
|
-
function
|
|
33770
|
+
function readString22(value) {
|
|
32640
33771
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
32641
33772
|
}
|
|
32642
33773
|
function readStringArray2(value) {
|
|
@@ -32646,7 +33777,7 @@ function readStringArray2(value) {
|
|
|
32646
33777
|
const seen = /* @__PURE__ */ new Set();
|
|
32647
33778
|
const values = [];
|
|
32648
33779
|
for (const item of value) {
|
|
32649
|
-
const text =
|
|
33780
|
+
const text = readString22(item);
|
|
32650
33781
|
if (!text || seen.has(text)) {
|
|
32651
33782
|
continue;
|
|
32652
33783
|
}
|
|
@@ -32663,7 +33794,7 @@ function readPositiveInteger3(value) {
|
|
|
32663
33794
|
return Math.floor(number);
|
|
32664
33795
|
}
|
|
32665
33796
|
function readReasoningSupportPolicy(value) {
|
|
32666
|
-
const normalized =
|
|
33797
|
+
const normalized = readString22(value)?.toLowerCase();
|
|
32667
33798
|
if (!normalized || normalized === "unknown" || normalized === "auto") {
|
|
32668
33799
|
return null;
|
|
32669
33800
|
}
|
|
@@ -32694,7 +33825,7 @@ function readNullableBoolean(value) {
|
|
|
32694
33825
|
if (typeof value === "boolean") {
|
|
32695
33826
|
return value;
|
|
32696
33827
|
}
|
|
32697
|
-
const normalized =
|
|
33828
|
+
const normalized = readString22(value)?.toLowerCase();
|
|
32698
33829
|
if (!normalized) {
|
|
32699
33830
|
return null;
|
|
32700
33831
|
}
|
|
@@ -32747,6 +33878,43 @@ async function reloadGatewayAfterProfileModelConfigChange(result, options) {
|
|
|
32747
33878
|
label: "\u6A21\u578B\u914D\u7F6E"
|
|
32748
33879
|
});
|
|
32749
33880
|
}
|
|
33881
|
+
async function refreshRuntimeAfterModelConfigChange(result, options) {
|
|
33882
|
+
const profileName = options.profileName?.trim() || "default";
|
|
33883
|
+
const label = options.label ?? "\u6A21\u578B\u914D\u7F6E";
|
|
33884
|
+
if (resolveConversationRunBackend() !== "tui_gateway") {
|
|
33885
|
+
return profileName === "default" ? reloadGatewayAfterModelConfigChange(result, options) : reloadGatewayAfterProfileModelConfigChange(result, {
|
|
33886
|
+
...options,
|
|
33887
|
+
profileName
|
|
33888
|
+
});
|
|
33889
|
+
}
|
|
33890
|
+
try {
|
|
33891
|
+
await restartTuiGatewayBackend({
|
|
33892
|
+
profileName,
|
|
33893
|
+
reason: "model_config_changed",
|
|
33894
|
+
logger: options.logger,
|
|
33895
|
+
paths: options.paths
|
|
33896
|
+
});
|
|
33897
|
+
return {
|
|
33898
|
+
...result,
|
|
33899
|
+
tuiGatewayRestarted: true,
|
|
33900
|
+
requiresGatewayReload: false,
|
|
33901
|
+
restartHint: `${label}\u5DF2\u4FDD\u5B58\uFF0C${profileName} Profile \u7684 tui_gateway \u5DF2\u91CD\u542F\u3002\u65B0\u7684 Run \u4F1A\u8BFB\u53D6\u6700\u65B0\u914D\u7F6E\u3002`
|
|
33902
|
+
};
|
|
33903
|
+
} catch (error) {
|
|
33904
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
33905
|
+
await options.logger.warn("tui_gateway_restart_after_model_config_failed", {
|
|
33906
|
+
profile: profileName,
|
|
33907
|
+
error: message
|
|
33908
|
+
});
|
|
33909
|
+
return {
|
|
33910
|
+
...result,
|
|
33911
|
+
tuiGatewayRestarted: false,
|
|
33912
|
+
reloadError: message,
|
|
33913
|
+
requiresGatewayReload: true,
|
|
33914
|
+
restartHint: `${label}\u5DF2\u4FDD\u5B58\uFF0C\u4F46 ${profileName} Profile \u7684 tui_gateway \u81EA\u52A8\u91CD\u542F\u5931\u8D25\uFF1A${message}`
|
|
33915
|
+
};
|
|
33916
|
+
}
|
|
33917
|
+
}
|
|
32750
33918
|
async function reloadGatewayAfterProfileConfigChange(result, options) {
|
|
32751
33919
|
try {
|
|
32752
33920
|
await reloadHermesGateway({
|
|
@@ -32793,7 +33961,7 @@ function registerModelConfigRoutes(router, options) {
|
|
|
32793
33961
|
router.get("/api/v1/model-configs", async (ctx) => {
|
|
32794
33962
|
await authenticateRequest(ctx, paths);
|
|
32795
33963
|
ctx.set("cache-control", "no-store");
|
|
32796
|
-
ctx.body = await
|
|
33964
|
+
ctx.body = await listVisibleHermesModelConfigs("default", paths);
|
|
32797
33965
|
});
|
|
32798
33966
|
router.get("/api/v1/model-configs/catalog", async (ctx) => {
|
|
32799
33967
|
await authenticateRequest(ctx, paths);
|
|
@@ -32831,8 +33999,16 @@ function registerModelConfigRoutes(router, options) {
|
|
|
32831
33999
|
readModelProviderConfigInput(body),
|
|
32832
34000
|
ctx.params.name
|
|
32833
34001
|
);
|
|
34002
|
+
const applied = shouldReloadGatewayAfterModelConfigChange(body, {
|
|
34003
|
+
defaultReload: true
|
|
34004
|
+
}) ? await refreshRuntimeAfterModelConfigChange(result, {
|
|
34005
|
+
paths,
|
|
34006
|
+
logger,
|
|
34007
|
+
profileName: ctx.params.name,
|
|
34008
|
+
label: "\u6A21\u578B Provider \u914D\u7F6E"
|
|
34009
|
+
}) : markModelConfigAppliedWithoutGatewayReload(result);
|
|
32834
34010
|
ctx.body = await enrichProviderModelConfigResult(
|
|
32835
|
-
|
|
34011
|
+
applied,
|
|
32836
34012
|
ctx.params.name,
|
|
32837
34013
|
paths
|
|
32838
34014
|
);
|
|
@@ -32865,8 +34041,16 @@ function registerModelConfigRoutes(router, options) {
|
|
|
32865
34041
|
body,
|
|
32866
34042
|
paths
|
|
32867
34043
|
);
|
|
34044
|
+
const applied = shouldReloadGatewayAfterModelConfigChange(body, {
|
|
34045
|
+
defaultReload: true
|
|
34046
|
+
}) ? await refreshRuntimeAfterModelConfigChange(result, {
|
|
34047
|
+
paths,
|
|
34048
|
+
logger,
|
|
34049
|
+
profileName: ctx.params.name,
|
|
34050
|
+
label: "\u6A21\u578B Provider \u914D\u7F6E"
|
|
34051
|
+
}) : markModelConfigAppliedWithoutGatewayReload(result);
|
|
32868
34052
|
ctx.body = await enrichProviderModelConfigResult(
|
|
32869
|
-
|
|
34053
|
+
applied,
|
|
32870
34054
|
ctx.params.name,
|
|
32871
34055
|
paths
|
|
32872
34056
|
);
|
|
@@ -32947,8 +34131,16 @@ function registerModelConfigRoutes(router, options) {
|
|
|
32947
34131
|
},
|
|
32948
34132
|
ctx.params.name
|
|
32949
34133
|
);
|
|
34134
|
+
const applied = shouldReloadGatewayAfterModelConfigChange(body, {
|
|
34135
|
+
defaultReload: true
|
|
34136
|
+
}) ? await refreshRuntimeAfterModelConfigChange(result, {
|
|
34137
|
+
paths,
|
|
34138
|
+
logger,
|
|
34139
|
+
profileName: ctx.params.name,
|
|
34140
|
+
label: "\u6A21\u578B Provider \u914D\u7F6E"
|
|
34141
|
+
}) : markModelConfigAppliedWithoutGatewayReload(result);
|
|
32950
34142
|
ctx.body = await enrichProviderModelConfigResult(
|
|
32951
|
-
|
|
34143
|
+
applied,
|
|
32952
34144
|
ctx.params.name,
|
|
32953
34145
|
paths
|
|
32954
34146
|
);
|
|
@@ -32980,8 +34172,14 @@ function registerModelConfigRoutes(router, options) {
|
|
|
32980
34172
|
provider.providerKey,
|
|
32981
34173
|
ctx.params.name
|
|
32982
34174
|
);
|
|
34175
|
+
const applied = await refreshRuntimeAfterModelConfigChange(result, {
|
|
34176
|
+
paths,
|
|
34177
|
+
logger,
|
|
34178
|
+
profileName: ctx.params.name,
|
|
34179
|
+
label: "\u6A21\u578B Provider \u914D\u7F6E"
|
|
34180
|
+
});
|
|
32983
34181
|
ctx.body = await enrichProviderModelConfigResult(
|
|
32984
|
-
|
|
34182
|
+
applied,
|
|
32985
34183
|
ctx.params.name,
|
|
32986
34184
|
paths
|
|
32987
34185
|
);
|
|
@@ -32999,7 +34197,12 @@ function registerModelConfigRoutes(router, options) {
|
|
|
32999
34197
|
);
|
|
33000
34198
|
}
|
|
33001
34199
|
ctx.body = latest ? await enrichProviderModelConfigResult(
|
|
33002
|
-
|
|
34200
|
+
await refreshRuntimeAfterModelConfigChange(latest, {
|
|
34201
|
+
paths,
|
|
34202
|
+
logger,
|
|
34203
|
+
profileName: ctx.params.name,
|
|
34204
|
+
label: "\u6A21\u578B Provider \u914D\u7F6E"
|
|
34205
|
+
}),
|
|
33003
34206
|
ctx.params.name,
|
|
33004
34207
|
paths
|
|
33005
34208
|
) : await listModelProviderResponse(ctx.params.name, paths);
|
|
@@ -33012,7 +34215,7 @@ function registerModelConfigRoutes(router, options) {
|
|
|
33012
34215
|
const result = await saveHermesModelConfig(readModelConfigInput(body));
|
|
33013
34216
|
const applied = shouldReloadGatewayAfterModelConfigChange(body, {
|
|
33014
34217
|
defaultReload: true
|
|
33015
|
-
}) ? await
|
|
34218
|
+
}) ? await refreshRuntimeAfterModelConfigChange(result, {
|
|
33016
34219
|
paths,
|
|
33017
34220
|
logger
|
|
33018
34221
|
}) : markModelConfigAppliedWithoutGatewayReload(result);
|
|
@@ -33028,8 +34231,10 @@ function registerModelConfigRoutes(router, options) {
|
|
|
33028
34231
|
router.patch("/api/v1/model-configs/defaults", async (ctx) => {
|
|
33029
34232
|
await authenticateRequest(ctx, paths);
|
|
33030
34233
|
const body = await readJsonBody(ctx.req);
|
|
34234
|
+
const input = readModelDefaultsInput(body);
|
|
33031
34235
|
try {
|
|
33032
|
-
|
|
34236
|
+
await assertModelDefaultsSelectable(input, "default", paths);
|
|
34237
|
+
const result = await saveHermesModelDefaults(input);
|
|
33033
34238
|
ctx.body = await enrichProviderModelConfigResult(result, "default", paths);
|
|
33034
34239
|
} catch (error) {
|
|
33035
34240
|
throw toModelConfigHttpError(error);
|
|
@@ -33041,7 +34246,9 @@ function registerModelConfigRoutes(router, options) {
|
|
|
33041
34246
|
const input = readModelDeleteInput(body);
|
|
33042
34247
|
try {
|
|
33043
34248
|
const result = await deleteHermesModelConfig(input);
|
|
33044
|
-
const applied = shouldReloadGatewayAfterModelConfigChange(body
|
|
34249
|
+
const applied = shouldReloadGatewayAfterModelConfigChange(body, {
|
|
34250
|
+
defaultReload: true
|
|
34251
|
+
}) ? await refreshRuntimeAfterModelConfigChange(result, {
|
|
33045
34252
|
paths,
|
|
33046
34253
|
logger
|
|
33047
34254
|
}) : markModelConfigAppliedWithoutGatewayReload(result);
|
|
@@ -33058,7 +34265,7 @@ function registerModelConfigRoutes(router, options) {
|
|
|
33058
34265
|
await authenticateRequest(ctx, paths);
|
|
33059
34266
|
await getHermesProfileStatus(ctx.params.name, paths);
|
|
33060
34267
|
ctx.set("cache-control", "no-store");
|
|
33061
|
-
ctx.body = await
|
|
34268
|
+
ctx.body = await listVisibleHermesModelConfigs(ctx.params.name, paths);
|
|
33062
34269
|
});
|
|
33063
34270
|
router.post("/api/v1/profiles/:name/model-configs", async (ctx) => {
|
|
33064
34271
|
await authenticateRequest(ctx, paths);
|
|
@@ -33071,7 +34278,7 @@ function registerModelConfigRoutes(router, options) {
|
|
|
33071
34278
|
);
|
|
33072
34279
|
const applied = shouldReloadGatewayAfterModelConfigChange(body, {
|
|
33073
34280
|
defaultReload: true
|
|
33074
|
-
}) ? await
|
|
34281
|
+
}) ? await refreshRuntimeAfterModelConfigChange(result, {
|
|
33075
34282
|
paths,
|
|
33076
34283
|
logger,
|
|
33077
34284
|
profileName: ctx.params.name
|
|
@@ -33093,7 +34300,9 @@ function registerModelConfigRoutes(router, options) {
|
|
|
33093
34300
|
await getHermesProfileStatus(input.sourceProfileName, paths);
|
|
33094
34301
|
try {
|
|
33095
34302
|
const result = await importHermesModelConfig(input, ctx.params.name);
|
|
33096
|
-
const applied = shouldReloadGatewayAfterModelConfigChange(body
|
|
34303
|
+
const applied = shouldReloadGatewayAfterModelConfigChange(body, {
|
|
34304
|
+
defaultReload: true
|
|
34305
|
+
}) ? await refreshRuntimeAfterModelConfigChange(result, {
|
|
33097
34306
|
paths,
|
|
33098
34307
|
logger,
|
|
33099
34308
|
profileName: ctx.params.name
|
|
@@ -33111,11 +34320,10 @@ function registerModelConfigRoutes(router, options) {
|
|
|
33111
34320
|
await authenticateRequest(ctx, paths);
|
|
33112
34321
|
await getHermesProfileStatus(ctx.params.name, paths);
|
|
33113
34322
|
const body = await readJsonBody(ctx.req);
|
|
34323
|
+
const input = readModelDefaultsInput(body);
|
|
33114
34324
|
try {
|
|
33115
|
-
|
|
33116
|
-
|
|
33117
|
-
ctx.params.name
|
|
33118
|
-
);
|
|
34325
|
+
await assertModelDefaultsSelectable(input, ctx.params.name, paths);
|
|
34326
|
+
const result = await saveHermesModelDefaults(input, ctx.params.name);
|
|
33119
34327
|
ctx.body = await enrichProviderModelConfigResult(
|
|
33120
34328
|
result,
|
|
33121
34329
|
ctx.params.name,
|
|
@@ -33132,7 +34340,9 @@ function registerModelConfigRoutes(router, options) {
|
|
|
33132
34340
|
const input = readModelDeleteInput(body);
|
|
33133
34341
|
try {
|
|
33134
34342
|
const result = await deleteHermesModelConfig(input, ctx.params.name);
|
|
33135
|
-
const applied = shouldReloadGatewayAfterModelConfigChange(body
|
|
34343
|
+
const applied = shouldReloadGatewayAfterModelConfigChange(body, {
|
|
34344
|
+
defaultReload: true
|
|
34345
|
+
}) ? await refreshRuntimeAfterModelConfigChange(result, {
|
|
33136
34346
|
paths,
|
|
33137
34347
|
logger,
|
|
33138
34348
|
profileName: ctx.params.name
|
|
@@ -33148,7 +34358,7 @@ function registerModelConfigRoutes(router, options) {
|
|
|
33148
34358
|
});
|
|
33149
34359
|
}
|
|
33150
34360
|
async function listModelProviderResponse(profileName, paths, options = {}) {
|
|
33151
|
-
const configs = await listHermesModelConfigs(profileName);
|
|
34361
|
+
const configs = options.includeDisconnected ? await listHermesModelConfigs(profileName) : await listVisibleHermesModelConfigs(profileName, paths);
|
|
33152
34362
|
const providerConfigs = await listHermesModelProviderConfigs(profileName);
|
|
33153
34363
|
const catalog = await readModelCapabilityCatalog(paths);
|
|
33154
34364
|
const providers = groupManagedModelsByProvider(
|
|
@@ -33172,6 +34382,92 @@ async function listModelProviderResponse(profileName, paths, options = {}) {
|
|
|
33172
34382
|
providers: visibleProviders
|
|
33173
34383
|
};
|
|
33174
34384
|
}
|
|
34385
|
+
async function listVisibleHermesModelConfigs(profileName, paths) {
|
|
34386
|
+
const configs = await listHermesModelConfigs(profileName);
|
|
34387
|
+
const disconnectedProviders = await readDisconnectedAuthBackedModelProviderKeys(
|
|
34388
|
+
paths,
|
|
34389
|
+
profileName
|
|
34390
|
+
);
|
|
34391
|
+
return filterHermesModelConfigsByProviderKeys(configs, disconnectedProviders);
|
|
34392
|
+
}
|
|
34393
|
+
async function assertModelDefaultsSelectable(input, profileName, paths) {
|
|
34394
|
+
const disconnectedProviders = await readDisconnectedAuthBackedModelProviderKeys(
|
|
34395
|
+
paths,
|
|
34396
|
+
profileName
|
|
34397
|
+
);
|
|
34398
|
+
assertProviderNotDisconnected(
|
|
34399
|
+
disconnectedProviders,
|
|
34400
|
+
input.taskModelProvider,
|
|
34401
|
+
"task model"
|
|
34402
|
+
);
|
|
34403
|
+
assertProviderNotDisconnected(
|
|
34404
|
+
disconnectedProviders,
|
|
34405
|
+
input.compressionModelProvider,
|
|
34406
|
+
"compression model"
|
|
34407
|
+
);
|
|
34408
|
+
if (disconnectedProviders.size === 0 || !input.taskModelId && !input.compressionModelId) {
|
|
34409
|
+
return;
|
|
34410
|
+
}
|
|
34411
|
+
const allConfigs = await listHermesModelConfigs(profileName);
|
|
34412
|
+
assertModelIdNotHiddenByDisconnect(allConfigs.models, disconnectedProviders, {
|
|
34413
|
+
id: input.taskModelId,
|
|
34414
|
+
provider: input.taskModelProvider,
|
|
34415
|
+
baseUrl: input.taskModelBaseUrl,
|
|
34416
|
+
apiMode: input.taskModelApiMode,
|
|
34417
|
+
label: "task model"
|
|
34418
|
+
});
|
|
34419
|
+
assertModelIdNotHiddenByDisconnect(allConfigs.models, disconnectedProviders, {
|
|
34420
|
+
id: input.compressionModelId,
|
|
34421
|
+
provider: input.compressionModelProvider,
|
|
34422
|
+
baseUrl: input.compressionModelBaseUrl,
|
|
34423
|
+
apiMode: input.compressionModelApiMode,
|
|
34424
|
+
label: "compression model"
|
|
34425
|
+
});
|
|
34426
|
+
}
|
|
34427
|
+
function assertProviderNotDisconnected(disconnectedProviders, provider, label) {
|
|
34428
|
+
if (provider && disconnectedProviders.has(provider)) {
|
|
34429
|
+
throw new LinkHttpError(
|
|
34430
|
+
409,
|
|
34431
|
+
"model_provider_disconnected",
|
|
34432
|
+
`The ${label} provider "${provider}" has been disconnected from this Profile.`
|
|
34433
|
+
);
|
|
34434
|
+
}
|
|
34435
|
+
}
|
|
34436
|
+
function assertModelIdNotHiddenByDisconnect(models, disconnectedProviders, selector) {
|
|
34437
|
+
const id = selector.id?.trim();
|
|
34438
|
+
if (!id) {
|
|
34439
|
+
return;
|
|
34440
|
+
}
|
|
34441
|
+
const matchingModels = models.filter((model) => {
|
|
34442
|
+
if (model.id !== id) {
|
|
34443
|
+
return false;
|
|
34444
|
+
}
|
|
34445
|
+
if (selector.provider && model.provider !== selector.provider) {
|
|
34446
|
+
return false;
|
|
34447
|
+
}
|
|
34448
|
+
if (selector.baseUrl !== void 0 && normalizeUrlForIdentity(model.baseUrl) !== normalizeUrlForIdentity(selector.baseUrl)) {
|
|
34449
|
+
return false;
|
|
34450
|
+
}
|
|
34451
|
+
if (selector.apiMode && model.apiMode !== selector.apiMode) {
|
|
34452
|
+
return false;
|
|
34453
|
+
}
|
|
34454
|
+
return true;
|
|
34455
|
+
});
|
|
34456
|
+
if (matchingModels.some((model) => !disconnectedProviders.has(model.provider))) {
|
|
34457
|
+
return;
|
|
34458
|
+
}
|
|
34459
|
+
const hiddenMatch = matchingModels.find(
|
|
34460
|
+
(model) => disconnectedProviders.has(model.provider)
|
|
34461
|
+
);
|
|
34462
|
+
if (!hiddenMatch) {
|
|
34463
|
+
return;
|
|
34464
|
+
}
|
|
34465
|
+
throw new LinkHttpError(
|
|
34466
|
+
409,
|
|
34467
|
+
"model_provider_disconnected",
|
|
34468
|
+
`The ${selector.label} "${id}" belongs to disconnected provider "${hiddenMatch.provider}".`
|
|
34469
|
+
);
|
|
34470
|
+
}
|
|
33175
34471
|
async function enrichProviderModelConfigResult(result, profileName, paths) {
|
|
33176
34472
|
const providerResponse = await listModelProviderResponse(profileName, paths);
|
|
33177
34473
|
return {
|
|
@@ -33422,7 +34718,7 @@ function modelProviderIdFromParts(input) {
|
|
|
33422
34718
|
input.keyEnv ?? "",
|
|
33423
34719
|
input.authType ?? ""
|
|
33424
34720
|
].join("");
|
|
33425
|
-
return `mp_${
|
|
34721
|
+
return `mp_${createHash10("sha256").update(identity).digest("hex").slice(0, 18)}`;
|
|
33426
34722
|
}
|
|
33427
34723
|
function mergeCredentialState(left, right) {
|
|
33428
34724
|
if (left === "configured" || right === "configured") {
|
|
@@ -33678,8 +34974,8 @@ function normalizeUrlForIdentity(value) {
|
|
|
33678
34974
|
return value.trim().replace(/\/+$/u, "").toLowerCase();
|
|
33679
34975
|
}
|
|
33680
34976
|
function readModelProviderConfigInput(body) {
|
|
33681
|
-
const providerName =
|
|
33682
|
-
const baseUrl =
|
|
34977
|
+
const providerName = readString21(body, "display_name") ?? readString21(body, "displayName") ?? readString21(body, "provider_name") ?? readString21(body, "providerName") ?? readString21(body, "name");
|
|
34978
|
+
const baseUrl = readString21(body, "base_url") ?? readString21(body, "baseUrl");
|
|
33683
34979
|
if (!providerName || !baseUrl) {
|
|
33684
34980
|
throw new LinkHttpError(
|
|
33685
34981
|
400,
|
|
@@ -33690,15 +34986,15 @@ function readModelProviderConfigInput(body) {
|
|
|
33690
34986
|
return {
|
|
33691
34987
|
providerName,
|
|
33692
34988
|
baseUrl,
|
|
33693
|
-
apiMode:
|
|
33694
|
-
keyEnv:
|
|
33695
|
-
apiKey:
|
|
34989
|
+
apiMode: readString21(body, "api_mode") ?? readString21(body, "apiMode") ?? readString21(body, "transport") ?? void 0,
|
|
34990
|
+
keyEnv: readString21(body, "key_env") ?? readString21(body, "keyEnv") ?? void 0,
|
|
34991
|
+
apiKey: readString21(body, "api_key") ?? readString21(body, "apiKey") ?? void 0
|
|
33696
34992
|
};
|
|
33697
34993
|
}
|
|
33698
34994
|
function readModelConfigInput(body) {
|
|
33699
|
-
const id =
|
|
33700
|
-
const provider =
|
|
33701
|
-
const baseUrl =
|
|
34995
|
+
const id = readString21(body, "id") ?? readString21(body, "model_id") ?? readString21(body, "modelId");
|
|
34996
|
+
const provider = readString21(body, "provider") ?? readString21(body, "provider_key") ?? readString21(body, "providerKey");
|
|
34997
|
+
const baseUrl = readString21(body, "base_url") ?? readString21(body, "baseUrl");
|
|
33702
34998
|
if (!id || !provider || !baseUrl) {
|
|
33703
34999
|
throw new LinkHttpError(
|
|
33704
35000
|
400,
|
|
@@ -33708,22 +35004,22 @@ function readModelConfigInput(body) {
|
|
|
33708
35004
|
}
|
|
33709
35005
|
return {
|
|
33710
35006
|
id,
|
|
33711
|
-
originalModelId:
|
|
33712
|
-
originalProvider:
|
|
33713
|
-
originalBaseUrl:
|
|
33714
|
-
originalApiMode:
|
|
35007
|
+
originalModelId: readString21(body, "original_model_id") ?? readString21(body, "originalModelId") ?? readString21(body, "original_id") ?? void 0,
|
|
35008
|
+
originalProvider: readString21(body, "original_provider") ?? readString21(body, "originalProvider") ?? readString21(body, "original_provider_key") ?? readString21(body, "originalProviderKey") ?? void 0,
|
|
35009
|
+
originalBaseUrl: readString21(body, "original_base_url") ?? readString21(body, "originalBaseUrl") ?? void 0,
|
|
35010
|
+
originalApiMode: readString21(body, "original_api_mode") ?? readString21(body, "originalApiMode") ?? void 0,
|
|
33715
35011
|
provider,
|
|
33716
|
-
providerName:
|
|
35012
|
+
providerName: readString21(body, "provider_name") ?? readString21(body, "providerName") ?? void 0,
|
|
33717
35013
|
baseUrl,
|
|
33718
|
-
apiKey:
|
|
33719
|
-
apiMode:
|
|
35014
|
+
apiKey: readString21(body, "api_key") ?? readString21(body, "apiKey") ?? void 0,
|
|
35015
|
+
apiMode: readString21(body, "api_mode") ?? readString21(body, "apiMode") ?? void 0,
|
|
33720
35016
|
contextLength: readPositiveInteger2(
|
|
33721
35017
|
body.context_length ?? body.contextLength
|
|
33722
35018
|
),
|
|
33723
|
-
keyEnv:
|
|
35019
|
+
keyEnv: readString21(body, "key_env") ?? readString21(body, "keyEnv") ?? void 0,
|
|
33724
35020
|
setDefault: readBoolean3(body.set_default ?? body.setDefault),
|
|
33725
|
-
reasoningEffort:
|
|
33726
|
-
reasoningSupportPolicy:
|
|
35021
|
+
reasoningEffort: readString21(body, "reasoning_effort") ?? readString21(body, "reasoningEffort") ?? void 0,
|
|
35022
|
+
reasoningSupportPolicy: readString21(body, "reasoning_support_policy") ?? readString21(body, "reasoningSupportPolicy") ?? readString21(body, "reasoning_support") ?? readString21(body, "reasoningSupport") ?? void 0,
|
|
33727
35023
|
supportsVision: readNullableBoolean2(
|
|
33728
35024
|
body.supports_vision ?? body.supportsVision
|
|
33729
35025
|
)
|
|
@@ -33731,28 +35027,28 @@ function readModelConfigInput(body) {
|
|
|
33731
35027
|
}
|
|
33732
35028
|
function readModelDefaultsInput(body) {
|
|
33733
35029
|
return {
|
|
33734
|
-
taskModelId:
|
|
33735
|
-
taskModelProvider:
|
|
33736
|
-
taskModelBaseUrl:
|
|
33737
|
-
taskModelApiMode:
|
|
33738
|
-
compressionModelId:
|
|
33739
|
-
compressionModelProvider:
|
|
33740
|
-
compressionModelBaseUrl:
|
|
33741
|
-
compressionModelApiMode:
|
|
33742
|
-
reasoningEffort:
|
|
33743
|
-
imageInputMode:
|
|
35030
|
+
taskModelId: readString21(body, "task_model_id") ?? readString21(body, "taskModelId") ?? readString21(body, "default_model_id") ?? readString21(body, "defaultModelId") ?? void 0,
|
|
35031
|
+
taskModelProvider: readString21(body, "task_model_provider") ?? readString21(body, "taskModelProvider") ?? readString21(body, "default_model_provider") ?? readString21(body, "defaultModelProvider") ?? void 0,
|
|
35032
|
+
taskModelBaseUrl: readString21(body, "task_model_base_url") ?? readString21(body, "taskModelBaseUrl") ?? readString21(body, "default_model_base_url") ?? readString21(body, "defaultModelBaseUrl") ?? void 0,
|
|
35033
|
+
taskModelApiMode: readString21(body, "task_model_api_mode") ?? readString21(body, "taskModelApiMode") ?? readString21(body, "default_model_api_mode") ?? readString21(body, "defaultModelApiMode") ?? void 0,
|
|
35034
|
+
compressionModelId: readString21(body, "compression_model_id") ?? readString21(body, "compressionModelId") ?? void 0,
|
|
35035
|
+
compressionModelProvider: readString21(body, "compression_model_provider") ?? readString21(body, "compressionModelProvider") ?? void 0,
|
|
35036
|
+
compressionModelBaseUrl: readString21(body, "compression_model_base_url") ?? readString21(body, "compressionModelBaseUrl") ?? void 0,
|
|
35037
|
+
compressionModelApiMode: readString21(body, "compression_model_api_mode") ?? readString21(body, "compressionModelApiMode") ?? void 0,
|
|
35038
|
+
reasoningEffort: readString21(body, "reasoning_effort") ?? readString21(body, "reasoningEffort") ?? readString21(body, "default_reasoning_effort") ?? readString21(body, "defaultReasoningEffort") ?? void 0,
|
|
35039
|
+
imageInputMode: readString21(body, "image_input_mode") ?? readString21(body, "imageInputMode") ?? void 0
|
|
33744
35040
|
};
|
|
33745
35041
|
}
|
|
33746
35042
|
function readModelDeleteInput(body) {
|
|
33747
|
-
const id =
|
|
35043
|
+
const id = readString21(body, "model_id") ?? readString21(body, "modelId");
|
|
33748
35044
|
if (!id) {
|
|
33749
35045
|
throw new LinkHttpError(400, "model_id_required", "model_id is required");
|
|
33750
35046
|
}
|
|
33751
35047
|
return {
|
|
33752
35048
|
id,
|
|
33753
|
-
provider:
|
|
33754
|
-
baseUrl:
|
|
33755
|
-
apiMode:
|
|
35049
|
+
provider: readString21(body, "provider") ?? readString21(body, "provider_key") ?? readString21(body, "providerKey") ?? void 0,
|
|
35050
|
+
baseUrl: readString21(body, "base_url") ?? readString21(body, "baseUrl") ?? void 0,
|
|
35051
|
+
apiMode: readString21(body, "api_mode") ?? readString21(body, "apiMode") ?? void 0
|
|
33756
35052
|
};
|
|
33757
35053
|
}
|
|
33758
35054
|
function readNullableBoolean2(value) {
|
|
@@ -33775,8 +35071,8 @@ function readNullableBoolean2(value) {
|
|
|
33775
35071
|
return void 0;
|
|
33776
35072
|
}
|
|
33777
35073
|
function readModelConfigImportInput(body) {
|
|
33778
|
-
const sourceProfileName =
|
|
33779
|
-
const modelId =
|
|
35074
|
+
const sourceProfileName = readString21(body, "source_profile") ?? readString21(body, "sourceProfile") ?? readString21(body, "source_profile_name") ?? readString21(body, "sourceProfileName");
|
|
35075
|
+
const modelId = readString21(body, "model_id") ?? readString21(body, "modelId") ?? readString21(body, "id");
|
|
33780
35076
|
if (!sourceProfileName || !modelId) {
|
|
33781
35077
|
throw new LinkHttpError(
|
|
33782
35078
|
400,
|
|
@@ -33787,9 +35083,9 @@ function readModelConfigImportInput(body) {
|
|
|
33787
35083
|
return {
|
|
33788
35084
|
sourceProfileName,
|
|
33789
35085
|
modelId,
|
|
33790
|
-
provider:
|
|
33791
|
-
baseUrl:
|
|
33792
|
-
apiMode:
|
|
35086
|
+
provider: readString21(body, "provider") ?? readString21(body, "provider_key") ?? readString21(body, "providerKey") ?? void 0,
|
|
35087
|
+
baseUrl: readString21(body, "base_url") ?? readString21(body, "baseUrl") ?? void 0,
|
|
35088
|
+
apiMode: readString21(body, "api_mode") ?? readString21(body, "apiMode") ?? void 0,
|
|
33793
35089
|
setDefault: readBoolean3(body.set_default ?? body.setDefault)
|
|
33794
35090
|
};
|
|
33795
35091
|
}
|
|
@@ -33830,12 +35126,12 @@ function registerProfileCatalogRoutes(router, options) {
|
|
|
33830
35126
|
ok: true,
|
|
33831
35127
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
33832
35128
|
profiles: await Promise.all(
|
|
33833
|
-
profiles.map((profile) => readProfileCatalogItem(profile))
|
|
35129
|
+
profiles.map((profile) => readProfileCatalogItem(profile, paths))
|
|
33834
35130
|
)
|
|
33835
35131
|
};
|
|
33836
35132
|
});
|
|
33837
35133
|
}
|
|
33838
|
-
async function readProfileCatalogItem(profile) {
|
|
35134
|
+
async function readProfileCatalogItem(profile, paths) {
|
|
33839
35135
|
const [apiServer, capabilities, permissions, modelConfigs] = await Promise.all([
|
|
33840
35136
|
readCatalogField(
|
|
33841
35137
|
"apiServer",
|
|
@@ -33849,7 +35145,10 @@ async function readProfileCatalogItem(profile) {
|
|
|
33849
35145
|
"permissions",
|
|
33850
35146
|
() => readHermesProfilePermissions(profile.name)
|
|
33851
35147
|
),
|
|
33852
|
-
readCatalogField(
|
|
35148
|
+
readCatalogField(
|
|
35149
|
+
"modelConfigs",
|
|
35150
|
+
() => listVisibleHermesModelConfigs2(profile.name, paths)
|
|
35151
|
+
)
|
|
33853
35152
|
]);
|
|
33854
35153
|
return {
|
|
33855
35154
|
profile,
|
|
@@ -33870,6 +35169,14 @@ async function readProfileCatalogItem(profile) {
|
|
|
33870
35169
|
]
|
|
33871
35170
|
};
|
|
33872
35171
|
}
|
|
35172
|
+
async function listVisibleHermesModelConfigs2(profileName, paths) {
|
|
35173
|
+
const configs = await listHermesModelConfigs(profileName);
|
|
35174
|
+
const disconnectedProviders = await readDisconnectedAuthBackedModelProviderKeys(
|
|
35175
|
+
paths,
|
|
35176
|
+
profileName
|
|
35177
|
+
);
|
|
35178
|
+
return filterHermesModelConfigsByProviderKeys(configs, disconnectedProviders);
|
|
35179
|
+
}
|
|
33873
35180
|
async function readCatalogField(field, load) {
|
|
33874
35181
|
try {
|
|
33875
35182
|
return { value: await load(), errors: [] };
|
|
@@ -35083,16 +36390,16 @@ function readProfilePermissionsInput(body) {
|
|
|
35083
36390
|
const approvals = readOptionalObject(body, "approvals");
|
|
35084
36391
|
if (approvals) {
|
|
35085
36392
|
input.approvals = {
|
|
35086
|
-
mode:
|
|
36393
|
+
mode: readString21(approvals, "mode") ?? readString21(approvals, "approval_mode") ?? readString21(approvals, "approvalMode") ?? void 0,
|
|
35087
36394
|
timeout: readPositiveInteger2(approvals.timeout),
|
|
35088
|
-
cronMode:
|
|
36395
|
+
cronMode: readString21(approvals, "cron_mode") ?? readString21(approvals, "cronMode") ?? void 0
|
|
35089
36396
|
};
|
|
35090
36397
|
}
|
|
35091
36398
|
const terminal = readOptionalObject(body, "terminal");
|
|
35092
36399
|
if (terminal) {
|
|
35093
36400
|
input.terminal = {
|
|
35094
|
-
backend:
|
|
35095
|
-
cwd:
|
|
36401
|
+
backend: readString21(terminal, "backend") ?? void 0,
|
|
36402
|
+
cwd: readString21(terminal, "cwd") ?? void 0,
|
|
35096
36403
|
containerCpu: readPositiveInteger2(
|
|
35097
36404
|
terminal.container_cpu ?? terminal.containerCpu
|
|
35098
36405
|
),
|
|
@@ -35423,9 +36730,9 @@ async function testHindsightProviderSettings(profileName, patch) {
|
|
|
35423
36730
|
const mode = normalizeHindsightMode(
|
|
35424
36731
|
patch.mode ?? config.mode ?? env.HINDSIGHT_MODE
|
|
35425
36732
|
);
|
|
35426
|
-
const apiUrl =
|
|
35427
|
-
const bankId =
|
|
35428
|
-
const apiKey =
|
|
36733
|
+
const apiUrl = readString23(patch.apiUrl) ?? readString23(config.api_url) ?? env.HINDSIGHT_API_URL ?? (mode === "cloud" ? HINDSIGHT_DEFAULT_API_URL : HINDSIGHT_DEFAULT_LOCAL_URL);
|
|
36734
|
+
const bankId = readString23(patch.bankId) ?? readString23(config.bank_id) ?? "hermes";
|
|
36735
|
+
const apiKey = readString23(patch.apiKey) ?? env.HINDSIGHT_API_KEY ?? readString23(config.apiKey) ?? readString23(config.api_key);
|
|
35429
36736
|
const baseUrl = normalizeHttpUrl2(apiUrl);
|
|
35430
36737
|
if (!baseUrl) {
|
|
35431
36738
|
return {
|
|
@@ -35959,7 +37266,7 @@ async function readProviderConfigurationStatus(profileName, provider) {
|
|
|
35959
37266
|
const config2 = await readJsonObject(
|
|
35960
37267
|
memoryProviderConfigPath(profileName, "honcho") ?? ""
|
|
35961
37268
|
);
|
|
35962
|
-
return isConfiguredEnvValue(env.HONCHO_API_KEY) || isConfiguredEnvValue(
|
|
37269
|
+
return isConfiguredEnvValue(env.HONCHO_API_KEY) || isConfiguredEnvValue(readString23(config2.apiKey)) || isConfiguredEnvValue(readString23(config2.api_key)) || isConfiguredEnvValue(readString23(config2.baseUrl)) ? { configured: true, issue: null } : {
|
|
35963
37270
|
configured: false,
|
|
35964
37271
|
issue: "Honcho \u9700\u8981\u5148\u586B\u5199 API Key\uFF0C\u6216\u5728 honcho.json \u914D\u7F6E self-hosted baseUrl\u3002"
|
|
35965
37272
|
};
|
|
@@ -35968,7 +37275,7 @@ async function readProviderConfigurationStatus(profileName, provider) {
|
|
|
35968
37275
|
const config2 = await readJsonObject(
|
|
35969
37276
|
memoryProviderConfigPath(profileName, "mem0") ?? ""
|
|
35970
37277
|
);
|
|
35971
|
-
return isConfiguredEnvValue(env.MEM0_API_KEY) || isConfiguredEnvValue(
|
|
37278
|
+
return isConfiguredEnvValue(env.MEM0_API_KEY) || isConfiguredEnvValue(readString23(config2.api_key)) ? { configured: true, issue: null } : {
|
|
35972
37279
|
configured: false,
|
|
35973
37280
|
issue: "Mem0 \u9700\u8981\u5148\u5728\u672C\u9875\u586B\u5199 API Key\uFF0CLink \u4F1A\u5199\u5165\u5F53\u524D Profile \u7684 .env\u3002"
|
|
35974
37281
|
};
|
|
@@ -36010,7 +37317,7 @@ async function readProviderConfigurationStatus(profileName, provider) {
|
|
|
36010
37317
|
memoryProviderConfigPath(profileName, provider) ?? ""
|
|
36011
37318
|
);
|
|
36012
37319
|
const mode = normalizeHindsightMode(config.mode ?? env.HINDSIGHT_MODE);
|
|
36013
|
-
const apiKey =
|
|
37320
|
+
const apiKey = readString23(config.apiKey) ?? readString23(config.api_key) ?? env.HINDSIGHT_API_KEY;
|
|
36014
37321
|
if (mode === "cloud") {
|
|
36015
37322
|
return isConfiguredEnvValue(apiKey) ? { configured: true, issue: null } : {
|
|
36016
37323
|
configured: false,
|
|
@@ -36018,15 +37325,15 @@ async function readProviderConfigurationStatus(profileName, provider) {
|
|
|
36018
37325
|
};
|
|
36019
37326
|
}
|
|
36020
37327
|
if (mode === "local_external") {
|
|
36021
|
-
const apiUrl =
|
|
37328
|
+
const apiUrl = readString23(config.api_url) ?? env.HINDSIGHT_API_URL ?? HINDSIGHT_DEFAULT_LOCAL_URL;
|
|
36022
37329
|
return isConfiguredEnvValue(apiUrl) ? { configured: true, issue: null } : {
|
|
36023
37330
|
configured: false,
|
|
36024
37331
|
issue: "Hindsight local_external \u9700\u8981\u914D\u7F6E\u53EF\u8BBF\u95EE\u7684 API URL\u3002"
|
|
36025
37332
|
};
|
|
36026
37333
|
}
|
|
36027
37334
|
if (mode === "local_embedded") {
|
|
36028
|
-
const llmProvider =
|
|
36029
|
-
const llmModel =
|
|
37335
|
+
const llmProvider = readString23(config.llm_provider) ?? "openai";
|
|
37336
|
+
const llmModel = readString23(config.llm_model);
|
|
36030
37337
|
if (!llmModel) {
|
|
36031
37338
|
return {
|
|
36032
37339
|
configured: false,
|
|
@@ -36034,7 +37341,7 @@ async function readProviderConfigurationStatus(profileName, provider) {
|
|
|
36034
37341
|
};
|
|
36035
37342
|
}
|
|
36036
37343
|
if (llmProvider === "openai_compatible" && !isConfiguredEnvValue(
|
|
36037
|
-
|
|
37344
|
+
readString23(config.llm_base_url) ?? env.HINDSIGHT_API_LLM_BASE_URL
|
|
36038
37345
|
)) {
|
|
36039
37346
|
return {
|
|
36040
37347
|
configured: false,
|
|
@@ -36042,7 +37349,7 @@ async function readProviderConfigurationStatus(profileName, provider) {
|
|
|
36042
37349
|
};
|
|
36043
37350
|
}
|
|
36044
37351
|
if (!["ollama", "lmstudio", "openai_compatible"].includes(llmProvider) && !isConfiguredEnvValue(
|
|
36045
|
-
|
|
37352
|
+
readString23(config.llmApiKey) ?? readString23(config.llm_api_key) ?? env.HINDSIGHT_LLM_API_KEY
|
|
36046
37353
|
)) {
|
|
36047
37354
|
return {
|
|
36048
37355
|
configured: false,
|
|
@@ -36098,8 +37405,8 @@ async function readProviderSettings(profileName, provider) {
|
|
|
36098
37405
|
secretSetting(
|
|
36099
37406
|
"apiKey",
|
|
36100
37407
|
"API Key",
|
|
36101
|
-
env.HONCHO_API_KEY ??
|
|
36102
|
-
isConfiguredEnvValue(env.HONCHO_API_KEY) || isConfiguredEnvValue(
|
|
37408
|
+
env.HONCHO_API_KEY ?? readString23(config.apiKey) ?? readString23(config.api_key),
|
|
37409
|
+
isConfiguredEnvValue(env.HONCHO_API_KEY) || isConfiguredEnvValue(readString23(config.apiKey)) || isConfiguredEnvValue(readString23(config.api_key))
|
|
36103
37410
|
),
|
|
36104
37411
|
stringSetting("workspace", "Workspace", config.workspace ?? "hermes"),
|
|
36105
37412
|
stringSetting("peerName", "\u7528\u6237 Peer", config.peerName ?? ""),
|
|
@@ -36141,8 +37448,8 @@ async function readProviderSettings(profileName, provider) {
|
|
|
36141
37448
|
secretSetting(
|
|
36142
37449
|
"apiKey",
|
|
36143
37450
|
"API Key",
|
|
36144
|
-
env.MEM0_API_KEY ??
|
|
36145
|
-
isConfiguredEnvValue(env.MEM0_API_KEY) || isConfiguredEnvValue(
|
|
37451
|
+
env.MEM0_API_KEY ?? readString23(config.apiKey) ?? readString23(config.api_key),
|
|
37452
|
+
isConfiguredEnvValue(env.MEM0_API_KEY) || isConfiguredEnvValue(readString23(config.apiKey)) || isConfiguredEnvValue(readString23(config.api_key))
|
|
36146
37453
|
),
|
|
36147
37454
|
stringSetting("userId", "User ID", config.user_id ?? "hermes-user"),
|
|
36148
37455
|
stringSetting("agentId", "Agent ID", config.agent_id ?? "hermes"),
|
|
@@ -36227,8 +37534,8 @@ async function readProviderSettings(profileName, provider) {
|
|
|
36227
37534
|
secretSetting(
|
|
36228
37535
|
"apiKey",
|
|
36229
37536
|
"Hindsight API Key",
|
|
36230
|
-
env.HINDSIGHT_API_KEY ??
|
|
36231
|
-
isConfiguredEnvValue(env.HINDSIGHT_API_KEY) || isConfiguredEnvValue(
|
|
37537
|
+
env.HINDSIGHT_API_KEY ?? readString23(config.apiKey) ?? readString23(config.api_key),
|
|
37538
|
+
isConfiguredEnvValue(env.HINDSIGHT_API_KEY) || isConfiguredEnvValue(readString23(config.apiKey)) || isConfiguredEnvValue(readString23(config.api_key))
|
|
36232
37539
|
),
|
|
36233
37540
|
stringSetting(
|
|
36234
37541
|
"bankId",
|
|
@@ -36250,8 +37557,8 @@ async function readProviderSettings(profileName, provider) {
|
|
|
36250
37557
|
secretSetting(
|
|
36251
37558
|
"llmApiKey",
|
|
36252
37559
|
"LLM API Key",
|
|
36253
|
-
env.HINDSIGHT_LLM_API_KEY ??
|
|
36254
|
-
isConfiguredEnvValue(env.HINDSIGHT_LLM_API_KEY) || isConfiguredEnvValue(
|
|
37560
|
+
env.HINDSIGHT_LLM_API_KEY ?? readString23(config.llmApiKey) ?? readString23(config.llm_api_key),
|
|
37561
|
+
isConfiguredEnvValue(env.HINDSIGHT_LLM_API_KEY) || isConfiguredEnvValue(readString23(config.llmApiKey)) || isConfiguredEnvValue(readString23(config.llm_api_key))
|
|
36255
37562
|
),
|
|
36256
37563
|
booleanSetting("autoRecall", "\u81EA\u52A8\u56DE\u5FC6", config.auto_recall ?? true),
|
|
36257
37564
|
booleanSetting("autoRetain", "\u81EA\u52A8\u6C89\u6DC0", config.auto_retain ?? true),
|
|
@@ -36370,11 +37677,11 @@ async function readCustomProviderRegistry(profileName) {
|
|
|
36370
37677
|
return { id: id2, label: id2, description: "\u81EA\u5B9A\u4E49 memory provider\u3002" };
|
|
36371
37678
|
}
|
|
36372
37679
|
const record = toRecord21(item);
|
|
36373
|
-
const id = normalizeCustomProviderId(
|
|
37680
|
+
const id = normalizeCustomProviderId(readString23(record.id) ?? "");
|
|
36374
37681
|
return {
|
|
36375
37682
|
id,
|
|
36376
|
-
label:
|
|
36377
|
-
description:
|
|
37683
|
+
label: readString23(record.label) ?? id,
|
|
37684
|
+
description: readString23(record.description) ?? "\u81EA\u5B9A\u4E49 memory provider\u3002"
|
|
36378
37685
|
};
|
|
36379
37686
|
}).filter((item) => item.id);
|
|
36380
37687
|
} catch {
|
|
@@ -36423,8 +37730,8 @@ async function discoverUserMemoryProviderDescriptors(profileName) {
|
|
|
36423
37730
|
const meta = await readPluginMetadata(providerDir);
|
|
36424
37731
|
descriptors.push({
|
|
36425
37732
|
id: providerId,
|
|
36426
|
-
label:
|
|
36427
|
-
description:
|
|
37733
|
+
label: readString23(meta.name) ?? providerId,
|
|
37734
|
+
description: readString23(meta.description) ?? "\u81EA\u5B9A\u4E49 memory provider\u3002"
|
|
36428
37735
|
});
|
|
36429
37736
|
}
|
|
36430
37737
|
return descriptors;
|
|
@@ -36586,7 +37893,7 @@ function isMemoryEnvKeyWritable(key) {
|
|
|
36586
37893
|
].includes(key);
|
|
36587
37894
|
}
|
|
36588
37895
|
function normalizeHindsightMode(value) {
|
|
36589
|
-
const mode =
|
|
37896
|
+
const mode = readString23(value) ?? "cloud";
|
|
36590
37897
|
return mode === "local" ? "local_embedded" : mode;
|
|
36591
37898
|
}
|
|
36592
37899
|
function normalizeHttpUrl2(value) {
|
|
@@ -36655,27 +37962,27 @@ function parseJsonObject2(text) {
|
|
|
36655
37962
|
}
|
|
36656
37963
|
}
|
|
36657
37964
|
function readHindsightError(json) {
|
|
36658
|
-
const detail =
|
|
37965
|
+
const detail = readString23(json.detail) ?? readString23(json.error);
|
|
36659
37966
|
return detail ? `\uFF1A${detail}` : "";
|
|
36660
37967
|
}
|
|
36661
37968
|
function hindsightSemanticIssue(pathName, json) {
|
|
36662
37969
|
if (pathName === "/health") {
|
|
36663
|
-
const status =
|
|
37970
|
+
const status = readString23(json.status);
|
|
36664
37971
|
return status && ["healthy", "ok"].includes(status.toLowerCase()) ? null : `\u5065\u5EB7\u72B6\u6001\u5F02\u5E38\uFF1A${status ?? "unknown"}`;
|
|
36665
37972
|
}
|
|
36666
37973
|
return null;
|
|
36667
37974
|
}
|
|
36668
37975
|
function summarizeHindsightProbe(pathName, json) {
|
|
36669
37976
|
if (pathName === "/health") {
|
|
36670
|
-
const status =
|
|
36671
|
-
const database =
|
|
37977
|
+
const status = readString23(json.status) ?? "ok";
|
|
37978
|
+
const database = readString23(json.database);
|
|
36672
37979
|
return database ? `${status}, database ${database}` : status;
|
|
36673
37980
|
}
|
|
36674
37981
|
if (pathName === "/version") {
|
|
36675
|
-
const version =
|
|
37982
|
+
const version = readString23(json.api_version);
|
|
36676
37983
|
return version ? `API ${version}` : "version endpoint reachable";
|
|
36677
37984
|
}
|
|
36678
|
-
const bankId =
|
|
37985
|
+
const bankId = readString23(json.bank_id);
|
|
36679
37986
|
return bankId ? `bank ${bankId} reachable` : "bank config reachable";
|
|
36680
37987
|
}
|
|
36681
37988
|
async function readActiveMemoryProvider(profileName) {
|
|
@@ -36690,7 +37997,7 @@ async function readActiveMemoryProvider(profileName) {
|
|
|
36690
37997
|
});
|
|
36691
37998
|
const config = raw ? toRecord21(YAML6.parse(raw)) : {};
|
|
36692
37999
|
const memory = toRecord21(config.memory);
|
|
36693
|
-
const provider =
|
|
38000
|
+
const provider = readString23(memory.provider);
|
|
36694
38001
|
if (!provider || provider === "built-in" || provider === "builtin" || provider === "built_in") {
|
|
36695
38002
|
return null;
|
|
36696
38003
|
}
|
|
@@ -36747,7 +38054,7 @@ function stringSetting(key, label, value, editable = true) {
|
|
|
36747
38054
|
return {
|
|
36748
38055
|
key,
|
|
36749
38056
|
label,
|
|
36750
|
-
value:
|
|
38057
|
+
value: readString23(value) ?? "",
|
|
36751
38058
|
editable,
|
|
36752
38059
|
kind: "string"
|
|
36753
38060
|
};
|
|
@@ -36759,7 +38066,7 @@ function secretSetting(key, label, value, configured) {
|
|
|
36759
38066
|
value: "",
|
|
36760
38067
|
editable: true,
|
|
36761
38068
|
kind: "secret",
|
|
36762
|
-
configured: configured || isConfiguredEnvValue(
|
|
38069
|
+
configured: configured || isConfiguredEnvValue(readString23(value))
|
|
36763
38070
|
};
|
|
36764
38071
|
}
|
|
36765
38072
|
function textSetting(key, label, value, editable = true) {
|
|
@@ -36772,7 +38079,7 @@ function textSetting(key, label, value, editable = true) {
|
|
|
36772
38079
|
};
|
|
36773
38080
|
}
|
|
36774
38081
|
function selectSetting(key, label, value, options, editable = true) {
|
|
36775
|
-
const stringValue =
|
|
38082
|
+
const stringValue = readString23(value) ?? options[0] ?? null;
|
|
36776
38083
|
return { key, label, value: stringValue, editable, kind: "select", options };
|
|
36777
38084
|
}
|
|
36778
38085
|
async function readMemoryLimits(profileName) {
|
|
@@ -36845,7 +38152,7 @@ function hashString(value) {
|
|
|
36845
38152
|
function toRecord21(value) {
|
|
36846
38153
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
|
|
36847
38154
|
}
|
|
36848
|
-
function
|
|
38155
|
+
function readString23(value) {
|
|
36849
38156
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
36850
38157
|
}
|
|
36851
38158
|
function readPositiveInteger4(value) {
|
|
@@ -37004,7 +38311,7 @@ function registerProfileMemoryRoutes(router, options) {
|
|
|
37004
38311
|
);
|
|
37005
38312
|
}
|
|
37006
38313
|
function readMemoryTarget(body) {
|
|
37007
|
-
const raw =
|
|
38314
|
+
const raw = readString21(body, "target");
|
|
37008
38315
|
if (raw === "memory" || raw === "user") {
|
|
37009
38316
|
return raw;
|
|
37010
38317
|
}
|
|
@@ -37015,7 +38322,7 @@ function readMemoryTarget(body) {
|
|
|
37015
38322
|
);
|
|
37016
38323
|
}
|
|
37017
38324
|
function readMemoryResetTarget(body) {
|
|
37018
|
-
const raw =
|
|
38325
|
+
const raw = readString21(body, "target") ?? "all";
|
|
37019
38326
|
if (raw === "all" || raw === "memory" || raw === "user") {
|
|
37020
38327
|
return raw;
|
|
37021
38328
|
}
|
|
@@ -37026,7 +38333,7 @@ function readMemoryResetTarget(body) {
|
|
|
37026
38333
|
);
|
|
37027
38334
|
}
|
|
37028
38335
|
function readRequiredMemoryContent(body) {
|
|
37029
|
-
const content =
|
|
38336
|
+
const content = readString21(body, "content") ?? readString21(body, "text");
|
|
37030
38337
|
if (!content) {
|
|
37031
38338
|
throw new LinkHttpError(
|
|
37032
38339
|
400,
|
|
@@ -37037,7 +38344,7 @@ function readRequiredMemoryContent(body) {
|
|
|
37037
38344
|
return content;
|
|
37038
38345
|
}
|
|
37039
38346
|
function readRequiredMemoryMatch(body) {
|
|
37040
|
-
const oldText =
|
|
38347
|
+
const oldText = readString21(body, "old_text") ?? readString21(body, "oldText") ?? readString21(body, "match");
|
|
37041
38348
|
if (!oldText) {
|
|
37042
38349
|
throw new LinkHttpError(
|
|
37043
38350
|
400,
|
|
@@ -37048,7 +38355,7 @@ function readRequiredMemoryMatch(body) {
|
|
|
37048
38355
|
return oldText;
|
|
37049
38356
|
}
|
|
37050
38357
|
function readRequiredMemoryProvider(body) {
|
|
37051
|
-
const provider =
|
|
38358
|
+
const provider = readString21(body, "provider") ?? readString21(body, "provider_id") ?? readString21(body, "providerId");
|
|
37052
38359
|
if (!provider) {
|
|
37053
38360
|
throw new LinkHttpError(
|
|
37054
38361
|
400,
|
|
@@ -37078,7 +38385,7 @@ function readMemorySettingsPatch(body, options = {}) {
|
|
|
37078
38385
|
input.userCharLimit = userCharLimit;
|
|
37079
38386
|
}
|
|
37080
38387
|
}
|
|
37081
|
-
const mode =
|
|
38388
|
+
const mode = readString21(body, "mode");
|
|
37082
38389
|
if (mode) {
|
|
37083
38390
|
input.mode = mode;
|
|
37084
38391
|
}
|
|
@@ -37094,7 +38401,7 @@ function readMemorySettingsPatch(body, options = {}) {
|
|
|
37094
38401
|
if (bankId !== void 0) {
|
|
37095
38402
|
input.bankId = bankId;
|
|
37096
38403
|
}
|
|
37097
|
-
const llmProvider =
|
|
38404
|
+
const llmProvider = readString21(body, "llm_provider") ?? readString21(body, "llmProvider");
|
|
37098
38405
|
if (llmProvider) {
|
|
37099
38406
|
input.llmProvider = llmProvider;
|
|
37100
38407
|
}
|
|
@@ -37130,11 +38437,11 @@ function readMemorySettingsPatch(body, options = {}) {
|
|
|
37130
38437
|
if (autoRetain !== void 0) {
|
|
37131
38438
|
input.autoRetain = autoRetain;
|
|
37132
38439
|
}
|
|
37133
|
-
const memoryMode =
|
|
38440
|
+
const memoryMode = readString21(body, "memory_mode") ?? readString21(body, "memoryMode");
|
|
37134
38441
|
if (memoryMode) {
|
|
37135
38442
|
input.memoryMode = memoryMode;
|
|
37136
38443
|
}
|
|
37137
|
-
const recallBudget =
|
|
38444
|
+
const recallBudget = readString21(body, "recall_budget") ?? readString21(body, "recallBudget");
|
|
37138
38445
|
if (recallBudget) {
|
|
37139
38446
|
input.recallBudget = recallBudget;
|
|
37140
38447
|
}
|
|
@@ -37150,11 +38457,11 @@ function readMemorySettingsPatch(body, options = {}) {
|
|
|
37150
38457
|
if (profileFrequency !== void 0) {
|
|
37151
38458
|
input.profileFrequency = profileFrequency;
|
|
37152
38459
|
}
|
|
37153
|
-
const captureMode =
|
|
38460
|
+
const captureMode = readString21(body, "capture_mode") ?? readString21(body, "captureMode");
|
|
37154
38461
|
if (captureMode) {
|
|
37155
38462
|
input.captureMode = captureMode;
|
|
37156
38463
|
}
|
|
37157
|
-
const searchMode =
|
|
38464
|
+
const searchMode = readString21(body, "search_mode") ?? readString21(body, "searchMode");
|
|
37158
38465
|
if (searchMode) {
|
|
37159
38466
|
input.searchMode = searchMode;
|
|
37160
38467
|
}
|
|
@@ -37188,11 +38495,11 @@ function readMemorySettingsPatch(body, options = {}) {
|
|
|
37188
38495
|
if (aiPeer !== void 0) {
|
|
37189
38496
|
input.aiPeer = aiPeer;
|
|
37190
38497
|
}
|
|
37191
|
-
const recallMode =
|
|
38498
|
+
const recallMode = readString21(body, "recall_mode") ?? readString21(body, "recallMode");
|
|
37192
38499
|
if (recallMode) {
|
|
37193
38500
|
input.recallMode = recallMode;
|
|
37194
38501
|
}
|
|
37195
|
-
const writeFrequency =
|
|
38502
|
+
const writeFrequency = readString21(body, "write_frequency") ?? readString21(body, "writeFrequency");
|
|
37196
38503
|
if (writeFrequency) {
|
|
37197
38504
|
input.writeFrequency = writeFrequency;
|
|
37198
38505
|
}
|
|
@@ -37200,7 +38507,7 @@ function readMemorySettingsPatch(body, options = {}) {
|
|
|
37200
38507
|
if (saveMessages !== void 0) {
|
|
37201
38508
|
input.saveMessages = saveMessages;
|
|
37202
38509
|
}
|
|
37203
|
-
const sessionStrategy =
|
|
38510
|
+
const sessionStrategy = readString21(body, "session_strategy") ?? readString21(body, "sessionStrategy");
|
|
37204
38511
|
if (sessionStrategy) {
|
|
37205
38512
|
input.sessionStrategy = sessionStrategy;
|
|
37206
38513
|
}
|
|
@@ -37480,13 +38787,13 @@ async function readSkillMetadata(input) {
|
|
|
37480
38787
|
const skillDir = path32.dirname(input.skillFile);
|
|
37481
38788
|
const { frontmatter, body } = parseSkillDocument(raw.slice(0, 4e3));
|
|
37482
38789
|
const name = normalizeSkillName(
|
|
37483
|
-
|
|
38790
|
+
readString24(frontmatter.name) ?? path32.basename(skillDir)
|
|
37484
38791
|
);
|
|
37485
38792
|
if (!name) {
|
|
37486
38793
|
return null;
|
|
37487
38794
|
}
|
|
37488
38795
|
const description = normalizeDescription(
|
|
37489
|
-
|
|
38796
|
+
readString24(frontmatter.description) ?? firstBodyDescription(body)
|
|
37490
38797
|
);
|
|
37491
38798
|
const provenance = input.provenance.get(name) ?? {
|
|
37492
38799
|
source: "local",
|
|
@@ -37613,8 +38920,8 @@ async function readHubInstalledSkills(root) {
|
|
|
37613
38920
|
for (const [name, rawEntry] of Object.entries(installed2)) {
|
|
37614
38921
|
const entry = toRecord22(rawEntry);
|
|
37615
38922
|
result.set(normalizeSkillName(name), {
|
|
37616
|
-
source:
|
|
37617
|
-
trust:
|
|
38923
|
+
source: readString24(entry.source) ?? "hub",
|
|
38924
|
+
trust: readString24(entry.trust_level) ?? null
|
|
37618
38925
|
});
|
|
37619
38926
|
}
|
|
37620
38927
|
return result;
|
|
@@ -37698,7 +39005,7 @@ function readStringList6(value) {
|
|
|
37698
39005
|
}
|
|
37699
39006
|
return value.filter((item) => typeof item === "string").map((item) => item.trim()).filter(Boolean);
|
|
37700
39007
|
}
|
|
37701
|
-
function
|
|
39008
|
+
function readString24(value) {
|
|
37702
39009
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
37703
39010
|
}
|
|
37704
39011
|
function toRecord22(value) {
|
|
@@ -38070,7 +39377,7 @@ function registerRunRoutes(router, options) {
|
|
|
38070
39377
|
await authenticateRequest(ctx, paths);
|
|
38071
39378
|
const language = readPreferredLanguage(ctx);
|
|
38072
39379
|
const body = await readJsonBody(ctx.req);
|
|
38073
|
-
const input =
|
|
39380
|
+
const input = readString21(body, "input");
|
|
38074
39381
|
if (!input) {
|
|
38075
39382
|
throw new LinkHttpError(400, "run_input_required", "input is required");
|
|
38076
39383
|
}
|
|
@@ -38078,12 +39385,12 @@ function registerRunRoutes(router, options) {
|
|
|
38078
39385
|
ctx.body = await createHermesRun(
|
|
38079
39386
|
{
|
|
38080
39387
|
input,
|
|
38081
|
-
instructions:
|
|
39388
|
+
instructions: readString21(body, "instructions") ?? void 0,
|
|
38082
39389
|
conversation_history: readConversationHistory(
|
|
38083
39390
|
body.conversation_history ?? body.conversationHistory
|
|
38084
39391
|
),
|
|
38085
|
-
session_id:
|
|
38086
|
-
session_key:
|
|
39392
|
+
session_id: readString21(body, "session_id") ?? readString21(body, "sessionId") ?? void 0,
|
|
39393
|
+
session_key: readString21(body, "session_key") ?? readString21(body, "sessionKey") ?? void 0
|
|
38087
39394
|
},
|
|
38088
39395
|
{ logger, profileName: readOptionalProfileName(body), language }
|
|
38089
39396
|
);
|
|
@@ -38496,20 +39803,20 @@ function normalizeServerReleaseSnapshot(payload) {
|
|
|
38496
39803
|
const remote = toNullableRecord(snapshot.remote);
|
|
38497
39804
|
return {
|
|
38498
39805
|
remote: remote ? normalizeServerRelease(remote) : null,
|
|
38499
|
-
cacheState:
|
|
38500
|
-
issue:
|
|
39806
|
+
cacheState: readString25(snapshot, "cache_state") ?? readString25(snapshot, "cacheState"),
|
|
39807
|
+
issue: readString25(snapshot, "issue")
|
|
38501
39808
|
};
|
|
38502
39809
|
}
|
|
38503
39810
|
function normalizeServerRelease(payload) {
|
|
38504
|
-
const tag =
|
|
38505
|
-
const name =
|
|
39811
|
+
const tag = readString25(payload, "tag");
|
|
39812
|
+
const name = readString25(payload, "name");
|
|
38506
39813
|
return {
|
|
38507
|
-
version:
|
|
39814
|
+
version: readString25(payload, "version") ?? extractSemver(name) ?? extractTagSemver(tag),
|
|
38508
39815
|
tag,
|
|
38509
39816
|
name,
|
|
38510
|
-
releaseUrl:
|
|
38511
|
-
publishedAt:
|
|
38512
|
-
fetchedAt:
|
|
39817
|
+
releaseUrl: readString25(payload, "releaseUrl") ?? readString25(payload, "release_url"),
|
|
39818
|
+
publishedAt: readString25(payload, "publishedAt") ?? readString25(payload, "published_at"),
|
|
39819
|
+
fetchedAt: readString25(payload, "fetchedAt") ?? readString25(payload, "fetched_at") ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
38513
39820
|
};
|
|
38514
39821
|
}
|
|
38515
39822
|
async function readReleaseCache(paths) {
|
|
@@ -38635,7 +39942,7 @@ function isRecentRunningState2(state) {
|
|
|
38635
39942
|
const startedAt = Date.parse(state.started_at);
|
|
38636
39943
|
return Number.isFinite(startedAt) && Date.now() - startedAt < 3e4;
|
|
38637
39944
|
}
|
|
38638
|
-
function
|
|
39945
|
+
function readString25(payload, key) {
|
|
38639
39946
|
const value = payload[key];
|
|
38640
39947
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
38641
39948
|
}
|
|
@@ -38810,12 +40117,12 @@ async function fetchRelayStreamBatchPolicy(serverBaseUrl, options = {}) {
|
|
|
38810
40117
|
}
|
|
38811
40118
|
}
|
|
38812
40119
|
function readRelayStreamBatchPolicy(input) {
|
|
38813
|
-
const record =
|
|
38814
|
-
const body =
|
|
40120
|
+
const record = readRecord2(input);
|
|
40121
|
+
const body = readRecord2(record?.policy) ?? readRecord2(record?.stream_batching) ?? record;
|
|
38815
40122
|
return normalizeRelayStreamBatchPolicy(body);
|
|
38816
40123
|
}
|
|
38817
40124
|
function normalizeRelayStreamBatchPolicy(input) {
|
|
38818
|
-
const record =
|
|
40125
|
+
const record = readRecord2(input);
|
|
38819
40126
|
if (!record) {
|
|
38820
40127
|
return null;
|
|
38821
40128
|
}
|
|
@@ -38829,7 +40136,7 @@ function normalizeRelayStreamBatchPolicy(input) {
|
|
|
38829
40136
|
flushBytes
|
|
38830
40137
|
};
|
|
38831
40138
|
}
|
|
38832
|
-
function
|
|
40139
|
+
function readRecord2(value) {
|
|
38833
40140
|
return value && typeof value === "object" ? value : null;
|
|
38834
40141
|
}
|
|
38835
40142
|
function readInteger5(value) {
|
|
@@ -39698,11 +41005,11 @@ async function mergeLastReportedPublicRoutes(paths, snapshotInput) {
|
|
|
39698
41005
|
const state = await readNetworkReportState(paths);
|
|
39699
41006
|
return {
|
|
39700
41007
|
...snapshotInput,
|
|
39701
|
-
publicIpv4s:
|
|
41008
|
+
publicIpv4s: uniqueStrings3([
|
|
39702
41009
|
...snapshotInput.publicIpv4s,
|
|
39703
41010
|
...state.lastReportedPublicIpv4s
|
|
39704
41011
|
]).slice(0, 2),
|
|
39705
|
-
publicIpv6s:
|
|
41012
|
+
publicIpv6s: uniqueStrings3([
|
|
39706
41013
|
...snapshotInput.publicIpv6s,
|
|
39707
41014
|
...state.lastReportedPublicIpv6s
|
|
39708
41015
|
]).slice(0, 2)
|
|
@@ -39794,7 +41101,7 @@ function sameStringList2(left, right) {
|
|
|
39794
41101
|
}
|
|
39795
41102
|
return left.every((value, index) => value === right[index]);
|
|
39796
41103
|
}
|
|
39797
|
-
function
|
|
41104
|
+
function uniqueStrings3(values) {
|
|
39798
41105
|
return [...new Set(values)];
|
|
39799
41106
|
}
|
|
39800
41107
|
function formatUtcDay(date) {
|
|
@@ -39833,7 +41140,7 @@ async function reportLinkStatusToServer(options = {}) {
|
|
|
39833
41140
|
public_ipv6s: routes.publicIpv6s,
|
|
39834
41141
|
reported_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
39835
41142
|
};
|
|
39836
|
-
const signature = signIdentityPayload(identity,
|
|
41143
|
+
const signature = signIdentityPayload(identity, canonicalJson3(payload));
|
|
39837
41144
|
const fetcher = options.fetchImpl ?? fetch;
|
|
39838
41145
|
const response = await fetcher(
|
|
39839
41146
|
`${config.serverBaseUrl.replace(/\/+$/u, "")}/api/v1/links/${encodeURIComponent(identity.link_id)}/report`,
|
|
@@ -39852,30 +41159,30 @@ async function reportLinkStatusToServer(options = {}) {
|
|
|
39852
41159
|
);
|
|
39853
41160
|
const body = await response.json().catch(() => null);
|
|
39854
41161
|
if (!response.ok || !body) {
|
|
39855
|
-
const message =
|
|
41162
|
+
const message = readErrorMessage5(body) ?? `HermesPilot Server request failed with HTTP ${response.status}`;
|
|
39856
41163
|
throw new LinkHttpError(response.status, "server_request_failed", message);
|
|
39857
41164
|
}
|
|
39858
41165
|
await markNetworkStatusReported(paths, routes);
|
|
39859
41166
|
return body;
|
|
39860
41167
|
}
|
|
39861
|
-
function
|
|
39862
|
-
return JSON.stringify(
|
|
41168
|
+
function canonicalJson3(value) {
|
|
41169
|
+
return JSON.stringify(sortJsonValue3(value));
|
|
39863
41170
|
}
|
|
39864
|
-
function
|
|
41171
|
+
function sortJsonValue3(value) {
|
|
39865
41172
|
if (Array.isArray(value)) {
|
|
39866
|
-
return value.map(
|
|
41173
|
+
return value.map(sortJsonValue3);
|
|
39867
41174
|
}
|
|
39868
41175
|
if (value && typeof value === "object") {
|
|
39869
41176
|
const record = value;
|
|
39870
41177
|
const sorted = {};
|
|
39871
41178
|
for (const key of Object.keys(record).sort()) {
|
|
39872
|
-
sorted[key] =
|
|
41179
|
+
sorted[key] = sortJsonValue3(record[key]);
|
|
39873
41180
|
}
|
|
39874
41181
|
return sorted;
|
|
39875
41182
|
}
|
|
39876
41183
|
return value;
|
|
39877
41184
|
}
|
|
39878
|
-
function
|
|
41185
|
+
function readErrorMessage5(payload) {
|
|
39879
41186
|
if (typeof payload !== "object" || payload === null) {
|
|
39880
41187
|
return null;
|
|
39881
41188
|
}
|
|
@@ -39895,20 +41202,23 @@ function startLanIpMonitor(options) {
|
|
|
39895
41202
|
let running = false;
|
|
39896
41203
|
let closed = false;
|
|
39897
41204
|
let current = Promise.resolve();
|
|
39898
|
-
const check =
|
|
41205
|
+
const check = (context = {}) => {
|
|
39899
41206
|
if (running || closed) {
|
|
39900
|
-
return;
|
|
41207
|
+
return current;
|
|
39901
41208
|
}
|
|
39902
41209
|
running = true;
|
|
39903
|
-
|
|
39904
|
-
|
|
39905
|
-
|
|
39906
|
-
|
|
39907
|
-
|
|
39908
|
-
|
|
39909
|
-
|
|
39910
|
-
|
|
39911
|
-
|
|
41210
|
+
current = (async () => {
|
|
41211
|
+
try {
|
|
41212
|
+
await checkLanIpChange(options, context);
|
|
41213
|
+
} catch (error) {
|
|
41214
|
+
void options.logger.warn("lan_ip_monitor_failed", {
|
|
41215
|
+
error: error instanceof Error ? error.message : String(error)
|
|
41216
|
+
});
|
|
41217
|
+
} finally {
|
|
41218
|
+
running = false;
|
|
41219
|
+
}
|
|
41220
|
+
})();
|
|
41221
|
+
return current;
|
|
39912
41222
|
};
|
|
39913
41223
|
current = check({ forceReport: true, publishToRelay: true, observePublicRoute: true });
|
|
39914
41224
|
const timer = setInterval(() => {
|
|
@@ -40622,7 +41932,8 @@ async function startDaemonProcess(paths = resolveRuntimePaths()) {
|
|
|
40622
41932
|
const child = spawn5(process.execPath, [scriptPath, "daemon-supervisor"], {
|
|
40623
41933
|
detached: true,
|
|
40624
41934
|
stdio: "ignore",
|
|
40625
|
-
env: process.env
|
|
41935
|
+
env: process.env,
|
|
41936
|
+
windowsHide: true
|
|
40626
41937
|
});
|
|
40627
41938
|
child.unref();
|
|
40628
41939
|
for (let index = 0; index < 12; index += 1) {
|
|
@@ -40675,7 +41986,8 @@ async function runDaemonSupervisor(paths = resolveRuntimePaths()) {
|
|
|
40675
41986
|
const startedAt = Date.now();
|
|
40676
41987
|
child = spawn5(process.execPath, [scriptPath, "daemon", "--foreground"], {
|
|
40677
41988
|
stdio: ["ignore", "pipe", "pipe"],
|
|
40678
|
-
env: process.env
|
|
41989
|
+
env: process.env,
|
|
41990
|
+
windowsHide: true
|
|
40679
41991
|
});
|
|
40680
41992
|
const childPid = child.pid ?? null;
|
|
40681
41993
|
child.stdout?.on("data", write);
|
|
@@ -41416,16 +42728,16 @@ function normalizeServerSnapshot(payload) {
|
|
|
41416
42728
|
if (!policy) {
|
|
41417
42729
|
return {
|
|
41418
42730
|
remote: null,
|
|
41419
|
-
issue:
|
|
42731
|
+
issue: readString26(snapshot, "issue")
|
|
41420
42732
|
};
|
|
41421
42733
|
}
|
|
41422
42734
|
const release = toNullableRecord2(snapshot.release);
|
|
41423
|
-
const currentVersion =
|
|
41424
|
-
const minSafeVersion =
|
|
42735
|
+
const currentVersion = readString26(policy, "current_version") ?? readString26(policy, "currentVersion");
|
|
42736
|
+
const minSafeVersion = readString26(policy, "min_safe_version") ?? readString26(policy, "minSafeVersion");
|
|
41425
42737
|
if (!currentVersion) {
|
|
41426
42738
|
return {
|
|
41427
42739
|
remote: null,
|
|
41428
|
-
issue:
|
|
42740
|
+
issue: readString26(snapshot, "issue")
|
|
41429
42741
|
};
|
|
41430
42742
|
}
|
|
41431
42743
|
return {
|
|
@@ -41433,10 +42745,10 @@ function normalizeServerSnapshot(payload) {
|
|
|
41433
42745
|
current_version: currentVersion,
|
|
41434
42746
|
min_safe_version: minSafeVersion,
|
|
41435
42747
|
target_version: currentVersion,
|
|
41436
|
-
release_url: release ?
|
|
41437
|
-
published_at: release ?
|
|
42748
|
+
release_url: release ? readString26(release, "release_url") ?? readString26(release, "releaseUrl") : null,
|
|
42749
|
+
published_at: release ? readString26(release, "published_at") ?? readString26(release, "publishedAt") : null
|
|
41438
42750
|
},
|
|
41439
|
-
issue:
|
|
42751
|
+
issue: readString26(snapshot, "issue")
|
|
41440
42752
|
};
|
|
41441
42753
|
}
|
|
41442
42754
|
async function fetchCurrentLinkReleaseFromServer(options, fetcher, channel) {
|
|
@@ -41850,7 +43162,7 @@ function toRecord25(value) {
|
|
|
41850
43162
|
function toNullableRecord2(value) {
|
|
41851
43163
|
return typeof value === "object" && value !== null ? value : null;
|
|
41852
43164
|
}
|
|
41853
|
-
function
|
|
43165
|
+
function readString26(payload, key) {
|
|
41854
43166
|
const value = payload[key];
|
|
41855
43167
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
41856
43168
|
}
|
|
@@ -41930,7 +43242,7 @@ async function postJson(fetcher, url, token, body) {
|
|
|
41930
43242
|
}
|
|
41931
43243
|
const payload = await response.json().catch(() => null);
|
|
41932
43244
|
if (!response.ok) {
|
|
41933
|
-
const message =
|
|
43245
|
+
const message = readErrorMessage6(payload) ?? `Relay request failed with HTTP ${response.status}`;
|
|
41934
43246
|
throw new Error(message);
|
|
41935
43247
|
}
|
|
41936
43248
|
if (!payload) {
|
|
@@ -41938,7 +43250,7 @@ async function postJson(fetcher, url, token, body) {
|
|
|
41938
43250
|
}
|
|
41939
43251
|
return payload;
|
|
41940
43252
|
}
|
|
41941
|
-
function
|
|
43253
|
+
function readErrorMessage6(payload) {
|
|
41942
43254
|
if (typeof payload !== "object" || payload === null) {
|
|
41943
43255
|
return null;
|
|
41944
43256
|
}
|
|
@@ -42272,12 +43584,12 @@ async function patchServerJson(serverBaseUrl, path37, token, body, options) {
|
|
|
42272
43584
|
async function readJsonResponse2(response) {
|
|
42273
43585
|
const payload = await response.json().catch(() => null);
|
|
42274
43586
|
if (!response.ok || !payload) {
|
|
42275
|
-
const message =
|
|
43587
|
+
const message = readErrorMessage7(payload) ?? `HermesPilot Server request failed with HTTP ${response.status}`;
|
|
42276
43588
|
throw new LinkHttpError(response.status, "server_request_failed", message);
|
|
42277
43589
|
}
|
|
42278
43590
|
return payload;
|
|
42279
43591
|
}
|
|
42280
|
-
function
|
|
43592
|
+
function readErrorMessage7(payload) {
|
|
42281
43593
|
if (typeof payload !== "object" || payload === null) {
|
|
42282
43594
|
return null;
|
|
42283
43595
|
}
|
|
@@ -42396,14 +43708,15 @@ function registerSystemRoutes(router, options) {
|
|
|
42396
43708
|
profile_skills: true,
|
|
42397
43709
|
profile_memory: true,
|
|
42398
43710
|
hermes_updates: true,
|
|
42399
|
-
app_push_notification_events: true
|
|
43711
|
+
app_push_notification_events: true,
|
|
43712
|
+
live_activity_progress_events: true
|
|
42400
43713
|
}
|
|
42401
43714
|
};
|
|
42402
43715
|
});
|
|
42403
43716
|
router.post("/api/v1/pairing/claim", async (ctx) => {
|
|
42404
43717
|
const body = await readJsonBody(ctx.req);
|
|
42405
|
-
const sessionId =
|
|
42406
|
-
const claimToken =
|
|
43718
|
+
const sessionId = readString21(body, "session_id") ?? readString21(body, "sessionId");
|
|
43719
|
+
const claimToken = readString21(body, "claim_token") ?? readString21(body, "claimToken");
|
|
42407
43720
|
if (!sessionId || !claimToken) {
|
|
42408
43721
|
throw new LinkHttpError(
|
|
42409
43722
|
400,
|
|
@@ -42414,10 +43727,10 @@ function registerSystemRoutes(router, options) {
|
|
|
42414
43727
|
const claimed = await claimPairing({
|
|
42415
43728
|
sessionId,
|
|
42416
43729
|
claimToken,
|
|
42417
|
-
deviceLabel:
|
|
42418
|
-
devicePlatform:
|
|
42419
|
-
deviceModel:
|
|
42420
|
-
appInstanceId:
|
|
43730
|
+
deviceLabel: readString21(body, "device_label") ?? readString21(body, "deviceLabel") ?? "HermesPilot App",
|
|
43731
|
+
devicePlatform: readString21(body, "device_platform") ?? readString21(body, "devicePlatform") ?? "unknown",
|
|
43732
|
+
deviceModel: readString21(body, "device_model") ?? readString21(body, "deviceModel"),
|
|
43733
|
+
appInstanceId: readString21(body, "app_instance_id") ?? readString21(body, "appInstanceId"),
|
|
42421
43734
|
paths
|
|
42422
43735
|
});
|
|
42423
43736
|
ctx.body = claimed;
|
|
@@ -42497,9 +43810,9 @@ function registerSystemRoutes(router, options) {
|
|
|
42497
43810
|
const body = await readJsonBody(ctx.req);
|
|
42498
43811
|
const session = await createDeviceSession(
|
|
42499
43812
|
{
|
|
42500
|
-
label:
|
|
42501
|
-
platform:
|
|
42502
|
-
model:
|
|
43813
|
+
label: readString21(body, "device_label") ?? readString21(body, "deviceLabel") ?? "HermesPilot App",
|
|
43814
|
+
platform: readString21(body, "device_platform") ?? readString21(body, "devicePlatform") ?? "unknown",
|
|
43815
|
+
model: readString21(body, "device_model") ?? readString21(body, "deviceModel"),
|
|
42503
43816
|
appInstanceId: auth.appInstanceId
|
|
42504
43817
|
},
|
|
42505
43818
|
paths
|
|
@@ -42528,7 +43841,7 @@ function registerSystemRoutes(router, options) {
|
|
|
42528
43841
|
});
|
|
42529
43842
|
router.post("/api/v1/auth/refresh", async (ctx) => {
|
|
42530
43843
|
const body = await readJsonBody(ctx.req);
|
|
42531
|
-
const refreshToken =
|
|
43844
|
+
const refreshToken = readString21(body, "refresh_token") ?? readString21(body, "refreshToken");
|
|
42532
43845
|
if (!refreshToken) {
|
|
42533
43846
|
throw new LinkHttpError(
|
|
42534
43847
|
400,
|
|
@@ -42539,10 +43852,10 @@ function registerSystemRoutes(router, options) {
|
|
|
42539
43852
|
const session = await refreshDeviceSession(
|
|
42540
43853
|
refreshToken,
|
|
42541
43854
|
{
|
|
42542
|
-
appInstanceId:
|
|
42543
|
-
label:
|
|
42544
|
-
platform:
|
|
42545
|
-
model:
|
|
43855
|
+
appInstanceId: readString21(body, "app_instance_id") ?? readString21(body, "appInstanceId"),
|
|
43856
|
+
label: readString21(body, "device_label") ?? readString21(body, "deviceLabel"),
|
|
43857
|
+
platform: readString21(body, "device_platform") ?? readString21(body, "devicePlatform"),
|
|
43858
|
+
model: readString21(body, "device_model") ?? readString21(body, "deviceModel")
|
|
42546
43859
|
},
|
|
42547
43860
|
paths
|
|
42548
43861
|
);
|
|
@@ -42561,7 +43874,7 @@ function registerSystemRoutes(router, options) {
|
|
|
42561
43874
|
});
|
|
42562
43875
|
router.post("/api/v1/auth/logout", async (ctx) => {
|
|
42563
43876
|
const body = await readJsonBody(ctx.req);
|
|
42564
|
-
const refreshToken =
|
|
43877
|
+
const refreshToken = readString21(body, "refresh_token") ?? readString21(body, "refreshToken");
|
|
42565
43878
|
if (refreshToken) {
|
|
42566
43879
|
await revokeDeviceRefreshToken(refreshToken, paths);
|
|
42567
43880
|
}
|
|
@@ -42802,7 +44115,7 @@ function registerSystemRoutes(router, options) {
|
|
|
42802
44115
|
router.patch("/api/v1/devices/:deviceId", async (ctx) => {
|
|
42803
44116
|
const auth = await authenticateRequest(ctx, paths);
|
|
42804
44117
|
const body = await readJsonBody(ctx.req);
|
|
42805
|
-
const label =
|
|
44118
|
+
const label = readString21(body, "label") ?? readString21(body, "device_label");
|
|
42806
44119
|
if (!label) {
|
|
42807
44120
|
throw new LinkHttpError(
|
|
42808
44121
|
400,
|
|
@@ -42846,7 +44159,7 @@ function isActiveCronJob(job) {
|
|
|
42846
44159
|
if (!enabled) {
|
|
42847
44160
|
return false;
|
|
42848
44161
|
}
|
|
42849
|
-
const state =
|
|
44162
|
+
const state = readString21(job, "state")?.toLowerCase();
|
|
42850
44163
|
return !["paused", "disabled", "completed", "deleted"].includes(state ?? "");
|
|
42851
44164
|
}
|
|
42852
44165
|
function filterLogsWithinHours(logs, hours, now = Date.now()) {
|
|
@@ -42890,8 +44203,8 @@ function registerWorkspaceRoutes(router, options) {
|
|
|
42890
44203
|
ctx.body = {
|
|
42891
44204
|
ok: true,
|
|
42892
44205
|
workspace: await conversations.createWorkspace({
|
|
42893
|
-
name:
|
|
42894
|
-
icon:
|
|
44206
|
+
name: readString21(body, "name") ?? "",
|
|
44207
|
+
icon: readString21(body, "icon") ?? void 0
|
|
42895
44208
|
})
|
|
42896
44209
|
};
|
|
42897
44210
|
});
|
|
@@ -42901,8 +44214,8 @@ function registerWorkspaceRoutes(router, options) {
|
|
|
42901
44214
|
ctx.body = {
|
|
42902
44215
|
ok: true,
|
|
42903
44216
|
workspace: await conversations.renameWorkspace(ctx.params.workspaceId, {
|
|
42904
|
-
name:
|
|
42905
|
-
icon:
|
|
44217
|
+
name: readString21(body, "name") ?? "",
|
|
44218
|
+
icon: readString21(body, "icon") ?? void 0
|
|
42906
44219
|
})
|
|
42907
44220
|
};
|
|
42908
44221
|
});
|
|
@@ -42985,8 +44298,8 @@ function registerLinkUpdateRoutes(router, options) {
|
|
|
42985
44298
|
ctx.body = await startLinkUpdate({
|
|
42986
44299
|
paths,
|
|
42987
44300
|
logger,
|
|
42988
|
-
channel:
|
|
42989
|
-
targetVersion:
|
|
44301
|
+
channel: readString21(body, "channel"),
|
|
44302
|
+
targetVersion: readString21(body, "target_version") ?? readString21(body, "targetVersion")
|
|
42990
44303
|
});
|
|
42991
44304
|
});
|
|
42992
44305
|
router.get("/api/v1/link/update/events", async (ctx) => {
|
|
@@ -43016,7 +44329,7 @@ import QRCode from "qrcode";
|
|
|
43016
44329
|
function registerPairingRoutes(router, options) {
|
|
43017
44330
|
const { paths } = options;
|
|
43018
44331
|
router.get("/pair", async (ctx) => {
|
|
43019
|
-
const sessionId =
|
|
44332
|
+
const sessionId = readString21(ctx.query, "session_id");
|
|
43020
44333
|
if (!sessionId) {
|
|
43021
44334
|
throw new LinkHttpError(400, "pairing_session_required", "session_id is required");
|
|
43022
44335
|
}
|
|
@@ -43041,7 +44354,7 @@ function registerPairingRoutes(router, options) {
|
|
|
43041
44354
|
ctx.body = page;
|
|
43042
44355
|
});
|
|
43043
44356
|
router.get("/api/v1/pairing/session", async (ctx) => {
|
|
43044
|
-
const sessionId =
|
|
44357
|
+
const sessionId = readString21(ctx.query, "session_id");
|
|
43045
44358
|
if (!sessionId) {
|
|
43046
44359
|
throw new LinkHttpError(400, "pairing_session_required", "session_id is required");
|
|
43047
44360
|
}
|
|
@@ -43511,7 +44824,7 @@ function registerInternalRoutes(router, options) {
|
|
|
43511
44824
|
router.post("/internal/deliver", async (ctx) => {
|
|
43512
44825
|
assertLoopbackRequest(ctx.req);
|
|
43513
44826
|
const body = await readJsonBody(ctx.req);
|
|
43514
|
-
const stagingDir =
|
|
44827
|
+
const stagingDir = readString21(body, "staging_dir") ?? readString21(body, "stagingDir");
|
|
43515
44828
|
if (!stagingDir) {
|
|
43516
44829
|
throw new LinkHttpError(
|
|
43517
44830
|
400,
|
|
@@ -43530,9 +44843,9 @@ function registerInternalRoutes(router, options) {
|
|
|
43530
44843
|
ctx.body = {
|
|
43531
44844
|
ok: true,
|
|
43532
44845
|
...await options.conversations.deliverFilesFromTool({
|
|
43533
|
-
profile:
|
|
43534
|
-
taskId:
|
|
43535
|
-
sessionId:
|
|
44846
|
+
profile: readString21(body, "profile") ?? readString21(body, "profile_name") ?? readString21(body, "profileName") ?? void 0,
|
|
44847
|
+
taskId: readString21(body, "task_id") ?? readString21(body, "taskId") ?? void 0,
|
|
44848
|
+
sessionId: readString21(body, "session_id") ?? readString21(body, "sessionId") ?? void 0,
|
|
43536
44849
|
files: body.files ?? body.file ?? body.path
|
|
43537
44850
|
})
|
|
43538
44851
|
};
|
|
@@ -43550,6 +44863,178 @@ function assertLoopbackRequest(request) {
|
|
|
43550
44863
|
);
|
|
43551
44864
|
}
|
|
43552
44865
|
|
|
44866
|
+
// src/http/routes/live-activities.ts
|
|
44867
|
+
var APP_INSTANCE_ID_PATTERN = /^appi_[A-Za-z0-9_-]{16,96}$/u;
|
|
44868
|
+
var LINK_ID_PATTERN = /^link_[A-Za-z0-9_-]{8,128}$/u;
|
|
44869
|
+
var CONVERSATION_ID_PATTERN2 = /^conv_[A-Za-z0-9_-]{8,128}$/u;
|
|
44870
|
+
var ACTIVITY_ID_PATTERN = /^[A-Za-z0-9._:-]{3,128}$/u;
|
|
44871
|
+
var TARGET_ID_PATTERN = /^[A-Za-z0-9._:-]{3,128}$/u;
|
|
44872
|
+
var RUN_ID_PATTERN = /^run_[A-Za-z0-9_-]{8,128}$/u;
|
|
44873
|
+
var OPERATION_ID_PATTERN = /^[A-Za-z0-9._:-]{3,128}$/u;
|
|
44874
|
+
var APNS_TOKEN_PATTERN = /^[0-9a-fA-F]{32,512}$/u;
|
|
44875
|
+
function registerLiveActivityRoutes(router, options) {
|
|
44876
|
+
const { paths } = options;
|
|
44877
|
+
router.get("/api/v1/live-activities/restore-state", async (ctx) => {
|
|
44878
|
+
const auth = await authenticateRequest(ctx, paths);
|
|
44879
|
+
const query = ctx.query;
|
|
44880
|
+
const targetKind = readTargetKindValue(
|
|
44881
|
+
readQueryValue(query, "target_kind") ?? readQueryValue(query, "targetKind")
|
|
44882
|
+
);
|
|
44883
|
+
const appInstanceId = auth.appInstanceId ?? readRequiredQueryPattern(query, ["app_instance_id", "appInstanceId"], APP_INSTANCE_ID_PATTERN, "app_instance_id_invalid");
|
|
44884
|
+
const state = await findLiveActivityRestoreState(paths, {
|
|
44885
|
+
accountId: auth.accountId ?? null,
|
|
44886
|
+
appInstanceId,
|
|
44887
|
+
conversationId: readRequiredQueryPattern(query, ["conversation_id", "conversationId"], CONVERSATION_ID_PATTERN2, "conversation_id_invalid"),
|
|
44888
|
+
targetKind,
|
|
44889
|
+
targetId: readRequiredQueryPattern(query, ["target_id", "targetId"], TARGET_ID_PATTERN, "target_id_invalid")
|
|
44890
|
+
});
|
|
44891
|
+
ctx.body = {
|
|
44892
|
+
ok: true,
|
|
44893
|
+
state: state ? {
|
|
44894
|
+
phase: state.phase,
|
|
44895
|
+
status_label: state.statusLabel,
|
|
44896
|
+
progress_text: state.progressText,
|
|
44897
|
+
detail_text: state.detailText ?? null,
|
|
44898
|
+
updated_at: state.updatedAt ?? null
|
|
44899
|
+
} : null
|
|
44900
|
+
};
|
|
44901
|
+
});
|
|
44902
|
+
router.post("/api/v1/live-activities", async (ctx) => {
|
|
44903
|
+
const auth = await authenticateRequest(ctx, paths);
|
|
44904
|
+
const body = await readJsonBody(ctx.req);
|
|
44905
|
+
const targetKind = readTargetKind(body);
|
|
44906
|
+
const runId = normalizePattern(readString21(body, "run_id") ?? readString21(body, "runId"), RUN_ID_PATTERN);
|
|
44907
|
+
const operationId = normalizePattern(
|
|
44908
|
+
readString21(body, "operation_id") ?? readString21(body, "operationId"),
|
|
44909
|
+
OPERATION_ID_PATTERN
|
|
44910
|
+
);
|
|
44911
|
+
if (targetKind === "run" && !runId) {
|
|
44912
|
+
throw new LinkHttpError(400, "run_id_required", "run_id is required for run Live Activity");
|
|
44913
|
+
}
|
|
44914
|
+
if (targetKind === "context_compression" && !operationId) {
|
|
44915
|
+
throw new LinkHttpError(400, "operation_id_required", "operation_id is required for context compression Live Activity");
|
|
44916
|
+
}
|
|
44917
|
+
const activity = await upsertLiveActivity(paths, {
|
|
44918
|
+
activityId: readRequiredPattern(body, ["activity_id", "activityId"], ACTIVITY_ID_PATTERN, "activity_id_invalid"),
|
|
44919
|
+
activityToken: readRequiredPattern(body, ["activity_token", "activityToken"], APNS_TOKEN_PATTERN, "activity_token_invalid").toLowerCase(),
|
|
44920
|
+
apnsEnvironment: readApnsEnvironment(body),
|
|
44921
|
+
apnsTopic: readRequiredString(body, ["apns_topic", "apnsTopic"], "apns_topic_required").slice(0, 160),
|
|
44922
|
+
linkId: readRequiredPattern(body, ["link_id", "linkId"], LINK_ID_PATTERN, "link_id_invalid"),
|
|
44923
|
+
accountId: auth.accountId ?? null,
|
|
44924
|
+
appInstanceId: auth.appInstanceId ?? readRequiredPattern(body, ["app_instance_id", "appInstanceId"], APP_INSTANCE_ID_PATTERN, "app_instance_id_invalid"),
|
|
44925
|
+
conversationId: readRequiredPattern(body, ["conversation_id", "conversationId"], CONVERSATION_ID_PATTERN2, "conversation_id_invalid"),
|
|
44926
|
+
conversationTitle: readOptionalString2(body, ["conversation_title", "conversationTitle", "title"], 80),
|
|
44927
|
+
targetKind,
|
|
44928
|
+
targetId: readRequiredPattern(body, ["target_id", "targetId"], TARGET_ID_PATTERN, "target_id_invalid"),
|
|
44929
|
+
runId: targetKind === "run" ? runId : null,
|
|
44930
|
+
operationId: targetKind === "context_compression" ? operationId : null,
|
|
44931
|
+
language: readLanguage2(body),
|
|
44932
|
+
privacyLevel: readPrivacyLevel(body),
|
|
44933
|
+
startedAt: readIso(body, ["started_at", "startedAt"]) ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
44934
|
+
});
|
|
44935
|
+
ctx.status = 201;
|
|
44936
|
+
ctx.body = {
|
|
44937
|
+
ok: true,
|
|
44938
|
+
activity_id: activity.activityId,
|
|
44939
|
+
status: activity.status
|
|
44940
|
+
};
|
|
44941
|
+
});
|
|
44942
|
+
router.post("/api/v1/live-activities/:activityId/end", async (ctx) => {
|
|
44943
|
+
await authenticateRequest(ctx, paths);
|
|
44944
|
+
const found = await endLiveActivity(paths, ctx.params.activityId);
|
|
44945
|
+
ctx.status = found ? 200 : 404;
|
|
44946
|
+
ctx.body = {
|
|
44947
|
+
ok: found,
|
|
44948
|
+
activity_id: ctx.params.activityId,
|
|
44949
|
+
status: found ? "ended" : "not_found"
|
|
44950
|
+
};
|
|
44951
|
+
});
|
|
44952
|
+
}
|
|
44953
|
+
function readTargetKind(body) {
|
|
44954
|
+
const value = readString21(body, "target_kind") ?? readString21(body, "targetKind");
|
|
44955
|
+
return readTargetKindValue(value);
|
|
44956
|
+
}
|
|
44957
|
+
function readTargetKindValue(value) {
|
|
44958
|
+
if (value === "run" || value === "context_compression") {
|
|
44959
|
+
return value;
|
|
44960
|
+
}
|
|
44961
|
+
throw new LinkHttpError(400, "target_kind_invalid", "target_kind must be run or context_compression");
|
|
44962
|
+
}
|
|
44963
|
+
function readRequiredQueryPattern(query, keys, pattern, code) {
|
|
44964
|
+
for (const key of keys) {
|
|
44965
|
+
const value = readQueryValue(query, key);
|
|
44966
|
+
if (value && pattern.test(value)) {
|
|
44967
|
+
return value;
|
|
44968
|
+
}
|
|
44969
|
+
}
|
|
44970
|
+
throw new LinkHttpError(400, code, `${keys[0]} is invalid`);
|
|
44971
|
+
}
|
|
44972
|
+
function readQueryValue(query, key) {
|
|
44973
|
+
return readQueryString(query[key]) ?? null;
|
|
44974
|
+
}
|
|
44975
|
+
function readApnsEnvironment(body) {
|
|
44976
|
+
const value = readString21(body, "apns_environment") ?? readString21(body, "apnsEnvironment");
|
|
44977
|
+
if (value === "sandbox" || value === "production") {
|
|
44978
|
+
return value;
|
|
44979
|
+
}
|
|
44980
|
+
throw new LinkHttpError(400, "apns_environment_invalid", "apns_environment must be sandbox or production");
|
|
44981
|
+
}
|
|
44982
|
+
function readLanguage2(body) {
|
|
44983
|
+
const value = readString21(body, "language") ?? readString21(body, "preferred_language") ?? readString21(body, "preferredLanguage");
|
|
44984
|
+
if (!value) {
|
|
44985
|
+
return null;
|
|
44986
|
+
}
|
|
44987
|
+
return value.toLowerCase().startsWith("zh") ? "zh" : "en";
|
|
44988
|
+
}
|
|
44989
|
+
function readPrivacyLevel(body) {
|
|
44990
|
+
const value = readString21(body, "privacy_level") ?? readString21(body, "privacyLevel");
|
|
44991
|
+
if (value === "minimal" || value === "detailed") {
|
|
44992
|
+
return value;
|
|
44993
|
+
}
|
|
44994
|
+
return "summary";
|
|
44995
|
+
}
|
|
44996
|
+
function readRequiredString(body, keys, code) {
|
|
44997
|
+
for (const key of keys) {
|
|
44998
|
+
const value = readString21(body, key);
|
|
44999
|
+
if (value) {
|
|
45000
|
+
return value;
|
|
45001
|
+
}
|
|
45002
|
+
}
|
|
45003
|
+
throw new LinkHttpError(400, code, `${keys[0]} is required`);
|
|
45004
|
+
}
|
|
45005
|
+
function readRequiredPattern(body, keys, pattern, code) {
|
|
45006
|
+
const value = readRequiredString(body, keys, code);
|
|
45007
|
+
if (!pattern.test(value)) {
|
|
45008
|
+
throw new LinkHttpError(400, code, `${keys[0]} is invalid`);
|
|
45009
|
+
}
|
|
45010
|
+
return value;
|
|
45011
|
+
}
|
|
45012
|
+
function readOptionalString2(body, keys, maxLength) {
|
|
45013
|
+
for (const key of keys) {
|
|
45014
|
+
const value = readString21(body, key);
|
|
45015
|
+
if (value) {
|
|
45016
|
+
return value.slice(0, maxLength);
|
|
45017
|
+
}
|
|
45018
|
+
}
|
|
45019
|
+
return null;
|
|
45020
|
+
}
|
|
45021
|
+
function normalizePattern(value, pattern) {
|
|
45022
|
+
return value && pattern.test(value) ? value : null;
|
|
45023
|
+
}
|
|
45024
|
+
function readIso(body, keys) {
|
|
45025
|
+
for (const key of keys) {
|
|
45026
|
+
const value = readString21(body, key);
|
|
45027
|
+
if (!value) {
|
|
45028
|
+
continue;
|
|
45029
|
+
}
|
|
45030
|
+
const timestamp = Date.parse(value);
|
|
45031
|
+
if (Number.isFinite(timestamp)) {
|
|
45032
|
+
return new Date(timestamp).toISOString();
|
|
45033
|
+
}
|
|
45034
|
+
}
|
|
45035
|
+
return null;
|
|
45036
|
+
}
|
|
45037
|
+
|
|
43553
45038
|
// src/http/app.ts
|
|
43554
45039
|
async function createApp(options = {}) {
|
|
43555
45040
|
const paths = options.paths ?? resolveRuntimePaths();
|
|
@@ -43607,6 +45092,7 @@ async function createApp(options = {}) {
|
|
|
43607
45092
|
syncCronDeliveries
|
|
43608
45093
|
});
|
|
43609
45094
|
registerWorkspaceRoutes(router, { paths, conversations });
|
|
45095
|
+
registerLiveActivityRoutes(router, { paths });
|
|
43610
45096
|
registerConversationRoutes(router, { paths, logger, conversations });
|
|
43611
45097
|
registerRunRoutes(router, { paths, logger, conversations });
|
|
43612
45098
|
registerProfileRoutes(router, { paths, logger, conversations });
|