slidev-addon-agent 0.0.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.
- package/LICENSE +21 -0
- package/README.md +54 -0
- package/agent/constants.ts +119 -0
- package/agent/deck-context.ts +67 -0
- package/agent/index.ts +201 -0
- package/agent/middleware.ts +163 -0
- package/agent/skills/slidev/README.md +61 -0
- package/agent/skills/slidev/SKILL.md +189 -0
- package/agent/skills/slidev/references/animation-click-marker.md +37 -0
- package/agent/skills/slidev/references/animation-drawing.md +68 -0
- package/agent/skills/slidev/references/animation-rough-marker.md +53 -0
- package/agent/skills/slidev/references/api-slide-hooks.md +37 -0
- package/agent/skills/slidev/references/build-og-image.md +36 -0
- package/agent/skills/slidev/references/build-pdf.md +40 -0
- package/agent/skills/slidev/references/build-remote-assets.md +34 -0
- package/agent/skills/slidev/references/build-seo-meta.md +43 -0
- package/agent/skills/slidev/references/code-groups.md +64 -0
- package/agent/skills/slidev/references/code-import-snippet.md +55 -0
- package/agent/skills/slidev/references/code-line-highlighting.md +50 -0
- package/agent/skills/slidev/references/code-line-numbers.md +46 -0
- package/agent/skills/slidev/references/code-magic-move.md +57 -0
- package/agent/skills/slidev/references/code-max-height.md +37 -0
- package/agent/skills/slidev/references/code-twoslash.md +42 -0
- package/agent/skills/slidev/references/core-animations.md +196 -0
- package/agent/skills/slidev/references/core-cli.md +140 -0
- package/agent/skills/slidev/references/core-components.md +197 -0
- package/agent/skills/slidev/references/core-exporting.md +148 -0
- package/agent/skills/slidev/references/core-frontmatter.md +195 -0
- package/agent/skills/slidev/references/core-global-context.md +155 -0
- package/agent/skills/slidev/references/core-headmatter.md +188 -0
- package/agent/skills/slidev/references/core-hosting.md +152 -0
- package/agent/skills/slidev/references/core-layouts.md +286 -0
- package/agent/skills/slidev/references/core-syntax.md +155 -0
- package/agent/skills/slidev/references/diagram-latex.md +55 -0
- package/agent/skills/slidev/references/diagram-mermaid.md +44 -0
- package/agent/skills/slidev/references/diagram-plantuml.md +45 -0
- package/agent/skills/slidev/references/editor-monaco-run.md +44 -0
- package/agent/skills/slidev/references/editor-monaco-write.md +24 -0
- package/agent/skills/slidev/references/editor-monaco.md +50 -0
- package/agent/skills/slidev/references/editor-prettier.md +40 -0
- package/agent/skills/slidev/references/editor-side.md +23 -0
- package/agent/skills/slidev/references/editor-vscode.md +55 -0
- package/agent/skills/slidev/references/layout-canvas-size.md +25 -0
- package/agent/skills/slidev/references/layout-draggable.md +57 -0
- package/agent/skills/slidev/references/layout-global-layers.md +50 -0
- package/agent/skills/slidev/references/layout-slots.md +75 -0
- package/agent/skills/slidev/references/layout-transform.md +33 -0
- package/agent/skills/slidev/references/layout-zoom.md +39 -0
- package/agent/skills/slidev/references/presenter-notes-ruby.md +35 -0
- package/agent/skills/slidev/references/presenter-recording.md +30 -0
- package/agent/skills/slidev/references/presenter-remote.md +40 -0
- package/agent/skills/slidev/references/presenter-timer.md +34 -0
- package/agent/skills/slidev/references/style-direction.md +34 -0
- package/agent/skills/slidev/references/style-icons.md +46 -0
- package/agent/skills/slidev/references/style-scoped.md +50 -0
- package/agent/skills/slidev/references/syntax-block-frontmatter.md +39 -0
- package/agent/skills/slidev/references/syntax-frontmatter-merging.md +49 -0
- package/agent/skills/slidev/references/syntax-importing-slides.md +60 -0
- package/agent/skills/slidev/references/syntax-mdc.md +51 -0
- package/agent/skills/slidev/references/tool-eject-theme.md +27 -0
- package/agent/tools/export-tool.ts +216 -0
- package/agent/tools/review-tool.ts +136 -0
- package/app/index.ts +124 -0
- package/components/MessageItem.vue +231 -0
- package/components/SlidevAgentNavButton.vue +48 -0
- package/components/SlidevAgentSidebar.vue +766 -0
- package/components/SubagentCard.vue +184 -0
- package/components/TypingDots.vue +62 -0
- package/dist/agent/constants.js +117 -0
- package/dist/agent/deck-context.js +47 -0
- package/dist/agent/index.js +167 -0
- package/dist/agent/middleware.js +134 -0
- package/dist/agent/slide-preview-tool.js +257 -0
- package/dist/agent/tools/export-tool.js +167 -0
- package/dist/agent/tools/review-tool.js +111 -0
- package/dist/app/index.js +101 -0
- package/dist/bin/slidev-agent.js +155 -0
- package/dist/lib/bridge.js +151 -0
- package/dist/lib/env.js +17 -0
- package/dist/lib/headless-tools.js +10 -0
- package/dist/lib/langgraph-init.js +59 -0
- package/dist/lib/review-tool.js +98 -0
- package/lib/bridge.ts +212 -0
- package/lib/config.ts +79 -0
- package/lib/env.ts +38 -0
- package/lib/headless-tool-impl.ts +26 -0
- package/lib/headless-tools.ts +11 -0
- package/lib/langgraph-init.ts +79 -0
- package/lib/messages.ts +169 -0
- package/lib/render-chat-markdown.ts +19 -0
- package/lib/sidebar.ts +573 -0
- package/lib/state.ts +44 -0
- package/package.json +65 -0
- package/public/deepagents.svg +12 -0
package/lib/sidebar.ts
ADDED
|
@@ -0,0 +1,573 @@
|
|
|
1
|
+
import { unref } from "vue"
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
getMessageContent,
|
|
5
|
+
getMessageRole,
|
|
6
|
+
getMessageToolCallId,
|
|
7
|
+
getMessageToolCalls,
|
|
8
|
+
getMessageToolName,
|
|
9
|
+
} from "./messages"
|
|
10
|
+
|
|
11
|
+
export type ToolCallResult = {
|
|
12
|
+
call?: {
|
|
13
|
+
id?: string
|
|
14
|
+
name?: string
|
|
15
|
+
args?: unknown
|
|
16
|
+
}
|
|
17
|
+
result?: unknown
|
|
18
|
+
/** From LangGraph SDK `getToolCallsWithResults`; omitted in some code paths. */
|
|
19
|
+
state?: "pending" | "completed" | "error"
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export type StreamToolCall = ToolCallResult
|
|
23
|
+
|
|
24
|
+
export type StreamSubagent = {
|
|
25
|
+
id: string
|
|
26
|
+
status: string
|
|
27
|
+
messages: unknown[]
|
|
28
|
+
toolCall: {
|
|
29
|
+
args?: Record<string, unknown>
|
|
30
|
+
id?: string
|
|
31
|
+
}
|
|
32
|
+
toolCalls: StreamToolCall[]
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export type SubagentActivity = {
|
|
36
|
+
id: string
|
|
37
|
+
type: string
|
|
38
|
+
typeHeadline: string
|
|
39
|
+
status: string
|
|
40
|
+
statusLabel: string
|
|
41
|
+
initiatorToolCallId: string
|
|
42
|
+
taskSummary: string
|
|
43
|
+
latestToolName: string
|
|
44
|
+
latestToolHeadline: string
|
|
45
|
+
latestToolArgs: string
|
|
46
|
+
/** Lifecycle of the **last** subagent tool only (max one row in the UI). */
|
|
47
|
+
latestToolState: "pending" | "completed" | "error"
|
|
48
|
+
latestToolStateLabel: string
|
|
49
|
+
latestToolSummary: string
|
|
50
|
+
files: string[]
|
|
51
|
+
messageCount: number
|
|
52
|
+
hasVisibleActivity: boolean
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
type SidebarBaseMessage = {
|
|
56
|
+
key: string
|
|
57
|
+
role: string
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export type SidebarToolMessage = SidebarBaseMessage & {
|
|
61
|
+
kind: "tool"
|
|
62
|
+
toolName: string
|
|
63
|
+
toolHeadline: string
|
|
64
|
+
argsSummary: string
|
|
65
|
+
resultSummary: string
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export type SidebarTextMessage = SidebarBaseMessage & {
|
|
69
|
+
kind: "message"
|
|
70
|
+
messageId: string
|
|
71
|
+
label: string
|
|
72
|
+
content: string
|
|
73
|
+
subagents: SubagentActivity[]
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export type SidebarMessage = SidebarToolMessage | SidebarTextMessage
|
|
77
|
+
|
|
78
|
+
export function normalizeMessages(value: unknown) {
|
|
79
|
+
const unwrapped = unref(value)
|
|
80
|
+
return Array.isArray(unwrapped) ? unwrapped : []
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function getMessageId(message: unknown): string {
|
|
84
|
+
if (!message || typeof message !== "object")
|
|
85
|
+
return ""
|
|
86
|
+
|
|
87
|
+
const id = Reflect.get(message, "id")
|
|
88
|
+
return typeof id === "string" ? id : ""
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function buildToolCallLookup(toolCalls: unknown, rawMessages: unknown[]) {
|
|
92
|
+
const lookup = new Map<string, ToolCallResult>()
|
|
93
|
+
|
|
94
|
+
normalizeMessages(toolCalls).forEach((entry) => {
|
|
95
|
+
if (!entry || typeof entry !== "object")
|
|
96
|
+
return
|
|
97
|
+
|
|
98
|
+
const call = Reflect.get(entry, "call")
|
|
99
|
+
if (!call || typeof call !== "object")
|
|
100
|
+
return
|
|
101
|
+
|
|
102
|
+
const id = Reflect.get(call, "id")
|
|
103
|
+
if (typeof id !== "string" || !id)
|
|
104
|
+
return
|
|
105
|
+
|
|
106
|
+
lookup.set(id, {
|
|
107
|
+
call: {
|
|
108
|
+
id,
|
|
109
|
+
name: typeof Reflect.get(call, "name") === "string"
|
|
110
|
+
? Reflect.get(call, "name") as string
|
|
111
|
+
: undefined,
|
|
112
|
+
args: Reflect.get(call, "args"),
|
|
113
|
+
},
|
|
114
|
+
result: Reflect.get(entry, "result"),
|
|
115
|
+
})
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
rawMessages.forEach((message) => {
|
|
119
|
+
getMessageToolCalls(message).forEach((toolCall) => {
|
|
120
|
+
if (!toolCall.id || lookup.has(toolCall.id))
|
|
121
|
+
return
|
|
122
|
+
|
|
123
|
+
lookup.set(toolCall.id, { call: toolCall })
|
|
124
|
+
})
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
return lookup
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export function collectActiveSubagentIds(activeSubagents: unknown) {
|
|
131
|
+
const ids = new Set<string>()
|
|
132
|
+
|
|
133
|
+
normalizeMessages(activeSubagents).forEach((subagent) => {
|
|
134
|
+
if (!subagent || typeof subagent !== "object")
|
|
135
|
+
return
|
|
136
|
+
|
|
137
|
+
const id = Reflect.get(subagent, "id")
|
|
138
|
+
if (typeof id === "string" && id)
|
|
139
|
+
ids.add(id)
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
return ids
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export function truncateText(value: string, maxLength = 96) {
|
|
146
|
+
if (value.length <= maxLength)
|
|
147
|
+
return value
|
|
148
|
+
|
|
149
|
+
return `${value.slice(0, maxLength - 1)}...`
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const TOOL_HEADLINES: Record<string, string> = {
|
|
153
|
+
slide_generator: "Generate or revise a slide",
|
|
154
|
+
slidev_go_to_slide: "Open generated slide",
|
|
155
|
+
slidev_review_screenshot: "Review slide screenshot",
|
|
156
|
+
read_file: "Read file",
|
|
157
|
+
write_file: "Write file",
|
|
158
|
+
edit_file: "Edit file",
|
|
159
|
+
ls: "List directory",
|
|
160
|
+
glob: "Find files",
|
|
161
|
+
grep: "Search in files",
|
|
162
|
+
execute: "Run command",
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function titleCaseFromSnake(name: string) {
|
|
166
|
+
return name
|
|
167
|
+
.split(/[_\s]+/)
|
|
168
|
+
.filter(Boolean)
|
|
169
|
+
.map(part => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase())
|
|
170
|
+
.join(" ")
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/** User-facing label for a tool or subagent identifier (snake_case). */
|
|
174
|
+
export function toolCallHeadline(rawName: string) {
|
|
175
|
+
const trimmed = rawName.trim()
|
|
176
|
+
if (!trimmed)
|
|
177
|
+
return "Tool"
|
|
178
|
+
|
|
179
|
+
const mapped = TOOL_HEADLINES[trimmed]
|
|
180
|
+
if (mapped)
|
|
181
|
+
return mapped
|
|
182
|
+
|
|
183
|
+
return titleCaseFromSnake(trimmed)
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export function summarizeToolResult(toolName: string, content: string) {
|
|
187
|
+
const trimmed = content.trim()
|
|
188
|
+
if (toolName === "slidev_go_to_slide") {
|
|
189
|
+
const toolUse = JSON.parse(content)
|
|
190
|
+
const payload = toolUse[Object.keys(toolUse)[0]]
|
|
191
|
+
return `Page: ${payload.page}`
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (!trimmed)
|
|
195
|
+
return "Completed"
|
|
196
|
+
|
|
197
|
+
const lines = trimmed.split("\n").map(line => line.trim()).filter(Boolean)
|
|
198
|
+
if (toolName === "read_file" && lines.length > 0)
|
|
199
|
+
return `${lines.length} line${lines.length === 1 ? "" : "s"}`
|
|
200
|
+
|
|
201
|
+
if (["ls", "glob", "grep"].includes(toolName) && lines.length > 0)
|
|
202
|
+
return `${lines.length} item${lines.length === 1 ? "" : "s"}`
|
|
203
|
+
|
|
204
|
+
return truncateText(lines[0] || trimmed)
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export function formatToolArgs(args: unknown) {
|
|
208
|
+
if (!args || typeof args !== "object")
|
|
209
|
+
return ""
|
|
210
|
+
|
|
211
|
+
const preferredKeys = ["path", "filePath", "file_path", "pattern", "glob", "command", "page"]
|
|
212
|
+
return preferredKeys
|
|
213
|
+
.map((key) => {
|
|
214
|
+
const value = Reflect.get(args, key)
|
|
215
|
+
if (typeof value !== "string" || !value.trim())
|
|
216
|
+
return ""
|
|
217
|
+
|
|
218
|
+
return `${key}: ${truncateText(value.trim(), 48)}`
|
|
219
|
+
})
|
|
220
|
+
.filter(Boolean)
|
|
221
|
+
.join(" · ")
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/** Prefer path-like fields; otherwise a compact JSON snapshot for subagent progress. */
|
|
225
|
+
export function formatToolArgsWithFallback(args: unknown, maxLength = 140) {
|
|
226
|
+
const preferred = formatToolArgs(args)
|
|
227
|
+
if (preferred)
|
|
228
|
+
return preferred
|
|
229
|
+
|
|
230
|
+
if (!args || typeof args !== "object")
|
|
231
|
+
return ""
|
|
232
|
+
|
|
233
|
+
try {
|
|
234
|
+
return truncateText(JSON.stringify(args), maxLength)
|
|
235
|
+
}
|
|
236
|
+
catch {
|
|
237
|
+
return ""
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function stringifyUnknownResult(value: unknown): string {
|
|
242
|
+
if (typeof value === "string")
|
|
243
|
+
return value
|
|
244
|
+
|
|
245
|
+
if (Array.isArray(value))
|
|
246
|
+
return value.map(entry => stringifyUnknownResult(entry)).filter(Boolean).join("\n")
|
|
247
|
+
|
|
248
|
+
if (!value || typeof value !== "object")
|
|
249
|
+
return ""
|
|
250
|
+
|
|
251
|
+
const content = Reflect.get(value, "content")
|
|
252
|
+
if (typeof content === "string")
|
|
253
|
+
return content
|
|
254
|
+
|
|
255
|
+
const text = Reflect.get(value, "text")
|
|
256
|
+
if (typeof text === "string")
|
|
257
|
+
return text
|
|
258
|
+
|
|
259
|
+
return ""
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function getStreamToolCallState(entry: StreamToolCall | undefined): "pending" | "completed" | "error" {
|
|
263
|
+
if (!entry)
|
|
264
|
+
return "pending"
|
|
265
|
+
|
|
266
|
+
if (entry.state)
|
|
267
|
+
return entry.state
|
|
268
|
+
|
|
269
|
+
const result = entry.result
|
|
270
|
+
if (result == null)
|
|
271
|
+
return "pending"
|
|
272
|
+
|
|
273
|
+
if (typeof result === "object" && Reflect.get(result, "status") === "error")
|
|
274
|
+
return "error"
|
|
275
|
+
|
|
276
|
+
return "completed"
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function labelForSubagentToolState(state: "pending" | "completed" | "error") {
|
|
280
|
+
switch (state) {
|
|
281
|
+
case "pending":
|
|
282
|
+
return "Running…"
|
|
283
|
+
case "completed":
|
|
284
|
+
return "Done"
|
|
285
|
+
case "error":
|
|
286
|
+
return "Error"
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function extractSubagentTask(toolCall: { args?: Record<string, unknown> } | undefined) {
|
|
291
|
+
const args = toolCall?.args
|
|
292
|
+
if (!args)
|
|
293
|
+
return ""
|
|
294
|
+
|
|
295
|
+
const preferredKeys = ["task", "prompt", "description", "instructions", "content", "goal"]
|
|
296
|
+
for (const key of preferredKeys) {
|
|
297
|
+
const value = Reflect.get(args, key)
|
|
298
|
+
if (typeof value === "string" && value.trim())
|
|
299
|
+
return truncateText(value.trim(), 140)
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
return ""
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function getSubagentType(toolCall: { args?: Record<string, unknown> } | undefined) {
|
|
306
|
+
const subagentType = toolCall?.args?.subagent_type
|
|
307
|
+
return typeof subagentType === "string" && subagentType
|
|
308
|
+
? subagentType
|
|
309
|
+
: "subagent"
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function normalizePossiblePath(value: unknown): string | null {
|
|
313
|
+
if (typeof value !== "string")
|
|
314
|
+
return null
|
|
315
|
+
|
|
316
|
+
const normalized = value.trim()
|
|
317
|
+
if (!normalized)
|
|
318
|
+
return null
|
|
319
|
+
|
|
320
|
+
if (
|
|
321
|
+
normalized.includes("/")
|
|
322
|
+
|| normalized.endsWith(".md")
|
|
323
|
+
|| normalized.endsWith(".vue")
|
|
324
|
+
|| normalized.endsWith(".ts")
|
|
325
|
+
) {
|
|
326
|
+
return normalized
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
return null
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
function collectPathsFromUnknown(value: unknown, files: Set<string>) {
|
|
333
|
+
if (typeof value === "string") {
|
|
334
|
+
const maybePath = normalizePossiblePath(value)
|
|
335
|
+
if (maybePath)
|
|
336
|
+
files.add(maybePath)
|
|
337
|
+
return
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
if (Array.isArray(value)) {
|
|
341
|
+
value.forEach(entry => collectPathsFromUnknown(entry, files))
|
|
342
|
+
return
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
if (!value || typeof value !== "object")
|
|
346
|
+
return
|
|
347
|
+
|
|
348
|
+
Object.entries(value).forEach(([key, entry]) => {
|
|
349
|
+
if (["path", "filePath", "file_path", "paths", "glob"].includes(key))
|
|
350
|
+
collectPathsFromUnknown(entry, files)
|
|
351
|
+
})
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function extractTouchedFiles(toolCalls: Array<{ call?: { name?: string, args?: unknown } }> | undefined) {
|
|
355
|
+
const files = new Set<string>()
|
|
356
|
+
|
|
357
|
+
toolCalls?.forEach((toolCall) => {
|
|
358
|
+
const callName = toolCall.call?.name
|
|
359
|
+
if (!callName)
|
|
360
|
+
return
|
|
361
|
+
|
|
362
|
+
if ([
|
|
363
|
+
"read_file",
|
|
364
|
+
"write_file",
|
|
365
|
+
"edit_file",
|
|
366
|
+
"ls",
|
|
367
|
+
"glob",
|
|
368
|
+
"grep",
|
|
369
|
+
"execute",
|
|
370
|
+
].includes(callName)) {
|
|
371
|
+
collectPathsFromUnknown(toolCall.call?.args, files)
|
|
372
|
+
}
|
|
373
|
+
})
|
|
374
|
+
|
|
375
|
+
return Array.from(files).slice(0, 8)
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
export function getSubagentStatusPriority(status: string) {
|
|
379
|
+
switch (status) {
|
|
380
|
+
case "running":
|
|
381
|
+
return 0
|
|
382
|
+
case "pending":
|
|
383
|
+
return 1
|
|
384
|
+
case "error":
|
|
385
|
+
return 2
|
|
386
|
+
case "complete":
|
|
387
|
+
return 3
|
|
388
|
+
default:
|
|
389
|
+
return 4
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
export function summarizeSubagent(subagent: StreamSubagent, activeSubagentIds: Set<string>): SubagentActivity {
|
|
394
|
+
const type = getSubagentType(subagent.toolCall)
|
|
395
|
+
const taskSummary = extractSubagentTask(subagent.toolCall)
|
|
396
|
+
const files = extractTouchedFiles(subagent.toolCalls)
|
|
397
|
+
const messageCount = subagent.messages.length
|
|
398
|
+
const latestToolCall = subagent.toolCalls.at(-1)
|
|
399
|
+
const latestToolName = latestToolCall?.call?.name || ""
|
|
400
|
+
const latestToolHeadline = latestToolName ? toolCallHeadline(latestToolName) : ""
|
|
401
|
+
const latestToolArgs = formatToolArgsWithFallback(latestToolCall?.call?.args)
|
|
402
|
+
const latestToolState = getStreamToolCallState(latestToolCall)
|
|
403
|
+
const latestToolStateLabel = labelForSubagentToolState(latestToolState)
|
|
404
|
+
const resultText = stringifyUnknownResult(latestToolCall?.result)
|
|
405
|
+
const latestToolSummary = latestToolName && latestToolState !== "pending"
|
|
406
|
+
? summarizeToolResult(latestToolName, resultText)
|
|
407
|
+
: ""
|
|
408
|
+
const isActive = activeSubagentIds.has(subagent.id) || ["running", "pending"].includes(subagent.status)
|
|
409
|
+
const status = isActive ? "running" : subagent.status
|
|
410
|
+
const statusLabel = status === "running" ? "running..." : status
|
|
411
|
+
|
|
412
|
+
return {
|
|
413
|
+
id: subagent.id,
|
|
414
|
+
type,
|
|
415
|
+
typeHeadline: toolCallHeadline(type),
|
|
416
|
+
status,
|
|
417
|
+
statusLabel,
|
|
418
|
+
initiatorToolCallId: subagent.toolCall?.id || "",
|
|
419
|
+
taskSummary,
|
|
420
|
+
latestToolName,
|
|
421
|
+
latestToolHeadline,
|
|
422
|
+
latestToolArgs,
|
|
423
|
+
latestToolState,
|
|
424
|
+
latestToolStateLabel,
|
|
425
|
+
latestToolSummary,
|
|
426
|
+
files,
|
|
427
|
+
messageCount,
|
|
428
|
+
hasVisibleActivity: Boolean(
|
|
429
|
+
taskSummary
|
|
430
|
+
|| latestToolName
|
|
431
|
+
|| files.length > 0
|
|
432
|
+
|| messageCount > 0,
|
|
433
|
+
),
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
export function summarizeSubagentActivities(
|
|
438
|
+
subagents: StreamSubagent[],
|
|
439
|
+
activeSubagentIds: Set<string>,
|
|
440
|
+
) {
|
|
441
|
+
return subagents
|
|
442
|
+
.map(subagent => summarizeSubagent(subagent, activeSubagentIds))
|
|
443
|
+
.filter(subagent => subagent.hasVisibleActivity || ["running", "pending", "error"].includes(subagent.status))
|
|
444
|
+
.sort((left, right) => {
|
|
445
|
+
const statusPriority = getSubagentStatusPriority(left.status) - getSubagentStatusPriority(right.status)
|
|
446
|
+
if (statusPriority !== 0)
|
|
447
|
+
return statusPriority
|
|
448
|
+
|
|
449
|
+
return left.type.localeCompare(right.type)
|
|
450
|
+
})
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
function getRoleLabel(role: string) {
|
|
454
|
+
if (role === "human")
|
|
455
|
+
return "You"
|
|
456
|
+
|
|
457
|
+
if (role === "assistant" || !role)
|
|
458
|
+
return "Agent"
|
|
459
|
+
|
|
460
|
+
return role.charAt(0).toUpperCase() + role.slice(1)
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
function getMessageKey(message: unknown, role: string, index: number) {
|
|
464
|
+
const messageId = getMessageId(message)
|
|
465
|
+
if (messageId)
|
|
466
|
+
return `message:${messageId}`
|
|
467
|
+
|
|
468
|
+
const toolCallId = getMessageToolCallId(message)
|
|
469
|
+
if (toolCallId)
|
|
470
|
+
return `tool:${toolCallId}`
|
|
471
|
+
|
|
472
|
+
const content = getMessageContent(message).trim()
|
|
473
|
+
return `${role || "message"}:${index}:${truncateText(content, 24)}`
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
export function buildSidebarMessages(options: {
|
|
477
|
+
rawMessages: unknown[]
|
|
478
|
+
toolCallLookup: Map<string, ToolCallResult>
|
|
479
|
+
getSubagentsByMessage: (messageId: string) => StreamSubagent[]
|
|
480
|
+
activeSubagentIds: Set<string>
|
|
481
|
+
knownSubagentIds: Set<string>
|
|
482
|
+
}) {
|
|
483
|
+
return options.rawMessages.map((message, index): SidebarMessage => {
|
|
484
|
+
const role = getMessageRole(message)
|
|
485
|
+
const key = getMessageKey(message, role, index)
|
|
486
|
+
|
|
487
|
+
if (role === "tool") {
|
|
488
|
+
const toolCallId = getMessageToolCallId(message)
|
|
489
|
+
const toolCall = toolCallId ? options.toolCallLookup.get(toolCallId) : undefined
|
|
490
|
+
const inlineToolCall = getMessageToolCalls(message).at(0)
|
|
491
|
+
const toolName = toolCall?.call?.name || inlineToolCall?.name || getMessageToolName(message) || "tool"
|
|
492
|
+
const argsSummary = formatToolArgs(toolCall?.call?.args ?? inlineToolCall?.args)
|
|
493
|
+
const resultSummary = summarizeToolResult(toolName, getMessageContent(message))
|
|
494
|
+
|
|
495
|
+
return {
|
|
496
|
+
key,
|
|
497
|
+
kind: "tool",
|
|
498
|
+
role,
|
|
499
|
+
toolName,
|
|
500
|
+
toolHeadline: toolCallHeadline(toolName),
|
|
501
|
+
argsSummary,
|
|
502
|
+
resultSummary,
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
const messageId = getMessageId(message)
|
|
507
|
+
const subagents = messageId
|
|
508
|
+
? options.getSubagentsByMessage(messageId)
|
|
509
|
+
.map(subagent => summarizeSubagent(subagent, options.activeSubagentIds))
|
|
510
|
+
.filter(subagent => {
|
|
511
|
+
return (
|
|
512
|
+
options.knownSubagentIds.has(subagent.id)
|
|
513
|
+
|| options.activeSubagentIds.has(subagent.id)
|
|
514
|
+
|| subagent.hasVisibleActivity
|
|
515
|
+
)
|
|
516
|
+
})
|
|
517
|
+
: []
|
|
518
|
+
|
|
519
|
+
return {
|
|
520
|
+
key,
|
|
521
|
+
kind: "message",
|
|
522
|
+
role,
|
|
523
|
+
messageId,
|
|
524
|
+
label: getRoleLabel(role),
|
|
525
|
+
content: getMessageContent(message),
|
|
526
|
+
subagents,
|
|
527
|
+
}
|
|
528
|
+
})
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
/** Parent `task` tool calls only spawn subagents; subagent cards already represent that work. */
|
|
532
|
+
const HIDDEN_TOOL_NAMES = new Set(["task"])
|
|
533
|
+
|
|
534
|
+
export function filterVisibleSidebarMessages(messages: SidebarMessage[]) {
|
|
535
|
+
return messages.filter((message) => {
|
|
536
|
+
if (message.kind === "tool") {
|
|
537
|
+
if (HIDDEN_TOOL_NAMES.has(message.toolName))
|
|
538
|
+
return false
|
|
539
|
+
|
|
540
|
+
return true
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
if (message.content.trim())
|
|
544
|
+
return true
|
|
545
|
+
|
|
546
|
+
return message.subagents.length > 0
|
|
547
|
+
})
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
export function getMappedSubagentIds(messages: SidebarMessage[]) {
|
|
551
|
+
const ids = new Set<string>()
|
|
552
|
+
|
|
553
|
+
messages.forEach((message) => {
|
|
554
|
+
if (message.kind !== "message")
|
|
555
|
+
return
|
|
556
|
+
|
|
557
|
+
message.subagents.forEach(subagent => ids.add(subagent.id))
|
|
558
|
+
})
|
|
559
|
+
|
|
560
|
+
return ids
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
export function createSubagentActivityMessage(subagent: SubagentActivity): SidebarTextMessage {
|
|
564
|
+
return {
|
|
565
|
+
key: `subagent:${subagent.id}`,
|
|
566
|
+
kind: "message",
|
|
567
|
+
role: "subagent",
|
|
568
|
+
messageId: "",
|
|
569
|
+
label: "Subagent",
|
|
570
|
+
content: "",
|
|
571
|
+
subagents: [subagent],
|
|
572
|
+
}
|
|
573
|
+
}
|
package/lib/state.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { reactive } from "vue"
|
|
2
|
+
|
|
3
|
+
const storageKey = "slidev-agent:ui"
|
|
4
|
+
|
|
5
|
+
function readInitialState() {
|
|
6
|
+
if (typeof window === "undefined")
|
|
7
|
+
return { isOpen: false }
|
|
8
|
+
|
|
9
|
+
try {
|
|
10
|
+
const raw = window.localStorage.getItem(storageKey)
|
|
11
|
+
if (!raw)
|
|
12
|
+
return { isOpen: false }
|
|
13
|
+
|
|
14
|
+
const parsed = JSON.parse(raw)
|
|
15
|
+
return {
|
|
16
|
+
isOpen: Boolean(parsed.isOpen),
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return { isOpen: false }
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const slidevAgentUiState = reactive(readInitialState())
|
|
25
|
+
|
|
26
|
+
export function setSlidevAgentOpen(nextValue: boolean) {
|
|
27
|
+
slidevAgentUiState.isOpen = nextValue
|
|
28
|
+
|
|
29
|
+
if (typeof window === "undefined")
|
|
30
|
+
return
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
window.localStorage.setItem(storageKey, JSON.stringify({
|
|
34
|
+
isOpen: slidevAgentUiState.isOpen,
|
|
35
|
+
}))
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
// Ignore storage failures so the sidebar still works.
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function toggleSlidevAgent() {
|
|
43
|
+
setSlidevAgentOpen(!slidevAgentUiState.isOpen)
|
|
44
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "slidev-addon-agent",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Slidev addon and wrapper CLI for LangChain-powered slide authoring.",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build": "tsc -p tsconfig.build.json"
|
|
8
|
+
},
|
|
9
|
+
"keywords": [
|
|
10
|
+
"slidev-addon",
|
|
11
|
+
"slidev",
|
|
12
|
+
"langchain",
|
|
13
|
+
"langgraph"
|
|
14
|
+
],
|
|
15
|
+
"bin": {
|
|
16
|
+
"slidev-agent": "./dist/bin/slidev-agent.js"
|
|
17
|
+
},
|
|
18
|
+
"exports": {
|
|
19
|
+
".": "./package.json",
|
|
20
|
+
"./agent": "./agent/index.ts",
|
|
21
|
+
"./app": "./app/index.ts"
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"README.md",
|
|
25
|
+
"agent",
|
|
26
|
+
"app",
|
|
27
|
+
"dist",
|
|
28
|
+
"components",
|
|
29
|
+
"lib",
|
|
30
|
+
"public"
|
|
31
|
+
],
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"@langchain/langgraph-cli": "^1.1.17",
|
|
34
|
+
"@langchain/vue": "^0.4.5",
|
|
35
|
+
"deepagents": "^1.8.8",
|
|
36
|
+
"dompurify": "^3.3.3",
|
|
37
|
+
"hono": "^4.12.10",
|
|
38
|
+
"langchain": "^1.3.0",
|
|
39
|
+
"marked": "^17.0.5",
|
|
40
|
+
"zod": "^4.3.6"
|
|
41
|
+
},
|
|
42
|
+
"peerDependencies": {
|
|
43
|
+
"@langchain/anthropic": "^1.3.26",
|
|
44
|
+
"@langchain/google": "^0.1.9",
|
|
45
|
+
"@langchain/langgraph": "^1.2.6",
|
|
46
|
+
"@langchain/openai": "^1.4.1",
|
|
47
|
+
"@slidev/cli": "^52.14.1",
|
|
48
|
+
"vue": "^3.5.0"
|
|
49
|
+
},
|
|
50
|
+
"peerDependenciesMeta": {
|
|
51
|
+
"@langchain/anthropic": {
|
|
52
|
+
"optional": true
|
|
53
|
+
},
|
|
54
|
+
"@langchain/google": {
|
|
55
|
+
"optional": true
|
|
56
|
+
},
|
|
57
|
+
"@langchain/openai": {
|
|
58
|
+
"optional": true
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
"devDependencies": {
|
|
62
|
+
"@types/node": "^25.5.0",
|
|
63
|
+
"typescript": "^5.9.3"
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<svg width="98" height="98" viewBox="0 0 98 98" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<rect width="97.2502" height="97.2502" rx="19.4855" fill="#7FC8FF"/>
|
|
3
|
+
<g clip-path="url(#clip0_195_1432)">
|
|
4
|
+
<path d="M72.5361 42.2004V22.0426H51.7354L51.7354 22.5212C62.5113 22.7991 71.2917 31.6243 72.1695 42.2004H72.5361Z" fill="#030710"/>
|
|
5
|
+
<path d="M49.223 22.0428H24.8759V63.0891C24.8759 70.6689 30.7215 75.3844 40.133 75.3844H72.5471V45.3962H49.223V22.0428Z" fill="#030710"/>
|
|
6
|
+
</g>
|
|
7
|
+
<defs>
|
|
8
|
+
<clipPath id="clip0_195_1432">
|
|
9
|
+
<rect width="47.6713" height="53.3416" fill="white" transform="translate(24.8799 22.0442)"/>
|
|
10
|
+
</clipPath>
|
|
11
|
+
</defs>
|
|
12
|
+
</svg>
|