@tanstack/ai-devtools-core 0.5.17 → 0.5.18

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.
@@ -2,7 +2,7 @@ import { constructCoreClass } from '@tanstack/devtools-utils/solid/class';
2
2
 
3
3
  // src/core.ts
4
4
  var [AiDevtoolsCore, AiDevtoolsCoreNoOp] = constructCoreClass(
5
- () => import('./Shell/EBEFU2GI.js')
5
+ () => import('./Shell/7YKZZJIO.js')
6
6
  );
7
7
 
8
8
  export { AiDevtoolsCore };
@@ -2,7 +2,7 @@ import { constructCoreClass } from '@tanstack/devtools-utils/solid/class';
2
2
 
3
3
  // src/core.ts
4
4
  var [AiDevtoolsCore, AiDevtoolsCoreNoOp] = constructCoreClass(
5
- () => import('./Shell/Y4V4YNYM.js')
5
+ () => import('./Shell/LR2PUWRS.js')
6
6
  );
7
7
 
8
8
  export { AiDevtoolsCore };
@@ -2,7 +2,7 @@ import { constructCoreClass } from '@tanstack/devtools-utils/solid/class';
2
2
 
3
3
  // src/core.ts
4
4
  var [AiDevtoolsCore, AiDevtoolsCoreNoOp] = constructCoreClass(
5
- () => import('./Shell/3BTXBRTQ.js')
5
+ () => import('./Shell/TTHBDBPP.js')
6
6
  );
7
7
 
8
8
  export { AiDevtoolsCore };
package/dist/server.js CHANGED
@@ -2,7 +2,7 @@ import { constructCoreClass } from '@tanstack/devtools-utils/solid/class';
2
2
 
3
3
  // src/core.ts
4
4
  var [AiDevtoolsCore, AiDevtoolsCoreNoOp] = constructCoreClass(
5
- () => import('./Shell/3BTXBRTQ.js')
5
+ () => import('./Shell/TTHBDBPP.js')
6
6
  );
7
7
 
8
8
  // src/index.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/ai-devtools-core",
3
- "version": "0.5.17",
3
+ "version": "0.5.18",
4
4
  "description": "Core TanStack AI Devtools plugin for inspecting chat messages, tool calls, streams, and errors.",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -84,7 +84,7 @@
84
84
  "@tanstack/devtools-utils": "^0.7.0",
85
85
  "goober": "^2.1.18",
86
86
  "solid-js": "^1.9.10",
87
- "@tanstack/ai": "^0.58.0",
87
+ "@tanstack/ai": "^0.59.0",
88
88
  "@tanstack/ai-event-client": "^0.12.1"
89
89
  },
90
90
  "devDependencies": {
@@ -7,20 +7,27 @@ import {
7
7
  isMessageHighlighted,
8
8
  } from '../hooks/preview-model'
9
9
  import { formatDuration } from '../utils'
10
+ import { buildSubagentSteps, groupTimeline } from '../../store/subagent-steps'
10
11
  import { IterationCard } from './IterationCard'
11
12
  import type { HoverTarget } from '../hooks/preview-model'
12
13
  import type { Iteration, Message } from '../../store/ai-store'
14
+ import type {
15
+ AgentStepGroup,
16
+ SubagentInfo,
17
+ TimelineGroup,
18
+ } from '../../store/subagent-steps'
13
19
  import type { Component } from 'solid-js'
14
20
 
15
21
  /** A group of iterations triggered by a single user message */
16
- interface UserMessageGroup {
17
- userMessage: Message | null
18
- iterations: Array<Iteration>
19
- }
22
+ type UserMessageGroup = TimelineGroup
20
23
 
21
24
  interface IterationTimelineProps {
22
25
  iterations: Array<Iteration>
23
26
  messages: Array<Message>
27
+ /** Child agents from the hook snapshot. Their steps get their own cards. */
28
+ subagents?: Array<SubagentInfo>
29
+ /** The chat's user messages, from the snapshot. They mark the turns. */
30
+ turns?: Array<Message>
24
31
  hoverTarget?: HoverTarget | null
25
32
  onHoverTarget?: (target: HoverTarget | null) => void
26
33
  }
@@ -33,47 +40,15 @@ export const IterationTimeline: Component<IterationTimelineProps> = (props) => {
33
40
  * Group iterations by user messages.
34
41
  * Memoized to avoid recomputing on unrelated store changes.
35
42
  */
36
- const groups = createMemo((): Array<UserMessageGroup> => {
37
- const userMessages = props.messages.filter((m) => m.role === 'user')
38
- const iters = props.iterations
39
-
40
- if (userMessages.length === 0) {
41
- return iters.length > 0 ? [{ userMessage: null, iterations: iters }] : []
42
- }
43
-
44
- const result: Array<UserMessageGroup> = []
45
-
46
- const sortedUsers = [...userMessages].sort(
47
- (a, b) => a.timestamp - b.timestamp,
48
- )
49
-
50
- for (const [u, currentUser] of sortedUsers.entries()) {
51
- const nextUser = sortedUsers[u + 1]
52
-
53
- const groupIters = iters.filter((it) => {
54
- if (it.startedAt < currentUser.timestamp) return false
55
- if (nextUser && it.startedAt >= nextUser.timestamp) return false
56
- return true
57
- })
58
-
59
- if (groupIters.length > 0) {
60
- result.push({ userMessage: currentUser, iterations: groupIters })
61
- }
62
- }
63
-
64
- // Catch any iterations before the first user message
65
- const firstUser = sortedUsers[0]
66
- if (firstUser) {
67
- const earlyIters = iters.filter(
68
- (it) => it.startedAt < firstUser.timestamp,
69
- )
70
- if (earlyIters.length > 0) {
71
- result.unshift({ userMessage: null, iterations: earlyIters })
72
- }
73
- }
74
-
75
- return result
76
- })
43
+ const groups = createMemo(
44
+ (): Array<UserMessageGroup> =>
45
+ groupTimeline(
46
+ props.iterations,
47
+ props.messages,
48
+ props.subagents ?? [],
49
+ props.turns ?? [],
50
+ ),
51
+ )
77
52
 
78
53
  return (
79
54
  <div class={s().container}>
@@ -468,9 +443,126 @@ const UserMessageGroupCard: Component<{
468
443
  />
469
444
  )}
470
445
  </For>
446
+ <For each={group().agents}>
447
+ {(agentGroup) => (
448
+ <SubagentStepsCard
449
+ group={agentGroup}
450
+ hoverTarget={props.hoverTarget}
451
+ onHoverTarget={props.onHoverTarget}
452
+ />
453
+ )}
454
+ </For>
471
455
  </div>
472
456
  </div>
473
457
  </div>
474
458
  </div>
475
459
  )
476
460
  }
461
+
462
+ /** The steps of one child agent, drawn with the same cards as the root. */
463
+ export const SubagentStepsCard: Component<{
464
+ group?: AgentStepGroup
465
+ /** Build the steps from the snapshot when no group is given. */
466
+ agent?: SubagentInfo
467
+ hoverTarget: HoverTarget | null
468
+ onHoverTarget?: (target: HoverTarget | null) => void
469
+ }> = (props) => {
470
+ const styles = useStyles()
471
+ const s = () => styles().iterationTimeline
472
+ const [isOpen, setIsOpen] = createSignal(true)
473
+
474
+ const group = createMemo((): AgentStepGroup | undefined => {
475
+ if (props.group) return props.group
476
+ if (!props.agent) return undefined
477
+ return {
478
+ agent: props.agent,
479
+ ...buildSubagentSteps(props.agent),
480
+ source: 'snapshot',
481
+ }
482
+ })
483
+ const agent = () => group()?.agent
484
+ const iterations = () => group()?.iterations ?? []
485
+
486
+ const accentClass = () => {
487
+ const status = agent()?.status
488
+ if (status === 'running') return s().cardActive
489
+ if (status === 'error') return s().cardError
490
+ if (status === 'finished') return s().cardCompleted
491
+ return ''
492
+ }
493
+
494
+ return (
495
+ <Show when={agent()}>
496
+ {(current) => (
497
+ <div
498
+ class={`${s().card} ${accentClass()}`}
499
+ style={{ 'margin-top': '8px' }}
500
+ data-testid="ai-devtools-subagent-steps"
501
+ data-agent={current().path}
502
+ data-source={group()?.source}
503
+ >
504
+ <div
505
+ class={s().cardHeader}
506
+ role="button"
507
+ tabIndex={0}
508
+ aria-expanded={isOpen()}
509
+ onClick={() => setIsOpen(!isOpen())}
510
+ onKeyDown={(event) => {
511
+ if (event.key !== 'Enter' && event.key !== ' ') return
512
+ event.preventDefault()
513
+ setIsOpen(!isOpen())
514
+ }}
515
+ >
516
+ <div class={s().userBubble}>
517
+ {current().name.charAt(0).toUpperCase()}
518
+ </div>
519
+ <div class={s().cardHeaderContent}>
520
+ <span class={s().cardHeaderLabel}>{current().path}</span>
521
+ <div class={s().cardSubtitle}>
522
+ <span class={s().subtitleText}>subagent</span>
523
+ <Show when={current().status}>
524
+ <span class={s().subtitleBadge}>{current().status}</span>
525
+ </Show>
526
+ <Show when={current().error}>
527
+ <span class={s().subtitleBadgeWarn}>{current().error}</span>
528
+ </Show>
529
+ </div>
530
+ </div>
531
+ <div class={s().cardHeaderBadges}>
532
+ <span
533
+ class={`${s().badge} ${s().badgeDuration}`}
534
+ title={`${iterations().length} ${iterations().length === 1 ? 'iteration' : 'iterations'}`}
535
+ >
536
+ 🔄 {iterations().length}
537
+ </span>
538
+ </div>
539
+ <span class={`${s().chevron} ${isOpen() ? s().chevronOpen : ''}`}>
540
+ {'▶'}
541
+ </span>
542
+ </div>
543
+ <div class={`${s().cardBody} ${isOpen() ? s().cardBodyOpen : ''}`}>
544
+ <div class={s().cardBodyInner}>
545
+ <div class={s().iterList}>
546
+ <For each={iterations()}>
547
+ {(iteration, index) => (
548
+ <IterationCard
549
+ iteration={iteration}
550
+ previousIteration={
551
+ index() > 0 ? iterations()[index() - 1] : undefined
552
+ }
553
+ messages={group()?.messages ?? []}
554
+ index={index()}
555
+ isLast={index() === iterations().length - 1}
556
+ hoverTarget={props.hoverTarget}
557
+ onHoverTarget={props.onHoverTarget}
558
+ />
559
+ )}
560
+ </For>
561
+ </div>
562
+ </div>
563
+ </div>
564
+ </div>
565
+ )}
566
+ </Show>
567
+ )
568
+ }