@tanstack/ai-client 0.22.1 → 0.23.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.
Files changed (72) hide show
  1. package/README.md +15 -1
  2. package/dist/esm/audio-recorder.js +190 -213
  3. package/dist/esm/audio-recorder.js.map +1 -1
  4. package/dist/esm/chat-client.d.ts +172 -3
  5. package/dist/esm/chat-client.js +1656 -1386
  6. package/dist/esm/chat-client.js.map +1 -1
  7. package/dist/esm/cleared-stream-tracker.d.ts +23 -0
  8. package/dist/esm/cleared-stream-tracker.js +97 -0
  9. package/dist/esm/cleared-stream-tracker.js.map +1 -0
  10. package/dist/esm/client-persistor.d.ts +25 -12
  11. package/dist/esm/client-persistor.js +260 -235
  12. package/dist/esm/client-persistor.js.map +1 -1
  13. package/dist/esm/connection-adapters.d.ts +231 -10
  14. package/dist/esm/connection-adapters.js +989 -574
  15. package/dist/esm/connection-adapters.js.map +1 -1
  16. package/dist/esm/devtools-noop.d.ts +1 -0
  17. package/dist/esm/devtools-noop.js +79 -139
  18. package/dist/esm/devtools-noop.js.map +1 -1
  19. package/dist/esm/devtools.d.ts +31 -1
  20. package/dist/esm/devtools.js +977 -1127
  21. package/dist/esm/devtools.js.map +1 -1
  22. package/dist/esm/events.js +224 -226
  23. package/dist/esm/events.js.map +1 -1
  24. package/dist/esm/generation-client.d.ts +145 -2
  25. package/dist/esm/generation-client.js +659 -321
  26. package/dist/esm/generation-client.js.map +1 -1
  27. package/dist/esm/generation-reconstruct.d.ts +21 -0
  28. package/dist/esm/generation-reconstruct.js +85 -0
  29. package/dist/esm/generation-reconstruct.js.map +1 -0
  30. package/dist/esm/generation-types.d.ts +289 -3
  31. package/dist/esm/generation-types.js +356 -13
  32. package/dist/esm/generation-types.js.map +1 -1
  33. package/dist/esm/index.d.ts +9 -4
  34. package/dist/esm/index.js +7 -39
  35. package/dist/esm/interrupt-manager.d.ts +77 -0
  36. package/dist/esm/interrupt-manager.js +787 -0
  37. package/dist/esm/interrupt-manager.js.map +1 -0
  38. package/dist/esm/mcp-app-bridge.js +56 -64
  39. package/dist/esm/mcp-app-bridge.js.map +1 -1
  40. package/dist/esm/realtime-client.js +366 -440
  41. package/dist/esm/realtime-client.js.map +1 -1
  42. package/dist/esm/response-stream.js +19 -26
  43. package/dist/esm/response-stream.js.map +1 -1
  44. package/dist/esm/sse-parser.js +44 -47
  45. package/dist/esm/sse-parser.js.map +1 -1
  46. package/dist/esm/sse-utils.js +8 -9
  47. package/dist/esm/sse-utils.js.map +1 -1
  48. package/dist/esm/storage-adapters.d.ts +62 -0
  49. package/dist/esm/storage-adapters.js +174 -0
  50. package/dist/esm/storage-adapters.js.map +1 -0
  51. package/dist/esm/types.d.ts +212 -10
  52. package/dist/esm/types.js +38 -7
  53. package/dist/esm/types.js.map +1 -1
  54. package/dist/esm/video-generation-client.d.ts +113 -2
  55. package/dist/esm/video-generation-client.js +665 -379
  56. package/dist/esm/video-generation-client.js.map +1 -1
  57. package/package.json +7 -7
  58. package/src/chat-client.ts +1079 -61
  59. package/src/cleared-stream-tracker.ts +151 -0
  60. package/src/client-persistor.ts +102 -33
  61. package/src/connection-adapters.ts +1185 -142
  62. package/src/devtools-noop.ts +4 -3
  63. package/src/devtools.ts +121 -3
  64. package/src/generation-client.ts +563 -13
  65. package/src/generation-reconstruct.ts +121 -0
  66. package/src/generation-types.ts +727 -3
  67. package/src/index.ts +56 -1
  68. package/src/interrupt-manager.ts +1440 -0
  69. package/src/storage-adapters.ts +242 -0
  70. package/src/types.ts +301 -9
  71. package/src/video-generation-client.ts +479 -13
  72. package/dist/esm/index.js.map +0 -1
@@ -86,6 +86,7 @@ export class NoOpChatDevtoolsBridge {
86
86
  return ''
87
87
  }
88
88
  observeChunk(_chunk: StreamChunk): void {}
89
+ recordMemoryState(_value: unknown): void {}
89
90
  beginRun(_runId: string, _threadId: string): void {}
90
91
  getCurrentRunEventContext(): ChatClientRunEventContext | undefined {
91
92
  return undefined
@@ -190,12 +191,12 @@ void _bridgeParity
190
191
  export const createNoOpChatDevtoolsBridge: ChatDevtoolsBridgeFactory = (
191
192
  options,
192
193
  ) =>
193
- // eslint-disable-next-line no-restricted-syntax -- see comment above
194
+ // oxlint-disable-next-line eslint-js/no-restricted-syntax -- see comment above
194
195
  new NoOpChatDevtoolsBridge(options) as unknown as ChatDevtoolsBridge
195
196
 
196
197
  export const createNoOpGenerationDevtoolsBridge: GenerationDevtoolsBridgeFactory =
197
198
  <TOutput>(options: GenerationDevtoolsBridgeOptions<TOutput>) =>
198
- // eslint-disable-next-line no-restricted-syntax -- see comment above
199
+ // oxlint-disable-next-line eslint-js/no-restricted-syntax -- see comment above
199
200
  new NoOpGenerationDevtoolsBridge<TOutput>(
200
201
  options,
201
202
  ) as unknown as GenerationDevtoolsBridge<TOutput>
@@ -205,7 +206,7 @@ export const createNoOpVideoDevtoolsBridge: VideoDevtoolsBridgeFactory = <
205
206
  >(
206
207
  options: VideoDevtoolsBridgeOptions<TOutput>,
207
208
  ) =>
208
- // eslint-disable-next-line no-restricted-syntax -- see comment above
209
+ // oxlint-disable-next-line eslint-js/no-restricted-syntax -- see comment above
209
210
  new NoOpVideoDevtoolsBridge<TOutput>(
210
211
  options,
211
212
  ) as unknown as VideoDevtoolsBridge<TOutput>
package/src/devtools.ts CHANGED
@@ -6,7 +6,10 @@ import {
6
6
  import { convertSchemaToJsonSchema } from '@tanstack/ai/client'
7
7
  import { DefaultChatClientEventEmitter } from './events'
8
8
  import type { AnyClientTool, StreamChunk } from '@tanstack/ai/client'
9
- import type { AIDevtoolsEventVisibility } from '@tanstack/ai-event-client'
9
+ import type {
10
+ AIDevtoolsEventVisibility,
11
+ MemoryScopeLite,
12
+ } from '@tanstack/ai-event-client'
10
13
  import type {
11
14
  ChatClientEventContext,
12
15
  ChatClientEventEmitter,
@@ -25,6 +28,33 @@ export interface AIDevtoolsDisplayOptions {
25
28
  name?: string
26
29
  }
27
30
 
31
+ /**
32
+ * Structural mirror of `MemoryStateEventValue` from `@tanstack/ai-memory` — the
33
+ * payload of the `memory:state` CUSTOM chunk. Kept local so `ai-client` doesn't
34
+ * depend on `ai-memory`; the memory middleware is the producer.
35
+ */
36
+ interface MemoryStateEventValue {
37
+ scope: MemoryScopeLite
38
+ adapter: string
39
+ query?: string
40
+ recall?: {
41
+ fragmentCount?: number
42
+ hasTools?: boolean
43
+ systemPromptChars?: number
44
+ durationMs?: number
45
+ }
46
+ snapshot?: {
47
+ takenAt: string
48
+ data: unknown
49
+ facts?: Array<{
50
+ id: string
51
+ text: string
52
+ source?: string
53
+ createdAt?: string
54
+ }>
55
+ }
56
+ }
57
+
28
58
  export interface AIDevtoolsClientMetadata extends AIDevtoolsDisplayOptions {
29
59
  framework?: string
30
60
  hookName: string
@@ -463,7 +493,7 @@ export class ClientDevtoolsBridge<TSnapshot extends object> {
463
493
  ...this.createMetadataPayload(),
464
494
  // Wire envelope uses Record<string, unknown>; widen the typed snapshot
465
495
  // here so the typed-snapshot constraint above can stay narrow.
466
- // eslint-disable-next-line no-restricted-syntax -- TSnapshot extends object is structurally compatible but TS can't see the missing index signature
496
+ // oxlint-disable-next-line eslint-js/no-restricted-syntax -- TSnapshot extends object is structurally compatible but TS can't see the missing index signature
467
497
  state: this.options.getSnapshot() as unknown as Record<string, unknown>,
468
498
  })
469
499
  }
@@ -626,8 +656,16 @@ export class ClientDevtoolsBridge<TSnapshot extends object> {
626
656
  this.emitRegistered()
627
657
  this.emitToolsRegistered()
628
658
  this.emitSnapshot()
659
+ this.onReplayState()
629
660
  }
630
661
 
662
+ /**
663
+ * Extension hook for subclasses to replay any additional cached state on a
664
+ * `devtools:request-state` (i.e. when a panel opens). Called only after the
665
+ * base guards (disposed/superseded/targetHookId) pass. No-op by default.
666
+ */
667
+ protected onReplayState(): void {}
668
+
631
669
  private async handleToolFixtureApply(
632
670
  event: AIDevtoolsEvent<AIDevtoolsToolFixture>,
633
671
  ): Promise<void> {
@@ -644,6 +682,9 @@ export class ClientDevtoolsBridge<TSnapshot extends object> {
644
682
  return false
645
683
  }
646
684
 
685
+ // Fixture routing: `hookId` wins when present (latest bridge for that
686
+ // registry key). `threadId` is the fallback for fixtures scoped only to a
687
+ // conversation / generation slot without a hook id.
647
688
  if (fixture.hookId) {
648
689
  return fixture.hookId === this.options.hookId
649
690
  }
@@ -657,13 +698,16 @@ export class ClientDevtoolsBridge<TSnapshot extends object> {
657
698
  return true
658
699
  }
659
700
 
660
- private createEnvelope(
701
+ protected createEnvelope(
661
702
  eventType:
662
703
  | 'hook:registered'
663
704
  | 'hook:updated'
664
705
  | 'hook:unregistered'
665
706
  | 'hook:state-snapshot'
666
707
  | 'tools:registered'
708
+ | 'memory:retrieve:started'
709
+ | 'memory:retrieve:completed'
710
+ | 'memory:snapshot'
667
711
  | AIDevtoolsRunEventType,
668
712
  visibility: AIDevtoolsEventVisibility = 'client-state',
669
713
  context: { runId?: string } = {},
@@ -740,6 +784,8 @@ export class ChatDevtoolsBridge extends ClientDevtoolsBridge<AIDevtoolsChatSnaps
740
784
  private currentStreamId: string | null = null
741
785
  private lastStreamId: string | null = null
742
786
  private lastRunEventContext: ChatClientRunEventContext | undefined
787
+ /** Last transported `memory:state` value, replayed when a panel opens. */
788
+ private lastMemoryStateValue: unknown = null
743
789
 
744
790
  constructor(options: ChatDevtoolsBridgeOptions) {
745
791
  super({
@@ -830,6 +876,78 @@ export class ChatDevtoolsBridge extends ClientDevtoolsBridge<AIDevtoolsChatSnaps
830
876
  }
831
877
  }
832
878
 
879
+ /**
880
+ * Record a transported `memory:state` value (the server memory middleware's
881
+ * per-turn recall metrics + store snapshot). Called from the chat client's
882
+ * `onCustomEvent` handler — the designated path for CUSTOM stream events —
883
+ * NOT from `observeChunk`. Re-emits the browser `memory:*` events the devtools
884
+ * store consumes, and caches the value so a panel opened later can replay it
885
+ * (see {@link onReplayState}). Symmetric with the generation bridge's
886
+ * `recordResultChange` / `recordProgressChange`.
887
+ */
888
+ recordMemoryState(rawValue: unknown): void {
889
+ this.lastMemoryStateValue = rawValue
890
+ this.emitMemoryState(rawValue)
891
+ }
892
+
893
+ /**
894
+ * Re-emit the browser-side `memory:*` events from a transported
895
+ * `memory:state` value. The devtools store consumes these to render the
896
+ * Memory tab (operations timeline + live contents). The recall pair mirrors
897
+ * the server middleware's own emits; the snapshot is included only when the
898
+ * adapter supports introspection.
899
+ */
900
+ private emitMemoryState(rawValue: unknown): void {
901
+ if (!rawValue || typeof rawValue !== 'object') return
902
+ const value = rawValue as MemoryStateEventValue
903
+ const scope = value.scope
904
+ const adapter = value.adapter
905
+ if (!scope || typeof adapter !== 'string') return
906
+ const runContext = this.currentRunId ? { runId: this.currentRunId } : {}
907
+
908
+ emitAIDevtoolsEvent('memory:retrieve:started', {
909
+ ...this.createEnvelope(
910
+ 'memory:retrieve:started',
911
+ 'client-state',
912
+ runContext,
913
+ ),
914
+ scope,
915
+ adapter,
916
+ query: value.query ?? '',
917
+ })
918
+ if (value.recall) {
919
+ emitAIDevtoolsEvent('memory:retrieve:completed', {
920
+ ...this.createEnvelope(
921
+ 'memory:retrieve:completed',
922
+ 'client-state',
923
+ runContext,
924
+ ),
925
+ scope,
926
+ adapter,
927
+ fragmentCount: value.recall.fragmentCount ?? 0,
928
+ hasTools: Boolean(value.recall.hasTools),
929
+ systemPromptChars: value.recall.systemPromptChars ?? 0,
930
+ durationMs: value.recall.durationMs ?? 0,
931
+ })
932
+ }
933
+ if (value.snapshot) {
934
+ emitAIDevtoolsEvent('memory:snapshot', {
935
+ ...this.createEnvelope('memory:snapshot', 'client-state', runContext),
936
+ scope,
937
+ adapter,
938
+ takenAt: value.snapshot.takenAt,
939
+ data: value.snapshot.data,
940
+ facts: value.snapshot.facts ?? [],
941
+ })
942
+ }
943
+ }
944
+
945
+ protected override onReplayState(): void {
946
+ if (this.lastMemoryStateValue != null) {
947
+ this.emitMemoryState(this.lastMemoryStateValue)
948
+ }
949
+ }
950
+
833
951
  getCurrentRunEventContext(): ChatClientRunEventContext | undefined {
834
952
  if (!this.currentRunId) return undefined
835
953
  return {