@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.
@@ -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 createHash7, randomUUID as randomUUID12 } from "crypto";
8
- import path25 from "path";
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}. File: {path}",
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\u6587\u4EF6\uFF1A{path}",
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\u6587\u4EF6\uFF1A{path}",
1793
- "autostart.status.disabled": "\u5F00\u673A\u81EA\u542F\uFF1A\u672A\u542F\u7528\u3002\u65B9\u5F0F\uFF1A{method}\uFF0C\u6587\u4EF6\uFF1A{path}",
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\u6587\u4EF6\uFF1A{path}",
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 ?? (sourceApiKey ? buildApiKeyEnvName(source.model.providerName, source.model.id) : void 0);
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 ? buildApiKeyEnvName(normalized.providerName, normalized.id) : void 0);
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 providerKey = requestedProviderKey ?? findProviderConfigKeyByVisibleEndpoint(providers, {
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 apiKey = input.apiKey?.trim();
2981
- const keyEnv = input.keyEnv?.trim() || existingKeyEnv || (apiKey ? buildApiKeyEnvName(providerName, "PROVIDER") : void 0);
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.7.9";
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 = createHash("sha256").update(input.runId).update("\0").update(input.messageId).update("\0").update(input.command).update("\0").update(input.patternKey ?? "").digest("hex").slice(0, 24);
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 createHash2 } from "crypto";
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 = createHash2("sha256").update(input.runId).update("\0").update(input.messageId).update("\0").update(input.kind).update("\0").update(input.hermesRequestId).digest("hex").slice(0, 24);
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 createHash3 } from "crypto";
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 createHash3("sha256").update(resolveMediaSourcePath(sourcePath)).digest("hex").slice(0, 32);
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
- var PROFILE_NAME_PATTERN3 = /^[a-zA-Z0-9._-]{1,64}$/u;
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 = normalizeProfileName3(profileName);
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 = normalizeProfileName3(
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 = normalizeProfileName3(
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 stat7(profilePath).then((value) => value.isDirectory()).catch((error) => {
12835
- if (isNodeError7(error, "ENOENT")) {
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 isNodeError7(error, code) {
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).catch(
13299
- () => null
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(runtime.profileName, model);
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 path12 from "path";
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 path12.join(
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 path12.join(paths.conversationsDir, conversationId, "delivery-staging");
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 path13 from "path";
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 path13.join(this.paths.indexesDir, "conversation-archive-plans");
14764
+ return path14.join(this.paths.indexesDir, "conversation-archive-plans");
13871
14765
  }
13872
14766
  planPath(planId) {
13873
- return path13.join(this.plansDir(), `${planId}.json`);
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 path14 from "path";
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 path14.join(this.paths.indexesDir, "conversation-clear-plans");
14829
+ return path15.join(this.paths.indexesDir, "conversation-clear-plans");
13936
14830
  }
13937
14831
  planPath(planId) {
13938
- return path14.join(this.plansDir(), `${planId}.json`);
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 stat8 } from "fs/promises";
14793
- import path15 from "path";
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 = path15.join(
15694
+ const dbPath = path16.join(
14801
15695
  resolveHermesProfileDir(resolvedProfileName),
14802
15696
  "state.db"
14803
15697
  );
14804
- const exists = await stat8(dbPath).then((value) => value.isFile()).catch((error) => {
14805
- if (isNodeError8(error, "ENOENT")) {
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 = path15.join(
15715
+ const dbPath = path16.join(
14822
15716
  resolveHermesProfileDir(resolvedProfileName),
14823
15717
  "state.db"
14824
15718
  );
14825
- const exists = await stat8(dbPath).then((value) => value.isFile()).catch((error) => {
14826
- if (isNodeError8(error, "ENOENT")) {
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 isNodeError8(error, code) {
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 stat9 } from "fs/promises";
15254
- import path16 from "path";
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 = path16.join(profileDir, "state.db");
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: path16.join(profileDir, "sessions"),
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 stat9(dbPath).then((value) => value.isFile()).catch((error) => {
15378
- if (isNodeError9(error, "ENOENT")) {
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 (isNodeError9(error, "ENOENT")) {
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
- path16.join(sessionsDir, `session_${sessionId}.${extension}`),
15509
- path16.join(sessionsDir, `${sessionId}.${extension}`)
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 isNodeError9(error, code) {
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 = normalizeProfileName3(
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 ? normalizeProfileName3(requestedProfileName) : existingProfileName;
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 createHash4 } from "crypto";
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" ? readErrorMessage2(input.payload) ?? actionSummary ?? void 0 : actionSummary ?? void 0;
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 createHash4("sha256").update(value).digest("hex").slice(0, 16);
18734
+ return createHash5("sha256").update(value).digest("hex").slice(0, 16);
17841
18735
  }
17842
- function readErrorMessage2(payload) {
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" && normalizeProfileName4(run.profile_name_snapshot ?? run.profile) === profileName
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 normalizeProfileName4(run?.profile_name_snapshot ?? run?.profile);
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 normalizeProfileName4(value) {
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 path17 from "path";
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 (isNodeError10(error, "ENOENT")) {
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 (isNodeError10(error, "ENOENT")) {
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(path17.join(this.conversationDir(conversationId), "attachments"), {
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 path17.join(this.paths.conversationsDir, conversationId);
19971
+ return path18.join(this.paths.conversationsDir, conversationId);
19078
19972
  }
19079
19973
  manifestPath(conversationId) {
19080
- return path17.join(this.conversationDir(conversationId), "manifest.json");
19974
+ return path18.join(this.conversationDir(conversationId), "manifest.json");
19081
19975
  }
19082
19976
  snapshotPath(conversationId) {
19083
- return path17.join(this.conversationDir(conversationId), "snapshot.json");
19977
+ return path18.join(this.conversationDir(conversationId), "snapshot.json");
19084
19978
  }
19085
19979
  eventsPath(conversationId) {
19086
- return path17.join(this.conversationDir(conversationId), "events.ndjson");
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 isNodeError10(error, code) {
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 stat11 } from "fs/promises";
19099
- import path19 from "path";
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 stat10 } from "fs/promises";
19103
- import path18 from "path";
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 = path18.resolve(stagingDir);
19175
- const relative = path18.relative(path18.resolve(paths.conversationsDir), resolvedDir);
19176
- if (!relative || relative.startsWith("..") || path18.isAbsolute(relative)) {
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(path18.sep);
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 (isNodeError11(error, "ENOENT")) {
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 = path18.join(stagingDir, entry.name);
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 stat10(sourcePath).catch((error) => {
19380
- if (isNodeError11(error, "ENOENT")) {
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: path18.basename(sourcePath),
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
- ...isNodeError11(error) && error.code ? { code: error.code } : {}
20308
+ ...isNodeError12(error) && error.code ? { code: error.code } : {}
19415
20309
  };
19416
20310
  }
19417
20311
  function isSupportedDeliveryFilename(filename) {
19418
- return SUPPORTED_DELIVERY_EXTENSIONS.has(path18.extname(filename).toLowerCase());
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 isNodeError11(error, code) {
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 = path19.join(profileDir, "state.db");
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 = path19.join(profileDir, "state.db");
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 (isNodeError12(error, "ENOENT")) {
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(() => path19.join(profileDir, "sessions"));
20904
- const transcriptPath = path19.join(sessionsDir, `${sessionId}.jsonl`);
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 (isNodeError12(error, "ENOENT")) {
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 stat11(filePath).then((value) => value.isFile()).catch((error) => {
21235
- if (isNodeError12(error, "ENOENT")) {
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 isNodeError12(error, code) {
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 stat12 } from "fs/promises";
21274
- import path20 from "path";
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 = path20.resolve(context.stagingDir);
21347
- const conversationsRoot = path20.resolve(paths.conversationsDir);
21348
- const relative = path20.relative(conversationsRoot, stagingDir);
21349
- if (!relative || relative.startsWith("..") || path20.isAbsolute(relative)) {
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 = path20.resolve(file.path);
22254
+ const sourcePath = path21.resolve(file.path);
21361
22255
  let baseName = sanitizeFilename(
21362
- file.caption || path20.basename(sourcePath),
22256
+ file.caption || path21.basename(sourcePath),
21363
22257
  `attachment-${index + 1}`
21364
22258
  );
21365
- const sourceExtension = path20.extname(sourcePath);
21366
- if (!path20.extname(baseName) && sourceExtension) {
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 stat12(sourcePath).catch((error) => {
21381
- if (isNodeError13(error, "ENOENT")) {
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 = path20.join(stagingDir, filename);
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 = path20.extname(filename);
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 isNodeError13(error, code) {
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 createHash6 } from "crypto";
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 stat13 } from "fs/promises";
22179
- import os5 from "os";
22180
- import path21 from "path";
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(path21.delimiter);
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 (path21.isAbsolute(command)) {
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(path21.delimiter)) {
23269
+ for (const dir of pathEnv.split(path22.delimiter)) {
22376
23270
  for (const extension of extensions) {
22377
- const candidate = path21.join(dir, `${command}${extension}`);
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 stat13(filePath);
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
- path21.join(sourceRoot, "venv", "Scripts", "python.exe"),
22400
- path21.join(sourceRoot, ".venv", "Scripts", "python.exe")
23293
+ path22.join(sourceRoot, "venv", "Scripts", "python.exe"),
23294
+ path22.join(sourceRoot, ".venv", "Scripts", "python.exe")
22401
23295
  ] : [
22402
- path21.join(sourceRoot, "venv", "bin", "python"),
22403
- path21.join(sourceRoot, ".venv", "bin", "python")
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
- path21.resolve(process.cwd(), "reference/hermes-agent"),
22433
- path21.resolve(process.cwd(), "../../reference/hermes-agent")
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 || !path21.isAbsolute(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 = path21.dirname(path21.resolve(executablePath));
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 = path21.dirname(cursor);
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
- path21.join(os5.homedir(), ".hermes", "hermes-agent"),
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(path21.join(localAppData, "hermes", "hermes-agent"));
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(path21.join(candidate, "tools")) && await isDirectory(path21.join(candidate, "hermes_cli"));
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 stat13(candidate).then((info) => info.isDirectory()).catch(() => false);
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 stat15 } from "fs/promises";
22524
- import path23 from "path";
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 stat14 } from "fs/promises";
22530
- import path22 from "path";
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 (isNodeError14(error, "ENOENT")) {
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 stat14(profile.path).then((value) => value.isDirectory()).catch((error) => {
22604
- if (isNodeError14(error, "ENOENT")) {
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(path22.join(profileDir, "skills")).catch(
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 stat14(targetPath).then(() => true).catch((error) => {
22717
- if (isNodeError14(error, "ENOENT")) {
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 stat14(profilePath).catch((error) => {
22726
- if (isNodeError14(error, "ENOENT")) {
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(path22.join(profilePath, ".env"));
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 isNodeError14(error, "EPERM");
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 isNodeError14(error, code) {
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 (isNodeError14(error, "ENOENT")) {
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 = path22.join(root, entry.name);
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 (isNodeError14(error, "ENOENT")) {
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 = normalizeProfileName5(profileName);
23888
+ const normalizedProfile = normalizeProfileName6(profileName);
22995
23889
  const profilePath = resolveHermesProfileDir(normalizedProfile);
22996
23890
  const configPath = resolveHermesConfigPath(normalizedProfile);
22997
- const pluginPath = path23.join(
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(path23.dirname(eventsPath), 448);
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 = normalizeProfileName5(input.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 (isNodeError15(error, "ENOENT")) {
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 path23.join(
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 = path23.join(input.pluginPath, "plugin.yaml");
23181
- const initPath = path23.join(input.pluginPath, "__init__.py");
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 (isNodeError15(error, "ENOENT")) {
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 = uniqueStrings([
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 = uniqueStrings(
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 = uniqueStrings(
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 = uniqueStrings([
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 = uniqueStrings([
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 = uniqueStrings([
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 = path23.join(profilePath, "plugins", LEGACY_PLUGIN_KEY);
24422
+ const legacyPath = path24.join(profilePath, "plugins", LEGACY_PLUGIN_KEY);
23529
24423
  const [manifest, init] = await Promise.all([
23530
- readFile14(path23.join(legacyPath, "plugin.yaml"), "utf8").catch(
24424
+ readFile14(path24.join(legacyPath, "plugin.yaml"), "utf8").catch(
23531
24425
  (error) => {
23532
- if (isNodeError15(error, "ENOENT")) {
24426
+ if (isNodeError16(error, "ENOENT")) {
23533
24427
  return null;
23534
24428
  }
23535
24429
  throw error;
23536
24430
  }
23537
24431
  ),
23538
- readFile14(path23.join(legacyPath, "__init__.py"), "utf8").catch(
24432
+ readFile14(path24.join(legacyPath, "__init__.py"), "utf8").catch(
23539
24433
  (error) => {
23540
- if (isNodeError15(error, "ENOENT")) {
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 path23.join(paths.homeDir, USAGE_PROBE_DIR, USAGE_PROBE_STATE_FILE);
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 (isNodeError15(error, "ENOENT")) {
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 stat15(filePath);
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 normalizeProfileName5(profileName) {
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(normalizeProfileName5(profileName));
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 uniqueStrings(entries) {
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 stat15(filePath).then((value) => value.isDirectory()).catch((error) => {
24051
- if (isNodeError15(error, "ENOENT")) {
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 isNodeError15(error, code) {
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 stat16 } from "fs/promises";
24155
- import path24 from "path";
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 = path24.join(profileDir, "state.db");
24232
- const sessionsDir = await readHermesSessionsDir(profileName).then((value) => value.sessionsDir).catch(() => path24.join(profileDir, "sessions"));
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 = path24.join(sessionsDir, `${sessionId}.jsonl`);
25189
+ const transcriptPath = path25.join(sessionsDir, `${sessionId}.jsonl`);
24296
25190
  const raw = await readFile15(transcriptPath, "utf8").catch((error) => {
24297
- if (isNodeError16(error, "ENOENT")) {
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 stat16(filePath).then((value) => value.isFile()).catch((error) => {
24512
- if (isNodeError16(error, "ENOENT")) {
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 isNodeError16(error, code) {
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 createHash5 } from "crypto";
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 createHash5("sha256").update(value).digest("hex").slice(0, 16);
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: readErrorMessage3(event.payload) ?? readDelta(event.payload) ?? "Hermes run failed"
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 readErrorMessage3(payload) {
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(readErrorMessage3(event.payload));
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
- readErrorMessage3(input.event.payload) ?? "Hermes run failed",
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 ? readErrorMessage3(source.payload) ?? void 0 : void 0;
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:${createHash6("sha256").update(raw).digest("hex")}`;
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: normalizeProfileName3(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 = normalizeProfileName3(input.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 (isNodeError17(error, "ENOENT")) {
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 normalizeProfileName3(profileName);
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 = createHash6("sha256").update(`${conversationId}:${runId}:${eventKind}`).digest("hex").slice(0, 24);
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 = createHash6("sha256").update(`${conversationId}:${runId}:${approvalId}:approval_required`).digest("hex").slice(0, 24);
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 = createHash6("sha256").update(`${conversationId}:${runId}:${inputRequestId}:input_required`).digest("hex").slice(0, 24);
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 isNodeError17(error, code) {
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(profile.profileName, normalizedModelId, {
28385
- provider: input.modelProvider,
28386
- baseUrl: input.modelBaseUrl,
28387
- apiMode: input.modelApiMode
28388
- }) : null;
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 = normalizeProfileName3(input.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" && normalizeProfileName3(
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 = normalizeProfileName3(
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 = normalizeProfileName3(input.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 (normalizeProfileName3(
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 = normalizeProfileName3(
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 = normalizeProfileName3(
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 path25.join(this.paths.indexesDir, "hermes-archive-state-sync.json");
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 = normalizeProfileName3(input.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 normalizeProfileName3(manifest.profile_name_snapshot ?? manifest.profile) === profileName;
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 = createHash7("sha256").update(`${conversationId}:${jobId}:${outputPath}:${eventKind}`).digest("hex").slice(0, 24);
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 createHash8 } from "crypto";
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 createHash8("sha256").update(value).digest("hex");
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 readString20(body, key) {
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 readString20(body, "profile") ?? readString20(body, "profile_name") ?? readString20(body, "profileName") ?? void 0;
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 = readString20(body, "reasoning_effort") ?? readString20(body, "reasoningEffort") ?? readString20(body, "default_reasoning_effort") ?? readString20(body, "defaultReasoningEffort");
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: readString20(body, "title") ?? void 0,
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: readString20(body, "model_id") ?? readString20(body, "modelId") ?? readString20(body, "model") ?? void 0,
30937
- modelProvider: readString20(body, "model_provider") ?? readString20(body, "modelProvider") ?? readString20(body, "provider") ?? void 0,
30938
- modelBaseUrl: readString20(body, "model_base_url") ?? readString20(body, "modelBaseUrl") ?? readString20(body, "base_url") ?? readString20(body, "baseUrl") ?? void 0,
30939
- modelApiMode: readString20(body, "model_api_mode") ?? readString20(body, "modelApiMode") ?? readString20(body, "api_mode") ?? readString20(body, "apiMode") ?? void 0,
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 = readString20(body, "content") ?? readString20(body, "text") ?? readString20(body, "input") ?? "";
32251
+ const content = readString21(body, "content") ?? readString21(body, "text") ?? readString21(body, "input") ?? "";
31017
32252
  const attachments = readMessageAttachments(body.attachments ?? body.blobs);
31018
- const mode = readString20(body, "mode") ?? readString20(body, "send_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: readString20(body, "client_message_id") ?? readString20(body, "clientMessageId") ?? void 0,
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: readString20(body, "focus") ?? readString20(body, "focus_topic") ?? readString20(body, "focusTopic") ?? void 0,
31109
- clientOperationId: readString20(body, "client_operation_id") ?? readString20(body, "clientOperationId") ?? readHeader(ctx, "idempotency-key") ?? void 0,
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 = readString20(body, "model_id") ?? readString20(body, "modelId") ?? readString20(body, "model");
31124
- const modelProvider = readString20(body, "model_provider") ?? readString20(body, "modelProvider") ?? readString20(body, "provider") ?? void 0;
31125
- const modelBaseUrl = readString20(body, "model_base_url") ?? readString20(body, "modelBaseUrl") ?? readString20(body, "base_url") ?? readString20(body, "baseUrl") ?? void 0;
31126
- const modelApiMode = readString20(body, "model_api_mode") ?? readString20(body, "modelApiMode") ?? readString20(body, "api_mode") ?? readString20(body, "apiMode") ?? void 0;
31127
- const rawReasoningEffort = readString20(body, "reasoning_effort") ?? readString20(body, "reasoningEffort") ?? readString20(body, "default_reasoning_effort") ?? readString20(body, "defaultReasoningEffort");
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 = readString20(body, "title") ?? readString20(body, "name") ?? readString20(body, "display_name");
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 = readString20(body, "scope") ?? "always";
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: readString20(body, "answer") ?? void 0,
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 readString20(body, "workspace_id") ?? readString20(body, "workspaceId");
32816
+ return readString21(body, "workspace_id") ?? readString21(body, "workspaceId");
31582
32817
  }
31583
32818
  if (Object.prototype.hasOwnProperty.call(body, "workspace")) {
31584
- return readString20(body, "workspace");
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 = readString20(body, "target_status") ?? readString20(body, "targetStatus") ?? "active";
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 = readString20(body, "kind") ?? readString20(body, "type");
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 = readString20(input.job, "id") ?? readString20(input.job, "job_id");
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 = readString20(body, "name") ?? readString20(body, "title");
32107
- const prompt = readString20(body, "prompt") ?? readString20(body, "description") ?? readString20(body, "task");
32108
- const schedule = readString20(body, "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 = readString20(body, "deliver") ?? HERMES_LINK_CRON_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 createHash9 } from "crypto";
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: readString21(record.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 = readString21(record.canonicalId) ?? readString21(record.canonical_id) ?? readString21(record.id);
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: readString21(record.source),
32635
- updatedAt: readString21(record.updatedAt ?? record.updated_at),
32636
- notes: readString21(record.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 readString21(value) {
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 = readString21(item);
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 = readString21(value)?.toLowerCase();
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 = readString21(value)?.toLowerCase();
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 listHermesModelConfigs();
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
- markModelConfigAppliedWithoutGatewayReload(result),
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
- markModelConfigAppliedWithoutGatewayReload(result),
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
- markModelConfigAppliedWithoutGatewayReload(result),
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
- markModelConfigAppliedWithoutGatewayReload(result),
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
- markModelConfigAppliedWithoutGatewayReload(latest),
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 reloadGatewayAfterModelConfigChange(result, {
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
- const result = await saveHermesModelDefaults(readModelDefaultsInput(body));
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) ? await reloadGatewayAfterModelConfigChange(result, {
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 listHermesModelConfigs(ctx.params.name);
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 reloadGatewayAfterProfileModelConfigChange(result, {
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) ? await reloadGatewayAfterProfileModelConfigChange(result, {
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
- const result = await saveHermesModelDefaults(
33116
- readModelDefaultsInput(body),
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) ? await reloadGatewayAfterProfileModelConfigChange(result, {
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_${createHash9("sha256").update(identity).digest("hex").slice(0, 18)}`;
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 = readString20(body, "display_name") ?? readString20(body, "displayName") ?? readString20(body, "provider_name") ?? readString20(body, "providerName") ?? readString20(body, "name");
33682
- const baseUrl = readString20(body, "base_url") ?? readString20(body, "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: readString20(body, "api_mode") ?? readString20(body, "apiMode") ?? readString20(body, "transport") ?? void 0,
33694
- keyEnv: readString20(body, "key_env") ?? readString20(body, "keyEnv") ?? void 0,
33695
- apiKey: readString20(body, "api_key") ?? readString20(body, "apiKey") ?? void 0
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 = readString20(body, "id") ?? readString20(body, "model_id") ?? readString20(body, "modelId");
33700
- const provider = readString20(body, "provider") ?? readString20(body, "provider_key") ?? readString20(body, "providerKey");
33701
- const baseUrl = readString20(body, "base_url") ?? readString20(body, "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: readString20(body, "original_model_id") ?? readString20(body, "originalModelId") ?? readString20(body, "original_id") ?? void 0,
33712
- originalProvider: readString20(body, "original_provider") ?? readString20(body, "originalProvider") ?? readString20(body, "original_provider_key") ?? readString20(body, "originalProviderKey") ?? void 0,
33713
- originalBaseUrl: readString20(body, "original_base_url") ?? readString20(body, "originalBaseUrl") ?? void 0,
33714
- originalApiMode: readString20(body, "original_api_mode") ?? readString20(body, "originalApiMode") ?? void 0,
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: readString20(body, "provider_name") ?? readString20(body, "providerName") ?? void 0,
35012
+ providerName: readString21(body, "provider_name") ?? readString21(body, "providerName") ?? void 0,
33717
35013
  baseUrl,
33718
- apiKey: readString20(body, "api_key") ?? readString20(body, "apiKey") ?? void 0,
33719
- apiMode: readString20(body, "api_mode") ?? readString20(body, "apiMode") ?? void 0,
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: readString20(body, "key_env") ?? readString20(body, "keyEnv") ?? void 0,
35019
+ keyEnv: readString21(body, "key_env") ?? readString21(body, "keyEnv") ?? void 0,
33724
35020
  setDefault: readBoolean3(body.set_default ?? body.setDefault),
33725
- reasoningEffort: readString20(body, "reasoning_effort") ?? readString20(body, "reasoningEffort") ?? void 0,
33726
- reasoningSupportPolicy: readString20(body, "reasoning_support_policy") ?? readString20(body, "reasoningSupportPolicy") ?? readString20(body, "reasoning_support") ?? readString20(body, "reasoningSupport") ?? void 0,
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: readString20(body, "task_model_id") ?? readString20(body, "taskModelId") ?? readString20(body, "default_model_id") ?? readString20(body, "defaultModelId") ?? void 0,
33735
- taskModelProvider: readString20(body, "task_model_provider") ?? readString20(body, "taskModelProvider") ?? readString20(body, "default_model_provider") ?? readString20(body, "defaultModelProvider") ?? void 0,
33736
- taskModelBaseUrl: readString20(body, "task_model_base_url") ?? readString20(body, "taskModelBaseUrl") ?? readString20(body, "default_model_base_url") ?? readString20(body, "defaultModelBaseUrl") ?? void 0,
33737
- taskModelApiMode: readString20(body, "task_model_api_mode") ?? readString20(body, "taskModelApiMode") ?? readString20(body, "default_model_api_mode") ?? readString20(body, "defaultModelApiMode") ?? void 0,
33738
- compressionModelId: readString20(body, "compression_model_id") ?? readString20(body, "compressionModelId") ?? void 0,
33739
- compressionModelProvider: readString20(body, "compression_model_provider") ?? readString20(body, "compressionModelProvider") ?? void 0,
33740
- compressionModelBaseUrl: readString20(body, "compression_model_base_url") ?? readString20(body, "compressionModelBaseUrl") ?? void 0,
33741
- compressionModelApiMode: readString20(body, "compression_model_api_mode") ?? readString20(body, "compressionModelApiMode") ?? void 0,
33742
- reasoningEffort: readString20(body, "reasoning_effort") ?? readString20(body, "reasoningEffort") ?? readString20(body, "default_reasoning_effort") ?? readString20(body, "defaultReasoningEffort") ?? void 0,
33743
- imageInputMode: readString20(body, "image_input_mode") ?? readString20(body, "imageInputMode") ?? void 0
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 = readString20(body, "model_id") ?? readString20(body, "modelId");
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: readString20(body, "provider") ?? readString20(body, "provider_key") ?? readString20(body, "providerKey") ?? void 0,
33754
- baseUrl: readString20(body, "base_url") ?? readString20(body, "baseUrl") ?? void 0,
33755
- apiMode: readString20(body, "api_mode") ?? readString20(body, "apiMode") ?? void 0
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 = readString20(body, "source_profile") ?? readString20(body, "sourceProfile") ?? readString20(body, "source_profile_name") ?? readString20(body, "sourceProfileName");
33779
- const modelId = readString20(body, "model_id") ?? readString20(body, "modelId") ?? readString20(body, "id");
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: readString20(body, "provider") ?? readString20(body, "provider_key") ?? readString20(body, "providerKey") ?? void 0,
33791
- baseUrl: readString20(body, "base_url") ?? readString20(body, "baseUrl") ?? void 0,
33792
- apiMode: readString20(body, "api_mode") ?? readString20(body, "apiMode") ?? void 0,
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("modelConfigs", () => listHermesModelConfigs(profile.name))
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: readString20(approvals, "mode") ?? readString20(approvals, "approval_mode") ?? readString20(approvals, "approvalMode") ?? void 0,
36393
+ mode: readString21(approvals, "mode") ?? readString21(approvals, "approval_mode") ?? readString21(approvals, "approvalMode") ?? void 0,
35087
36394
  timeout: readPositiveInteger2(approvals.timeout),
35088
- cronMode: readString20(approvals, "cron_mode") ?? readString20(approvals, "cronMode") ?? void 0
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: readString20(terminal, "backend") ?? void 0,
35095
- cwd: readString20(terminal, "cwd") ?? void 0,
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 = readString22(patch.apiUrl) ?? readString22(config.api_url) ?? env.HINDSIGHT_API_URL ?? (mode === "cloud" ? HINDSIGHT_DEFAULT_API_URL : HINDSIGHT_DEFAULT_LOCAL_URL);
35427
- const bankId = readString22(patch.bankId) ?? readString22(config.bank_id) ?? "hermes";
35428
- const apiKey = readString22(patch.apiKey) ?? env.HINDSIGHT_API_KEY ?? readString22(config.apiKey) ?? readString22(config.api_key);
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(readString22(config2.apiKey)) || isConfiguredEnvValue(readString22(config2.api_key)) || isConfiguredEnvValue(readString22(config2.baseUrl)) ? { configured: true, issue: null } : {
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(readString22(config2.api_key)) ? { configured: true, issue: null } : {
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 = readString22(config.apiKey) ?? readString22(config.api_key) ?? env.HINDSIGHT_API_KEY;
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 = readString22(config.api_url) ?? env.HINDSIGHT_API_URL ?? HINDSIGHT_DEFAULT_LOCAL_URL;
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 = readString22(config.llm_provider) ?? "openai";
36029
- const llmModel = readString22(config.llm_model);
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
- readString22(config.llm_base_url) ?? env.HINDSIGHT_API_LLM_BASE_URL
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
- readString22(config.llmApiKey) ?? readString22(config.llm_api_key) ?? env.HINDSIGHT_LLM_API_KEY
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 ?? readString22(config.apiKey) ?? readString22(config.api_key),
36102
- isConfiguredEnvValue(env.HONCHO_API_KEY) || isConfiguredEnvValue(readString22(config.apiKey)) || isConfiguredEnvValue(readString22(config.api_key))
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 ?? readString22(config.apiKey) ?? readString22(config.api_key),
36145
- isConfiguredEnvValue(env.MEM0_API_KEY) || isConfiguredEnvValue(readString22(config.apiKey)) || isConfiguredEnvValue(readString22(config.api_key))
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 ?? readString22(config.apiKey) ?? readString22(config.api_key),
36231
- isConfiguredEnvValue(env.HINDSIGHT_API_KEY) || isConfiguredEnvValue(readString22(config.apiKey)) || isConfiguredEnvValue(readString22(config.api_key))
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 ?? readString22(config.llmApiKey) ?? readString22(config.llm_api_key),
36254
- isConfiguredEnvValue(env.HINDSIGHT_LLM_API_KEY) || isConfiguredEnvValue(readString22(config.llmApiKey)) || isConfiguredEnvValue(readString22(config.llm_api_key))
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(readString22(record.id) ?? "");
37680
+ const id = normalizeCustomProviderId(readString23(record.id) ?? "");
36374
37681
  return {
36375
37682
  id,
36376
- label: readString22(record.label) ?? id,
36377
- description: readString22(record.description) ?? "\u81EA\u5B9A\u4E49 memory provider\u3002"
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: readString22(meta.name) ?? providerId,
36427
- description: readString22(meta.description) ?? "\u81EA\u5B9A\u4E49 memory provider\u3002"
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 = readString22(value) ?? "cloud";
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 = readString22(json.detail) ?? readString22(json.error);
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 = readString22(json.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 = readString22(json.status) ?? "ok";
36671
- const database = readString22(json.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 = readString22(json.api_version);
37982
+ const version = readString23(json.api_version);
36676
37983
  return version ? `API ${version}` : "version endpoint reachable";
36677
37984
  }
36678
- const bankId = readString22(json.bank_id);
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 = readString22(memory.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: readString22(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(readString22(value))
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 = readString22(value) ?? options[0] ?? null;
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 readString22(value) {
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 = readString20(body, "target");
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 = readString20(body, "target") ?? "all";
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 = readString20(body, "content") ?? readString20(body, "text");
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 = readString20(body, "old_text") ?? readString20(body, "oldText") ?? readString20(body, "match");
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 = readString20(body, "provider") ?? readString20(body, "provider_id") ?? readString20(body, "providerId");
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 = readString20(body, "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 = readString20(body, "llm_provider") ?? readString20(body, "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 = readString20(body, "memory_mode") ?? readString20(body, "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 = readString20(body, "recall_budget") ?? readString20(body, "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 = readString20(body, "capture_mode") ?? readString20(body, "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 = readString20(body, "search_mode") ?? readString20(body, "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 = readString20(body, "recall_mode") ?? readString20(body, "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 = readString20(body, "write_frequency") ?? readString20(body, "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 = readString20(body, "session_strategy") ?? readString20(body, "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
- readString23(frontmatter.name) ?? path32.basename(skillDir)
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
- readString23(frontmatter.description) ?? firstBodyDescription(body)
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: readString23(entry.source) ?? "hub",
37617
- trust: readString23(entry.trust_level) ?? null
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 readString23(value) {
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 = readString20(body, "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: readString20(body, "instructions") ?? void 0,
39388
+ instructions: readString21(body, "instructions") ?? void 0,
38082
39389
  conversation_history: readConversationHistory(
38083
39390
  body.conversation_history ?? body.conversationHistory
38084
39391
  ),
38085
- session_id: readString20(body, "session_id") ?? readString20(body, "sessionId") ?? void 0,
38086
- session_key: readString20(body, "session_key") ?? readString20(body, "sessionKey") ?? void 0
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: readString24(snapshot, "cache_state") ?? readString24(snapshot, "cacheState"),
38500
- issue: readString24(snapshot, "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 = readString24(payload, "tag");
38505
- const name = readString24(payload, "name");
39811
+ const tag = readString25(payload, "tag");
39812
+ const name = readString25(payload, "name");
38506
39813
  return {
38507
- version: readString24(payload, "version") ?? extractSemver(name) ?? extractTagSemver(tag),
39814
+ version: readString25(payload, "version") ?? extractSemver(name) ?? extractTagSemver(tag),
38508
39815
  tag,
38509
39816
  name,
38510
- releaseUrl: readString24(payload, "releaseUrl") ?? readString24(payload, "release_url"),
38511
- publishedAt: readString24(payload, "publishedAt") ?? readString24(payload, "published_at"),
38512
- fetchedAt: readString24(payload, "fetchedAt") ?? readString24(payload, "fetched_at") ?? (/* @__PURE__ */ new Date()).toISOString()
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 readString24(payload, key) {
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 = readRecord(input);
38814
- const body = readRecord(record?.policy) ?? readRecord(record?.stream_batching) ?? record;
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 = readRecord(input);
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 readRecord(value) {
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: uniqueStrings2([
41008
+ publicIpv4s: uniqueStrings3([
39702
41009
  ...snapshotInput.publicIpv4s,
39703
41010
  ...state.lastReportedPublicIpv4s
39704
41011
  ]).slice(0, 2),
39705
- publicIpv6s: uniqueStrings2([
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 uniqueStrings2(values) {
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, canonicalJson2(payload));
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 = readErrorMessage4(body) ?? `HermesPilot Server request failed with HTTP ${response.status}`;
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 canonicalJson2(value) {
39862
- return JSON.stringify(sortJsonValue2(value));
41168
+ function canonicalJson3(value) {
41169
+ return JSON.stringify(sortJsonValue3(value));
39863
41170
  }
39864
- function sortJsonValue2(value) {
41171
+ function sortJsonValue3(value) {
39865
41172
  if (Array.isArray(value)) {
39866
- return value.map(sortJsonValue2);
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] = sortJsonValue2(record[key]);
41179
+ sorted[key] = sortJsonValue3(record[key]);
39873
41180
  }
39874
41181
  return sorted;
39875
41182
  }
39876
41183
  return value;
39877
41184
  }
39878
- function readErrorMessage4(payload) {
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 = async (context = {}) => {
41205
+ const check = (context = {}) => {
39899
41206
  if (running || closed) {
39900
- return;
41207
+ return current;
39901
41208
  }
39902
41209
  running = true;
39903
- try {
39904
- await checkLanIpChange(options, context);
39905
- } catch (error) {
39906
- void options.logger.warn("lan_ip_monitor_failed", {
39907
- error: error instanceof Error ? error.message : String(error)
39908
- });
39909
- } finally {
39910
- running = false;
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: readString25(snapshot, "issue")
42731
+ issue: readString26(snapshot, "issue")
41420
42732
  };
41421
42733
  }
41422
42734
  const release = toNullableRecord2(snapshot.release);
41423
- const currentVersion = readString25(policy, "current_version") ?? readString25(policy, "currentVersion");
41424
- const minSafeVersion = readString25(policy, "min_safe_version") ?? readString25(policy, "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: readString25(snapshot, "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 ? readString25(release, "release_url") ?? readString25(release, "releaseUrl") : null,
41437
- published_at: release ? readString25(release, "published_at") ?? readString25(release, "publishedAt") : null
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: readString25(snapshot, "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 readString25(payload, key) {
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 = readErrorMessage5(payload) ?? `Relay request failed with HTTP ${response.status}`;
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 readErrorMessage5(payload) {
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 = readErrorMessage6(payload) ?? `HermesPilot Server request failed with HTTP ${response.status}`;
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 readErrorMessage6(payload) {
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 = readString20(body, "session_id") ?? readString20(body, "sessionId");
42406
- const claimToken = readString20(body, "claim_token") ?? readString20(body, "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: readString20(body, "device_label") ?? readString20(body, "deviceLabel") ?? "HermesPilot App",
42418
- devicePlatform: readString20(body, "device_platform") ?? readString20(body, "devicePlatform") ?? "unknown",
42419
- deviceModel: readString20(body, "device_model") ?? readString20(body, "deviceModel"),
42420
- appInstanceId: readString20(body, "app_instance_id") ?? readString20(body, "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: readString20(body, "device_label") ?? readString20(body, "deviceLabel") ?? "HermesPilot App",
42501
- platform: readString20(body, "device_platform") ?? readString20(body, "devicePlatform") ?? "unknown",
42502
- model: readString20(body, "device_model") ?? readString20(body, "deviceModel"),
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 = readString20(body, "refresh_token") ?? readString20(body, "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: readString20(body, "app_instance_id") ?? readString20(body, "appInstanceId"),
42543
- label: readString20(body, "device_label") ?? readString20(body, "deviceLabel"),
42544
- platform: readString20(body, "device_platform") ?? readString20(body, "devicePlatform"),
42545
- model: readString20(body, "device_model") ?? readString20(body, "deviceModel")
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 = readString20(body, "refresh_token") ?? readString20(body, "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 = readString20(body, "label") ?? readString20(body, "device_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 = readString20(job, "state")?.toLowerCase();
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: readString20(body, "name") ?? "",
42894
- icon: readString20(body, "icon") ?? void 0
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: readString20(body, "name") ?? "",
42905
- icon: readString20(body, "icon") ?? void 0
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: readString20(body, "channel"),
42989
- targetVersion: readString20(body, "target_version") ?? readString20(body, "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 = readString20(ctx.query, "session_id");
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 = readString20(ctx.query, "session_id");
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 = readString20(body, "staging_dir") ?? readString20(body, "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: readString20(body, "profile") ?? readString20(body, "profile_name") ?? readString20(body, "profileName") ?? void 0,
43534
- taskId: readString20(body, "task_id") ?? readString20(body, "taskId") ?? void 0,
43535
- sessionId: readString20(body, "session_id") ?? readString20(body, "sessionId") ?? void 0,
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 });