@swarmclawai/swarmclaw 0.9.2 → 0.9.4
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/README.md +12 -10
- package/bundled-skills/google-workspace/SKILL.md +2 -0
- package/package.json +1 -1
- package/src/app/agents/page.tsx +2 -1
- package/src/app/api/chatrooms/[id]/chat/route.ts +1 -1
- package/src/app/api/clawhub/install/route.ts +2 -0
- package/src/app/api/skills/[id]/route.ts +4 -0
- package/src/app/api/skills/route.ts +4 -0
- package/src/app/globals.css +28 -0
- package/src/app/home/page.tsx +11 -0
- package/src/app/settings/page.tsx +12 -5
- package/src/components/agents/agent-sheet.tsx +5 -5
- package/src/components/connectors/connector-list.tsx +2 -5
- package/src/components/logs/log-list.tsx +2 -5
- package/src/components/providers/provider-list.tsx +2 -5
- package/src/components/runs/run-list.tsx +2 -6
- package/src/components/schedules/schedule-list.tsx +7 -1
- package/src/components/ui/full-screen-loader.tsx +0 -29
- package/src/components/ui/page-loader.tsx +69 -0
- package/src/lib/runtime/runtime-loop.ts +21 -1
- package/src/lib/server/agents/agent-thread-session.test.ts +64 -0
- package/src/lib/server/agents/agent-thread-session.ts +1 -1
- package/src/lib/server/agents/main-agent-loop-advanced.test.ts +77 -0
- package/src/lib/server/agents/main-agent-loop.ts +259 -0
- package/src/lib/server/agents/orchestrator-lg.ts +12 -8
- package/src/lib/server/agents/orchestrator.ts +11 -7
- package/src/lib/server/chat-execution/chat-execution-advanced.test.ts +11 -10
- package/src/lib/server/chat-execution/chat-execution-session-sync.test.ts +116 -3
- package/src/lib/server/chat-execution/chat-execution-utils.test.ts +56 -0
- package/src/lib/server/chat-execution/chat-execution-utils.ts +24 -0
- package/src/lib/server/chat-execution/chat-execution.ts +116 -29
- package/src/lib/server/chat-execution/chat-streaming-utils.ts +1 -38
- package/src/lib/server/chat-execution/stream-agent-chat.test.ts +67 -76
- package/src/lib/server/chat-execution/stream-agent-chat.ts +119 -110
- package/src/lib/server/chat-execution/stream-continuation.ts +1 -1
- package/src/lib/server/chatrooms/chatroom-helpers.test.ts +26 -0
- package/src/lib/server/chatrooms/chatroom-helpers.ts +11 -8
- package/src/lib/server/connectors/contact-boundaries.ts +101 -0
- package/src/lib/server/connectors/manager.test.ts +504 -73
- package/src/lib/server/connectors/manager.ts +41 -10
- package/src/lib/server/connectors/session-consolidation.ts +2 -0
- package/src/lib/server/connectors/session-kind.ts +7 -0
- package/src/lib/server/connectors/session.test.ts +104 -0
- package/src/lib/server/connectors/session.ts +5 -2
- package/src/lib/server/identity-continuity.test.ts +4 -3
- package/src/lib/server/identity-continuity.ts +8 -4
- package/src/lib/server/memory/memory-policy.test.ts +5 -15
- package/src/lib/server/memory/memory-policy.ts +11 -41
- package/src/lib/server/memory/session-archive-memory.ts +2 -1
- package/src/lib/server/runtime/heartbeat-service.test.ts +46 -0
- package/src/lib/server/runtime/heartbeat-service.ts +5 -1
- package/src/lib/server/runtime/runtime-settings.test.ts +4 -4
- package/src/lib/server/runtime/runtime-settings.ts +4 -0
- package/src/lib/server/runtime/session-run-manager.ts +2 -0
- package/src/lib/server/session-reset-policy.test.ts +17 -3
- package/src/lib/server/session-reset-policy.ts +4 -2
- package/src/lib/server/session-tools/connector.ts +11 -10
- package/src/lib/server/session-tools/crud.ts +41 -7
- package/src/lib/server/session-tools/delegate.ts +3 -3
- package/src/lib/server/session-tools/index.ts +2 -0
- package/src/lib/server/session-tools/manage-skills.test.ts +194 -0
- package/src/lib/server/session-tools/memory.ts +209 -48
- package/src/lib/server/session-tools/skill-runtime.test.ts +175 -0
- package/src/lib/server/session-tools/skill-runtime.ts +382 -0
- package/src/lib/server/session-tools/skills.ts +575 -0
- package/src/lib/server/skills/runtime-skill-resolver.test.ts +162 -0
- package/src/lib/server/skills/runtime-skill-resolver.ts +750 -0
- package/src/lib/server/skills/skill-discovery.ts +4 -0
- package/src/lib/server/skills/skills-normalize.test.ts +28 -0
- package/src/lib/server/skills/skills-normalize.ts +93 -1
- package/src/lib/server/storage.ts +1 -1
- package/src/lib/server/tasks/task-followups.test.ts +124 -0
- package/src/lib/server/tasks/task-followups.ts +88 -13
- package/src/types/index.ts +30 -2
- package/src/views/settings/section-runtime-loop.tsx +38 -0
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
import { tool, type StructuredToolInterface } from '@langchain/core/tools'
|
|
3
|
+
import type { Agent, Session, SessionSkillRuntimeState } from '@/types'
|
|
4
|
+
import { errorMessage } from '@/lib/shared-utils'
|
|
5
|
+
import { loadAgent, loadSkills, patchSession } from '@/lib/server/storage'
|
|
6
|
+
import {
|
|
7
|
+
findResolvedSkill,
|
|
8
|
+
recommendRuntimeSkillsForTask,
|
|
9
|
+
resolveRuntimeSkills,
|
|
10
|
+
type ResolvedRuntimeSkill,
|
|
11
|
+
type RuntimeSkillRecommendation,
|
|
12
|
+
type RuntimeSkillSnapshot,
|
|
13
|
+
} from '@/lib/server/skills/runtime-skill-resolver'
|
|
14
|
+
import type { ToolBuildContext } from './context'
|
|
15
|
+
import { normalizeToolInputArgs } from './normalize-tool-args'
|
|
16
|
+
|
|
17
|
+
function resolveActiveAgent(bctx: ToolBuildContext): Agent | null {
|
|
18
|
+
const agentId = bctx.ctx?.agentId
|
|
19
|
+
if (!agentId) return null
|
|
20
|
+
return loadAgent(agentId) as Agent | null
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function resolveCurrentSession(bctx: ToolBuildContext): Session | null {
|
|
24
|
+
const session = bctx.resolveCurrentSession?.()
|
|
25
|
+
if (!session || typeof session !== 'object') return null
|
|
26
|
+
return session as Session
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function selectedSkillIdFromSession(session: Session | null): string | null {
|
|
30
|
+
const selectedSkillId = typeof session?.skillRuntimeState?.selectedSkillId === 'string'
|
|
31
|
+
? session.skillRuntimeState.selectedSkillId.trim()
|
|
32
|
+
: ''
|
|
33
|
+
return selectedSkillId || null
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function buildRuntimeSnapshot(bctx: ToolBuildContext): RuntimeSkillSnapshot {
|
|
37
|
+
const session = resolveCurrentSession(bctx)
|
|
38
|
+
const activeAgent = resolveActiveAgent(bctx)
|
|
39
|
+
return resolveRuntimeSkills({
|
|
40
|
+
cwd: bctx.cwd,
|
|
41
|
+
enabledPlugins: bctx.activePlugins,
|
|
42
|
+
agentSkillIds: activeAgent?.skillIds || [],
|
|
43
|
+
storedSkills: loadSkills(),
|
|
44
|
+
selectedSkillId: selectedSkillIdFromSession(session),
|
|
45
|
+
})
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function summarizeRuntimeSkill(skill: ResolvedRuntimeSkill): Record<string, unknown> {
|
|
49
|
+
return {
|
|
50
|
+
id: skill.id,
|
|
51
|
+
storageId: skill.storageId || null,
|
|
52
|
+
key: skill.key,
|
|
53
|
+
name: skill.name,
|
|
54
|
+
description: skill.description || '',
|
|
55
|
+
status: skill.status,
|
|
56
|
+
source: skill.source,
|
|
57
|
+
attached: skill.attached,
|
|
58
|
+
selected: skill.selected,
|
|
59
|
+
eligible: skill.eligible,
|
|
60
|
+
missing: skill.missing,
|
|
61
|
+
toolNames: skill.toolNames,
|
|
62
|
+
capabilities: skill.capabilities,
|
|
63
|
+
executionMode: skill.executionMode,
|
|
64
|
+
runnable: skill.runnable,
|
|
65
|
+
invocation: skill.invocation || null,
|
|
66
|
+
commandDispatch: skill.commandDispatch || null,
|
|
67
|
+
dispatchBlocker: skill.dispatchBlocker || null,
|
|
68
|
+
sourcePath: skill.sourcePath || null,
|
|
69
|
+
sourceUrl: skill.sourceUrl || null,
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function parseSearchLimit(raw: unknown, fallback = 8): number {
|
|
74
|
+
const parsed = typeof raw === 'number' ? raw : Number.parseInt(String(raw || ''), 10)
|
|
75
|
+
if (!Number.isFinite(parsed)) return fallback
|
|
76
|
+
return Math.max(1, Math.min(20, Math.trunc(parsed)))
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function resolveSkillSelector(rawArgs: Record<string, unknown>): string {
|
|
80
|
+
for (const key of ['id', 'skillId', 'name']) {
|
|
81
|
+
const value = rawArgs[key]
|
|
82
|
+
if (typeof value === 'string' && value.trim()) return value.trim()
|
|
83
|
+
}
|
|
84
|
+
return ''
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function resolveTargetSkill(params: {
|
|
88
|
+
rawArgs: Record<string, unknown>
|
|
89
|
+
snapshot: RuntimeSkillSnapshot
|
|
90
|
+
requireExplicit?: boolean
|
|
91
|
+
}): ResolvedRuntimeSkill | null {
|
|
92
|
+
const selector = resolveSkillSelector(params.rawArgs)
|
|
93
|
+
if (selector) return findResolvedSkill(params.snapshot.skills, selector)
|
|
94
|
+
|
|
95
|
+
const query = typeof params.rawArgs.query === 'string' ? params.rawArgs.query.trim() : ''
|
|
96
|
+
if (query) {
|
|
97
|
+
const ranked = recommendRuntimeSkillsForTask(params.snapshot.skills, query)
|
|
98
|
+
const top = ranked.find((entry) => entry.skill.eligible || entry.skill.runnable)
|
|
99
|
+
|| ranked[0]
|
|
100
|
+
return top?.skill || null
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (!params.requireExplicit && params.snapshot.selectedSkill) return params.snapshot.selectedSkill
|
|
104
|
+
return null
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function persistSkillRuntimeState(params: {
|
|
108
|
+
bctx: ToolBuildContext
|
|
109
|
+
skill: ResolvedRuntimeSkill
|
|
110
|
+
action: NonNullable<SessionSkillRuntimeState['lastAction']>
|
|
111
|
+
toolName?: string | null
|
|
112
|
+
}): void {
|
|
113
|
+
if (!params.bctx.ctx?.sessionId) return
|
|
114
|
+
patchSession(params.bctx.ctx.sessionId, (currentSession) => {
|
|
115
|
+
if (!currentSession) return currentSession
|
|
116
|
+
const current = currentSession.skillRuntimeState && typeof currentSession.skillRuntimeState === 'object'
|
|
117
|
+
? currentSession.skillRuntimeState
|
|
118
|
+
: {}
|
|
119
|
+
currentSession.skillRuntimeState = {
|
|
120
|
+
...current,
|
|
121
|
+
selectedSkillId: params.skill.id,
|
|
122
|
+
selectedSkillName: params.skill.name,
|
|
123
|
+
selectedAt: current.selectedSkillId === params.skill.id ? current.selectedAt || Date.now() : Date.now(),
|
|
124
|
+
lastAction: params.action,
|
|
125
|
+
lastRunAt: params.action === 'run' ? Date.now() : current.lastRunAt || null,
|
|
126
|
+
lastRunToolName: params.action === 'run' ? params.toolName || null : current.lastRunToolName || null,
|
|
127
|
+
}
|
|
128
|
+
currentSession.updatedAt = Date.now()
|
|
129
|
+
return currentSession
|
|
130
|
+
})
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function parseJsonValue(value: string): unknown {
|
|
134
|
+
try {
|
|
135
|
+
return JSON.parse(value)
|
|
136
|
+
} catch {
|
|
137
|
+
return null
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function parseJsonObject(value: string): Record<string, unknown> | null {
|
|
142
|
+
const parsed = parseJsonValue(value)
|
|
143
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) return parsed as Record<string, unknown>
|
|
144
|
+
return null
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function normalizeDispatchArgs(rawArgs: Record<string, unknown>): Record<string, unknown> {
|
|
148
|
+
const reserved = new Set([
|
|
149
|
+
'action',
|
|
150
|
+
'id',
|
|
151
|
+
'skillId',
|
|
152
|
+
'name',
|
|
153
|
+
'query',
|
|
154
|
+
'args',
|
|
155
|
+
'payload',
|
|
156
|
+
'parameters',
|
|
157
|
+
'toolArgs',
|
|
158
|
+
'input',
|
|
159
|
+
'limit',
|
|
160
|
+
])
|
|
161
|
+
|
|
162
|
+
for (const key of ['toolArgs', 'args', 'parameters', 'payload']) {
|
|
163
|
+
const value = rawArgs[key]
|
|
164
|
+
if (value && typeof value === 'object' && !Array.isArray(value)) return value as Record<string, unknown>
|
|
165
|
+
if (typeof value === 'string' && value.trim()) {
|
|
166
|
+
const parsed = parseJsonObject(value)
|
|
167
|
+
if (parsed) return parsed
|
|
168
|
+
return { input: value.trim() }
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const directFields = Object.fromEntries(
|
|
173
|
+
Object.entries(rawArgs).filter(([key, value]) => !reserved.has(key) && value !== undefined),
|
|
174
|
+
)
|
|
175
|
+
if (Object.keys(directFields).length > 0) return directFields
|
|
176
|
+
|
|
177
|
+
if (typeof rawArgs.input === 'string' && rawArgs.input.trim()) {
|
|
178
|
+
const parsed = parseJsonObject(rawArgs.input)
|
|
179
|
+
if (parsed) return parsed
|
|
180
|
+
return { input: rawArgs.input.trim() }
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return {}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function explainSkillBlocker(skill: ResolvedRuntimeSkill | null): Record<string, unknown> {
|
|
187
|
+
if (!skill) {
|
|
188
|
+
return {
|
|
189
|
+
ok: false,
|
|
190
|
+
blocker: 'No selected skill is available for this run.',
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (skill.executionMode === 'prompt') {
|
|
195
|
+
return {
|
|
196
|
+
ok: false,
|
|
197
|
+
skill: summarizeRuntimeSkill(skill),
|
|
198
|
+
blocker: 'This skill does not expose executable dispatch metadata.',
|
|
199
|
+
nextAction: 'Call use_skill with action="load" once, then follow the loaded guidance.',
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (!skill.runnable) {
|
|
204
|
+
return {
|
|
205
|
+
ok: false,
|
|
206
|
+
skill: summarizeRuntimeSkill(skill),
|
|
207
|
+
blocker: skill.dispatchBlocker || 'The selected skill is not runnable in the current session.',
|
|
208
|
+
missing: skill.missing,
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return {
|
|
213
|
+
ok: true,
|
|
214
|
+
skill: summarizeRuntimeSkill(skill),
|
|
215
|
+
blocker: null,
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
async function dispatchSkillRun(params: {
|
|
220
|
+
bctx: ToolBuildContext
|
|
221
|
+
skill: ResolvedRuntimeSkill
|
|
222
|
+
rawArgs: Record<string, unknown>
|
|
223
|
+
}): Promise<string> {
|
|
224
|
+
const dispatch = params.skill.commandDispatch
|
|
225
|
+
if (!dispatch || dispatch.kind !== 'tool') {
|
|
226
|
+
return JSON.stringify({
|
|
227
|
+
ok: true,
|
|
228
|
+
executed: false,
|
|
229
|
+
mode: 'prompt_guidance',
|
|
230
|
+
skill: summarizeRuntimeSkill(params.skill),
|
|
231
|
+
guidance: params.skill.content,
|
|
232
|
+
message: 'This skill has no executable dispatch surface. Follow the loaded guidance in the next step.',
|
|
233
|
+
})
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (!params.skill.runnable) {
|
|
237
|
+
return JSON.stringify({
|
|
238
|
+
ok: false,
|
|
239
|
+
executed: false,
|
|
240
|
+
mode: 'dispatch_blocked',
|
|
241
|
+
skill: summarizeRuntimeSkill(params.skill),
|
|
242
|
+
blocker: params.skill.dispatchBlocker || 'The selected skill is not runnable in this session.',
|
|
243
|
+
missing: params.skill.missing,
|
|
244
|
+
})
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if (dispatch.toolName === 'use_skill') {
|
|
248
|
+
return JSON.stringify({
|
|
249
|
+
ok: false,
|
|
250
|
+
executed: false,
|
|
251
|
+
mode: 'dispatch_blocked',
|
|
252
|
+
skill: summarizeRuntimeSkill(params.skill),
|
|
253
|
+
blocker: 'A skill cannot dispatch back into use_skill.',
|
|
254
|
+
})
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const toolArgs = normalizeDispatchArgs(params.rawArgs)
|
|
258
|
+
const { buildSessionTools } = await import('./index')
|
|
259
|
+
const built = await buildSessionTools(params.bctx.cwd, params.bctx.activePlugins, params.bctx.ctx)
|
|
260
|
+
try {
|
|
261
|
+
const targetTool = built.tools.find((entry) => entry.name === dispatch.toolName)
|
|
262
|
+
if (!targetTool) {
|
|
263
|
+
return JSON.stringify({
|
|
264
|
+
ok: false,
|
|
265
|
+
executed: false,
|
|
266
|
+
mode: 'dispatch_blocked',
|
|
267
|
+
skill: summarizeRuntimeSkill(params.skill),
|
|
268
|
+
blocker: `Dispatch tool "${dispatch.toolName}" is not available in this session.`,
|
|
269
|
+
})
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const toolOutput = await targetTool.invoke(toolArgs)
|
|
273
|
+
persistSkillRuntimeState({
|
|
274
|
+
bctx: params.bctx,
|
|
275
|
+
skill: params.skill,
|
|
276
|
+
action: 'run',
|
|
277
|
+
toolName: dispatch.toolName,
|
|
278
|
+
})
|
|
279
|
+
|
|
280
|
+
return JSON.stringify({
|
|
281
|
+
ok: true,
|
|
282
|
+
executed: true,
|
|
283
|
+
mode: 'dispatch',
|
|
284
|
+
skill: summarizeRuntimeSkill(params.skill),
|
|
285
|
+
dispatchedTool: dispatch.toolName,
|
|
286
|
+
toolArgs,
|
|
287
|
+
toolOutput: typeof toolOutput === 'string'
|
|
288
|
+
? (parseJsonValue(toolOutput) ?? toolOutput)
|
|
289
|
+
: toolOutput,
|
|
290
|
+
})
|
|
291
|
+
} finally {
|
|
292
|
+
await built.cleanup()
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
export function buildSkillRuntimeTools(bctx: ToolBuildContext): StructuredToolInterface[] {
|
|
297
|
+
return [
|
|
298
|
+
tool(
|
|
299
|
+
async (rawArgs) => {
|
|
300
|
+
const normalized = normalizeToolInputArgs((rawArgs ?? {}) as Record<string, unknown>)
|
|
301
|
+
const action = typeof normalized.action === 'string' ? normalized.action.trim().toLowerCase() : ''
|
|
302
|
+
const snapshot = buildRuntimeSnapshot(bctx)
|
|
303
|
+
|
|
304
|
+
try {
|
|
305
|
+
switch (action) {
|
|
306
|
+
case 'list': {
|
|
307
|
+
const query = typeof normalized.query === 'string' ? normalized.query.trim() : ''
|
|
308
|
+
const limit = parseSearchLimit(normalized.limit, 12)
|
|
309
|
+
const ranked: RuntimeSkillRecommendation[] = query
|
|
310
|
+
? recommendRuntimeSkillsForTask(snapshot.skills, query, bctx.activePlugins)
|
|
311
|
+
: snapshot.skills.map((skill) => ({ skill, score: skill.score, reasons: skill.matchReasons }))
|
|
312
|
+
return JSON.stringify({
|
|
313
|
+
selectedSkillId: snapshot.selectedSkill?.id || null,
|
|
314
|
+
skills: ranked.slice(0, limit).map((entry) => ({
|
|
315
|
+
...summarizeRuntimeSkill(entry.skill),
|
|
316
|
+
score: entry.score,
|
|
317
|
+
reasons: entry.reasons,
|
|
318
|
+
})),
|
|
319
|
+
})
|
|
320
|
+
}
|
|
321
|
+
case 'select': {
|
|
322
|
+
const target = resolveTargetSkill({ rawArgs: normalized, snapshot, requireExplicit: true })
|
|
323
|
+
if (!target) return JSON.stringify({ ok: false, blocker: 'No matching skill found to select.' })
|
|
324
|
+
persistSkillRuntimeState({ bctx, skill: target, action: 'select' })
|
|
325
|
+
return JSON.stringify({
|
|
326
|
+
ok: true,
|
|
327
|
+
selected: true,
|
|
328
|
+
skill: summarizeRuntimeSkill(target),
|
|
329
|
+
})
|
|
330
|
+
}
|
|
331
|
+
case 'load': {
|
|
332
|
+
const target = resolveTargetSkill({ rawArgs: normalized, snapshot })
|
|
333
|
+
if (!target) return JSON.stringify({ ok: false, blocker: 'No selected or matching skill found to load.' })
|
|
334
|
+
persistSkillRuntimeState({ bctx, skill: target, action: 'load' })
|
|
335
|
+
return JSON.stringify({
|
|
336
|
+
ok: true,
|
|
337
|
+
loaded: true,
|
|
338
|
+
skill: summarizeRuntimeSkill(target),
|
|
339
|
+
guidance: target.content,
|
|
340
|
+
})
|
|
341
|
+
}
|
|
342
|
+
case 'run': {
|
|
343
|
+
const target = resolveTargetSkill({ rawArgs: normalized, snapshot })
|
|
344
|
+
if (!target) return JSON.stringify({ ok: false, blocker: 'No selected or matching skill found to run.' })
|
|
345
|
+
persistSkillRuntimeState({ bctx, skill: target, action: 'select' })
|
|
346
|
+
return dispatchSkillRun({ bctx, skill: target, rawArgs: normalized })
|
|
347
|
+
}
|
|
348
|
+
case 'explain_blocker': {
|
|
349
|
+
const target = resolveTargetSkill({ rawArgs: normalized, snapshot })
|
|
350
|
+
return JSON.stringify(explainSkillBlocker(target))
|
|
351
|
+
}
|
|
352
|
+
default:
|
|
353
|
+
return `Error: Unknown action "${action}".`
|
|
354
|
+
}
|
|
355
|
+
} catch (err: unknown) {
|
|
356
|
+
return `Error: ${errorMessage(err)}`
|
|
357
|
+
}
|
|
358
|
+
},
|
|
359
|
+
{
|
|
360
|
+
name: 'use_skill',
|
|
361
|
+
description: [
|
|
362
|
+
'Runtime skill selection and execution surface.',
|
|
363
|
+
'Use `list` to inspect available skills, `select` to persist one for the current task, `load` to fetch its guidance, `run` to dispatch executable skills through their bound tools, and `explain_blocker` to understand why a selected skill cannot run.',
|
|
364
|
+
'Prefer this tool over stuffing many skill bodies into the prompt.',
|
|
365
|
+
].join('\n\n'),
|
|
366
|
+
schema: z.object({
|
|
367
|
+
action: z.enum(['list', 'select', 'load', 'run', 'explain_blocker']),
|
|
368
|
+
id: z.string().optional().describe('Skill runtime id or stored skill id'),
|
|
369
|
+
skillId: z.string().optional().describe('Alternate skill selector'),
|
|
370
|
+
name: z.string().optional().describe('Skill name selector'),
|
|
371
|
+
query: z.string().optional().describe('Task query used to rank/select a skill'),
|
|
372
|
+
limit: z.number().optional().describe('Maximum number of listed skills'),
|
|
373
|
+
input: z.string().optional().describe('String input forwarded to the dispatched tool when the skill uses raw dispatch'),
|
|
374
|
+
args: z.union([z.string(), z.record(z.string(), z.unknown())]).optional().describe('Tool arguments for action="run"'),
|
|
375
|
+
parameters: z.union([z.string(), z.record(z.string(), z.unknown())]).optional().describe('Alternate tool arguments for action="run"'),
|
|
376
|
+
payload: z.union([z.string(), z.record(z.string(), z.unknown())]).optional().describe('Alternate tool arguments for action="run"'),
|
|
377
|
+
toolArgs: z.record(z.string(), z.unknown()).optional().describe('Object args forwarded directly to the dispatched tool'),
|
|
378
|
+
}).passthrough(),
|
|
379
|
+
},
|
|
380
|
+
),
|
|
381
|
+
]
|
|
382
|
+
}
|