@hermespilot/link 0.8.0 → 0.8.1-beta.1

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,7 +4,7 @@ import Router from "@koa/router";
4
4
 
5
5
  // src/conversations/conversation-service.ts
6
6
  import { EventEmitter } from "events";
7
- import { createHash as createHash7, randomUUID as randomUUID12 } from "crypto";
7
+ import { createHash as createHash8, randomUUID as randomUUID12 } from "crypto";
8
8
  import path26 from "path";
9
9
 
10
10
  // src/database/link-database.ts
@@ -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...",
@@ -7215,7 +7613,7 @@ function isConversationMissingError(error) {
7215
7613
  }
7216
7614
 
7217
7615
  // src/constants.ts
7218
- var LINK_VERSION = "0.8.0";
7616
+ var LINK_VERSION = "0.8.1-beta.1";
7219
7617
  var LINK_COMMAND = "hermeslink";
7220
7618
  var LINK_DEFAULT_PORT = 52379;
7221
7619
  var LINK_RUNTIME_DIR_NAME = ".hermeslink";
@@ -7526,6 +7924,271 @@ function readErrorMessage(payload) {
7526
7924
  return typeof message === "string" ? message : null;
7527
7925
  }
7528
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
+
7529
8192
  // src/hermes/gateway.ts
7530
8193
  import { execFile as execFile2, spawn } from "child_process";
7531
8194
  import { access, readFile as readFile5, stat as stat4 } from "fs/promises";
@@ -11465,7 +12128,7 @@ function toRecord3(value) {
11465
12128
  }
11466
12129
 
11467
12130
  // src/conversations/approvals.ts
11468
- import { createHash } from "crypto";
12131
+ import { createHash as createHash2 } from "crypto";
11469
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";
11470
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.";
11471
12134
  var LINK_APPROVAL_CONFIRM_HINT_ZH = "\u8BF7\u786E\u8BA4\u662F\u5426\u5141\u8BB8 Hermes \u7EE7\u7EED\u6267\u884C\u8FD9\u6761\u547D\u4EE4\u3002";
@@ -11565,7 +12228,7 @@ function coerceRecord(value) {
11565
12228
  return toRecord4(value);
11566
12229
  }
11567
12230
  function createApprovalId(input) {
11568
- 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);
11569
12232
  return `approval_${digest}`;
11570
12233
  }
11571
12234
  function readString6(payload, key) {
@@ -11583,7 +12246,7 @@ function toRecord4(value) {
11583
12246
  }
11584
12247
 
11585
12248
  // src/conversations/input-requests.ts
11586
- import { createHash as createHash2 } from "crypto";
12249
+ import { createHash as createHash3 } from "crypto";
11587
12250
  function extractInputRequestFromEvent(input) {
11588
12251
  const kind = readInputRequestKind(input.event.payloadType);
11589
12252
  if (!kind) {
@@ -11731,7 +12394,7 @@ function readInputRequestKind(payloadType) {
11731
12394
  return null;
11732
12395
  }
11733
12396
  function createInputRequestId(input) {
11734
- 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);
11735
12398
  return `input_${digest}`;
11736
12399
  }
11737
12400
  function readString7(payload, key) {
@@ -12207,7 +12870,7 @@ import { mkdir as mkdir6, readFile as readFile7, readdir as readdir4, rm as rm3,
12207
12870
  import path11 from "path";
12208
12871
 
12209
12872
  // src/conversations/media.ts
12210
- import { createHash as createHash3 } from "crypto";
12873
+ import { createHash as createHash4 } from "crypto";
12211
12874
  import path10 from "path";
12212
12875
 
12213
12876
  // src/conversations/delivery-contract.ts
@@ -12419,7 +13082,7 @@ function readImportedMediaSourceKeys(message) {
12419
13082
  );
12420
13083
  }
12421
13084
  function mediaSourceKey(sourcePath) {
12422
- return createHash3("sha256").update(resolveMediaSourcePath(sourcePath)).digest("hex").slice(0, 32);
13085
+ return createHash4("sha256").update(resolveMediaSourcePath(sourcePath)).digest("hex").slice(0, 32);
12423
13086
  }
12424
13087
  function sanitizeFilename(value, fallback) {
12425
13088
  const base = path10.basename((value ?? "").replace(/[\r\n\t]/gu, " ").trim());
@@ -17685,7 +18348,7 @@ function isConversationNotFoundError(error) {
17685
18348
  }
17686
18349
 
17687
18350
  // src/conversations/agent-events.ts
17688
- import { createHash as createHash4 } from "crypto";
18351
+ import { createHash as createHash5 } from "crypto";
17689
18352
  var APP_TOOL_EVENT_FIELDS_TO_DROP = /* @__PURE__ */ new Set([
17690
18353
  "output",
17691
18354
  "content",
@@ -17726,7 +18389,7 @@ function projectAgentEvent(input) {
17726
18389
  summary,
17727
18390
  args
17728
18391
  });
17729
- 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;
17730
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`));
17731
18394
  return {
17732
18395
  id,
@@ -18068,9 +18731,9 @@ function stableStringify(value) {
18068
18731
  }
18069
18732
  }
18070
18733
  function hashAgentEventKey(value) {
18071
- return createHash4("sha256").update(value).digest("hex").slice(0, 16);
18734
+ return createHash5("sha256").update(value).digest("hex").slice(0, 16);
18072
18735
  }
18073
- function readErrorMessage2(payload) {
18736
+ function readErrorMessage3(payload) {
18074
18737
  if (typeof payload.error === "string" && payload.error.trim()) {
18075
18738
  return payload.error.trim();
18076
18739
  }
@@ -21705,7 +22368,7 @@ function isNodeError14(error, code) {
21705
22368
  }
21706
22369
 
21707
22370
  // src/conversations/run-lifecycle.ts
21708
- import { createHash as createHash6 } from "crypto";
22371
+ import { createHash as createHash7 } from "crypto";
21709
22372
  import { readdir as readdir9 } from "fs/promises";
21710
22373
 
21711
22374
  // src/hermes/api-server.ts
@@ -24775,7 +25438,7 @@ function isNodeError17(error, code) {
24775
25438
  }
24776
25439
 
24777
25440
  // src/conversations/run-tool-event-ids.ts
24778
- import { createHash as createHash5 } from "crypto";
25441
+ import { createHash as createHash6 } from "crypto";
24779
25442
  var RunToolEventIdCoalescer = class {
24780
25443
  scope;
24781
25444
  ordinal = 0;
@@ -24940,7 +25603,7 @@ function stableStringify2(value) {
24940
25603
  }
24941
25604
  }
24942
25605
  function hashStableValue(value) {
24943
- return createHash5("sha256").update(value).digest("hex").slice(0, 16);
25606
+ return createHash6("sha256").update(value).digest("hex").slice(0, 16);
24944
25607
  }
24945
25608
  function readString17(payload, key) {
24946
25609
  const value = payload[key];
@@ -24964,7 +25627,7 @@ function normalizeHermesStreamEvent(event) {
24964
25627
  ...event.payload,
24965
25628
  type: "run.failed",
24966
25629
  error: {
24967
- message: readErrorMessage3(event.payload) ?? readDelta(event.payload) ?? "Hermes run failed"
25630
+ message: readErrorMessage4(event.payload) ?? readDelta(event.payload) ?? "Hermes run failed"
24968
25631
  }
24969
25632
  }
24970
25633
  };
@@ -25154,7 +25817,7 @@ function normalizeStreamingTextDelta(currentText, nextChunk) {
25154
25817
  }
25155
25818
  return nextChunk;
25156
25819
  }
25157
- function readErrorMessage3(payload) {
25820
+ function readErrorMessage4(payload) {
25158
25821
  if (typeof payload.error === "string" && payload.error.trim()) {
25159
25822
  return payload.error.trim();
25160
25823
  }
@@ -25197,7 +25860,7 @@ function isTopLevelErrorEvent(event) {
25197
25860
  if (type.startsWith("tool.")) {
25198
25861
  return false;
25199
25862
  }
25200
- 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));
25201
25864
  }
25202
25865
  function readChatCompletionDelta(payload) {
25203
25866
  const choice = readFirstChoice(payload);
@@ -26173,7 +26836,7 @@ var ConversationRunLifecycle = class {
26173
26836
  await this.failRun(
26174
26837
  input.conversationId,
26175
26838
  input.runId,
26176
- readErrorMessage3(input.event.payload) ?? "Hermes run failed",
26839
+ readErrorMessage4(input.event.payload) ?? "Hermes run failed",
26177
26840
  input.event
26178
26841
  );
26179
26842
  return true;
@@ -27375,7 +28038,7 @@ ${details.join("\n")}` : localizedEmptyHermesResponseMessage(language);
27375
28038
  run.status = "failed";
27376
28039
  run.completed_at = (/* @__PURE__ */ new Date()).toISOString();
27377
28040
  run.error_message = message;
27378
- run.error_detail = source ? readErrorMessage3(source.payload) ?? void 0 : void 0;
28041
+ run.error_detail = source ? readErrorMessage4(source.payload) ?? void 0 : void 0;
27379
28042
  const language = run.language === "en" ? "en" : "zh-CN";
27380
28043
  const visibleMessage = formatFailureMessage(
27381
28044
  message,
@@ -27728,7 +28391,7 @@ ${details.join("\n")}` : localizedEmptyHermesResponseMessage(language);
27728
28391
  if (raw.length <= 200) {
27729
28392
  return raw;
27730
28393
  }
27731
- return `hermespilot:${createHash6("sha256").update(raw).digest("hex")}`;
28394
+ return `hermespilot:${createHash7("sha256").update(raw).digest("hex")}`;
27732
28395
  }
27733
28396
  async assistantMessageIdForRun(conversationId, runId) {
27734
28397
  const snapshot = await this.deps.readSnapshot(conversationId).catch(() => null);
@@ -28345,15 +29008,15 @@ function previewText2(message) {
28345
29008
  return text ? text.slice(0, 512) : null;
28346
29009
  }
28347
29010
  function runNotificationSourceEventId(conversationId, runId, eventKind) {
28348
- 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);
28349
29012
  return `${conversationId}:${eventKind}:${digest}`;
28350
29013
  }
28351
29014
  function approvalNotificationSourceEventId(conversationId, runId, approvalId) {
28352
- 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);
28353
29016
  return `${conversationId}:approval_required:${digest}`;
28354
29017
  }
28355
29018
  function inputRequestNotificationSourceEventId(conversationId, runId, inputRequestId) {
28356
- 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);
28357
29020
  return `${conversationId}:input_required:${digest}`;
28358
29021
  }
28359
29022
  async function closeSseIterator(iterator) {
@@ -30084,8 +30747,40 @@ var ConversationService = class {
30084
30747
  const event = await this.store.appendEvent(conversationId, input);
30085
30748
  this.emitter.emit(this.liveEventName(conversationId), event);
30086
30749
  this.emitter.emit(ALL_CONVERSATION_EVENTS, event);
30750
+ void this.reportLiveActivityEvent(conversationId, event);
30087
30751
  return event;
30088
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
+ }
30089
30784
  abortActiveRunsForConversation(conversationId) {
30090
30785
  for (const [runId, active] of this.activeRunControllers) {
30091
30786
  if (active.conversationId !== conversationId) {
@@ -30123,7 +30818,7 @@ function findApproval(snapshot, approvalId) {
30123
30818
  return null;
30124
30819
  }
30125
30820
  function cronNotificationSourceEventId(conversationId, jobId, outputPath, eventKind) {
30126
- 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);
30127
30822
  return `${conversationId}:${eventKind}:${digest}`;
30128
30823
  }
30129
30824
  function conversationHermesSessionIds(manifest) {
@@ -30189,12 +30884,315 @@ function notificationPreviewText(message) {
30189
30884
  const text = messageText(message).replace(/<[^>]+>/gu, " ").replace(/\s+/gu, " ").trim();
30190
30885
  return text ? text.slice(0, 512) : null;
30191
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
+ }
30192
31190
  function approvalRestartText(language, zh, en) {
30193
31191
  return language === "en" ? en : zh;
30194
31192
  }
30195
31193
 
30196
31194
  // src/security/devices.ts
30197
- 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";
30198
31196
  var ACCESS_TOKEN_TTL_MS = 15 * 60 * 1e3;
30199
31197
  var REFRESH_TOKEN_TTL_MS = 90 * 24 * 60 * 60 * 1e3;
30200
31198
  var DEVICE_SEEN_WRITE_INTERVAL_MS = 60 * 60 * 1e3;
@@ -30490,7 +31488,7 @@ function randomToken(prefix) {
30490
31488
  return `${prefix}${randomBytes3(24).toString("base64url")}`;
30491
31489
  }
30492
31490
  function sha256(value) {
30493
- return createHash8("sha256").update(value).digest("hex");
31491
+ return createHash9("sha256").update(value).digest("hex");
30494
31492
  }
30495
31493
  function safeEqual(left, right) {
30496
31494
  const leftBytes = Buffer.from(left);
@@ -30697,7 +31695,7 @@ async function readRawBody(request, maxBytes) {
30697
31695
  }
30698
31696
  return Buffer.concat(chunks);
30699
31697
  }
30700
- function readString20(body, key) {
31698
+ function readString21(body, key) {
30701
31699
  const value = body[key];
30702
31700
  return typeof value === "string" && value.trim() ? value.trim() : null;
30703
31701
  }
@@ -30732,7 +31730,7 @@ function readSupportedLanguage(value) {
30732
31730
  return null;
30733
31731
  }
30734
31732
  function readOptionalProfileName(body) {
30735
- 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;
30736
31734
  }
30737
31735
  function readStringArray(body, ...keys) {
30738
31736
  for (const key of keys) {
@@ -31152,7 +32150,7 @@ function registerConversationRoutes(router, options) {
31152
32150
  const language = readPreferredLanguage(ctx);
31153
32151
  const body = await readJsonBody(ctx.req);
31154
32152
  ctx.status = 201;
31155
- 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");
31156
32154
  const reasoningEffort = normalizeReasoningEffort(rawReasoningEffort);
31157
32155
  if (rawReasoningEffort && !reasoningEffort) {
31158
32156
  throw new LinkHttpError(
@@ -31165,15 +32163,15 @@ function registerConversationRoutes(router, options) {
31165
32163
  ok: true,
31166
32164
  conversation: localizeConversationSummary(
31167
32165
  await conversations.createConversation({
31168
- title: readString20(body, "title") ?? void 0,
32166
+ title: readString21(body, "title") ?? void 0,
31169
32167
  profileName: readOptionalProfileName(body),
31170
32168
  accountId: auth.accountId,
31171
32169
  appInstanceId: auth.appInstanceId,
31172
32170
  workspaceId: readConversationWorkspaceId(body),
31173
- modelId: readString20(body, "model_id") ?? readString20(body, "modelId") ?? readString20(body, "model") ?? void 0,
31174
- modelProvider: readString20(body, "model_provider") ?? readString20(body, "modelProvider") ?? readString20(body, "provider") ?? void 0,
31175
- modelBaseUrl: readString20(body, "model_base_url") ?? readString20(body, "modelBaseUrl") ?? readString20(body, "base_url") ?? readString20(body, "baseUrl") ?? void 0,
31176
- 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,
31177
32175
  reasoningEffort
31178
32176
  }),
31179
32177
  language
@@ -31250,9 +32248,9 @@ function registerConversationRoutes(router, options) {
31250
32248
  const auth = await authenticateRequest(ctx, paths);
31251
32249
  const language = readPreferredLanguage(ctx);
31252
32250
  const body = await readJsonBody(ctx.req);
31253
- const content = readString20(body, "content") ?? readString20(body, "text") ?? readString20(body, "input") ?? "";
32251
+ const content = readString21(body, "content") ?? readString21(body, "text") ?? readString21(body, "input") ?? "";
31254
32252
  const attachments = readMessageAttachments(body.attachments ?? body.blobs);
31255
- const mode = readString20(body, "mode") ?? readString20(body, "send_mode");
32253
+ const mode = readString21(body, "mode") ?? readString21(body, "send_mode");
31256
32254
  if (mode && mode !== "message" && mode !== "goal") {
31257
32255
  throw new LinkHttpError(
31258
32256
  400,
@@ -31275,7 +32273,7 @@ function registerConversationRoutes(router, options) {
31275
32273
  conversationId: ctx.params.conversationId,
31276
32274
  content,
31277
32275
  attachments,
31278
- clientMessageId: readString20(body, "client_message_id") ?? readString20(body, "clientMessageId") ?? void 0,
32276
+ clientMessageId: readString21(body, "client_message_id") ?? readString21(body, "clientMessageId") ?? void 0,
31279
32277
  idempotencyKey: readHeader(ctx, "idempotency-key") ?? void 0,
31280
32278
  profileName: readOptionalProfileName(body),
31281
32279
  mode: mode === "goal" ? "goal" : "message",
@@ -31342,8 +32340,8 @@ function registerConversationRoutes(router, options) {
31342
32340
  ...localizeConversationResult(
31343
32341
  await conversations.startContextCompression({
31344
32342
  conversationId: ctx.params.conversationId,
31345
- focus: readString20(body, "focus") ?? readString20(body, "focus_topic") ?? readString20(body, "focusTopic") ?? void 0,
31346
- 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,
31347
32345
  createUserMessage: false,
31348
32346
  accountId: auth.accountId,
31349
32347
  appInstanceId: auth.appInstanceId,
@@ -31357,11 +32355,11 @@ function registerConversationRoutes(router, options) {
31357
32355
  router.patch("/api/v1/conversations/:conversationId/model", async (ctx) => {
31358
32356
  await authenticateRequest(ctx, paths);
31359
32357
  const body = await readJsonBody(ctx.req);
31360
- const modelId = readString20(body, "model_id") ?? readString20(body, "modelId") ?? readString20(body, "model");
31361
- const modelProvider = readString20(body, "model_provider") ?? readString20(body, "modelProvider") ?? readString20(body, "provider") ?? void 0;
31362
- const modelBaseUrl = readString20(body, "model_base_url") ?? readString20(body, "modelBaseUrl") ?? readString20(body, "base_url") ?? readString20(body, "baseUrl") ?? void 0;
31363
- const modelApiMode = readString20(body, "model_api_mode") ?? readString20(body, "modelApiMode") ?? readString20(body, "api_mode") ?? readString20(body, "apiMode") ?? void 0;
31364
- 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");
31365
32363
  const reasoningEffort = normalizeReasoningEffort(rawReasoningEffort);
31366
32364
  if (rawReasoningEffort && !reasoningEffort) {
31367
32365
  throw new LinkHttpError(
@@ -31411,7 +32409,7 @@ function registerConversationRoutes(router, options) {
31411
32409
  await authenticateRequest(ctx, paths);
31412
32410
  const language = readPreferredLanguage(ctx);
31413
32411
  const body = await readJsonBody(ctx.req);
31414
- 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");
31415
32413
  if (!title) {
31416
32414
  throw new LinkHttpError(400, "title_required", "title is required");
31417
32415
  }
@@ -31603,7 +32601,7 @@ function registerConversationRoutes(router, options) {
31603
32601
  async (ctx) => {
31604
32602
  await authenticateRequest(ctx, paths);
31605
32603
  const body = await readJsonBody(ctx.req);
31606
- const scope = readString20(body, "scope") ?? "always";
32604
+ const scope = readString21(body, "scope") ?? "always";
31607
32605
  ctx.body = {
31608
32606
  ok: true,
31609
32607
  ...await conversations.resolveApproval({
@@ -31653,7 +32651,7 @@ function registerConversationRoutes(router, options) {
31653
32651
  conversationId: ctx.params.conversationId,
31654
32652
  requestId: ctx.params.requestId,
31655
32653
  kind,
31656
- answer: readString20(body, "answer") ?? void 0,
32654
+ answer: readString21(body, "answer") ?? void 0,
31657
32655
  password: readRawString(body, "password"),
31658
32656
  value: readRawString(body, "value")
31659
32657
  })
@@ -31815,10 +32813,10 @@ function readConversationWorkspaceFilter(query) {
31815
32813
  }
31816
32814
  function readConversationWorkspaceId(body) {
31817
32815
  if (Object.prototype.hasOwnProperty.call(body, "workspace_id") || Object.prototype.hasOwnProperty.call(body, "workspaceId")) {
31818
- return readString20(body, "workspace_id") ?? readString20(body, "workspaceId");
32816
+ return readString21(body, "workspace_id") ?? readString21(body, "workspaceId");
31819
32817
  }
31820
32818
  if (Object.prototype.hasOwnProperty.call(body, "workspace")) {
31821
- return readString20(body, "workspace");
32819
+ return readString21(body, "workspace");
31822
32820
  }
31823
32821
  return void 0;
31824
32822
  }
@@ -31912,7 +32910,7 @@ function resolveConversationEventCursor(input) {
31912
32910
  return Math.max(queryAfter, headerAfter);
31913
32911
  }
31914
32912
  function readConversationClearPlanTargetStatus(body) {
31915
- const raw = readString20(body, "target_status") ?? readString20(body, "targetStatus") ?? "active";
32913
+ const raw = readString21(body, "target_status") ?? readString21(body, "targetStatus") ?? "active";
31916
32914
  if (raw === "active" || raw === "archived") {
31917
32915
  return raw;
31918
32916
  }
@@ -31923,7 +32921,7 @@ function readConversationClearPlanTargetStatus(body) {
31923
32921
  );
31924
32922
  }
31925
32923
  function readInputRequestKind2(body) {
31926
- const kind = readString20(body, "kind") ?? readString20(body, "type");
32924
+ const kind = readString21(body, "kind") ?? readString21(body, "type");
31927
32925
  if (kind === "clarify" || kind === "sudo" || kind === "secret") {
31928
32926
  return kind;
31929
32927
  }
@@ -32321,7 +33319,7 @@ function toHermesCronJobInput(input) {
32321
33319
  };
32322
33320
  }
32323
33321
  async function bindAndDecorateCronJobForHermesLink(input) {
32324
- const jobId = readString20(input.job, "id") ?? readString20(input.job, "job_id");
33322
+ const jobId = readString21(input.job, "id") ?? readString21(input.job, "job_id");
32325
33323
  if (!jobId) {
32326
33324
  return input.job;
32327
33325
  }
@@ -32340,9 +33338,9 @@ async function bindAndDecorateCronJobForHermesLink(input) {
32340
33338
  }
32341
33339
  function readCronJobCreateInput(body) {
32342
33340
  const input = {};
32343
- const name = readString20(body, "name") ?? readString20(body, "title");
32344
- const prompt = readString20(body, "prompt") ?? readString20(body, "description") ?? readString20(body, "task");
32345
- 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");
32346
33344
  if (!name) {
32347
33345
  throw new LinkHttpError(400, "cron_job_name_required", "name is required");
32348
33346
  }
@@ -32363,7 +33361,7 @@ function readCronJobCreateInput(body) {
32363
33361
  input.name = name;
32364
33362
  input.prompt = prompt;
32365
33363
  input.schedule = schedule;
32366
- input.deliver = readString20(body, "deliver") ?? HERMES_LINK_CRON_DELIVER;
33364
+ input.deliver = readString21(body, "deliver") ?? HERMES_LINK_CRON_DELIVER;
32367
33365
  const skills = readOptionalCronSkills(body);
32368
33366
  if (skills) {
32369
33367
  input.skills = skills;
@@ -32491,7 +33489,7 @@ function assertCronJobId(jobId) {
32491
33489
  }
32492
33490
 
32493
33491
  // src/http/routes/model-configs.ts
32494
- import { createHash as createHash9 } from "crypto";
33492
+ import { createHash as createHash10 } from "crypto";
32495
33493
 
32496
33494
  // src/model-catalog/catalog.ts
32497
33495
  import { randomInt } from "crypto";
@@ -32727,7 +33725,7 @@ function normalizeModelCapabilityCatalog(value) {
32727
33725
  const models = rawModels.map(normalizeModelCapabilityEntry).filter((entry) => entry !== null);
32728
33726
  return {
32729
33727
  schemaVersion: readPositiveInteger3(record.schemaVersion) ?? 1,
32730
- updatedAt: readString21(record.updatedAt),
33728
+ updatedAt: readString22(record.updatedAt),
32731
33729
  models
32732
33730
  };
32733
33731
  }
@@ -32736,7 +33734,7 @@ function normalizeModelCapabilityEntry(value) {
32736
33734
  return null;
32737
33735
  }
32738
33736
  const record = value;
32739
- 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);
32740
33738
  if (!canonicalId) {
32741
33739
  return null;
32742
33740
  }
@@ -32764,12 +33762,12 @@ function normalizeModelCapabilityEntry(value) {
32764
33762
  outputModalities: readStringArray2(
32765
33763
  record.outputModalities ?? record.output_modalities
32766
33764
  ),
32767
- source: readString21(record.source),
32768
- updatedAt: readString21(record.updatedAt ?? record.updated_at),
32769
- notes: readString21(record.notes)
33765
+ source: readString22(record.source),
33766
+ updatedAt: readString22(record.updatedAt ?? record.updated_at),
33767
+ notes: readString22(record.notes)
32770
33768
  };
32771
33769
  }
32772
- function readString21(value) {
33770
+ function readString22(value) {
32773
33771
  return typeof value === "string" && value.trim() ? value.trim() : null;
32774
33772
  }
32775
33773
  function readStringArray2(value) {
@@ -32779,7 +33777,7 @@ function readStringArray2(value) {
32779
33777
  const seen = /* @__PURE__ */ new Set();
32780
33778
  const values = [];
32781
33779
  for (const item of value) {
32782
- const text = readString21(item);
33780
+ const text = readString22(item);
32783
33781
  if (!text || seen.has(text)) {
32784
33782
  continue;
32785
33783
  }
@@ -32796,7 +33794,7 @@ function readPositiveInteger3(value) {
32796
33794
  return Math.floor(number);
32797
33795
  }
32798
33796
  function readReasoningSupportPolicy(value) {
32799
- const normalized = readString21(value)?.toLowerCase();
33797
+ const normalized = readString22(value)?.toLowerCase();
32800
33798
  if (!normalized || normalized === "unknown" || normalized === "auto") {
32801
33799
  return null;
32802
33800
  }
@@ -32827,7 +33825,7 @@ function readNullableBoolean(value) {
32827
33825
  if (typeof value === "boolean") {
32828
33826
  return value;
32829
33827
  }
32830
- const normalized = readString21(value)?.toLowerCase();
33828
+ const normalized = readString22(value)?.toLowerCase();
32831
33829
  if (!normalized) {
32832
33830
  return null;
32833
33831
  }
@@ -33720,7 +34718,7 @@ function modelProviderIdFromParts(input) {
33720
34718
  input.keyEnv ?? "",
33721
34719
  input.authType ?? ""
33722
34720
  ].join("");
33723
- return `mp_${createHash9("sha256").update(identity).digest("hex").slice(0, 18)}`;
34721
+ return `mp_${createHash10("sha256").update(identity).digest("hex").slice(0, 18)}`;
33724
34722
  }
33725
34723
  function mergeCredentialState(left, right) {
33726
34724
  if (left === "configured" || right === "configured") {
@@ -33976,8 +34974,8 @@ function normalizeUrlForIdentity(value) {
33976
34974
  return value.trim().replace(/\/+$/u, "").toLowerCase();
33977
34975
  }
33978
34976
  function readModelProviderConfigInput(body) {
33979
- const providerName = readString20(body, "display_name") ?? readString20(body, "displayName") ?? readString20(body, "provider_name") ?? readString20(body, "providerName") ?? readString20(body, "name");
33980
- 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");
33981
34979
  if (!providerName || !baseUrl) {
33982
34980
  throw new LinkHttpError(
33983
34981
  400,
@@ -33988,15 +34986,15 @@ function readModelProviderConfigInput(body) {
33988
34986
  return {
33989
34987
  providerName,
33990
34988
  baseUrl,
33991
- apiMode: readString20(body, "api_mode") ?? readString20(body, "apiMode") ?? readString20(body, "transport") ?? void 0,
33992
- keyEnv: readString20(body, "key_env") ?? readString20(body, "keyEnv") ?? void 0,
33993
- 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
33994
34992
  };
33995
34993
  }
33996
34994
  function readModelConfigInput(body) {
33997
- const id = readString20(body, "id") ?? readString20(body, "model_id") ?? readString20(body, "modelId");
33998
- const provider = readString20(body, "provider") ?? readString20(body, "provider_key") ?? readString20(body, "providerKey");
33999
- 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");
34000
34998
  if (!id || !provider || !baseUrl) {
34001
34999
  throw new LinkHttpError(
34002
35000
  400,
@@ -34006,22 +35004,22 @@ function readModelConfigInput(body) {
34006
35004
  }
34007
35005
  return {
34008
35006
  id,
34009
- originalModelId: readString20(body, "original_model_id") ?? readString20(body, "originalModelId") ?? readString20(body, "original_id") ?? void 0,
34010
- originalProvider: readString20(body, "original_provider") ?? readString20(body, "originalProvider") ?? readString20(body, "original_provider_key") ?? readString20(body, "originalProviderKey") ?? void 0,
34011
- originalBaseUrl: readString20(body, "original_base_url") ?? readString20(body, "originalBaseUrl") ?? void 0,
34012
- 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,
34013
35011
  provider,
34014
- providerName: readString20(body, "provider_name") ?? readString20(body, "providerName") ?? void 0,
35012
+ providerName: readString21(body, "provider_name") ?? readString21(body, "providerName") ?? void 0,
34015
35013
  baseUrl,
34016
- apiKey: readString20(body, "api_key") ?? readString20(body, "apiKey") ?? void 0,
34017
- 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,
34018
35016
  contextLength: readPositiveInteger2(
34019
35017
  body.context_length ?? body.contextLength
34020
35018
  ),
34021
- keyEnv: readString20(body, "key_env") ?? readString20(body, "keyEnv") ?? void 0,
35019
+ keyEnv: readString21(body, "key_env") ?? readString21(body, "keyEnv") ?? void 0,
34022
35020
  setDefault: readBoolean3(body.set_default ?? body.setDefault),
34023
- reasoningEffort: readString20(body, "reasoning_effort") ?? readString20(body, "reasoningEffort") ?? void 0,
34024
- 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,
34025
35023
  supportsVision: readNullableBoolean2(
34026
35024
  body.supports_vision ?? body.supportsVision
34027
35025
  )
@@ -34029,28 +35027,28 @@ function readModelConfigInput(body) {
34029
35027
  }
34030
35028
  function readModelDefaultsInput(body) {
34031
35029
  return {
34032
- taskModelId: readString20(body, "task_model_id") ?? readString20(body, "taskModelId") ?? readString20(body, "default_model_id") ?? readString20(body, "defaultModelId") ?? void 0,
34033
- taskModelProvider: readString20(body, "task_model_provider") ?? readString20(body, "taskModelProvider") ?? readString20(body, "default_model_provider") ?? readString20(body, "defaultModelProvider") ?? void 0,
34034
- taskModelBaseUrl: readString20(body, "task_model_base_url") ?? readString20(body, "taskModelBaseUrl") ?? readString20(body, "default_model_base_url") ?? readString20(body, "defaultModelBaseUrl") ?? void 0,
34035
- taskModelApiMode: readString20(body, "task_model_api_mode") ?? readString20(body, "taskModelApiMode") ?? readString20(body, "default_model_api_mode") ?? readString20(body, "defaultModelApiMode") ?? void 0,
34036
- compressionModelId: readString20(body, "compression_model_id") ?? readString20(body, "compressionModelId") ?? void 0,
34037
- compressionModelProvider: readString20(body, "compression_model_provider") ?? readString20(body, "compressionModelProvider") ?? void 0,
34038
- compressionModelBaseUrl: readString20(body, "compression_model_base_url") ?? readString20(body, "compressionModelBaseUrl") ?? void 0,
34039
- compressionModelApiMode: readString20(body, "compression_model_api_mode") ?? readString20(body, "compressionModelApiMode") ?? void 0,
34040
- reasoningEffort: readString20(body, "reasoning_effort") ?? readString20(body, "reasoningEffort") ?? readString20(body, "default_reasoning_effort") ?? readString20(body, "defaultReasoningEffort") ?? void 0,
34041
- 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
34042
35040
  };
34043
35041
  }
34044
35042
  function readModelDeleteInput(body) {
34045
- const id = readString20(body, "model_id") ?? readString20(body, "modelId");
35043
+ const id = readString21(body, "model_id") ?? readString21(body, "modelId");
34046
35044
  if (!id) {
34047
35045
  throw new LinkHttpError(400, "model_id_required", "model_id is required");
34048
35046
  }
34049
35047
  return {
34050
35048
  id,
34051
- provider: readString20(body, "provider") ?? readString20(body, "provider_key") ?? readString20(body, "providerKey") ?? void 0,
34052
- baseUrl: readString20(body, "base_url") ?? readString20(body, "baseUrl") ?? void 0,
34053
- 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
34054
35052
  };
34055
35053
  }
34056
35054
  function readNullableBoolean2(value) {
@@ -34073,8 +35071,8 @@ function readNullableBoolean2(value) {
34073
35071
  return void 0;
34074
35072
  }
34075
35073
  function readModelConfigImportInput(body) {
34076
- const sourceProfileName = readString20(body, "source_profile") ?? readString20(body, "sourceProfile") ?? readString20(body, "source_profile_name") ?? readString20(body, "sourceProfileName");
34077
- 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");
34078
35076
  if (!sourceProfileName || !modelId) {
34079
35077
  throw new LinkHttpError(
34080
35078
  400,
@@ -34085,9 +35083,9 @@ function readModelConfigImportInput(body) {
34085
35083
  return {
34086
35084
  sourceProfileName,
34087
35085
  modelId,
34088
- provider: readString20(body, "provider") ?? readString20(body, "provider_key") ?? readString20(body, "providerKey") ?? void 0,
34089
- baseUrl: readString20(body, "base_url") ?? readString20(body, "baseUrl") ?? void 0,
34090
- 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,
34091
35089
  setDefault: readBoolean3(body.set_default ?? body.setDefault)
34092
35090
  };
34093
35091
  }
@@ -35392,16 +36390,16 @@ function readProfilePermissionsInput(body) {
35392
36390
  const approvals = readOptionalObject(body, "approvals");
35393
36391
  if (approvals) {
35394
36392
  input.approvals = {
35395
- 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,
35396
36394
  timeout: readPositiveInteger2(approvals.timeout),
35397
- cronMode: readString20(approvals, "cron_mode") ?? readString20(approvals, "cronMode") ?? void 0
36395
+ cronMode: readString21(approvals, "cron_mode") ?? readString21(approvals, "cronMode") ?? void 0
35398
36396
  };
35399
36397
  }
35400
36398
  const terminal = readOptionalObject(body, "terminal");
35401
36399
  if (terminal) {
35402
36400
  input.terminal = {
35403
- backend: readString20(terminal, "backend") ?? void 0,
35404
- cwd: readString20(terminal, "cwd") ?? void 0,
36401
+ backend: readString21(terminal, "backend") ?? void 0,
36402
+ cwd: readString21(terminal, "cwd") ?? void 0,
35405
36403
  containerCpu: readPositiveInteger2(
35406
36404
  terminal.container_cpu ?? terminal.containerCpu
35407
36405
  ),
@@ -35732,9 +36730,9 @@ async function testHindsightProviderSettings(profileName, patch) {
35732
36730
  const mode = normalizeHindsightMode(
35733
36731
  patch.mode ?? config.mode ?? env.HINDSIGHT_MODE
35734
36732
  );
35735
- const apiUrl = readString22(patch.apiUrl) ?? readString22(config.api_url) ?? env.HINDSIGHT_API_URL ?? (mode === "cloud" ? HINDSIGHT_DEFAULT_API_URL : HINDSIGHT_DEFAULT_LOCAL_URL);
35736
- const bankId = readString22(patch.bankId) ?? readString22(config.bank_id) ?? "hermes";
35737
- 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);
35738
36736
  const baseUrl = normalizeHttpUrl2(apiUrl);
35739
36737
  if (!baseUrl) {
35740
36738
  return {
@@ -36268,7 +37266,7 @@ async function readProviderConfigurationStatus(profileName, provider) {
36268
37266
  const config2 = await readJsonObject(
36269
37267
  memoryProviderConfigPath(profileName, "honcho") ?? ""
36270
37268
  );
36271
- 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 } : {
36272
37270
  configured: false,
36273
37271
  issue: "Honcho \u9700\u8981\u5148\u586B\u5199 API Key\uFF0C\u6216\u5728 honcho.json \u914D\u7F6E self-hosted baseUrl\u3002"
36274
37272
  };
@@ -36277,7 +37275,7 @@ async function readProviderConfigurationStatus(profileName, provider) {
36277
37275
  const config2 = await readJsonObject(
36278
37276
  memoryProviderConfigPath(profileName, "mem0") ?? ""
36279
37277
  );
36280
- 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 } : {
36281
37279
  configured: false,
36282
37280
  issue: "Mem0 \u9700\u8981\u5148\u5728\u672C\u9875\u586B\u5199 API Key\uFF0CLink \u4F1A\u5199\u5165\u5F53\u524D Profile \u7684 .env\u3002"
36283
37281
  };
@@ -36319,7 +37317,7 @@ async function readProviderConfigurationStatus(profileName, provider) {
36319
37317
  memoryProviderConfigPath(profileName, provider) ?? ""
36320
37318
  );
36321
37319
  const mode = normalizeHindsightMode(config.mode ?? env.HINDSIGHT_MODE);
36322
- 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;
36323
37321
  if (mode === "cloud") {
36324
37322
  return isConfiguredEnvValue(apiKey) ? { configured: true, issue: null } : {
36325
37323
  configured: false,
@@ -36327,15 +37325,15 @@ async function readProviderConfigurationStatus(profileName, provider) {
36327
37325
  };
36328
37326
  }
36329
37327
  if (mode === "local_external") {
36330
- 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;
36331
37329
  return isConfiguredEnvValue(apiUrl) ? { configured: true, issue: null } : {
36332
37330
  configured: false,
36333
37331
  issue: "Hindsight local_external \u9700\u8981\u914D\u7F6E\u53EF\u8BBF\u95EE\u7684 API URL\u3002"
36334
37332
  };
36335
37333
  }
36336
37334
  if (mode === "local_embedded") {
36337
- const llmProvider = readString22(config.llm_provider) ?? "openai";
36338
- const llmModel = readString22(config.llm_model);
37335
+ const llmProvider = readString23(config.llm_provider) ?? "openai";
37336
+ const llmModel = readString23(config.llm_model);
36339
37337
  if (!llmModel) {
36340
37338
  return {
36341
37339
  configured: false,
@@ -36343,7 +37341,7 @@ async function readProviderConfigurationStatus(profileName, provider) {
36343
37341
  };
36344
37342
  }
36345
37343
  if (llmProvider === "openai_compatible" && !isConfiguredEnvValue(
36346
- readString22(config.llm_base_url) ?? env.HINDSIGHT_API_LLM_BASE_URL
37344
+ readString23(config.llm_base_url) ?? env.HINDSIGHT_API_LLM_BASE_URL
36347
37345
  )) {
36348
37346
  return {
36349
37347
  configured: false,
@@ -36351,7 +37349,7 @@ async function readProviderConfigurationStatus(profileName, provider) {
36351
37349
  };
36352
37350
  }
36353
37351
  if (!["ollama", "lmstudio", "openai_compatible"].includes(llmProvider) && !isConfiguredEnvValue(
36354
- 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
36355
37353
  )) {
36356
37354
  return {
36357
37355
  configured: false,
@@ -36407,8 +37405,8 @@ async function readProviderSettings(profileName, provider) {
36407
37405
  secretSetting(
36408
37406
  "apiKey",
36409
37407
  "API Key",
36410
- env.HONCHO_API_KEY ?? readString22(config.apiKey) ?? readString22(config.api_key),
36411
- 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))
36412
37410
  ),
36413
37411
  stringSetting("workspace", "Workspace", config.workspace ?? "hermes"),
36414
37412
  stringSetting("peerName", "\u7528\u6237 Peer", config.peerName ?? ""),
@@ -36450,8 +37448,8 @@ async function readProviderSettings(profileName, provider) {
36450
37448
  secretSetting(
36451
37449
  "apiKey",
36452
37450
  "API Key",
36453
- env.MEM0_API_KEY ?? readString22(config.apiKey) ?? readString22(config.api_key),
36454
- 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))
36455
37453
  ),
36456
37454
  stringSetting("userId", "User ID", config.user_id ?? "hermes-user"),
36457
37455
  stringSetting("agentId", "Agent ID", config.agent_id ?? "hermes"),
@@ -36536,8 +37534,8 @@ async function readProviderSettings(profileName, provider) {
36536
37534
  secretSetting(
36537
37535
  "apiKey",
36538
37536
  "Hindsight API Key",
36539
- env.HINDSIGHT_API_KEY ?? readString22(config.apiKey) ?? readString22(config.api_key),
36540
- 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))
36541
37539
  ),
36542
37540
  stringSetting(
36543
37541
  "bankId",
@@ -36559,8 +37557,8 @@ async function readProviderSettings(profileName, provider) {
36559
37557
  secretSetting(
36560
37558
  "llmApiKey",
36561
37559
  "LLM API Key",
36562
- env.HINDSIGHT_LLM_API_KEY ?? readString22(config.llmApiKey) ?? readString22(config.llm_api_key),
36563
- 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))
36564
37562
  ),
36565
37563
  booleanSetting("autoRecall", "\u81EA\u52A8\u56DE\u5FC6", config.auto_recall ?? true),
36566
37564
  booleanSetting("autoRetain", "\u81EA\u52A8\u6C89\u6DC0", config.auto_retain ?? true),
@@ -36679,11 +37677,11 @@ async function readCustomProviderRegistry(profileName) {
36679
37677
  return { id: id2, label: id2, description: "\u81EA\u5B9A\u4E49 memory provider\u3002" };
36680
37678
  }
36681
37679
  const record = toRecord21(item);
36682
- const id = normalizeCustomProviderId(readString22(record.id) ?? "");
37680
+ const id = normalizeCustomProviderId(readString23(record.id) ?? "");
36683
37681
  return {
36684
37682
  id,
36685
- label: readString22(record.label) ?? id,
36686
- 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"
36687
37685
  };
36688
37686
  }).filter((item) => item.id);
36689
37687
  } catch {
@@ -36732,8 +37730,8 @@ async function discoverUserMemoryProviderDescriptors(profileName) {
36732
37730
  const meta = await readPluginMetadata(providerDir);
36733
37731
  descriptors.push({
36734
37732
  id: providerId,
36735
- label: readString22(meta.name) ?? providerId,
36736
- 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"
36737
37735
  });
36738
37736
  }
36739
37737
  return descriptors;
@@ -36895,7 +37893,7 @@ function isMemoryEnvKeyWritable(key) {
36895
37893
  ].includes(key);
36896
37894
  }
36897
37895
  function normalizeHindsightMode(value) {
36898
- const mode = readString22(value) ?? "cloud";
37896
+ const mode = readString23(value) ?? "cloud";
36899
37897
  return mode === "local" ? "local_embedded" : mode;
36900
37898
  }
36901
37899
  function normalizeHttpUrl2(value) {
@@ -36964,27 +37962,27 @@ function parseJsonObject2(text) {
36964
37962
  }
36965
37963
  }
36966
37964
  function readHindsightError(json) {
36967
- const detail = readString22(json.detail) ?? readString22(json.error);
37965
+ const detail = readString23(json.detail) ?? readString23(json.error);
36968
37966
  return detail ? `\uFF1A${detail}` : "";
36969
37967
  }
36970
37968
  function hindsightSemanticIssue(pathName, json) {
36971
37969
  if (pathName === "/health") {
36972
- const status = readString22(json.status);
37970
+ const status = readString23(json.status);
36973
37971
  return status && ["healthy", "ok"].includes(status.toLowerCase()) ? null : `\u5065\u5EB7\u72B6\u6001\u5F02\u5E38\uFF1A${status ?? "unknown"}`;
36974
37972
  }
36975
37973
  return null;
36976
37974
  }
36977
37975
  function summarizeHindsightProbe(pathName, json) {
36978
37976
  if (pathName === "/health") {
36979
- const status = readString22(json.status) ?? "ok";
36980
- const database = readString22(json.database);
37977
+ const status = readString23(json.status) ?? "ok";
37978
+ const database = readString23(json.database);
36981
37979
  return database ? `${status}, database ${database}` : status;
36982
37980
  }
36983
37981
  if (pathName === "/version") {
36984
- const version = readString22(json.api_version);
37982
+ const version = readString23(json.api_version);
36985
37983
  return version ? `API ${version}` : "version endpoint reachable";
36986
37984
  }
36987
- const bankId = readString22(json.bank_id);
37985
+ const bankId = readString23(json.bank_id);
36988
37986
  return bankId ? `bank ${bankId} reachable` : "bank config reachable";
36989
37987
  }
36990
37988
  async function readActiveMemoryProvider(profileName) {
@@ -36999,7 +37997,7 @@ async function readActiveMemoryProvider(profileName) {
36999
37997
  });
37000
37998
  const config = raw ? toRecord21(YAML6.parse(raw)) : {};
37001
37999
  const memory = toRecord21(config.memory);
37002
- const provider = readString22(memory.provider);
38000
+ const provider = readString23(memory.provider);
37003
38001
  if (!provider || provider === "built-in" || provider === "builtin" || provider === "built_in") {
37004
38002
  return null;
37005
38003
  }
@@ -37056,7 +38054,7 @@ function stringSetting(key, label, value, editable = true) {
37056
38054
  return {
37057
38055
  key,
37058
38056
  label,
37059
- value: readString22(value) ?? "",
38057
+ value: readString23(value) ?? "",
37060
38058
  editable,
37061
38059
  kind: "string"
37062
38060
  };
@@ -37068,7 +38066,7 @@ function secretSetting(key, label, value, configured) {
37068
38066
  value: "",
37069
38067
  editable: true,
37070
38068
  kind: "secret",
37071
- configured: configured || isConfiguredEnvValue(readString22(value))
38069
+ configured: configured || isConfiguredEnvValue(readString23(value))
37072
38070
  };
37073
38071
  }
37074
38072
  function textSetting(key, label, value, editable = true) {
@@ -37081,7 +38079,7 @@ function textSetting(key, label, value, editable = true) {
37081
38079
  };
37082
38080
  }
37083
38081
  function selectSetting(key, label, value, options, editable = true) {
37084
- const stringValue = readString22(value) ?? options[0] ?? null;
38082
+ const stringValue = readString23(value) ?? options[0] ?? null;
37085
38083
  return { key, label, value: stringValue, editable, kind: "select", options };
37086
38084
  }
37087
38085
  async function readMemoryLimits(profileName) {
@@ -37154,7 +38152,7 @@ function hashString(value) {
37154
38152
  function toRecord21(value) {
37155
38153
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
37156
38154
  }
37157
- function readString22(value) {
38155
+ function readString23(value) {
37158
38156
  return typeof value === "string" && value.trim() ? value.trim() : null;
37159
38157
  }
37160
38158
  function readPositiveInteger4(value) {
@@ -37313,7 +38311,7 @@ function registerProfileMemoryRoutes(router, options) {
37313
38311
  );
37314
38312
  }
37315
38313
  function readMemoryTarget(body) {
37316
- const raw = readString20(body, "target");
38314
+ const raw = readString21(body, "target");
37317
38315
  if (raw === "memory" || raw === "user") {
37318
38316
  return raw;
37319
38317
  }
@@ -37324,7 +38322,7 @@ function readMemoryTarget(body) {
37324
38322
  );
37325
38323
  }
37326
38324
  function readMemoryResetTarget(body) {
37327
- const raw = readString20(body, "target") ?? "all";
38325
+ const raw = readString21(body, "target") ?? "all";
37328
38326
  if (raw === "all" || raw === "memory" || raw === "user") {
37329
38327
  return raw;
37330
38328
  }
@@ -37335,7 +38333,7 @@ function readMemoryResetTarget(body) {
37335
38333
  );
37336
38334
  }
37337
38335
  function readRequiredMemoryContent(body) {
37338
- const content = readString20(body, "content") ?? readString20(body, "text");
38336
+ const content = readString21(body, "content") ?? readString21(body, "text");
37339
38337
  if (!content) {
37340
38338
  throw new LinkHttpError(
37341
38339
  400,
@@ -37346,7 +38344,7 @@ function readRequiredMemoryContent(body) {
37346
38344
  return content;
37347
38345
  }
37348
38346
  function readRequiredMemoryMatch(body) {
37349
- 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");
37350
38348
  if (!oldText) {
37351
38349
  throw new LinkHttpError(
37352
38350
  400,
@@ -37357,7 +38355,7 @@ function readRequiredMemoryMatch(body) {
37357
38355
  return oldText;
37358
38356
  }
37359
38357
  function readRequiredMemoryProvider(body) {
37360
- 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");
37361
38359
  if (!provider) {
37362
38360
  throw new LinkHttpError(
37363
38361
  400,
@@ -37387,7 +38385,7 @@ function readMemorySettingsPatch(body, options = {}) {
37387
38385
  input.userCharLimit = userCharLimit;
37388
38386
  }
37389
38387
  }
37390
- const mode = readString20(body, "mode");
38388
+ const mode = readString21(body, "mode");
37391
38389
  if (mode) {
37392
38390
  input.mode = mode;
37393
38391
  }
@@ -37403,7 +38401,7 @@ function readMemorySettingsPatch(body, options = {}) {
37403
38401
  if (bankId !== void 0) {
37404
38402
  input.bankId = bankId;
37405
38403
  }
37406
- const llmProvider = readString20(body, "llm_provider") ?? readString20(body, "llmProvider");
38404
+ const llmProvider = readString21(body, "llm_provider") ?? readString21(body, "llmProvider");
37407
38405
  if (llmProvider) {
37408
38406
  input.llmProvider = llmProvider;
37409
38407
  }
@@ -37439,11 +38437,11 @@ function readMemorySettingsPatch(body, options = {}) {
37439
38437
  if (autoRetain !== void 0) {
37440
38438
  input.autoRetain = autoRetain;
37441
38439
  }
37442
- const memoryMode = readString20(body, "memory_mode") ?? readString20(body, "memoryMode");
38440
+ const memoryMode = readString21(body, "memory_mode") ?? readString21(body, "memoryMode");
37443
38441
  if (memoryMode) {
37444
38442
  input.memoryMode = memoryMode;
37445
38443
  }
37446
- const recallBudget = readString20(body, "recall_budget") ?? readString20(body, "recallBudget");
38444
+ const recallBudget = readString21(body, "recall_budget") ?? readString21(body, "recallBudget");
37447
38445
  if (recallBudget) {
37448
38446
  input.recallBudget = recallBudget;
37449
38447
  }
@@ -37459,11 +38457,11 @@ function readMemorySettingsPatch(body, options = {}) {
37459
38457
  if (profileFrequency !== void 0) {
37460
38458
  input.profileFrequency = profileFrequency;
37461
38459
  }
37462
- const captureMode = readString20(body, "capture_mode") ?? readString20(body, "captureMode");
38460
+ const captureMode = readString21(body, "capture_mode") ?? readString21(body, "captureMode");
37463
38461
  if (captureMode) {
37464
38462
  input.captureMode = captureMode;
37465
38463
  }
37466
- const searchMode = readString20(body, "search_mode") ?? readString20(body, "searchMode");
38464
+ const searchMode = readString21(body, "search_mode") ?? readString21(body, "searchMode");
37467
38465
  if (searchMode) {
37468
38466
  input.searchMode = searchMode;
37469
38467
  }
@@ -37497,11 +38495,11 @@ function readMemorySettingsPatch(body, options = {}) {
37497
38495
  if (aiPeer !== void 0) {
37498
38496
  input.aiPeer = aiPeer;
37499
38497
  }
37500
- const recallMode = readString20(body, "recall_mode") ?? readString20(body, "recallMode");
38498
+ const recallMode = readString21(body, "recall_mode") ?? readString21(body, "recallMode");
37501
38499
  if (recallMode) {
37502
38500
  input.recallMode = recallMode;
37503
38501
  }
37504
- const writeFrequency = readString20(body, "write_frequency") ?? readString20(body, "writeFrequency");
38502
+ const writeFrequency = readString21(body, "write_frequency") ?? readString21(body, "writeFrequency");
37505
38503
  if (writeFrequency) {
37506
38504
  input.writeFrequency = writeFrequency;
37507
38505
  }
@@ -37509,7 +38507,7 @@ function readMemorySettingsPatch(body, options = {}) {
37509
38507
  if (saveMessages !== void 0) {
37510
38508
  input.saveMessages = saveMessages;
37511
38509
  }
37512
- const sessionStrategy = readString20(body, "session_strategy") ?? readString20(body, "sessionStrategy");
38510
+ const sessionStrategy = readString21(body, "session_strategy") ?? readString21(body, "sessionStrategy");
37513
38511
  if (sessionStrategy) {
37514
38512
  input.sessionStrategy = sessionStrategy;
37515
38513
  }
@@ -37789,13 +38787,13 @@ async function readSkillMetadata(input) {
37789
38787
  const skillDir = path32.dirname(input.skillFile);
37790
38788
  const { frontmatter, body } = parseSkillDocument(raw.slice(0, 4e3));
37791
38789
  const name = normalizeSkillName(
37792
- readString23(frontmatter.name) ?? path32.basename(skillDir)
38790
+ readString24(frontmatter.name) ?? path32.basename(skillDir)
37793
38791
  );
37794
38792
  if (!name) {
37795
38793
  return null;
37796
38794
  }
37797
38795
  const description = normalizeDescription(
37798
- readString23(frontmatter.description) ?? firstBodyDescription(body)
38796
+ readString24(frontmatter.description) ?? firstBodyDescription(body)
37799
38797
  );
37800
38798
  const provenance = input.provenance.get(name) ?? {
37801
38799
  source: "local",
@@ -37922,8 +38920,8 @@ async function readHubInstalledSkills(root) {
37922
38920
  for (const [name, rawEntry] of Object.entries(installed2)) {
37923
38921
  const entry = toRecord22(rawEntry);
37924
38922
  result.set(normalizeSkillName(name), {
37925
- source: readString23(entry.source) ?? "hub",
37926
- trust: readString23(entry.trust_level) ?? null
38923
+ source: readString24(entry.source) ?? "hub",
38924
+ trust: readString24(entry.trust_level) ?? null
37927
38925
  });
37928
38926
  }
37929
38927
  return result;
@@ -38007,7 +39005,7 @@ function readStringList6(value) {
38007
39005
  }
38008
39006
  return value.filter((item) => typeof item === "string").map((item) => item.trim()).filter(Boolean);
38009
39007
  }
38010
- function readString23(value) {
39008
+ function readString24(value) {
38011
39009
  return typeof value === "string" && value.trim() ? value.trim() : null;
38012
39010
  }
38013
39011
  function toRecord22(value) {
@@ -38379,7 +39377,7 @@ function registerRunRoutes(router, options) {
38379
39377
  await authenticateRequest(ctx, paths);
38380
39378
  const language = readPreferredLanguage(ctx);
38381
39379
  const body = await readJsonBody(ctx.req);
38382
- const input = readString20(body, "input");
39380
+ const input = readString21(body, "input");
38383
39381
  if (!input) {
38384
39382
  throw new LinkHttpError(400, "run_input_required", "input is required");
38385
39383
  }
@@ -38387,12 +39385,12 @@ function registerRunRoutes(router, options) {
38387
39385
  ctx.body = await createHermesRun(
38388
39386
  {
38389
39387
  input,
38390
- instructions: readString20(body, "instructions") ?? void 0,
39388
+ instructions: readString21(body, "instructions") ?? void 0,
38391
39389
  conversation_history: readConversationHistory(
38392
39390
  body.conversation_history ?? body.conversationHistory
38393
39391
  ),
38394
- session_id: readString20(body, "session_id") ?? readString20(body, "sessionId") ?? void 0,
38395
- 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
38396
39394
  },
38397
39395
  { logger, profileName: readOptionalProfileName(body), language }
38398
39396
  );
@@ -38805,20 +39803,20 @@ function normalizeServerReleaseSnapshot(payload) {
38805
39803
  const remote = toNullableRecord(snapshot.remote);
38806
39804
  return {
38807
39805
  remote: remote ? normalizeServerRelease(remote) : null,
38808
- cacheState: readString24(snapshot, "cache_state") ?? readString24(snapshot, "cacheState"),
38809
- issue: readString24(snapshot, "issue")
39806
+ cacheState: readString25(snapshot, "cache_state") ?? readString25(snapshot, "cacheState"),
39807
+ issue: readString25(snapshot, "issue")
38810
39808
  };
38811
39809
  }
38812
39810
  function normalizeServerRelease(payload) {
38813
- const tag = readString24(payload, "tag");
38814
- const name = readString24(payload, "name");
39811
+ const tag = readString25(payload, "tag");
39812
+ const name = readString25(payload, "name");
38815
39813
  return {
38816
- version: readString24(payload, "version") ?? extractSemver(name) ?? extractTagSemver(tag),
39814
+ version: readString25(payload, "version") ?? extractSemver(name) ?? extractTagSemver(tag),
38817
39815
  tag,
38818
39816
  name,
38819
- releaseUrl: readString24(payload, "releaseUrl") ?? readString24(payload, "release_url"),
38820
- publishedAt: readString24(payload, "publishedAt") ?? readString24(payload, "published_at"),
38821
- 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()
38822
39820
  };
38823
39821
  }
38824
39822
  async function readReleaseCache(paths) {
@@ -38944,7 +39942,7 @@ function isRecentRunningState2(state) {
38944
39942
  const startedAt = Date.parse(state.started_at);
38945
39943
  return Number.isFinite(startedAt) && Date.now() - startedAt < 3e4;
38946
39944
  }
38947
- function readString24(payload, key) {
39945
+ function readString25(payload, key) {
38948
39946
  const value = payload[key];
38949
39947
  return typeof value === "string" && value.trim() ? value.trim() : null;
38950
39948
  }
@@ -39119,12 +40117,12 @@ async function fetchRelayStreamBatchPolicy(serverBaseUrl, options = {}) {
39119
40117
  }
39120
40118
  }
39121
40119
  function readRelayStreamBatchPolicy(input) {
39122
- const record = readRecord(input);
39123
- 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;
39124
40122
  return normalizeRelayStreamBatchPolicy(body);
39125
40123
  }
39126
40124
  function normalizeRelayStreamBatchPolicy(input) {
39127
- const record = readRecord(input);
40125
+ const record = readRecord2(input);
39128
40126
  if (!record) {
39129
40127
  return null;
39130
40128
  }
@@ -39138,7 +40136,7 @@ function normalizeRelayStreamBatchPolicy(input) {
39138
40136
  flushBytes
39139
40137
  };
39140
40138
  }
39141
- function readRecord(value) {
40139
+ function readRecord2(value) {
39142
40140
  return value && typeof value === "object" ? value : null;
39143
40141
  }
39144
40142
  function readInteger5(value) {
@@ -40142,7 +41140,7 @@ async function reportLinkStatusToServer(options = {}) {
40142
41140
  public_ipv6s: routes.publicIpv6s,
40143
41141
  reported_at: (/* @__PURE__ */ new Date()).toISOString()
40144
41142
  };
40145
- const signature = signIdentityPayload(identity, canonicalJson2(payload));
41143
+ const signature = signIdentityPayload(identity, canonicalJson3(payload));
40146
41144
  const fetcher = options.fetchImpl ?? fetch;
40147
41145
  const response = await fetcher(
40148
41146
  `${config.serverBaseUrl.replace(/\/+$/u, "")}/api/v1/links/${encodeURIComponent(identity.link_id)}/report`,
@@ -40161,30 +41159,30 @@ async function reportLinkStatusToServer(options = {}) {
40161
41159
  );
40162
41160
  const body = await response.json().catch(() => null);
40163
41161
  if (!response.ok || !body) {
40164
- 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}`;
40165
41163
  throw new LinkHttpError(response.status, "server_request_failed", message);
40166
41164
  }
40167
41165
  await markNetworkStatusReported(paths, routes);
40168
41166
  return body;
40169
41167
  }
40170
- function canonicalJson2(value) {
40171
- return JSON.stringify(sortJsonValue2(value));
41168
+ function canonicalJson3(value) {
41169
+ return JSON.stringify(sortJsonValue3(value));
40172
41170
  }
40173
- function sortJsonValue2(value) {
41171
+ function sortJsonValue3(value) {
40174
41172
  if (Array.isArray(value)) {
40175
- return value.map(sortJsonValue2);
41173
+ return value.map(sortJsonValue3);
40176
41174
  }
40177
41175
  if (value && typeof value === "object") {
40178
41176
  const record = value;
40179
41177
  const sorted = {};
40180
41178
  for (const key of Object.keys(record).sort()) {
40181
- sorted[key] = sortJsonValue2(record[key]);
41179
+ sorted[key] = sortJsonValue3(record[key]);
40182
41180
  }
40183
41181
  return sorted;
40184
41182
  }
40185
41183
  return value;
40186
41184
  }
40187
- function readErrorMessage4(payload) {
41185
+ function readErrorMessage5(payload) {
40188
41186
  if (typeof payload !== "object" || payload === null) {
40189
41187
  return null;
40190
41188
  }
@@ -40204,20 +41202,23 @@ function startLanIpMonitor(options) {
40204
41202
  let running = false;
40205
41203
  let closed = false;
40206
41204
  let current = Promise.resolve();
40207
- const check = async (context = {}) => {
41205
+ const check = (context = {}) => {
40208
41206
  if (running || closed) {
40209
- return;
41207
+ return current;
40210
41208
  }
40211
41209
  running = true;
40212
- try {
40213
- await checkLanIpChange(options, context);
40214
- } catch (error) {
40215
- void options.logger.warn("lan_ip_monitor_failed", {
40216
- error: error instanceof Error ? error.message : String(error)
40217
- });
40218
- } finally {
40219
- running = false;
40220
- }
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;
40221
41222
  };
40222
41223
  current = check({ forceReport: true, publishToRelay: true, observePublicRoute: true });
40223
41224
  const timer = setInterval(() => {
@@ -40931,7 +41932,8 @@ async function startDaemonProcess(paths = resolveRuntimePaths()) {
40931
41932
  const child = spawn5(process.execPath, [scriptPath, "daemon-supervisor"], {
40932
41933
  detached: true,
40933
41934
  stdio: "ignore",
40934
- env: process.env
41935
+ env: process.env,
41936
+ windowsHide: true
40935
41937
  });
40936
41938
  child.unref();
40937
41939
  for (let index = 0; index < 12; index += 1) {
@@ -40984,7 +41986,8 @@ async function runDaemonSupervisor(paths = resolveRuntimePaths()) {
40984
41986
  const startedAt = Date.now();
40985
41987
  child = spawn5(process.execPath, [scriptPath, "daemon", "--foreground"], {
40986
41988
  stdio: ["ignore", "pipe", "pipe"],
40987
- env: process.env
41989
+ env: process.env,
41990
+ windowsHide: true
40988
41991
  });
40989
41992
  const childPid = child.pid ?? null;
40990
41993
  child.stdout?.on("data", write);
@@ -41725,16 +42728,16 @@ function normalizeServerSnapshot(payload) {
41725
42728
  if (!policy) {
41726
42729
  return {
41727
42730
  remote: null,
41728
- issue: readString25(snapshot, "issue")
42731
+ issue: readString26(snapshot, "issue")
41729
42732
  };
41730
42733
  }
41731
42734
  const release = toNullableRecord2(snapshot.release);
41732
- const currentVersion = readString25(policy, "current_version") ?? readString25(policy, "currentVersion");
41733
- 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");
41734
42737
  if (!currentVersion) {
41735
42738
  return {
41736
42739
  remote: null,
41737
- issue: readString25(snapshot, "issue")
42740
+ issue: readString26(snapshot, "issue")
41738
42741
  };
41739
42742
  }
41740
42743
  return {
@@ -41742,10 +42745,10 @@ function normalizeServerSnapshot(payload) {
41742
42745
  current_version: currentVersion,
41743
42746
  min_safe_version: minSafeVersion,
41744
42747
  target_version: currentVersion,
41745
- release_url: release ? readString25(release, "release_url") ?? readString25(release, "releaseUrl") : null,
41746
- 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
41747
42750
  },
41748
- issue: readString25(snapshot, "issue")
42751
+ issue: readString26(snapshot, "issue")
41749
42752
  };
41750
42753
  }
41751
42754
  async function fetchCurrentLinkReleaseFromServer(options, fetcher, channel) {
@@ -42159,7 +43162,7 @@ function toRecord25(value) {
42159
43162
  function toNullableRecord2(value) {
42160
43163
  return typeof value === "object" && value !== null ? value : null;
42161
43164
  }
42162
- function readString25(payload, key) {
43165
+ function readString26(payload, key) {
42163
43166
  const value = payload[key];
42164
43167
  return typeof value === "string" && value.trim() ? value.trim() : null;
42165
43168
  }
@@ -42239,7 +43242,7 @@ async function postJson(fetcher, url, token, body) {
42239
43242
  }
42240
43243
  const payload = await response.json().catch(() => null);
42241
43244
  if (!response.ok) {
42242
- const message = readErrorMessage5(payload) ?? `Relay request failed with HTTP ${response.status}`;
43245
+ const message = readErrorMessage6(payload) ?? `Relay request failed with HTTP ${response.status}`;
42243
43246
  throw new Error(message);
42244
43247
  }
42245
43248
  if (!payload) {
@@ -42247,7 +43250,7 @@ async function postJson(fetcher, url, token, body) {
42247
43250
  }
42248
43251
  return payload;
42249
43252
  }
42250
- function readErrorMessage5(payload) {
43253
+ function readErrorMessage6(payload) {
42251
43254
  if (typeof payload !== "object" || payload === null) {
42252
43255
  return null;
42253
43256
  }
@@ -42581,12 +43584,12 @@ async function patchServerJson(serverBaseUrl, path37, token, body, options) {
42581
43584
  async function readJsonResponse2(response) {
42582
43585
  const payload = await response.json().catch(() => null);
42583
43586
  if (!response.ok || !payload) {
42584
- 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}`;
42585
43588
  throw new LinkHttpError(response.status, "server_request_failed", message);
42586
43589
  }
42587
43590
  return payload;
42588
43591
  }
42589
- function readErrorMessage6(payload) {
43592
+ function readErrorMessage7(payload) {
42590
43593
  if (typeof payload !== "object" || payload === null) {
42591
43594
  return null;
42592
43595
  }
@@ -42705,14 +43708,15 @@ function registerSystemRoutes(router, options) {
42705
43708
  profile_skills: true,
42706
43709
  profile_memory: true,
42707
43710
  hermes_updates: true,
42708
- app_push_notification_events: true
43711
+ app_push_notification_events: true,
43712
+ live_activity_progress_events: true
42709
43713
  }
42710
43714
  };
42711
43715
  });
42712
43716
  router.post("/api/v1/pairing/claim", async (ctx) => {
42713
43717
  const body = await readJsonBody(ctx.req);
42714
- const sessionId = readString20(body, "session_id") ?? readString20(body, "sessionId");
42715
- 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");
42716
43720
  if (!sessionId || !claimToken) {
42717
43721
  throw new LinkHttpError(
42718
43722
  400,
@@ -42723,10 +43727,10 @@ function registerSystemRoutes(router, options) {
42723
43727
  const claimed = await claimPairing({
42724
43728
  sessionId,
42725
43729
  claimToken,
42726
- deviceLabel: readString20(body, "device_label") ?? readString20(body, "deviceLabel") ?? "HermesPilot App",
42727
- devicePlatform: readString20(body, "device_platform") ?? readString20(body, "devicePlatform") ?? "unknown",
42728
- deviceModel: readString20(body, "device_model") ?? readString20(body, "deviceModel"),
42729
- 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"),
42730
43734
  paths
42731
43735
  });
42732
43736
  ctx.body = claimed;
@@ -42806,9 +43810,9 @@ function registerSystemRoutes(router, options) {
42806
43810
  const body = await readJsonBody(ctx.req);
42807
43811
  const session = await createDeviceSession(
42808
43812
  {
42809
- label: readString20(body, "device_label") ?? readString20(body, "deviceLabel") ?? "HermesPilot App",
42810
- platform: readString20(body, "device_platform") ?? readString20(body, "devicePlatform") ?? "unknown",
42811
- 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"),
42812
43816
  appInstanceId: auth.appInstanceId
42813
43817
  },
42814
43818
  paths
@@ -42837,7 +43841,7 @@ function registerSystemRoutes(router, options) {
42837
43841
  });
42838
43842
  router.post("/api/v1/auth/refresh", async (ctx) => {
42839
43843
  const body = await readJsonBody(ctx.req);
42840
- const refreshToken = readString20(body, "refresh_token") ?? readString20(body, "refreshToken");
43844
+ const refreshToken = readString21(body, "refresh_token") ?? readString21(body, "refreshToken");
42841
43845
  if (!refreshToken) {
42842
43846
  throw new LinkHttpError(
42843
43847
  400,
@@ -42848,10 +43852,10 @@ function registerSystemRoutes(router, options) {
42848
43852
  const session = await refreshDeviceSession(
42849
43853
  refreshToken,
42850
43854
  {
42851
- appInstanceId: readString20(body, "app_instance_id") ?? readString20(body, "appInstanceId"),
42852
- label: readString20(body, "device_label") ?? readString20(body, "deviceLabel"),
42853
- platform: readString20(body, "device_platform") ?? readString20(body, "devicePlatform"),
42854
- 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")
42855
43859
  },
42856
43860
  paths
42857
43861
  );
@@ -42870,7 +43874,7 @@ function registerSystemRoutes(router, options) {
42870
43874
  });
42871
43875
  router.post("/api/v1/auth/logout", async (ctx) => {
42872
43876
  const body = await readJsonBody(ctx.req);
42873
- const refreshToken = readString20(body, "refresh_token") ?? readString20(body, "refreshToken");
43877
+ const refreshToken = readString21(body, "refresh_token") ?? readString21(body, "refreshToken");
42874
43878
  if (refreshToken) {
42875
43879
  await revokeDeviceRefreshToken(refreshToken, paths);
42876
43880
  }
@@ -43111,7 +44115,7 @@ function registerSystemRoutes(router, options) {
43111
44115
  router.patch("/api/v1/devices/:deviceId", async (ctx) => {
43112
44116
  const auth = await authenticateRequest(ctx, paths);
43113
44117
  const body = await readJsonBody(ctx.req);
43114
- const label = readString20(body, "label") ?? readString20(body, "device_label");
44118
+ const label = readString21(body, "label") ?? readString21(body, "device_label");
43115
44119
  if (!label) {
43116
44120
  throw new LinkHttpError(
43117
44121
  400,
@@ -43155,7 +44159,7 @@ function isActiveCronJob(job) {
43155
44159
  if (!enabled) {
43156
44160
  return false;
43157
44161
  }
43158
- const state = readString20(job, "state")?.toLowerCase();
44162
+ const state = readString21(job, "state")?.toLowerCase();
43159
44163
  return !["paused", "disabled", "completed", "deleted"].includes(state ?? "");
43160
44164
  }
43161
44165
  function filterLogsWithinHours(logs, hours, now = Date.now()) {
@@ -43199,8 +44203,8 @@ function registerWorkspaceRoutes(router, options) {
43199
44203
  ctx.body = {
43200
44204
  ok: true,
43201
44205
  workspace: await conversations.createWorkspace({
43202
- name: readString20(body, "name") ?? "",
43203
- icon: readString20(body, "icon") ?? void 0
44206
+ name: readString21(body, "name") ?? "",
44207
+ icon: readString21(body, "icon") ?? void 0
43204
44208
  })
43205
44209
  };
43206
44210
  });
@@ -43210,8 +44214,8 @@ function registerWorkspaceRoutes(router, options) {
43210
44214
  ctx.body = {
43211
44215
  ok: true,
43212
44216
  workspace: await conversations.renameWorkspace(ctx.params.workspaceId, {
43213
- name: readString20(body, "name") ?? "",
43214
- icon: readString20(body, "icon") ?? void 0
44217
+ name: readString21(body, "name") ?? "",
44218
+ icon: readString21(body, "icon") ?? void 0
43215
44219
  })
43216
44220
  };
43217
44221
  });
@@ -43294,8 +44298,8 @@ function registerLinkUpdateRoutes(router, options) {
43294
44298
  ctx.body = await startLinkUpdate({
43295
44299
  paths,
43296
44300
  logger,
43297
- channel: readString20(body, "channel"),
43298
- targetVersion: readString20(body, "target_version") ?? readString20(body, "targetVersion")
44301
+ channel: readString21(body, "channel"),
44302
+ targetVersion: readString21(body, "target_version") ?? readString21(body, "targetVersion")
43299
44303
  });
43300
44304
  });
43301
44305
  router.get("/api/v1/link/update/events", async (ctx) => {
@@ -43325,7 +44329,7 @@ import QRCode from "qrcode";
43325
44329
  function registerPairingRoutes(router, options) {
43326
44330
  const { paths } = options;
43327
44331
  router.get("/pair", async (ctx) => {
43328
- const sessionId = readString20(ctx.query, "session_id");
44332
+ const sessionId = readString21(ctx.query, "session_id");
43329
44333
  if (!sessionId) {
43330
44334
  throw new LinkHttpError(400, "pairing_session_required", "session_id is required");
43331
44335
  }
@@ -43350,7 +44354,7 @@ function registerPairingRoutes(router, options) {
43350
44354
  ctx.body = page;
43351
44355
  });
43352
44356
  router.get("/api/v1/pairing/session", async (ctx) => {
43353
- const sessionId = readString20(ctx.query, "session_id");
44357
+ const sessionId = readString21(ctx.query, "session_id");
43354
44358
  if (!sessionId) {
43355
44359
  throw new LinkHttpError(400, "pairing_session_required", "session_id is required");
43356
44360
  }
@@ -43820,7 +44824,7 @@ function registerInternalRoutes(router, options) {
43820
44824
  router.post("/internal/deliver", async (ctx) => {
43821
44825
  assertLoopbackRequest(ctx.req);
43822
44826
  const body = await readJsonBody(ctx.req);
43823
- const stagingDir = readString20(body, "staging_dir") ?? readString20(body, "stagingDir");
44827
+ const stagingDir = readString21(body, "staging_dir") ?? readString21(body, "stagingDir");
43824
44828
  if (!stagingDir) {
43825
44829
  throw new LinkHttpError(
43826
44830
  400,
@@ -43839,9 +44843,9 @@ function registerInternalRoutes(router, options) {
43839
44843
  ctx.body = {
43840
44844
  ok: true,
43841
44845
  ...await options.conversations.deliverFilesFromTool({
43842
- profile: readString20(body, "profile") ?? readString20(body, "profile_name") ?? readString20(body, "profileName") ?? void 0,
43843
- taskId: readString20(body, "task_id") ?? readString20(body, "taskId") ?? void 0,
43844
- 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,
43845
44849
  files: body.files ?? body.file ?? body.path
43846
44850
  })
43847
44851
  };
@@ -43859,6 +44863,178 @@ function assertLoopbackRequest(request) {
43859
44863
  );
43860
44864
  }
43861
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
+
43862
45038
  // src/http/app.ts
43863
45039
  async function createApp(options = {}) {
43864
45040
  const paths = options.paths ?? resolveRuntimePaths();
@@ -43916,6 +45092,7 @@ async function createApp(options = {}) {
43916
45092
  syncCronDeliveries
43917
45093
  });
43918
45094
  registerWorkspaceRoutes(router, { paths, conversations });
45095
+ registerLiveActivityRoutes(router, { paths });
43919
45096
  registerConversationRoutes(router, { paths, logger, conversations });
43920
45097
  registerRunRoutes(router, { paths, logger, conversations });
43921
45098
  registerProfileRoutes(router, { paths, logger, conversations });