@jingyi0605/codingns4dsh 0.2.1-rc.4 → 0.2.1-rc.5

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.
Files changed (189) hide show
  1. package/README.en.md +3 -3
  2. package/README.md +3 -3
  3. package/data/build/dist/client/bundle.js +1672 -178
  4. package/data/build/dist/client/bundle.js.map +1 -1
  5. package/data/build/dist/client/cli-session-config-cache.d.ts.map +1 -1
  6. package/data/build/dist/client/cli-session-config-cache.js +9 -3
  7. package/data/build/dist/client/cli-session-config-cache.js.map +1 -1
  8. package/data/build/dist/client/client.native-workspace-store.js +7 -1
  9. package/data/build/dist/client/client.native-workspace-store.js.map +1 -1
  10. package/data/build/dist/client/client.transport.js +244 -50
  11. package/data/build/dist/client/client.transport.js.map +1 -1
  12. package/data/build/dist/client/desktop-assistant.js +242 -48
  13. package/data/build/dist/client/features/cli-adapters.d.ts.map +1 -1
  14. package/data/build/dist/client/features/cli-adapters.js +13 -1
  15. package/data/build/dist/client/features/cli-adapters.js.map +1 -1
  16. package/data/build/dist/client/features/index.d.ts.map +1 -1
  17. package/data/build/dist/client/features/index.js +2 -0
  18. package/data/build/dist/client/features/index.js.map +1 -1
  19. package/data/build/dist/client/features/magpie-management-page.d.ts.map +1 -1
  20. package/data/build/dist/client/features/magpie-management-page.js +80 -31
  21. package/data/build/dist/client/features/magpie-management-page.js.map +1 -1
  22. package/data/build/dist/client/features/magpie-management.d.ts.map +1 -1
  23. package/data/build/dist/client/features/magpie-management.js +3 -2
  24. package/data/build/dist/client/features/magpie-management.js.map +1 -1
  25. package/data/build/dist/client/features/plugin-update.d.ts +3 -0
  26. package/data/build/dist/client/features/plugin-update.d.ts.map +1 -0
  27. package/data/build/dist/client/features/plugin-update.js +8 -0
  28. package/data/build/dist/client/features/plugin-update.js.map +1 -0
  29. package/data/build/dist/client/features/types.d.ts +3 -0
  30. package/data/build/dist/client/features/types.d.ts.map +1 -1
  31. package/data/build/dist/client/locale.d.ts.map +1 -1
  32. package/data/build/dist/client/locale.js +3 -0
  33. package/data/build/dist/client/locale.js.map +1 -1
  34. package/data/build/dist/client/locales/magpie.d.ts.map +1 -1
  35. package/data/build/dist/client/locales/magpie.js +60 -2
  36. package/data/build/dist/client/locales/magpie.js.map +1 -1
  37. package/data/build/dist/client/locales/pluginUpdate.d.ts +3 -0
  38. package/data/build/dist/client/locales/pluginUpdate.d.ts.map +1 -0
  39. package/data/build/dist/client/locales/pluginUpdate.js +131 -0
  40. package/data/build/dist/client/locales/pluginUpdate.js.map +1 -0
  41. package/data/build/dist/client/locales/usage.d.ts.map +1 -1
  42. package/data/build/dist/client/locales/usage.js +2 -0
  43. package/data/build/dist/client/locales/usage.js.map +1 -1
  44. package/data/build/dist/client/magpie-context-meter.d.ts +18 -0
  45. package/data/build/dist/client/magpie-context-meter.d.ts.map +1 -0
  46. package/data/build/dist/client/magpie-context-meter.js +274 -0
  47. package/data/build/dist/client/magpie-context-meter.js.map +1 -0
  48. package/data/build/dist/client/magpie-context-store.d.ts +32 -0
  49. package/data/build/dist/client/magpie-context-store.d.ts.map +1 -0
  50. package/data/build/dist/client/magpie-context-store.js +165 -0
  51. package/data/build/dist/client/magpie-context-store.js.map +1 -0
  52. package/data/build/dist/client/settings-promo-styles.d.ts +5 -0
  53. package/data/build/dist/client/settings-promo-styles.d.ts.map +1 -1
  54. package/data/build/dist/client/settings-promo-styles.js +15 -2
  55. package/data/build/dist/client/settings-promo-styles.js.map +1 -1
  56. package/data/build/dist/client/settings-promo.d.ts +4 -2
  57. package/data/build/dist/client/settings-promo.d.ts.map +1 -1
  58. package/data/build/dist/client/settings-promo.js +4 -4
  59. package/data/build/dist/client/settings-promo.js.map +1 -1
  60. package/data/build/dist/client/settings-section.d.ts +2 -3
  61. package/data/build/dist/client/settings-section.d.ts.map +1 -1
  62. package/data/build/dist/client/settings-section.js +7 -7
  63. package/data/build/dist/client/settings-section.js.map +1 -1
  64. package/data/build/dist/client/settings-update.d.ts +8 -0
  65. package/data/build/dist/client/settings-update.d.ts.map +1 -0
  66. package/data/build/dist/client/settings-update.js +57 -0
  67. package/data/build/dist/client/settings-update.js.map +1 -0
  68. package/data/build/dist/client/subscription-slot.d.ts.map +1 -1
  69. package/data/build/dist/client/subscription-slot.js +47 -21
  70. package/data/build/dist/client/subscription-slot.js.map +1 -1
  71. package/data/build/dist/client/update-panel-styles.d.ts +29 -0
  72. package/data/build/dist/client/update-panel-styles.d.ts.map +1 -0
  73. package/data/build/dist/client/update-panel-styles.js +69 -0
  74. package/data/build/dist/client/update-panel-styles.js.map +1 -0
  75. package/data/build/dist/client/update-panel.d.ts +7 -0
  76. package/data/build/dist/client/update-panel.d.ts.map +1 -0
  77. package/data/build/dist/client/update-panel.js +52 -0
  78. package/data/build/dist/client/update-panel.js.map +1 -0
  79. package/data/build/dist/client/update-store.d.ts +43 -0
  80. package/data/build/dist/client/update-store.d.ts.map +1 -0
  81. package/data/build/dist/client/update-store.js +207 -0
  82. package/data/build/dist/client/update-store.js.map +1 -0
  83. package/data/build/dist/dsh-capabilities/client/plugin-reload-adapter.d.ts +4 -0
  84. package/data/build/dist/dsh-capabilities/client/plugin-reload-adapter.d.ts.map +1 -0
  85. package/data/build/dist/dsh-capabilities/client/plugin-reload-adapter.js +6 -0
  86. package/data/build/dist/dsh-capabilities/client/plugin-reload-adapter.js.map +1 -0
  87. package/data/build/dist/dsh-capabilities/client/session-performance-adapter.d.ts +15 -1
  88. package/data/build/dist/dsh-capabilities/client/session-performance-adapter.d.ts.map +1 -1
  89. package/data/build/dist/dsh-capabilities/client/session-performance-adapter.js +53 -21
  90. package/data/build/dist/dsh-capabilities/client/session-performance-adapter.js.map +1 -1
  91. package/data/build/dist/dsh-capabilities/host/plugin-update-adapter.d.ts +22 -0
  92. package/data/build/dist/dsh-capabilities/host/plugin-update-adapter.d.ts.map +1 -0
  93. package/data/build/dist/dsh-capabilities/host/plugin-update-adapter.js +71 -0
  94. package/data/build/dist/dsh-capabilities/host/plugin-update-adapter.js.map +1 -0
  95. package/data/build/dist/dsh-capabilities/host/session-model-selection-adapter.d.ts +4 -0
  96. package/data/build/dist/dsh-capabilities/host/session-model-selection-adapter.d.ts.map +1 -1
  97. package/data/build/dist/dsh-capabilities/host/session-model-selection-adapter.js +16 -1
  98. package/data/build/dist/dsh-capabilities/host/session-model-selection-adapter.js.map +1 -1
  99. package/data/build/dist/dsh-capabilities/matrix.d.ts.map +1 -1
  100. package/data/build/dist/dsh-capabilities/matrix.js +4 -0
  101. package/data/build/dist/dsh-capabilities/matrix.js.map +1 -1
  102. package/data/build/dist/dsh-capabilities/routes.d.ts.map +1 -1
  103. package/data/build/dist/dsh-capabilities/routes.js +10 -0
  104. package/data/build/dist/dsh-capabilities/routes.js.map +1 -1
  105. package/data/build/dist/dsh-capabilities/types.d.ts +1 -1
  106. package/data/build/dist/dsh-capabilities/types.d.ts.map +1 -1
  107. package/data/build/dist/dsh-capabilities/types.js.map +1 -1
  108. package/data/build/dist/host/cli-adapters/feature.d.ts.map +1 -1
  109. package/data/build/dist/host/cli-adapters/feature.js +174 -26
  110. package/data/build/dist/host/cli-adapters/feature.js.map +1 -1
  111. package/data/build/dist/host/cli-adapters/magpie-context.d.ts +9 -0
  112. package/data/build/dist/host/cli-adapters/magpie-context.d.ts.map +1 -0
  113. package/data/build/dist/host/cli-adapters/magpie-context.js +128 -0
  114. package/data/build/dist/host/cli-adapters/magpie-context.js.map +1 -0
  115. package/data/build/dist/host/cli-adapters/magpie-update.d.ts.map +1 -1
  116. package/data/build/dist/host/cli-adapters/magpie-update.js +14 -4
  117. package/data/build/dist/host/cli-adapters/magpie-update.js.map +1 -1
  118. package/data/build/dist/host/cli-adapters/magpie-usage.d.ts +12 -2
  119. package/data/build/dist/host/cli-adapters/magpie-usage.d.ts.map +1 -1
  120. package/data/build/dist/host/cli-adapters/magpie-usage.js +114 -50
  121. package/data/build/dist/host/cli-adapters/magpie-usage.js.map +1 -1
  122. package/data/build/dist/host/cli-adapters/registry.d.ts.map +1 -1
  123. package/data/build/dist/host/cli-adapters/registry.js +6 -5
  124. package/data/build/dist/host/cli-adapters/registry.js.map +1 -1
  125. package/data/build/dist/host/features/index.d.ts.map +1 -1
  126. package/data/build/dist/host/features/index.js +2 -0
  127. package/data/build/dist/host/features/index.js.map +1 -1
  128. package/data/build/dist/host/features/plugin-update.d.ts +6 -0
  129. package/data/build/dist/host/features/plugin-update.d.ts.map +1 -0
  130. package/data/build/dist/host/features/plugin-update.js +45 -0
  131. package/data/build/dist/host/features/plugin-update.js.map +1 -0
  132. package/data/build/dist/host/index.d.ts.map +1 -1
  133. package/data/build/dist/host/index.js +31 -1
  134. package/data/build/dist/host/index.js.map +1 -1
  135. package/data/build/dist/host/native-session-bridge.d.ts +8 -0
  136. package/data/build/dist/host/native-session-bridge.d.ts.map +1 -1
  137. package/data/build/dist/host/native-session-bridge.js +43 -0
  138. package/data/build/dist/host/native-session-bridge.js.map +1 -1
  139. package/data/build/dist/host/rpc.d.ts +4 -1
  140. package/data/build/dist/host/rpc.d.ts.map +1 -1
  141. package/data/build/dist/host/rpc.js +33 -7
  142. package/data/build/dist/host/rpc.js.map +1 -1
  143. package/data/build/dist/host/session-handle-eviction.d.ts +29 -0
  144. package/data/build/dist/host/session-handle-eviction.d.ts.map +1 -0
  145. package/data/build/dist/host/session-handle-eviction.js +413 -0
  146. package/data/build/dist/host/session-handle-eviction.js.map +1 -0
  147. package/data/build/dist/host/settings.d.ts.map +1 -1
  148. package/data/build/dist/host/settings.js +12 -1
  149. package/data/build/dist/host/settings.js.map +1 -1
  150. package/data/build/dist/host/update/application-result.d.ts +3 -0
  151. package/data/build/dist/host/update/application-result.d.ts.map +1 -0
  152. package/data/build/dist/host/update/application-result.js +13 -0
  153. package/data/build/dist/host/update/application-result.js.map +1 -0
  154. package/data/build/dist/host/update/http.d.ts +7 -0
  155. package/data/build/dist/host/update/http.d.ts.map +1 -0
  156. package/data/build/dist/host/update/http.js +55 -0
  157. package/data/build/dist/host/update/http.js.map +1 -0
  158. package/data/build/dist/host/update/metadata.d.ts +14 -0
  159. package/data/build/dist/host/update/metadata.d.ts.map +1 -0
  160. package/data/build/dist/host/update/metadata.js +75 -0
  161. package/data/build/dist/host/update/metadata.js.map +1 -0
  162. package/data/build/dist/host/update/release-notes.d.ts +6 -0
  163. package/data/build/dist/host/update/release-notes.d.ts.map +1 -0
  164. package/data/build/dist/host/update/release-notes.js +52 -0
  165. package/data/build/dist/host/update/release-notes.js.map +1 -0
  166. package/data/build/dist/host/update/service.d.ts +25 -0
  167. package/data/build/dist/host/update/service.d.ts.map +1 -0
  168. package/data/build/dist/host/update/service.js +209 -0
  169. package/data/build/dist/host/update/service.js.map +1 -0
  170. package/data/build/dist/shared/contracts/config.d.ts +17 -0
  171. package/data/build/dist/shared/contracts/config.d.ts.map +1 -1
  172. package/data/build/dist/shared/contracts/config.js +8 -0
  173. package/data/build/dist/shared/contracts/config.js.map +1 -1
  174. package/data/build/dist/shared/contracts/magpie-management.d.ts +40 -0
  175. package/data/build/dist/shared/contracts/magpie-management.d.ts.map +1 -1
  176. package/data/build/dist/shared/contracts/plugin-update.d.ts +93 -0
  177. package/data/build/dist/shared/contracts/plugin-update.d.ts.map +1 -0
  178. package/data/build/dist/shared/contracts/plugin-update.js +128 -0
  179. package/data/build/dist/shared/contracts/plugin-update.js.map +1 -0
  180. package/data/build/dist/shared/contracts/subscription.d.ts +3 -0
  181. package/data/build/dist/shared/contracts/subscription.d.ts.map +1 -1
  182. package/data/build/dist/shared/contracts/subscription.js.map +1 -1
  183. package/data/build/dist/shared/contracts/version.d.ts +1 -1
  184. package/data/build/dist/shared/contracts/version.js +1 -1
  185. package/package.json +2 -1
  186. package/profile/README.md +2 -2
  187. package/profile/package.json +2 -2
  188. package/profile/version.json +1 -1
  189. package/version.json +1 -1
@@ -21,7 +21,7 @@ import { CodingNsCliSessionStore } from './session-store.js';
21
21
  import { CodingNsDshMessageProjector } from './dsh-message-projector.js';
22
22
  import { CommandCodeSubscriptionService } from './command-code-subscription.js';
23
23
  import { ProviderSubscriptionService } from './provider-subscription.js';
24
- import { attachMagpieSessionNames, MagpieUsageService, magpieAgentForAdapter } from './magpie-usage.js';
24
+ import { attachMagpieSessionNames, MagpieUsageService, magpieAgentForAdapter, magpieUsageAgentAliases } from './magpie-usage.js';
25
25
  import { readDshProviderSource } from './dsh-config.js';
26
26
  import { MagpieManagementService } from './magpie-management.js';
27
27
  import { MagpieWebService } from './magpie-web.js';
@@ -89,6 +89,7 @@ export function createCliAdaptersFeature(options = {}) {
89
89
  };
90
90
  let subscriptions = options.subscriptions ?? buildSubscriptions(context.services.settings?.get());
91
91
  let magpieDetection = options.magpieDetection ?? new MagpieInstallDetectionService(options.magpieDetectionOptions);
92
+ let magpieDetectionRequest = 0;
92
93
  // 最近一次可用的 Magpie 入口;探测会随用户指定的路径变化而重建,Web 服务总是读最新值。
93
94
  let magpieCommand = null;
94
95
  let magpieVersion = null;
@@ -387,6 +388,14 @@ export function createCliAdaptersFeature(options = {}) {
387
388
  case 'subscription': {
388
389
  const subscription = readSubscriptionRequest(payload);
389
390
  const requestSignal = signal ?? lifetime.signal;
391
+ // A newly opened native DSH session may not be in the Host bridge's
392
+ // local store yet. Adopt it before reading the Registry, otherwise
393
+ // the native model/provider selection is invisible and the request
394
+ // incorrectly falls back to the official DSH balance.
395
+ if (subscription.adapterId === 'dsh' && subscription.sessionId !== undefined && nativeSessions !== undefined) {
396
+ await nativeSessions.ensure(subscription.sessionId);
397
+ requestSignal.throwIfAborted();
398
+ }
390
399
  const session = subscription.sessionId === undefined ? undefined : registry.getSession(subscription.sessionId);
391
400
  if (context.services.settings?.get().modules.magpieManagement === true
392
401
  && session !== undefined && session.adapterId === subscription.adapterId) {
@@ -394,9 +403,9 @@ export function createCliAdaptersFeature(options = {}) {
394
403
  requestSignal.throwIfAborted();
395
404
  const snapshot = magpie ?? (subscription.sessionId === undefined
396
405
  ? null
397
- : await pendingMagpieUsage(magpieUsage, nativeSessions, subscription.sessionId, session, requestSignal));
406
+ : await pendingMagpieUsage(magpieUsage, nativeSessions, subscription.sessionId, session, requestSignal, subscription.includeRoute));
398
407
  if (snapshot !== null) {
399
- const sessionName = subscription.sessionId === undefined ? undefined : readDshSessionDisplayName(subscription.sessionId, sessionStore, nativeSessions);
408
+ const sessionName = subscription.sessionId === undefined ? undefined : readDshSessionDisplayName(subscription.sessionId, sessionStore, nativeSessions, context.services.dshContext);
400
409
  return { providerUsage: null, magpie: sessionName === undefined ? snapshot : { ...snapshot, sessionName } };
401
410
  }
402
411
  }
@@ -430,31 +439,54 @@ export function createCliAdaptersFeature(options = {}) {
430
439
  const signal = isAbortSignal(rpcSignal) ? rpcSignal : lifetime.signal;
431
440
  // 安装探测本身就是启用前的判断依据,因此不要求模块已启用。
432
441
  if (action === 'management/detection') {
442
+ const requestId = ++magpieDetectionRequest;
433
443
  const request = readMagpieDetectionRequest(payload);
434
444
  const force = readMagpieDetectionForce(payload);
435
- // A custom path is request-scoped; it must not poison later automatic detection.
445
+ // Only a successful custom probe replaces the Host-session selection.
436
446
  const customCommand = normalizeCustomCommand(request.commandPath);
437
447
  const detectionService = customCommand === null
438
448
  ? magpieDetection
439
449
  : new MagpieInstallDetectionService({ ...options.magpieDetectionOptions, customCommand });
440
450
  const detection = await detectionService.detect(customCommand === null ? {} : { commandPath: customCommand }, { force: force || customCommand !== null });
441
- magpieCommand = MagpieInstallDetectionService.isUsable(detection) ? detection.command : null;
442
- magpieVersion = MagpieInstallDetectionService.isUsable(detection) ? detection.version : null;
451
+ if (requestId !== magpieDetectionRequest || lifetime.signal.aborted)
452
+ return detection;
453
+ const usable = MagpieInstallDetectionService.isUsable(detection);
454
+ if (usable || customCommand === null) {
455
+ if (usable && customCommand !== null)
456
+ magpieDetection = detectionService;
457
+ magpieInstalled = usable;
458
+ magpieCommand = usable ? detection.command : null;
459
+ magpieVersion = usable ? detection.version : null;
460
+ scheduleMagpieWebSync(context.services.settings?.get().modules.magpieManagement === true && usable);
461
+ }
443
462
  return detection;
444
463
  }
464
+ if (action === 'context') {
465
+ const request = readMagpieContextRequest(payload);
466
+ const target = await resolveMagpieContextTarget(request.sessionId, sessionStore, nativeSessions, magpieUsage, signal);
467
+ if (target === null || !magpieInstalled)
468
+ return null;
469
+ try {
470
+ await magpieWeb.start();
471
+ }
472
+ catch {
473
+ return null;
474
+ }
475
+ return magpieUsage.readContext(target, signal);
476
+ }
445
477
  if (context.services.settings?.get().modules.magpieManagement !== true) {
446
478
  throw new CodingNsRpcError('CODINGNS_MAGPIE_DISABLED', 'Magpie management is disabled');
447
479
  }
448
480
  switch (action) {
449
481
  case 'usage/dashboard': {
450
482
  const dashboard = await magpieUsage.readDashboard(readMagpieDashboardPeriod(payload), signal);
451
- return attachMagpieSessionNames(dashboard, await magpieSessionNameBindings(sessionStore, nativeSessions, signal));
483
+ return attachMagpieSessionNames(dashboard, await magpieSessionNameBindings(sessionStore, nativeSessions, signal, context.services.dshContext));
452
484
  }
453
485
  case 'usage/dashboard/stream': {
454
486
  const input = asRecord(payload);
455
487
  const period = readMagpieDashboardPeriod(payload);
456
488
  const force = input?.force === true;
457
- return streamMagpieDashboard(magpieUsage, period, signal, force, sessionStore, nativeSessions);
489
+ return streamMagpieDashboard(magpieUsage, period, signal, force, sessionStore, nativeSessions, context.services.dshContext);
458
490
  }
459
491
  case 'usage/requests': {
460
492
  const request = readMagpieRequestOptions(payload);
@@ -463,6 +495,7 @@ export function createCliAdaptersFeature(options = {}) {
463
495
  return page;
464
496
  return {
465
497
  ...page,
498
+ sessionName: readDshSessionDisplayName(request.sessionId, sessionStore, nativeSessions, context.services.dshContext) ?? null,
466
499
  sessionMetrics: readMagpieSessionMetrics(sessionStore, nativeSessions, request.sessionId, request.sessionIds),
467
500
  };
468
501
  }
@@ -1544,6 +1577,11 @@ function readOptionalText(value) {
1544
1577
  function readNativeModelSelection(nativeSessions, sessionId) {
1545
1578
  if (nativeSessions === undefined)
1546
1579
  return undefined;
1580
+ const current = nativeSessions.modelSelection?.(sessionId);
1581
+ if (current !== undefined && current.model.trim() !== '') {
1582
+ const provider = current.provider.trim();
1583
+ return provider === '' ? { modelId: current.model.trim() } : { providerId: provider, modelId: current.model.trim() };
1584
+ }
1547
1585
  let session;
1548
1586
  try {
1549
1587
  session = nativeSessions.get(sessionId);
@@ -1587,6 +1625,43 @@ function isMagpieGatewayProvider(providerId) {
1587
1625
  return isMagpieGateway(source.baseUrl);
1588
1626
  return /^magpie(?:[-_.].*)?$/iu.test(providerId);
1589
1627
  }
1628
+ /** Resolve the exact identities needed for one Magpie context read. */
1629
+ async function resolveMagpieContextTarget(sessionId, sessionStore, nativeSessions, magpieUsage, signal) {
1630
+ const normalizedSessionId = sessionId.trim();
1631
+ if (normalizedSessionId === '')
1632
+ return null;
1633
+ let stored = sessionStore.get(normalizedSessionId);
1634
+ if (stored === undefined && nativeSessions !== undefined) {
1635
+ try {
1636
+ await nativeSessions.ensure(normalizedSessionId);
1637
+ signal.throwIfAborted();
1638
+ }
1639
+ catch {
1640
+ return null;
1641
+ }
1642
+ stored = sessionStore.get(normalizedSessionId);
1643
+ }
1644
+ const adapterId = stored?.adapterId ?? DSH_NATIVE_SESSION_ADAPTER_ID;
1645
+ const selection = adapterId === DSH_NATIVE_SESSION_ADAPTER_ID
1646
+ ? readNativeModelSelection(nativeSessions, normalizedSessionId)
1647
+ : undefined;
1648
+ const modelId = stored?.modelId?.trim() || selection?.modelId;
1649
+ if (modelId === undefined || modelId === '')
1650
+ return null;
1651
+ const providerId = stored?.providerId?.trim() || selection?.providerId;
1652
+ if (adapterId === DSH_NATIVE_SESSION_ADAPTER_ID) {
1653
+ if (providerId === undefined || !isMagpieGatewayProvider(providerId))
1654
+ return null;
1655
+ }
1656
+ else if (await magpieUsage.readModelMetadata(adapterId, modelId, signal) === null) {
1657
+ return null;
1658
+ }
1659
+ const agents = magpieUsageAgentAliases(adapterId);
1660
+ if (agents.length === 0)
1661
+ return null;
1662
+ const sessionIds = [...new Set([normalizedSessionId, stored?.providerSessionId?.trim() ?? ''].filter(Boolean))];
1663
+ return { agents, sessionIds };
1664
+ }
1590
1665
  /**
1591
1666
  * 读到用量之后仍然要判断「这个模型是不是 Magpie 的」。
1592
1667
  *
@@ -1595,21 +1670,25 @@ function isMagpieGatewayProvider(providerId) {
1595
1670
  * 会话 ID 在滚动升级期间并不相等,这时返回 null 会让 Host 直接回退到官方余量。
1596
1671
  * 只有确认模型由 Magpie 提供时才下发占位快照,避免官方数字抢占首屏。
1597
1672
  */
1598
- async function pendingMagpieUsage(magpie, nativeSessions, sessionId, session, signal) {
1673
+ async function pendingMagpieUsage(magpie, nativeSessions, sessionId, session, signal, includeRoute = false) {
1599
1674
  const agent = magpieAgentForAdapter(session.adapterId);
1600
1675
  const modelId = session.modelId?.trim();
1601
1676
  if (agent === null || modelId === undefined || modelId === '')
1602
1677
  return null;
1603
1678
  try {
1604
- const selectedProviderId = session.providerId?.trim();
1605
- const providerId = selectedProviderId === undefined || selectedProviderId === ''
1606
- ? readNativeModelSelection(nativeSessions, sessionId)?.providerId
1607
- : selectedProviderId;
1608
- if (providerId === undefined || !isMagpieGatewayProvider(providerId))
1609
- return null;
1679
+ if (session.adapterId === DSH_NATIVE_SESSION_ADAPTER_ID) {
1680
+ const providerId = session.providerId?.trim() || readNativeModelSelection(nativeSessions, sessionId)?.providerId;
1681
+ if (providerId === undefined || !isMagpieGatewayProvider(providerId))
1682
+ return null;
1683
+ }
1610
1684
  const confirmed = await magpie.readModelMetadata(session.adapterId, modelId, signal);
1611
1685
  signal.throwIfAborted();
1612
- return confirmed === null ? null : pendingMagpieSnapshot(agent, modelId);
1686
+ if (confirmed === null)
1687
+ return null;
1688
+ // Gateway totals do not depend on a provider session binding. Keep them
1689
+ // while the exact session row is pending, without attributing other rows.
1690
+ return await magpie.read(session, signal, includeRoute, sessionId, { allowPendingSession: true })
1691
+ ?? pendingMagpieSnapshot(agent, modelId);
1613
1692
  }
1614
1693
  catch {
1615
1694
  // 目录不可读、超时或请求被取消:保持原有回退语义,不伪造 Magpie 用量。
@@ -2137,8 +2216,8 @@ function readMagpieDashboardPeriod(payload) {
2137
2216
  return value;
2138
2217
  throw new TypeError('Magpie usage period must be today, week, month, or all');
2139
2218
  }
2140
- async function* streamMagpieDashboard(service, period, signal, force, sessionStore, nativeSessions) {
2141
- const names = magpieSessionNameBindings(sessionStore, nativeSessions, signal).catch(() => []);
2219
+ async function* streamMagpieDashboard(service, period, signal, force, sessionStore, nativeSessions, dshContext) {
2220
+ const names = magpieSessionNameBindings(sessionStore, nativeSessions, signal, dshContext).catch(() => []);
2142
2221
  let latest;
2143
2222
  for await (const chunk of service.readDashboardStream(period, signal, force)) {
2144
2223
  latest = chunk.dashboard;
@@ -2162,6 +2241,12 @@ function readMagpieDetectionRequest(payload) {
2162
2241
  function readMagpieDetectionForce(payload) {
2163
2242
  return asRecord(payload)?.force === true;
2164
2243
  }
2244
+ function readMagpieContextRequest(payload) {
2245
+ const sessionId = readOptionalMagpieIdentity(asRecord(payload)?.sessionId, 512);
2246
+ if (sessionId === undefined)
2247
+ throw new TypeError('Magpie context requires a session ID');
2248
+ return { sessionId };
2249
+ }
2165
2250
  function readMagpieRequestOptions(payload) {
2166
2251
  const record = asRecord(payload);
2167
2252
  if (record === null)
@@ -2245,7 +2330,7 @@ function readOptionalMagpieIdentityList(value, maxItems, maxLength) {
2245
2330
  throw new TypeError('Magpie session IDs are invalid');
2246
2331
  return [...new Set(values)];
2247
2332
  }
2248
- async function magpieSessionNameBindings(sessionStore, nativeSessions, signal) {
2333
+ async function magpieSessionNameBindings(sessionStore, nativeSessions, signal, dshContext) {
2249
2334
  const records = sessionStore.list({ includeArchived: true });
2250
2335
  const bindings = [];
2251
2336
  const knownSessionIds = new Set();
@@ -2261,13 +2346,13 @@ async function magpieSessionNameBindings(sessionStore, nativeSessions, signal) {
2261
2346
  const nativeNames = new Map();
2262
2347
  for (const value of nativeValues) {
2263
2348
  const sessionId = nativeSessionId(value);
2264
- const name = readNativeSessionDisplayName(value);
2349
+ const name = readNativeSessionDisplayName(value, dshContext);
2265
2350
  if (sessionId !== undefined && name !== undefined)
2266
2351
  nativeNames.set(sessionId, name);
2267
2352
  }
2268
2353
  for (const record of records) {
2269
2354
  knownSessionIds.add(record.dshSessionId);
2270
- const name = nativeNames.get(record.dshSessionId) ?? readDshSessionDisplayName(record.dshSessionId, sessionStore, nativeSessions);
2355
+ const name = nativeNames.get(record.dshSessionId) ?? readDshSessionDisplayName(record.dshSessionId, sessionStore, nativeSessions, dshContext);
2271
2356
  bindings.push({
2272
2357
  dshSessionId: record.dshSessionId,
2273
2358
  adapterId: record.adapterId,
@@ -2282,31 +2367,94 @@ async function magpieSessionNameBindings(sessionStore, nativeSessions, signal) {
2282
2367
  const sessionId = nativeSessionId(value);
2283
2368
  if (sessionId === undefined || knownSessionIds.has(sessionId))
2284
2369
  continue;
2285
- const name = readNativeSessionDisplayName(value);
2370
+ const name = readNativeSessionDisplayName(value, dshContext);
2286
2371
  bindings.push({ dshSessionId: sessionId, adapterId: 'dsh', ...(name === undefined ? {} : { name }) });
2287
2372
  }
2288
2373
  return bindings;
2289
2374
  }
2290
- function readDshSessionDisplayName(sessionId, sessionStore, nativeSessions) {
2291
- return readNativeSessionDisplayName(nativeSessions?.get(sessionId)) ?? cleanDisplayName(sessionStore.get(sessionId)?.title);
2375
+ function readDshSessionDisplayName(sessionId, sessionStore, nativeSessions, dshContext) {
2376
+ const direct = readNativeSession(nativeSessions, sessionId);
2377
+ const directName = readNativeSessionDisplayName(direct, dshContext);
2378
+ if (directName !== undefined)
2379
+ return directName;
2380
+ const directRecord = sessionStore.get(sessionId);
2381
+ const directRecordName = cleanDisplayName(directRecord?.title);
2382
+ if (directRecordName !== undefined)
2383
+ return directRecordName;
2384
+ const binding = sessionStore.list({ includeArchived: true }).find((record) => record.dshSessionId === sessionId || record.providerSessionId === sessionId);
2385
+ if (binding === undefined)
2386
+ return undefined;
2387
+ return readNativeSessionDisplayName(readNativeSession(nativeSessions, binding.dshSessionId), dshContext)
2388
+ ?? cleanDisplayName(binding.title);
2292
2389
  }
2293
- function readNativeSessionDisplayName(value) {
2390
+ function readNativeSessionDisplayName(value, dshContext) {
2294
2391
  const root = asRecord(value);
2295
2392
  if (root === null)
2296
2393
  return undefined;
2297
2394
  const projection = asRecord(root.projections) ?? asRecord(asRecord(root.record)?.projections);
2298
2395
  const values = asRecord(projection?.values);
2299
2396
  const record = asRecord(root.record);
2397
+ const header = asRecord(root.header);
2398
+ const recordHeader = asRecord(record?.header);
2300
2399
  const rows = asRecord(record?.rows);
2301
2400
  const rowProjection = asRecord(rows?.projections);
2302
2401
  const rowValues = asRecord(rowProjection?.values);
2303
- for (const candidate of [values?.title, rowValues?.title, root.title, root.name, root.displayName, record?.title, record?.name]) {
2402
+ const titleService = asRecord(readOptionalDshService(dshContext, 'sessionTitle'));
2403
+ if (typeof titleService?.get === 'function') {
2404
+ try {
2405
+ const snapshot = titleService.get(value);
2406
+ const title = cleanDisplayName(asRecord(snapshot)?.title ?? snapshot);
2407
+ if (title !== undefined)
2408
+ return title;
2409
+ }
2410
+ catch { /* The optional native title service may not accept this session shape. */ }
2411
+ }
2412
+ for (const candidate of [
2413
+ values?.title, rowValues?.title, root.title, root.name, root.displayName,
2414
+ header?.title, header?.name, header?.displayName,
2415
+ record?.title, record?.name, recordHeader?.title, recordHeader?.name,
2416
+ ]) {
2304
2417
  const title = cleanDisplayName(candidate);
2305
2418
  if (title !== undefined)
2306
2419
  return title;
2307
2420
  }
2421
+ if (typeof root.snapshotEvents === 'function') {
2422
+ try {
2423
+ const events = root.snapshotEvents.call(value);
2424
+ if (Array.isArray(events)) {
2425
+ for (let index = events.length - 1; index >= 0; index -= 1) {
2426
+ const event = asRecord(events[index]);
2427
+ if (event?.type !== 'session/title')
2428
+ continue;
2429
+ const title = cleanDisplayName(asRecord(event.data)?.title ?? event.title);
2430
+ if (title !== undefined)
2431
+ return title;
2432
+ }
2433
+ }
2434
+ }
2435
+ catch { /* A missing or closed native history cannot block usage rendering. */ }
2436
+ }
2308
2437
  return undefined;
2309
2438
  }
2439
+ function readNativeSession(nativeSessions, sessionId) {
2440
+ try {
2441
+ return nativeSessions?.get(sessionId);
2442
+ }
2443
+ catch {
2444
+ return undefined;
2445
+ }
2446
+ }
2447
+ function readOptionalDshService(context, name) {
2448
+ const get = context?.get;
2449
+ if (typeof get !== 'function')
2450
+ return undefined;
2451
+ try {
2452
+ return get.call(context, name);
2453
+ }
2454
+ catch {
2455
+ return undefined;
2456
+ }
2457
+ }
2310
2458
  function cleanDisplayName(value) {
2311
2459
  if (typeof value !== 'string')
2312
2460
  return undefined;