@tanstack/ai-devtools-core 0.5.9 → 0.5.11
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.
- package/dist/Shell/{G2SFOZV2.js → DCUFLPG6.js} +518 -71
- package/dist/{production/Shell/WD22ACIB.js → Shell/JT2B6WKX.js} +774 -155
- package/dist/Shell/{DRZSZBFE.js → YBXRSRRK.js} +774 -155
- package/dist/dev.js +1 -1
- package/dist/index.js +1 -1
- package/dist/production/Shell/{G2SFOZV2.js → DCUFLPG6.js} +518 -71
- package/dist/{Shell/WD22ACIB.js → production/Shell/JT2B6WKX.js} +774 -155
- package/dist/production/Shell/{DRZSZBFE.js → YBXRSRRK.js} +774 -155
- package/dist/production/dev.js +1 -1
- package/dist/production/index.js +1 -1
- package/dist/production/server.js +1 -1
- package/dist/server.js +1 -1
- package/package.json +3 -3
- package/src/components/conversation/IterationCard.tsx +11 -5
- package/src/components/hooks/CompactionPanel.tsx +223 -0
- package/src/components/hooks/HookDashboard.tsx +3 -2
- package/src/components/hooks/HookDetails.tsx +44 -6
- package/src/components/hooks/SkillsPanel.tsx +125 -0
- package/src/components/hooks/index.ts +2 -0
- package/src/store/ai-context.tsx +138 -5
- package/src/store/compaction-registry.ts +121 -0
- package/src/store/skills-registry.ts +50 -0
- package/src/styles/use-styles.ts +2 -3
package/src/store/ai-context.tsx
CHANGED
|
@@ -6,9 +6,9 @@ import {
|
|
|
6
6
|
applyHookEvent,
|
|
7
7
|
clearHookRegistry,
|
|
8
8
|
createHookRegistryState,
|
|
9
|
+
markHookViewed,
|
|
9
10
|
removeSavedFixture,
|
|
10
11
|
replaceSavedFixtures,
|
|
11
|
-
setActiveHook,
|
|
12
12
|
} from './hook-registry'
|
|
13
13
|
import {
|
|
14
14
|
createClientToolCallMessage,
|
|
@@ -20,6 +20,16 @@ import {
|
|
|
20
20
|
clearMemoryRegistry,
|
|
21
21
|
createMemoryRegistryState,
|
|
22
22
|
} from './memory-registry'
|
|
23
|
+
import {
|
|
24
|
+
applyCompactionEvent,
|
|
25
|
+
clearCompactionRegistry,
|
|
26
|
+
createCompactionRegistryState,
|
|
27
|
+
} from './compaction-registry'
|
|
28
|
+
import {
|
|
29
|
+
applySkillsSnapshot,
|
|
30
|
+
clearSkillsRegistry,
|
|
31
|
+
createSkillsRegistryState,
|
|
32
|
+
} from './skills-registry'
|
|
23
33
|
import type { ContentPartSource, TokenUsage } from '@tanstack/ai'
|
|
24
34
|
import type {
|
|
25
35
|
ContentPart,
|
|
@@ -28,6 +38,8 @@ import type {
|
|
|
28
38
|
} from '@tanstack/ai-event-client'
|
|
29
39
|
import type { HookRegistryState, ToolFixtureRecord } from './hook-registry'
|
|
30
40
|
import type { MemoryRegistryState } from './memory-registry'
|
|
41
|
+
import type { CompactionRegistryState } from './compaction-registry'
|
|
42
|
+
import type { SkillsRegistryState } from './skills-registry'
|
|
31
43
|
import type { ParentComponent } from 'solid-js'
|
|
32
44
|
|
|
33
45
|
interface MessagePart {
|
|
@@ -236,6 +248,8 @@ interface AIStoreState {
|
|
|
236
248
|
activeConversationId: string | null
|
|
237
249
|
hooks: HookRegistryState
|
|
238
250
|
memory: MemoryRegistryState
|
|
251
|
+
compaction: CompactionRegistryState
|
|
252
|
+
skills: SkillsRegistryState
|
|
239
253
|
}
|
|
240
254
|
|
|
241
255
|
interface AIContextValue {
|
|
@@ -244,6 +258,8 @@ interface AIContextValue {
|
|
|
244
258
|
selectConversation: (id: string) => void
|
|
245
259
|
clearHooks: () => void
|
|
246
260
|
clearMemory: () => void
|
|
261
|
+
clearCompaction: () => void
|
|
262
|
+
clearSkills: () => void
|
|
247
263
|
selectHook: (id: string | null) => void
|
|
248
264
|
saveToolFixture: (fixture: ToolFixtureRecord) => void
|
|
249
265
|
deleteToolFixture: (fixtureId: string) => void
|
|
@@ -266,6 +282,8 @@ export const AIProvider: ParentComponent = (props) => {
|
|
|
266
282
|
activeConversationId: null,
|
|
267
283
|
hooks: createHookRegistryState(),
|
|
268
284
|
memory: createMemoryRegistryState(),
|
|
285
|
+
compaction: createCompactionRegistryState(),
|
|
286
|
+
skills: createSkillsRegistryState(),
|
|
269
287
|
})
|
|
270
288
|
|
|
271
289
|
const streamToConversation = new Map<string, string>()
|
|
@@ -661,15 +679,36 @@ export const AIProvider: ParentComponent = (props) => {
|
|
|
661
679
|
)
|
|
662
680
|
}
|
|
663
681
|
|
|
664
|
-
function
|
|
682
|
+
function clearCompaction() {
|
|
665
683
|
setState(
|
|
666
|
-
'
|
|
667
|
-
produce((
|
|
668
|
-
|
|
684
|
+
'compaction',
|
|
685
|
+
produce((compaction: CompactionRegistryState) => {
|
|
686
|
+
clearCompactionRegistry(compaction)
|
|
669
687
|
}),
|
|
670
688
|
)
|
|
671
689
|
}
|
|
672
690
|
|
|
691
|
+
function clearSkills() {
|
|
692
|
+
setState(
|
|
693
|
+
'skills',
|
|
694
|
+
produce((skills: SkillsRegistryState) => {
|
|
695
|
+
clearSkillsRegistry(skills)
|
|
696
|
+
}),
|
|
697
|
+
)
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
function selectHook(id: string | null) {
|
|
701
|
+
setState('hooks', 'activeHookId', id)
|
|
702
|
+
if (id) {
|
|
703
|
+
setState(
|
|
704
|
+
'hooks',
|
|
705
|
+
produce((hooks: HookRegistryState) => {
|
|
706
|
+
markHookViewed(hooks, id)
|
|
707
|
+
}),
|
|
708
|
+
)
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
|
|
673
712
|
function saveToolFixture(fixture: ToolFixtureRecord) {
|
|
674
713
|
const fixtures = mergeFixtures(fixture, state.hooks.fixtures)
|
|
675
714
|
setState(
|
|
@@ -1248,6 +1287,14 @@ export const AIProvider: ParentComponent = (props) => {
|
|
|
1248
1287
|
}),
|
|
1249
1288
|
)
|
|
1250
1289
|
}),
|
|
1290
|
+
aiEventClient.on('skills:snapshot', (e) => {
|
|
1291
|
+
setState(
|
|
1292
|
+
'skills',
|
|
1293
|
+
produce((skills: SkillsRegistryState) => {
|
|
1294
|
+
applySkillsSnapshot(skills, e.payload)
|
|
1295
|
+
}),
|
|
1296
|
+
)
|
|
1297
|
+
}),
|
|
1251
1298
|
)
|
|
1252
1299
|
|
|
1253
1300
|
const recordRunEvent = (
|
|
@@ -2958,6 +3005,90 @@ export const AIProvider: ParentComponent = (props) => {
|
|
|
2958
3005
|
}),
|
|
2959
3006
|
)
|
|
2960
3007
|
|
|
3008
|
+
const recordCompactionLifecycle = (
|
|
3009
|
+
kind: 'started' | 'state' | 'ended',
|
|
3010
|
+
hookName: 'onCompactStart' | 'onCompact' | 'onCompactEnd',
|
|
3011
|
+
payload: {
|
|
3012
|
+
timestamp: number
|
|
3013
|
+
requestId?: string
|
|
3014
|
+
streamId?: string
|
|
3015
|
+
clientId?: string
|
|
3016
|
+
hookId?: string
|
|
3017
|
+
threadId?: string
|
|
3018
|
+
runId?: string
|
|
3019
|
+
eventId?: string
|
|
3020
|
+
before?: number
|
|
3021
|
+
after?: number
|
|
3022
|
+
messagesBefore?: number
|
|
3023
|
+
messagesAfter?: number
|
|
3024
|
+
reusedCheckpoint?: boolean
|
|
3025
|
+
maxTokens?: number
|
|
3026
|
+
strategyKey?: string
|
|
3027
|
+
durationMs?: number
|
|
3028
|
+
dropped?: CompactionRegistryState['events'][number]['dropped']
|
|
3029
|
+
result?: CompactionRegistryState['events'][number]['result']
|
|
3030
|
+
},
|
|
3031
|
+
) => {
|
|
3032
|
+
setState(
|
|
3033
|
+
'compaction',
|
|
3034
|
+
produce((compaction: CompactionRegistryState) => {
|
|
3035
|
+
applyCompactionEvent(compaction, kind, payload)
|
|
3036
|
+
}),
|
|
3037
|
+
)
|
|
3038
|
+
|
|
3039
|
+
const { requestId, streamId, clientId } = payload
|
|
3040
|
+
const conversationId =
|
|
3041
|
+
(clientId && state.conversations[clientId] ? clientId : undefined) ||
|
|
3042
|
+
(streamId ? streamToConversation.get(streamId) : undefined) ||
|
|
3043
|
+
(requestId ? requestToConversation.get(requestId) : undefined)
|
|
3044
|
+
if (!conversationId || !state.conversations[conversationId]) return
|
|
3045
|
+
|
|
3046
|
+
const conv = state.conversations[conversationId]
|
|
3047
|
+
const iterIndex = findLatestIterationIndex(conv, requestId)
|
|
3048
|
+
if (iterIndex < 0) return
|
|
3049
|
+
|
|
3050
|
+
const mwEvent: MiddlewareEvent = {
|
|
3051
|
+
id: `mw-cmp-${kind}-${Date.now()}-${Math.random()}`,
|
|
3052
|
+
middlewareName: 'compaction',
|
|
3053
|
+
hookName,
|
|
3054
|
+
timestamp: payload.timestamp,
|
|
3055
|
+
hasTransform: kind === 'state',
|
|
3056
|
+
configChanges: {
|
|
3057
|
+
before: payload.before,
|
|
3058
|
+
after: payload.after,
|
|
3059
|
+
messagesBefore: payload.messagesBefore,
|
|
3060
|
+
messagesAfter: payload.messagesAfter,
|
|
3061
|
+
reusedCheckpoint: payload.reusedCheckpoint,
|
|
3062
|
+
maxTokens: payload.maxTokens,
|
|
3063
|
+
strategyKey: payload.strategyKey,
|
|
3064
|
+
durationMs: payload.durationMs,
|
|
3065
|
+
},
|
|
3066
|
+
}
|
|
3067
|
+
|
|
3068
|
+
setState(
|
|
3069
|
+
'conversations',
|
|
3070
|
+
conversationId,
|
|
3071
|
+
'iterations',
|
|
3072
|
+
iterIndex,
|
|
3073
|
+
'middlewareEvents',
|
|
3074
|
+
produce((arr: Array<MiddlewareEvent>) => {
|
|
3075
|
+
arr.push(mwEvent)
|
|
3076
|
+
}),
|
|
3077
|
+
)
|
|
3078
|
+
}
|
|
3079
|
+
|
|
3080
|
+
cleanupFns.push(
|
|
3081
|
+
aiEventClient.on('compaction:started', (e) => {
|
|
3082
|
+
recordCompactionLifecycle('started', 'onCompactStart', e.payload)
|
|
3083
|
+
}),
|
|
3084
|
+
aiEventClient.on('compaction:state', (e) => {
|
|
3085
|
+
recordCompactionLifecycle('state', 'onCompact', e.payload)
|
|
3086
|
+
}),
|
|
3087
|
+
aiEventClient.on('compaction:ended', (e) => {
|
|
3088
|
+
recordCompactionLifecycle('ended', 'onCompactEnd', e.payload)
|
|
3089
|
+
}),
|
|
3090
|
+
)
|
|
3091
|
+
|
|
2961
3092
|
cleanupFns.push(
|
|
2962
3093
|
aiEventClient.on('summarize:request:started', (e) => {
|
|
2963
3094
|
const { requestId, model, inputLength, timestamp, clientId } = e.payload
|
|
@@ -3465,6 +3596,8 @@ export const AIProvider: ParentComponent = (props) => {
|
|
|
3465
3596
|
selectConversation,
|
|
3466
3597
|
clearHooks,
|
|
3467
3598
|
clearMemory,
|
|
3599
|
+
clearCompaction,
|
|
3600
|
+
clearSkills,
|
|
3468
3601
|
selectHook,
|
|
3469
3602
|
saveToolFixture,
|
|
3470
3603
|
deleteToolFixture,
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
const MAX_EVENTS = 50
|
|
2
|
+
|
|
3
|
+
export type CompactionEventKind = 'started' | 'state' | 'ended'
|
|
4
|
+
|
|
5
|
+
export interface CompactionMessagePreview {
|
|
6
|
+
role: string
|
|
7
|
+
tokens: number
|
|
8
|
+
text: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface CompactionEventRecord {
|
|
12
|
+
id: string
|
|
13
|
+
kind: CompactionEventKind
|
|
14
|
+
timestamp: number
|
|
15
|
+
hookId?: string
|
|
16
|
+
clientId?: string
|
|
17
|
+
threadId?: string
|
|
18
|
+
runId?: string
|
|
19
|
+
before?: number
|
|
20
|
+
after?: number
|
|
21
|
+
messagesBefore?: number
|
|
22
|
+
messagesAfter?: number
|
|
23
|
+
reusedCheckpoint?: boolean
|
|
24
|
+
maxTokens?: number
|
|
25
|
+
strategyKey?: string
|
|
26
|
+
durationMs?: number
|
|
27
|
+
dropped?: Array<CompactionMessagePreview>
|
|
28
|
+
result?: Array<CompactionMessagePreview>
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface CompactionRegistryState {
|
|
32
|
+
events: Array<CompactionEventRecord>
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function createCompactionRegistryState(): CompactionRegistryState {
|
|
36
|
+
return { events: [] }
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface CompactionLifecycleInput {
|
|
40
|
+
timestamp: number
|
|
41
|
+
eventId?: string
|
|
42
|
+
hookId?: string
|
|
43
|
+
clientId?: string
|
|
44
|
+
threadId?: string
|
|
45
|
+
runId?: string
|
|
46
|
+
before?: number
|
|
47
|
+
after?: number
|
|
48
|
+
messagesBefore?: number
|
|
49
|
+
messagesAfter?: number
|
|
50
|
+
reusedCheckpoint?: boolean
|
|
51
|
+
maxTokens?: number
|
|
52
|
+
strategyKey?: string
|
|
53
|
+
durationMs?: number
|
|
54
|
+
dropped?: Array<CompactionMessagePreview>
|
|
55
|
+
result?: Array<CompactionMessagePreview>
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function eventId(
|
|
59
|
+
kind: CompactionEventKind,
|
|
60
|
+
event: CompactionLifecycleInput,
|
|
61
|
+
): string {
|
|
62
|
+
return [
|
|
63
|
+
'cmp',
|
|
64
|
+
kind,
|
|
65
|
+
String(event.timestamp),
|
|
66
|
+
event.hookId ?? event.clientId ?? event.threadId ?? 'unknown',
|
|
67
|
+
String(event.before ?? event.after ?? ''),
|
|
68
|
+
].join('-')
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Append one compaction lifecycle event. Newest stays at the end. */
|
|
72
|
+
export function applyCompactionEvent(
|
|
73
|
+
state: CompactionRegistryState,
|
|
74
|
+
kind: CompactionEventKind,
|
|
75
|
+
event: CompactionLifecycleInput,
|
|
76
|
+
): void {
|
|
77
|
+
state.events.push({
|
|
78
|
+
id: event.eventId ?? eventId(kind, event),
|
|
79
|
+
kind,
|
|
80
|
+
timestamp: event.timestamp,
|
|
81
|
+
hookId: event.hookId,
|
|
82
|
+
clientId: event.clientId,
|
|
83
|
+
threadId: event.threadId,
|
|
84
|
+
runId: event.runId,
|
|
85
|
+
before: event.before,
|
|
86
|
+
after: event.after,
|
|
87
|
+
messagesBefore: event.messagesBefore,
|
|
88
|
+
messagesAfter: event.messagesAfter,
|
|
89
|
+
reusedCheckpoint: event.reusedCheckpoint,
|
|
90
|
+
maxTokens: event.maxTokens,
|
|
91
|
+
strategyKey: event.strategyKey,
|
|
92
|
+
durationMs: event.durationMs,
|
|
93
|
+
dropped: event.dropped,
|
|
94
|
+
result: event.result,
|
|
95
|
+
})
|
|
96
|
+
if (state.events.length > MAX_EVENTS) {
|
|
97
|
+
state.events.splice(0, state.events.length - MAX_EVENTS)
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function clearCompactionRegistry(state: CompactionRegistryState): void {
|
|
102
|
+
state.events = []
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function compactionEventsForHook(
|
|
106
|
+
state: CompactionRegistryState,
|
|
107
|
+
hook: { id: string; clientId?: string; threadId?: string },
|
|
108
|
+
): Array<CompactionEventRecord> {
|
|
109
|
+
const matched = state.events.filter((event) => {
|
|
110
|
+
if (event.hookId && event.hookId === hook.id) return true
|
|
111
|
+
if (event.clientId && hook.clientId && event.clientId === hook.clientId) {
|
|
112
|
+
return true
|
|
113
|
+
}
|
|
114
|
+
if (event.threadId && hook.threadId && event.threadId === hook.threadId) {
|
|
115
|
+
return true
|
|
116
|
+
}
|
|
117
|
+
return false
|
|
118
|
+
})
|
|
119
|
+
if (matched.length > 0) return matched
|
|
120
|
+
return state.events
|
|
121
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DevTools accumulator for the `skills:snapshot` event. Pure reducers so the
|
|
3
|
+
* mapping is unit-testable without a Solid store.
|
|
4
|
+
*
|
|
5
|
+
* Keyed by hookId (the chat client that received the CUSTOM chunk). The Skills
|
|
6
|
+
* tab resolves the active hook and reads that snapshot.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export interface SkillCatalogEntry {
|
|
10
|
+
name: string
|
|
11
|
+
description: string
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface SkillsSnapshot {
|
|
15
|
+
catalog: Array<SkillCatalogEntry>
|
|
16
|
+
activated: Array<string>
|
|
17
|
+
updatedAt: number
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface SkillsRegistryState {
|
|
21
|
+
snapshots: Record<string, SkillsSnapshot>
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function createSkillsRegistryState(): SkillsRegistryState {
|
|
25
|
+
return { snapshots: {} }
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function clearSkillsRegistry(state: SkillsRegistryState): void {
|
|
29
|
+
state.snapshots = {}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function applySkillsSnapshot(
|
|
33
|
+
state: SkillsRegistryState,
|
|
34
|
+
payload: {
|
|
35
|
+
hookId?: string
|
|
36
|
+
catalog?: Array<SkillCatalogEntry>
|
|
37
|
+
activated?: Array<string>
|
|
38
|
+
timestamp?: number
|
|
39
|
+
},
|
|
40
|
+
): void {
|
|
41
|
+
const key =
|
|
42
|
+
payload.hookId && payload.hookId.length > 0 ? payload.hookId : '_default'
|
|
43
|
+
const catalog = Array.isArray(payload.catalog) ? payload.catalog : []
|
|
44
|
+
const activated = Array.isArray(payload.activated) ? payload.activated : []
|
|
45
|
+
state.snapshots[key] = {
|
|
46
|
+
catalog,
|
|
47
|
+
activated,
|
|
48
|
+
updatedAt: payload.timestamp ?? Date.now(),
|
|
49
|
+
}
|
|
50
|
+
}
|
package/src/styles/use-styles.ts
CHANGED
|
@@ -1695,7 +1695,6 @@ const stylesFactory = (theme: 'light' | 'dark') => {
|
|
|
1695
1695
|
border: 1px solid ${t(colors.gray[200], colors.darkGray[600])};
|
|
1696
1696
|
border-radius: ${border.radius.sm};
|
|
1697
1697
|
background: ${t(colors.white, colors.darkGray[900])};
|
|
1698
|
-
min-height: 260px;
|
|
1699
1698
|
max-height: 420px;
|
|
1700
1699
|
overflow: auto;
|
|
1701
1700
|
padding: ${size[2]} ${size[2]} ${size[2]} ${size[5]};
|
|
@@ -2570,7 +2569,7 @@ const stylesFactory = (theme: 'light' | 'dark') => {
|
|
|
2570
2569
|
gap: ${size[3]};
|
|
2571
2570
|
align-items: start;
|
|
2572
2571
|
& > * {
|
|
2573
|
-
|
|
2572
|
+
max-height: 420px;
|
|
2574
2573
|
border: 1px solid oklch(0.28 0.03 260);
|
|
2575
2574
|
border-radius: 6px;
|
|
2576
2575
|
background: oklch(0.18 0.02 260);
|
|
@@ -3634,7 +3633,6 @@ const stylesFactory = (theme: 'light' | 'dark') => {
|
|
|
3634
3633
|
`,
|
|
3635
3634
|
stepJsonPanel: css`
|
|
3636
3635
|
margin: ${size[1.5]} ${size[3]};
|
|
3637
|
-
min-height: 280px;
|
|
3638
3636
|
max-height: 520px;
|
|
3639
3637
|
overflow: auto;
|
|
3640
3638
|
padding: ${size[2]} ${size[2]} ${size[2]} ${size[5]};
|
|
@@ -3652,6 +3650,7 @@ const stylesFactory = (theme: 'light' | 'dark') => {
|
|
|
3652
3650
|
grid-template-columns: repeat(2, minmax(320px, 1fr));
|
|
3653
3651
|
gap: ${size[3]};
|
|
3654
3652
|
align-items: start;
|
|
3653
|
+
padding-left: ${size[3]};
|
|
3655
3654
|
padding-right: ${size[3]};
|
|
3656
3655
|
@media (max-width: 760px) {
|
|
3657
3656
|
grid-template-columns: 1fr;
|