@swarmclawai/swarmclaw 1.2.6 → 1.2.9
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 +54 -23
- package/next.config.ts +1 -0
- package/package.json +4 -3
- package/scripts/easy-setup.mjs +1 -1
- package/scripts/postinstall.mjs +1 -1
- package/skills/swarmclaw.md +115 -0
- package/skills/tools/browser.md +131 -0
- package/skills/tools/execute.md +98 -0
- package/skills/tools/files.md +98 -0
- package/skills/tools/memory.md +104 -0
- package/skills/tools/platform.md +144 -0
- package/skills/tools/skills.md +83 -0
- package/src/app/agents/[id]/page.tsx +1 -18
- package/src/app/api/agents/thread-route.test.ts +0 -1
- package/src/app/api/approvals/route.test.ts +6 -22
- package/src/app/api/chats/[id]/messages/route.ts +23 -19
- package/src/app/api/chats/messages-route.test.ts +105 -51
- package/src/app/api/connectors/route.ts +2 -2
- package/src/app/api/mcp-servers/[id]/test/route.ts +3 -2
- package/src/app/api/openclaw/deploy/route.ts +2 -0
- package/src/app/api/portability/export/route.ts +8 -0
- package/src/app/api/portability/import/route.test.ts +80 -0
- package/src/app/api/portability/import/route.ts +28 -0
- package/src/app/api/settings/route.ts +0 -2
- package/src/app/api/setup/doctor/route.ts +4 -4
- package/src/app/api/wallets/[id]/route.ts +15 -157
- package/src/app/api/wallets/generate/route.ts +22 -0
- package/src/app/api/wallets/route.test.ts +147 -0
- package/src/app/api/wallets/route.ts +13 -95
- package/src/app/autonomy/page.tsx +2 -57
- package/src/app/protocols/page.tsx +2 -21
- package/src/app/settings/page.tsx +0 -9
- package/src/app/wallets/page.tsx +105 -5
- package/src/cli/index.js +21 -33
- package/src/cli/spec.js +19 -30
- package/src/components/agents/agent-chat-list.tsx +23 -1
- package/src/components/agents/agent-sheet.tsx +2 -40
- package/src/components/agents/inspector-panel.tsx +165 -131
- package/src/components/chat/chat-area.tsx +38 -9
- package/src/components/chat/chat-card.tsx +0 -31
- package/src/components/chat/message-bubble.tsx +1 -108
- package/src/components/chat/message-list.tsx +33 -19
- package/src/components/connectors/connector-sheet.tsx +25 -1
- package/src/components/gateways/gateway-sheet.tsx +5 -2
- package/src/components/layout/sidebar-rail.tsx +6 -10
- package/src/components/projects/project-detail.tsx +3 -35
- package/src/components/projects/tabs/overview-tab.tsx +3 -59
- package/src/components/projects/tabs/work-tab.tsx +7 -77
- package/src/components/protocols/structured-session-launcher.tsx +1 -22
- package/src/components/shared/connector-platform-icon.tsx +1 -0
- package/src/components/tasks/task-card.tsx +4 -34
- package/src/components/tasks/task-sheet.tsx +6 -36
- package/src/components/wallets/wallet-list.tsx +150 -0
- package/src/lib/agent-execute-defaults.test.ts +24 -0
- package/src/lib/agent-execute-defaults.ts +62 -0
- package/src/lib/app/navigation.test.ts +0 -13
- package/src/lib/app/navigation.ts +2 -7
- package/src/lib/app/view-constants.ts +14 -19
- package/src/lib/chat/queued-message-queue.test.ts +134 -1
- package/src/lib/chat/queued-message-queue.ts +77 -2
- package/src/lib/server/agents/agent-service.ts +5 -0
- package/src/lib/server/agents/agent-thread-session.ts +0 -1
- package/src/lib/server/agents/delegation-advisory.test.ts +0 -1
- package/src/lib/server/agents/delegation-jobs.test.ts +0 -69
- package/src/lib/server/agents/delegation-jobs.ts +0 -25
- package/src/lib/server/agents/main-agent-loop.ts +1 -49
- package/src/lib/server/agents/subagent-runtime.ts +0 -1
- package/src/lib/server/approval-match.ts +0 -85
- package/src/lib/server/approvals.test.ts +6 -6
- package/src/lib/server/approvals.ts +0 -6
- package/src/lib/server/autonomy/supervisor-reflection.test.ts +0 -1
- package/src/lib/server/builtin-extensions.ts +1 -2
- package/src/lib/server/capability-router.test.ts +0 -2
- package/src/lib/server/chat-execution/chat-execution-advanced.test.ts +1 -1
- package/src/lib/server/chat-execution/chat-execution-tool-events.test.ts +15 -14
- package/src/lib/server/chat-execution/chat-execution-types.ts +0 -2
- package/src/lib/server/chat-execution/chat-execution-utils.ts +2 -4
- package/src/lib/server/chat-execution/chat-streaming-utils.ts +2 -30
- package/src/lib/server/chat-execution/chat-turn-finalization.ts +1 -36
- package/src/lib/server/chat-execution/chat-turn-preparation.ts +81 -64
- package/src/lib/server/chat-execution/chat-turn-stream-execution.ts +4 -0
- package/src/lib/server/chat-execution/continuation-evaluator.ts +8 -0
- package/src/lib/server/chat-execution/iteration-event-handler.ts +0 -24
- package/src/lib/server/chat-execution/memory-mutation-tools.ts +1 -1
- package/src/lib/server/chat-execution/message-classifier.test.ts +0 -45
- package/src/lib/server/chat-execution/message-classifier.ts +11 -16
- package/src/lib/server/chat-execution/prompt-builder.test.ts +27 -0
- package/src/lib/server/chat-execution/prompt-builder.ts +14 -31
- package/src/lib/server/chat-execution/prompt-mode.test.ts +24 -0
- package/src/lib/server/chat-execution/prompt-mode.ts +5 -1
- package/src/lib/server/chat-execution/prompt-sections.ts +0 -1
- package/src/lib/server/chat-execution/situational-awareness.test.ts +2 -73
- package/src/lib/server/chat-execution/situational-awareness.ts +4 -38
- package/src/lib/server/chat-execution/stream-agent-chat.test.ts +13 -126
- package/src/lib/server/chat-execution/stream-agent-chat.ts +46 -21
- package/src/lib/server/chat-execution/stream-continuation.test.ts +4 -52
- package/src/lib/server/chat-execution/stream-continuation.ts +6 -48
- package/src/lib/server/chatrooms/chatroom-routing.test.ts +4 -0
- package/src/lib/server/chatrooms/session-mailbox.ts +0 -10
- package/src/lib/server/chats/chat-session-service.ts +3 -5
- package/src/lib/server/connectors/connector-inbound.ts +0 -1
- package/src/lib/server/connectors/connector-lifecycle.ts +19 -3
- package/src/lib/server/connectors/connector-service.ts +39 -9
- package/src/lib/server/connectors/discord.ts +2 -2
- package/src/lib/server/connectors/matrix.ts +3 -2
- package/src/lib/server/connectors/signal.ts +5 -4
- package/src/lib/server/connectors/slack.ts +10 -9
- package/src/lib/server/connectors/swarmdock-bidding.ts +74 -0
- package/src/lib/server/connectors/swarmdock-payloads.test.ts +85 -0
- package/src/lib/server/connectors/swarmdock-secret.test.ts +128 -0
- package/src/lib/server/connectors/swarmdock-secret.ts +152 -0
- package/src/lib/server/connectors/swarmdock-tasks.ts +119 -0
- package/src/lib/server/connectors/swarmdock.ts +255 -0
- package/src/lib/server/connectors/teams.ts +3 -2
- package/src/lib/server/connectors/telegram.ts +4 -4
- package/src/lib/server/connectors/whatsapp.ts +2 -2
- package/src/lib/server/daemon/controller.ts +7 -0
- package/src/lib/server/execution-brief.test.ts +2 -25
- package/src/lib/server/execution-brief.ts +12 -35
- package/src/lib/server/execution-engine/task-attempt.ts +0 -1
- package/src/lib/server/gateways/gateway-profile-service.ts +19 -1
- package/src/lib/server/messages/message-repository.test.ts +70 -0
- package/src/lib/server/messages/message-repository.ts +11 -6
- package/src/lib/server/openclaw/deploy.ts +32 -2
- package/src/lib/server/persistence/storage-context.ts +0 -5
- package/src/lib/server/plugins-advanced.test.ts +1 -2
- package/src/lib/server/portability/export.ts +109 -0
- package/src/lib/server/portability/import.ts +159 -0
- package/src/lib/server/protocols/protocol-normalization.ts +0 -4
- package/src/lib/server/protocols/protocol-queries.ts +0 -6
- package/src/lib/server/protocols/protocol-run-lifecycle.ts +4 -32
- package/src/lib/server/protocols/protocol-service.ts +0 -1
- package/src/lib/server/protocols/protocol-step-helpers.ts +0 -4
- package/src/lib/server/protocols/protocol-step-processors.ts +0 -6
- package/src/lib/server/protocols/protocol-swarm.ts +0 -2
- package/src/lib/server/protocols/protocol-types.ts +0 -2
- package/src/lib/server/provider-health.ts +1 -10
- package/src/lib/server/runtime/daemon-state/core.ts +0 -9
- package/src/lib/server/runtime/daemon-state.test.ts +0 -35
- package/src/lib/server/runtime/heartbeat-service.ts +3 -23
- package/src/lib/server/runtime/process-manager.ts +13 -9
- package/src/lib/server/runtime/queue/core.ts +11 -33
- package/src/lib/server/runtime/runtime-storage-write-paths.test.ts +6 -6
- package/src/lib/server/runtime/scheduler.ts +0 -13
- package/src/lib/server/runtime/session-run-manager/drain.ts +0 -24
- package/src/lib/server/runtime/session-run-manager/enqueue.ts +0 -1
- package/src/lib/server/runtime/session-run-manager/queries.ts +15 -1
- package/src/lib/server/runtime/session-run-manager/recovery.ts +0 -1
- package/src/lib/server/runtime/session-run-manager.test.ts +58 -28
- package/src/lib/server/sandbox/session-runtime.test.ts +18 -1
- package/src/lib/server/sandbox/session-runtime.ts +40 -28
- package/src/lib/server/session-tools/autonomy-tools.test.ts +7 -9
- package/src/lib/server/session-tools/context.ts +1 -1
- package/src/lib/server/session-tools/credential-env.ts +109 -0
- package/src/lib/server/session-tools/crud.ts +3 -17
- package/src/lib/server/session-tools/delegate.ts +0 -4
- package/src/lib/server/session-tools/edit_file.ts +3 -2
- package/src/lib/server/session-tools/execute.test.ts +58 -0
- package/src/lib/server/session-tools/execute.ts +334 -0
- package/src/lib/server/session-tools/files-tool.ts +635 -0
- package/src/lib/server/session-tools/index.ts +14 -8
- package/src/lib/server/session-tools/memory-tool.ts +242 -0
- package/src/lib/server/session-tools/memory.ts +1 -1
- package/src/lib/server/session-tools/openclaw-nodes.ts +3 -2
- package/src/lib/server/session-tools/openclaw-workspace.ts +3 -2
- package/src/lib/server/session-tools/platform-tool.ts +617 -0
- package/src/lib/server/session-tools/session-info.ts +3 -2
- package/src/lib/server/session-tools/session-tools-wiring.test.ts +3 -4
- package/src/lib/server/session-tools/shell.ts +7 -122
- package/src/lib/server/session-tools/skills-tool.ts +396 -0
- package/src/lib/server/session-tools/team-context.ts +0 -3
- package/src/lib/server/session-tools/web.ts +2 -2
- package/src/lib/server/storage-normalization.ts +10 -0
- package/src/lib/server/storage.ts +18 -45
- package/src/lib/server/tasks/task-checkout.ts +59 -0
- package/src/lib/server/tasks/task-lifecycle.ts +2 -0
- package/src/lib/server/tasks/task-route-service.ts +4 -26
- package/src/lib/server/tasks/task-service.ts +0 -7
- package/src/lib/server/tool-aliases.ts +2 -2
- package/src/lib/server/tool-capability-policy-advanced.test.ts +13 -6
- package/src/lib/server/tool-capability-policy.test.ts +2 -1
- package/src/lib/server/tool-capability-policy.ts +60 -35
- package/src/lib/server/tool-planning.ts +11 -12
- package/src/lib/server/universal-tool-access.ts +0 -1
- package/src/lib/server/wallets/wallet-crypto.ts +33 -0
- package/src/lib/server/wallets/wallet-repository.ts +24 -0
- package/src/lib/server/wallets/wallet-service.ts +119 -0
- package/src/lib/server/working-state/extraction.ts +8 -42
- package/src/lib/server/working-state/normalization.ts +10 -103
- package/src/lib/server/working-state/service.ts +12 -21
- package/src/lib/setup-defaults.ts +5 -0
- package/src/lib/strip-internal-metadata.test.ts +1 -1
- package/src/lib/strip-internal-metadata.ts +1 -1
- package/src/lib/tool-definitions.ts +1 -1
- package/src/lib/validation/schemas.test.ts +16 -0
- package/src/lib/validation/schemas.ts +49 -2
- package/src/stores/slices/data-slice.ts +5 -1
- package/src/stores/slices/ui-slice.ts +0 -4
- package/src/stores/use-chat-store.test.ts +231 -0
- package/src/stores/use-chat-store.ts +62 -13
- package/src/types/agent.ts +264 -0
- package/src/types/app-settings.ts +173 -0
- package/src/types/approval.ts +25 -0
- package/src/types/connector.ts +188 -0
- package/src/types/extension.ts +386 -0
- package/src/types/index.ts +16 -3555
- package/src/types/message.ts +56 -0
- package/src/types/misc.ts +737 -0
- package/src/types/protocol.ts +420 -0
- package/src/types/provider.ts +52 -0
- package/src/types/run.ts +180 -0
- package/src/types/schedule.ts +59 -0
- package/src/types/session.ts +215 -0
- package/src/types/skill.ts +157 -0
- package/src/types/swarmdock.ts +29 -0
- package/src/types/task.ts +144 -0
- package/src/types/working-state.ts +204 -0
- package/src/views/settings/section-heartbeat.tsx +2 -2
- package/src/views/settings/section-runtime-loop.tsx +0 -14
- package/src/app/api/canvas/[sessionId]/route.ts +0 -35
- package/src/app/api/missions/[id]/actions/route.ts +0 -31
- package/src/app/api/missions/[id]/events/route.ts +0 -14
- package/src/app/api/missions/[id]/route.ts +0 -10
- package/src/app/api/missions/route.test.ts +0 -244
- package/src/app/api/missions/route.ts +0 -57
- package/src/app/api/wallets/[id]/approve/route.ts +0 -79
- package/src/app/api/wallets/[id]/balance-history/route.ts +0 -18
- package/src/app/api/wallets/[id]/send/route.ts +0 -113
- package/src/app/api/wallets/[id]/transactions/route.ts +0 -18
- package/src/app/missions/[id]/page.tsx +0 -3
- package/src/app/missions/page.tsx +0 -685
- package/src/components/canvas/canvas-panel.tsx +0 -267
- package/src/components/wallets/wallet-approval-dialog.tsx +0 -107
- package/src/components/wallets/wallet-panel.tsx +0 -1010
- package/src/components/wallets/wallet-section.tsx +0 -260
- package/src/features/missions/queries.ts +0 -23
- package/src/lib/canvas-content.test.ts +0 -360
- package/src/lib/canvas-content.ts +0 -198
- package/src/lib/server/canvas-content.test.ts +0 -32
- package/src/lib/server/canvas-content.ts +0 -6
- package/src/lib/server/ethereum.ts +0 -591
- package/src/lib/server/evm-swap.ts +0 -476
- package/src/lib/server/missions/mission-intent.test.ts +0 -63
- package/src/lib/server/missions/mission-intent.ts +0 -569
- package/src/lib/server/missions/mission-repository.ts +0 -74
- package/src/lib/server/missions/mission-service/actions.ts +0 -6
- package/src/lib/server/missions/mission-service/bindings.ts +0 -9
- package/src/lib/server/missions/mission-service/context.ts +0 -4
- package/src/lib/server/missions/mission-service/core.ts +0 -2271
- package/src/lib/server/missions/mission-service/queries.ts +0 -12
- package/src/lib/server/missions/mission-service/recovery.ts +0 -5
- package/src/lib/server/missions/mission-service/ticks.ts +0 -9
- package/src/lib/server/missions/mission-service.test.ts +0 -888
- package/src/lib/server/missions/mission-service.ts +0 -6
- package/src/lib/server/session-tools/canvas.ts +0 -105
- package/src/lib/server/session-tools/sandbox.ts +0 -281
- package/src/lib/server/session-tools/wallet-tool.test.ts +0 -150
- package/src/lib/server/session-tools/wallet.ts +0 -1287
- package/src/lib/server/solana.ts +0 -327
- package/src/lib/server/wallet/wallet-execution.test.ts +0 -198
- package/src/lib/server/wallet/wallet-portfolio.test.ts +0 -98
- package/src/lib/server/wallet/wallet-portfolio.ts +0 -772
- package/src/lib/server/wallet/wallet-service.test.ts +0 -81
- package/src/lib/server/wallet/wallet-service.ts +0 -225
- package/src/lib/wallet/wallet-transactions.test.ts +0 -75
- package/src/lib/wallet/wallet-transactions.ts +0 -43
- package/src/lib/wallet/wallet.test.ts +0 -333
- package/src/lib/wallet/wallet.ts +0 -183
- package/src/views/settings/section-wallets.tsx +0 -35
|
@@ -10,9 +10,7 @@ import {
|
|
|
10
10
|
removeManagedProcess,
|
|
11
11
|
startManagedProcess,
|
|
12
12
|
writeManagedProcessStdin,
|
|
13
|
-
type SandboxOptions,
|
|
14
13
|
} from '@/lib/server/runtime/process-manager'
|
|
15
|
-
import { ensureSessionSandbox, resolveSandboxWorkdir, type AgentSandboxConfig } from '@/lib/server/sandbox/session-runtime'
|
|
16
14
|
import type { ToolBuildContext } from './context'
|
|
17
15
|
import { safePath, truncate, coerceEnvMap, MAX_OUTPUT } from './context'
|
|
18
16
|
import { checkFileAccess } from './file-access-policy'
|
|
@@ -21,7 +19,6 @@ import { registerNativeCapability } from '../native-capabilities'
|
|
|
21
19
|
import { safeJsonParseObject } from '../json-utils'
|
|
22
20
|
import { normalizeToolInputArgs } from './normalize-tool-args'
|
|
23
21
|
import { errorMessage } from '@/lib/shared-utils'
|
|
24
|
-
import { executeSandboxExec, executeListRuntimes, type SandboxContext } from './sandbox'
|
|
25
22
|
|
|
26
23
|
function resolveShellWorkdir(baseCwd: string, requestedWorkdir?: string, scope?: 'workspace' | 'machine'): string {
|
|
27
24
|
const raw = typeof requestedWorkdir === 'string' ? requestedWorkdir.trim() : ''
|
|
@@ -43,12 +40,6 @@ export function rewriteShellWorkspaceAliases(baseCwd: string, command: string):
|
|
|
43
40
|
return rewritten
|
|
44
41
|
}
|
|
45
42
|
|
|
46
|
-
export function rewriteShellWorkspaceAliasesForSandbox(command: string): string {
|
|
47
|
-
let rewritten = command
|
|
48
|
-
rewritten = rewritten.replace(/(^|[\s"'`(=;])workspace\//g, '$1/workspace/')
|
|
49
|
-
return rewritten
|
|
50
|
-
}
|
|
51
|
-
|
|
52
43
|
export function stripManagedBackgroundSuffix(command: string): string {
|
|
53
44
|
return command.replace(/\s*&\s*$/, '').trim()
|
|
54
45
|
}
|
|
@@ -193,60 +184,6 @@ function checkShellFileAccessPolicy(
|
|
|
193
184
|
return null
|
|
194
185
|
}
|
|
195
186
|
|
|
196
|
-
async function resolveSandboxOptions(params: {
|
|
197
|
-
cwd: string
|
|
198
|
-
workdir?: string
|
|
199
|
-
session?: Session | null
|
|
200
|
-
agentId?: string | null
|
|
201
|
-
sessionId?: string | null
|
|
202
|
-
env: Record<string, string>
|
|
203
|
-
config?: AgentSandboxConfig | null
|
|
204
|
-
filesystemScope?: 'workspace' | 'machine'
|
|
205
|
-
}): Promise<{
|
|
206
|
-
sandbox?: SandboxOptions
|
|
207
|
-
hostWorkdir: string
|
|
208
|
-
workspaceEnv: string
|
|
209
|
-
sessionCwdEnv: string
|
|
210
|
-
}> {
|
|
211
|
-
const hostWorkdir = resolveShellWorkdir(params.cwd, params.workdir, params.filesystemScope)
|
|
212
|
-
const sandboxSession = await ensureSessionSandbox({
|
|
213
|
-
config: params.config,
|
|
214
|
-
session: params.session,
|
|
215
|
-
agentId: params.agentId,
|
|
216
|
-
sessionId: params.sessionId,
|
|
217
|
-
workspaceDir: params.cwd,
|
|
218
|
-
})
|
|
219
|
-
|
|
220
|
-
if (!sandboxSession) {
|
|
221
|
-
return {
|
|
222
|
-
hostWorkdir,
|
|
223
|
-
workspaceEnv: params.cwd,
|
|
224
|
-
sessionCwdEnv: hostWorkdir,
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
const sandboxHostWorkdir = fs.existsSync(hostWorkdir) && fs.statSync(hostWorkdir).isDirectory()
|
|
229
|
-
? hostWorkdir
|
|
230
|
-
: sandboxSession.workspaceDir
|
|
231
|
-
const resolved = resolveSandboxWorkdir({
|
|
232
|
-
workspaceDir: sandboxSession.workspaceDir,
|
|
233
|
-
hostWorkdir: sandboxHostWorkdir,
|
|
234
|
-
containerWorkdir: sandboxSession.containerWorkdir,
|
|
235
|
-
})
|
|
236
|
-
|
|
237
|
-
return {
|
|
238
|
-
hostWorkdir: resolved.hostWorkdir,
|
|
239
|
-
workspaceEnv: sandboxSession.containerWorkdir,
|
|
240
|
-
sessionCwdEnv: resolved.containerWorkdir,
|
|
241
|
-
sandbox: {
|
|
242
|
-
kind: 'persistent',
|
|
243
|
-
containerName: sandboxSession.containerName,
|
|
244
|
-
containerWorkdir: resolved.containerWorkdir,
|
|
245
|
-
env: params.env,
|
|
246
|
-
},
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
|
|
250
187
|
async function executeShellAction(
|
|
251
188
|
args: Record<string, unknown>,
|
|
252
189
|
bctx: {
|
|
@@ -255,7 +192,6 @@ async function executeShellAction(
|
|
|
255
192
|
sessionId?: string | null
|
|
256
193
|
resolveCurrentSession?: () => Session | null
|
|
257
194
|
fileAccessPolicy?: { allowedPaths?: string[]; blockedPaths?: string[] } | null
|
|
258
|
-
sandboxConfig?: AgentSandboxConfig | null
|
|
259
195
|
filesystemScope?: 'workspace' | 'machine'
|
|
260
196
|
commandTimeoutMs?: number
|
|
261
197
|
},
|
|
@@ -284,37 +220,11 @@ async function executeShellAction(
|
|
|
284
220
|
return 'Error: This command would terminate the SwarmClaw server. Use a more targeted command that only affects your project processes.'
|
|
285
221
|
}
|
|
286
222
|
const envMap = coerceEnvMap(env) || {}
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
let commandForExecution = rewriteShellWorkspaceAliases(bctx.cwd, command)
|
|
290
|
-
let sandboxMode: 'container' | 'host' | 'host-fallback' = 'host'
|
|
291
|
-
try {
|
|
292
|
-
const resolved = await resolveSandboxOptions({
|
|
293
|
-
cwd: bctx.cwd,
|
|
294
|
-
workdir,
|
|
295
|
-
session: bctx.resolveCurrentSession?.() || null,
|
|
296
|
-
agentId: bctx.agentId || null,
|
|
297
|
-
sessionId: bctx.sessionId || null,
|
|
298
|
-
env: envMap,
|
|
299
|
-
config: bctx.sandboxConfig,
|
|
300
|
-
filesystemScope: bctx.filesystemScope,
|
|
301
|
-
})
|
|
302
|
-
sandbox = resolved.sandbox
|
|
303
|
-
hostWorkdir = resolved.hostWorkdir
|
|
304
|
-
if (!envMap.WORKSPACE) envMap.WORKSPACE = resolved.workspaceEnv
|
|
305
|
-
if (!envMap.SESSION_CWD) envMap.SESSION_CWD = resolved.sessionCwdEnv
|
|
306
|
-
if (sandbox) {
|
|
307
|
-
commandForExecution = rewriteShellWorkspaceAliasesForSandbox(command)
|
|
308
|
-
if (!envMap.HOME) envMap.HOME = resolved.sessionCwdEnv
|
|
309
|
-
if (!envMap.TMPDIR) envMap.TMPDIR = '/tmp'
|
|
310
|
-
sandboxMode = 'container'
|
|
311
|
-
}
|
|
312
|
-
} catch {
|
|
313
|
-
sandboxMode = 'host-fallback'
|
|
314
|
-
}
|
|
223
|
+
const hostWorkdir = resolveShellWorkdir(bctx.cwd, workdir, bctx.filesystemScope)
|
|
224
|
+
const commandForExecution = rewriteShellWorkspaceAliases(bctx.cwd, command)
|
|
315
225
|
if (!envMap.WORKSPACE) envMap.WORKSPACE = bctx.cwd
|
|
316
226
|
if (!envMap.SESSION_CWD) envMap.SESSION_CWD = hostWorkdir
|
|
317
|
-
if (!envMap.SWARMCLAW_SANDBOX_MODE) envMap.SWARMCLAW_SANDBOX_MODE =
|
|
227
|
+
if (!envMap.SWARMCLAW_SANDBOX_MODE) envMap.SWARMCLAW_SANDBOX_MODE = 'host'
|
|
318
228
|
const effectiveBackground = !!background || (typeof commandForExecution === 'string' && isLikelyServerCommand(commandForExecution))
|
|
319
229
|
const managedCommand = effectiveBackground ? stripManagedBackgroundSuffix(commandForExecution) : commandForExecution
|
|
320
230
|
const result = await startManagedProcess({
|
|
@@ -329,7 +239,6 @@ async function executeShellAction(
|
|
|
329
239
|
: effectiveBackground
|
|
330
240
|
? undefined // let process-manager use DEFAULT_TIMEOUT_MS (30 min)
|
|
331
241
|
: (bctx.commandTimeoutMs || 30000),
|
|
332
|
-
sandbox,
|
|
333
242
|
})
|
|
334
243
|
if (result.status === 'completed') return truncate(result.output || '(no output)', MAX_OUTPUT)
|
|
335
244
|
return JSON.stringify({ status: 'running', processId: result.processId, tail: result.tail || '' }, null, 2)
|
|
@@ -344,27 +253,6 @@ async function executeShellAction(
|
|
|
344
253
|
return killManagedProcess(processId!, killSignal).ok ? `Killed ${processId}` : `Error`
|
|
345
254
|
}
|
|
346
255
|
case 'remove': return removeManagedProcess(processId!).ok ? `Removed ${processId}` : `Error`
|
|
347
|
-
case 'sandbox_exec': {
|
|
348
|
-
const sandboxCtx: SandboxContext = {
|
|
349
|
-
sessionId: bctx.sessionId || undefined,
|
|
350
|
-
agentId: bctx.agentId || null,
|
|
351
|
-
cwd: bctx.cwd,
|
|
352
|
-
config: bctx.sandboxConfig,
|
|
353
|
-
resolveCurrentSession: bctx.resolveCurrentSession,
|
|
354
|
-
}
|
|
355
|
-
return executeSandboxExec(normalized, sandboxCtx)
|
|
356
|
-
}
|
|
357
|
-
case 'sandbox_list_runtimes':
|
|
358
|
-
case 'list_runtimes': {
|
|
359
|
-
const sandboxCtx: SandboxContext = {
|
|
360
|
-
sessionId: bctx.sessionId || undefined,
|
|
361
|
-
agentId: bctx.agentId || null,
|
|
362
|
-
cwd: bctx.cwd,
|
|
363
|
-
config: bctx.sandboxConfig,
|
|
364
|
-
resolveCurrentSession: bctx.resolveCurrentSession,
|
|
365
|
-
}
|
|
366
|
-
return executeListRuntimes(sandboxCtx)
|
|
367
|
-
}
|
|
368
256
|
default: return `Error: Unknown action "${action}"`
|
|
369
257
|
}
|
|
370
258
|
} catch (err: unknown) {
|
|
@@ -379,22 +267,20 @@ const ShellExtension: Extension = {
|
|
|
379
267
|
name: 'Core Shell',
|
|
380
268
|
description: 'Execute shell commands and manage background processes. Use for git, curl, and other CLI tools.',
|
|
381
269
|
hooks: {
|
|
382
|
-
getCapabilityDescription: () => 'I can run shell commands with the unified `shell` tool. Use action `execute` for commands (including git, curl, and other CLI tools)
|
|
383
|
-
getOperatingGuidance: () => ['Shell: use `shell` with `{"action":"execute","command":"..."}` for servers, installs, scripts, git, and curl. Use `background=true` for long-lived processes.', 'Use
|
|
270
|
+
getCapabilityDescription: () => 'I can run shell commands with the unified `shell` tool. Use action `execute` for commands (including git, curl, and other CLI tools) and `list` / `status` / `poll` / `log` for long-lived processes.',
|
|
271
|
+
getOperatingGuidance: () => ['Shell: use `shell` with `{"action":"execute","command":"..."}` for servers, installs, scripts, git, and curl. Use `background=true` for long-lived processes.', 'Use the separate `execute` tool for sandboxed just-bash execution.', 'Verify servers with `shell` status/log actions and liveness probes before claiming success.', 'Resolve IPs/URLs via shell — never use placeholders. Retry path errors without workdir override.'],
|
|
384
272
|
} as ExtensionHooks,
|
|
385
273
|
tools: [
|
|
386
274
|
{
|
|
387
275
|
name: 'shell',
|
|
388
|
-
description: 'Execute commands and manage processes. Use for git, curl, and other CLI tools.
|
|
276
|
+
description: 'Execute commands and manage processes. Use for git, curl, long-lived servers, and other host CLI tools.',
|
|
389
277
|
parameters: {
|
|
390
278
|
type: 'object',
|
|
391
279
|
properties: {
|
|
392
|
-
action: { type: 'string', enum: ['execute', 'list', 'status', 'poll', 'log', 'write', 'kill', 'remove'
|
|
280
|
+
action: { type: 'string', enum: ['execute', 'list', 'status', 'poll', 'log', 'write', 'kill', 'remove'] },
|
|
393
281
|
command: { type: 'string' },
|
|
394
282
|
processId: { type: 'string' },
|
|
395
283
|
background: { type: 'boolean' },
|
|
396
|
-
language: { type: 'string', enum: ['javascript', 'typescript'], description: 'Language for sandbox_exec action' },
|
|
397
|
-
code: { type: 'string', description: 'Code to execute for sandbox_exec action' },
|
|
398
284
|
},
|
|
399
285
|
required: ['action']
|
|
400
286
|
},
|
|
@@ -414,7 +300,6 @@ export function buildShellTools(bctx: ToolBuildContext) {
|
|
|
414
300
|
cwd: bctx.cwd,
|
|
415
301
|
resolveCurrentSession: bctx.resolveCurrentSession,
|
|
416
302
|
fileAccessPolicy: bctx.fileAccessPolicy,
|
|
417
|
-
sandboxConfig: bctx.sandboxConfig,
|
|
418
303
|
filesystemScope: bctx.filesystemScope,
|
|
419
304
|
commandTimeoutMs: bctx.commandTimeoutMs,
|
|
420
305
|
}),
|
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* skills — Lightweight skill file reader.
|
|
3
|
+
*
|
|
4
|
+
* Agents call this tool to discover and load skill files (markdown docs)
|
|
5
|
+
* that teach them how to use tools, APIs, or workflows. Two directories
|
|
6
|
+
* are scanned: `skills/` (built-in, checked into the repo) and
|
|
7
|
+
* `data/skills/` (user-created at runtime).
|
|
8
|
+
*
|
|
9
|
+
* Actions:
|
|
10
|
+
* read — load a skill file by name
|
|
11
|
+
* list — browse available skills with descriptions
|
|
12
|
+
* search — find skills by keyword match in content
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import fs from 'fs'
|
|
16
|
+
import path from 'path'
|
|
17
|
+
import { z } from 'zod'
|
|
18
|
+
import { tool } from '@langchain/core/tools'
|
|
19
|
+
import type { Extension, ExtensionHooks } from '@/types'
|
|
20
|
+
import { registerNativeCapability } from '../native-capabilities'
|
|
21
|
+
import { errorMessage } from '@/lib/shared-utils'
|
|
22
|
+
import { log } from '../logger'
|
|
23
|
+
import { normalizeToolInputArgs } from './normalize-tool-args'
|
|
24
|
+
import type { ToolBuildContext } from './context'
|
|
25
|
+
import { truncate, MAX_OUTPUT } from './context'
|
|
26
|
+
|
|
27
|
+
const TAG = 'skills-tool'
|
|
28
|
+
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
// Skill directory resolution
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
|
|
33
|
+
/** Root of the project — two levels above `src/lib/server/session-tools/` */
|
|
34
|
+
function projectRoot(): string {
|
|
35
|
+
return path.resolve(__dirname, '..', '..', '..', '..')
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function builtinSkillsDir(): string {
|
|
39
|
+
return path.join(projectRoot(), 'skills')
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function userSkillsDir(): string {
|
|
43
|
+
return path.join(projectRoot(), 'data', 'skills')
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
// Skill file discovery
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
|
|
50
|
+
interface SkillEntry {
|
|
51
|
+
name: string
|
|
52
|
+
description: string
|
|
53
|
+
source: 'builtin' | 'user'
|
|
54
|
+
filePath: string
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Extract the first non-empty, non-frontmatter, non-heading content line
|
|
59
|
+
* from a markdown file to use as a short description.
|
|
60
|
+
*/
|
|
61
|
+
function extractDescription(content: string): string {
|
|
62
|
+
let inFrontmatter = false
|
|
63
|
+
for (const rawLine of content.split('\n')) {
|
|
64
|
+
const line = rawLine.trim()
|
|
65
|
+
if (line === '---') {
|
|
66
|
+
inFrontmatter = !inFrontmatter
|
|
67
|
+
continue
|
|
68
|
+
}
|
|
69
|
+
if (inFrontmatter) continue
|
|
70
|
+
if (!line || line.startsWith('#')) continue
|
|
71
|
+
return line.length > 120 ? line.slice(0, 117) + '...' : line
|
|
72
|
+
}
|
|
73
|
+
return ''
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Walk a skills directory and collect all `.md` files as skill entries.
|
|
78
|
+
* Supports both flat files (`skills/swarmclaw.md`) and directory-based
|
|
79
|
+
* skills (`skills/github/SKILL.md`).
|
|
80
|
+
*/
|
|
81
|
+
function discoverSkillsInDir(dir: string, source: 'builtin' | 'user'): SkillEntry[] {
|
|
82
|
+
if (!fs.existsSync(dir)) return []
|
|
83
|
+
|
|
84
|
+
const entries: SkillEntry[] = []
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
const items = fs.readdirSync(dir, { withFileTypes: true })
|
|
88
|
+
|
|
89
|
+
for (const item of items) {
|
|
90
|
+
if (item.name.startsWith('.')) continue
|
|
91
|
+
|
|
92
|
+
const fullPath = path.join(dir, item.name)
|
|
93
|
+
|
|
94
|
+
if (item.isFile() && item.name.endsWith('.md')) {
|
|
95
|
+
// Flat markdown file: skills/swarmclaw.md
|
|
96
|
+
const name = item.name.replace(/\.md$/i, '')
|
|
97
|
+
const content = readFileSafe(fullPath)
|
|
98
|
+
entries.push({
|
|
99
|
+
name,
|
|
100
|
+
description: extractDescription(content),
|
|
101
|
+
source,
|
|
102
|
+
filePath: fullPath,
|
|
103
|
+
})
|
|
104
|
+
} else if (item.isDirectory()) {
|
|
105
|
+
// Directory-based skill: walk subdirectory for .md files
|
|
106
|
+
const subItems = readdirSafe(fullPath)
|
|
107
|
+
for (const sub of subItems) {
|
|
108
|
+
if (sub.startsWith('.')) continue
|
|
109
|
+
const subPath = path.join(fullPath, sub)
|
|
110
|
+
|
|
111
|
+
if (sub.endsWith('.md') && fs.statSync(subPath).isFile()) {
|
|
112
|
+
const subName = sub === 'SKILL.md'
|
|
113
|
+
? item.name
|
|
114
|
+
: `${item.name}/${sub.replace(/\.md$/i, '')}`
|
|
115
|
+
const content = readFileSafe(subPath)
|
|
116
|
+
entries.push({
|
|
117
|
+
name: subName,
|
|
118
|
+
description: extractDescription(content),
|
|
119
|
+
source,
|
|
120
|
+
filePath: subPath,
|
|
121
|
+
})
|
|
122
|
+
} else if (fs.statSync(subPath).isDirectory()) {
|
|
123
|
+
// One level deeper: skills/tools/files.md
|
|
124
|
+
const deepItems = readdirSafe(subPath)
|
|
125
|
+
for (const deep of deepItems) {
|
|
126
|
+
if (deep.startsWith('.') || !deep.endsWith('.md')) continue
|
|
127
|
+
const deepPath = path.join(subPath, deep)
|
|
128
|
+
if (!fs.statSync(deepPath).isFile()) continue
|
|
129
|
+
const deepName = `${item.name}/${sub}/${deep.replace(/\.md$/i, '')}`
|
|
130
|
+
const content = readFileSafe(deepPath)
|
|
131
|
+
entries.push({
|
|
132
|
+
name: deepName,
|
|
133
|
+
description: extractDescription(content),
|
|
134
|
+
source,
|
|
135
|
+
filePath: deepPath,
|
|
136
|
+
})
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// If SKILL.md or named .md exist but weren't caught by the loop
|
|
142
|
+
// (they would have been), this is just for safety — dedup by filePath later
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
} catch (err: unknown) {
|
|
146
|
+
log.warn(TAG, `Failed to scan skill directory: ${dir}`, { error: errorMessage(err) })
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return entries
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function readdirSafe(dir: string): string[] {
|
|
153
|
+
try {
|
|
154
|
+
return fs.readdirSync(dir).filter((name) => !name.startsWith('.'))
|
|
155
|
+
} catch {
|
|
156
|
+
return []
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function readFileSafe(filePath: string): string {
|
|
161
|
+
try {
|
|
162
|
+
return fs.readFileSync(filePath, 'utf-8')
|
|
163
|
+
} catch {
|
|
164
|
+
return ''
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function discoverAllSkills(): SkillEntry[] {
|
|
169
|
+
const builtin = discoverSkillsInDir(builtinSkillsDir(), 'builtin')
|
|
170
|
+
const user = discoverSkillsInDir(userSkillsDir(), 'user')
|
|
171
|
+
|
|
172
|
+
// Dedup by filePath
|
|
173
|
+
const seen = new Set<string>()
|
|
174
|
+
const deduped: SkillEntry[] = []
|
|
175
|
+
for (const entry of [...builtin, ...user]) {
|
|
176
|
+
if (seen.has(entry.filePath)) continue
|
|
177
|
+
seen.add(entry.filePath)
|
|
178
|
+
deduped.push(entry)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return deduped
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// ---------------------------------------------------------------------------
|
|
185
|
+
// Action: read
|
|
186
|
+
// ---------------------------------------------------------------------------
|
|
187
|
+
|
|
188
|
+
function resolveSkillFile(name: string): SkillEntry | null {
|
|
189
|
+
const skills = discoverAllSkills()
|
|
190
|
+
const normalized = name.trim().toLowerCase()
|
|
191
|
+
|
|
192
|
+
// Exact match by name
|
|
193
|
+
const exact = skills.find((s) => s.name.toLowerCase() === normalized)
|
|
194
|
+
if (exact) return exact
|
|
195
|
+
|
|
196
|
+
// Partial match: name ends with the query
|
|
197
|
+
const partial = skills.find((s) => s.name.toLowerCase().endsWith(normalized))
|
|
198
|
+
if (partial) return partial
|
|
199
|
+
|
|
200
|
+
// Partial match: name contains the query
|
|
201
|
+
const contains = skills.find((s) => s.name.toLowerCase().includes(normalized))
|
|
202
|
+
if (contains) return contains
|
|
203
|
+
|
|
204
|
+
return null
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function actionRead(name: string): string {
|
|
208
|
+
if (!name) {
|
|
209
|
+
return 'Error: `name` parameter is required for action "read". Provide the skill name to load.'
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const skill = resolveSkillFile(name)
|
|
213
|
+
if (!skill) {
|
|
214
|
+
const available = discoverAllSkills().map((s) => s.name)
|
|
215
|
+
return JSON.stringify({
|
|
216
|
+
error: `Skill "${name}" not found.`,
|
|
217
|
+
available: available.slice(0, 20),
|
|
218
|
+
hint: 'Use action="list" to see all available skills, or action="search" with a query.',
|
|
219
|
+
})
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const content = readFileSafe(skill.filePath)
|
|
223
|
+
if (!content) {
|
|
224
|
+
return `Error: Skill file "${skill.filePath}" exists but could not be read.`
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
log.info(TAG, `Read skill: ${skill.name}`, { source: skill.source, filePath: skill.filePath })
|
|
228
|
+
|
|
229
|
+
return truncate(content, MAX_OUTPUT)
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// ---------------------------------------------------------------------------
|
|
233
|
+
// Action: list
|
|
234
|
+
// ---------------------------------------------------------------------------
|
|
235
|
+
|
|
236
|
+
function actionList(): string {
|
|
237
|
+
const skills = discoverAllSkills()
|
|
238
|
+
|
|
239
|
+
if (skills.length === 0) {
|
|
240
|
+
return JSON.stringify({
|
|
241
|
+
skills: [],
|
|
242
|
+
message: 'No skill files found. Create .md files in skills/ or data/skills/ to add skills.',
|
|
243
|
+
})
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const listing = skills.map((s) => ({
|
|
247
|
+
name: s.name,
|
|
248
|
+
description: s.description,
|
|
249
|
+
source: s.source,
|
|
250
|
+
}))
|
|
251
|
+
|
|
252
|
+
return JSON.stringify({ skills: listing, total: listing.length })
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// ---------------------------------------------------------------------------
|
|
256
|
+
// Action: search
|
|
257
|
+
// ---------------------------------------------------------------------------
|
|
258
|
+
|
|
259
|
+
function actionSearch(query: string): string {
|
|
260
|
+
if (!query) {
|
|
261
|
+
return 'Error: `query` parameter is required for action "search".'
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const skills = discoverAllSkills()
|
|
265
|
+
const terms = query.toLowerCase().split(/\s+/).filter(Boolean)
|
|
266
|
+
|
|
267
|
+
const results: Array<{ name: string; description: string; source: string; matchCount: number }> = []
|
|
268
|
+
|
|
269
|
+
for (const skill of skills) {
|
|
270
|
+
const content = readFileSafe(skill.filePath).toLowerCase()
|
|
271
|
+
const nameAndDesc = `${skill.name} ${skill.description}`.toLowerCase()
|
|
272
|
+
|
|
273
|
+
let matchCount = 0
|
|
274
|
+
for (const term of terms) {
|
|
275
|
+
if (nameAndDesc.includes(term)) matchCount += 2
|
|
276
|
+
else if (content.includes(term)) matchCount += 1
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
if (matchCount > 0) {
|
|
280
|
+
results.push({
|
|
281
|
+
name: skill.name,
|
|
282
|
+
description: skill.description,
|
|
283
|
+
source: skill.source,
|
|
284
|
+
matchCount,
|
|
285
|
+
})
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
results.sort((a, b) => b.matchCount - a.matchCount)
|
|
290
|
+
|
|
291
|
+
return JSON.stringify({
|
|
292
|
+
query,
|
|
293
|
+
results: results.slice(0, 15),
|
|
294
|
+
total: results.length,
|
|
295
|
+
})
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// ---------------------------------------------------------------------------
|
|
299
|
+
// Main action dispatcher
|
|
300
|
+
// ---------------------------------------------------------------------------
|
|
301
|
+
|
|
302
|
+
function executeSkillsAction(rawArgs: Record<string, unknown>): string {
|
|
303
|
+
const normalized = normalizeToolInputArgs(rawArgs)
|
|
304
|
+
const action = typeof normalized.action === 'string' ? normalized.action.trim().toLowerCase() : ''
|
|
305
|
+
const name = typeof normalized.name === 'string' ? normalized.name.trim() : ''
|
|
306
|
+
const query = typeof normalized.query === 'string' ? normalized.query.trim() : ''
|
|
307
|
+
|
|
308
|
+
try {
|
|
309
|
+
switch (action) {
|
|
310
|
+
case 'read':
|
|
311
|
+
return actionRead(name || query)
|
|
312
|
+
case 'list':
|
|
313
|
+
return actionList()
|
|
314
|
+
case 'search':
|
|
315
|
+
return actionSearch(query || name)
|
|
316
|
+
default:
|
|
317
|
+
return `Error: Unknown action "${action}". Supported actions: read, list, search.`
|
|
318
|
+
}
|
|
319
|
+
} catch (err: unknown) {
|
|
320
|
+
return `Error: ${errorMessage(err)}`
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// ---------------------------------------------------------------------------
|
|
325
|
+
// Extension registration
|
|
326
|
+
// ---------------------------------------------------------------------------
|
|
327
|
+
|
|
328
|
+
const SkillsExtension: Extension = {
|
|
329
|
+
name: 'Core Skills',
|
|
330
|
+
description: 'Discover and load skill files that teach agents how to use tools, APIs, and workflows.',
|
|
331
|
+
hooks: {
|
|
332
|
+
getCapabilityDescription: () =>
|
|
333
|
+
'I can discover and read skill files with the `skills` tool. ' +
|
|
334
|
+
'Skills are markdown documents that teach me how to use specific tools, APIs, or workflows. ' +
|
|
335
|
+
'Built-in skills live in `skills/` and user-created skills in `data/skills/`.',
|
|
336
|
+
getOperatingGuidance: () =>
|
|
337
|
+
'Use `skills` with action="list" to see what skills are available. ' +
|
|
338
|
+
'Use action="read" with a name to load a specific skill. ' +
|
|
339
|
+
'Use action="search" with a query to find skills by keyword. ' +
|
|
340
|
+
'Load a skill before attempting unfamiliar tools or APIs.',
|
|
341
|
+
} as ExtensionHooks,
|
|
342
|
+
tools: [
|
|
343
|
+
{
|
|
344
|
+
name: 'skills',
|
|
345
|
+
description:
|
|
346
|
+
'Discover and load skill files (markdown docs) that teach how to use tools, APIs, and workflows. ' +
|
|
347
|
+
'Actions: "read" (load a skill by name), "list" (browse available skills), "search" (find by keyword). ' +
|
|
348
|
+
'Load a skill before attempting unfamiliar tools or APIs.',
|
|
349
|
+
parameters: {
|
|
350
|
+
type: 'object',
|
|
351
|
+
properties: {
|
|
352
|
+
action: {
|
|
353
|
+
type: 'string',
|
|
354
|
+
enum: ['read', 'list', 'search'],
|
|
355
|
+
description: 'The action to perform',
|
|
356
|
+
},
|
|
357
|
+
name: {
|
|
358
|
+
type: 'string',
|
|
359
|
+
description: 'Skill name for action="read" (e.g., "tools/files", "swarmclaw")',
|
|
360
|
+
},
|
|
361
|
+
query: {
|
|
362
|
+
type: 'string',
|
|
363
|
+
description: 'Search query for action="search", or alternate name for action="read"',
|
|
364
|
+
},
|
|
365
|
+
},
|
|
366
|
+
required: ['action'],
|
|
367
|
+
},
|
|
368
|
+
execute: async (args) => executeSkillsAction(args),
|
|
369
|
+
},
|
|
370
|
+
],
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
registerNativeCapability('skills', SkillsExtension)
|
|
374
|
+
|
|
375
|
+
// ---------------------------------------------------------------------------
|
|
376
|
+
// Tool builder (called from session-tools/index.ts)
|
|
377
|
+
// ---------------------------------------------------------------------------
|
|
378
|
+
|
|
379
|
+
export function buildSkillsTools(bctx: ToolBuildContext) {
|
|
380
|
+
if (!bctx.hasExtension('skills')) return []
|
|
381
|
+
|
|
382
|
+
return [
|
|
383
|
+
tool(
|
|
384
|
+
async (args) => executeSkillsAction((args ?? {}) as Record<string, unknown>),
|
|
385
|
+
{
|
|
386
|
+
name: 'skills',
|
|
387
|
+
description: SkillsExtension.tools![0].description,
|
|
388
|
+
schema: z.object({
|
|
389
|
+
action: z.enum(['read', 'list', 'search']).describe('The action to perform'),
|
|
390
|
+
name: z.string().optional().describe('Skill name for action="read"'),
|
|
391
|
+
query: z.string().optional().describe('Search query for action="search"'),
|
|
392
|
+
}).passthrough(),
|
|
393
|
+
},
|
|
394
|
+
),
|
|
395
|
+
]
|
|
396
|
+
}
|
|
@@ -168,9 +168,6 @@ async function formatPeerContext(agentId: string, peerId: string): Promise<strin
|
|
|
168
168
|
const activeSession = Object.values(sessions).find(
|
|
169
169
|
(s) => s.agentId === peerId && s.active,
|
|
170
170
|
)
|
|
171
|
-
if (activeSession?.missionId) {
|
|
172
|
-
result.activeMission = { missionId: activeSession.missionId }
|
|
173
|
-
}
|
|
174
171
|
} catch { /* non-critical */ }
|
|
175
172
|
|
|
176
173
|
// Enforce output cap — drop large fields instead of slicing mid-JSON
|
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
removeBrowserSessionRecord,
|
|
26
26
|
upsertBrowserSessionRecord,
|
|
27
27
|
} from '../browser-state'
|
|
28
|
-
import {
|
|
28
|
+
import { resolveSandboxSessionContext } from '@/lib/server/sandbox/session-runtime'
|
|
29
29
|
import {
|
|
30
30
|
destroySandboxBrowser,
|
|
31
31
|
ensureSandboxBrowser,
|
|
@@ -328,7 +328,7 @@ export function buildWebTools(bctx: ToolBuildContext): StructuredToolInterface[]
|
|
|
328
328
|
if (sandboxRuntimePromise) return sandboxRuntimePromise
|
|
329
329
|
sandboxRuntimePromise = (async () => {
|
|
330
330
|
try {
|
|
331
|
-
const sandbox =
|
|
331
|
+
const sandbox = resolveSandboxSessionContext({
|
|
332
332
|
config: bctx.sandboxConfig,
|
|
333
333
|
session: currentSession,
|
|
334
334
|
agentId: currentSession.agentId ?? ctx?.agentId ?? null,
|
|
@@ -435,6 +435,7 @@ export function normalizeStoredRecord(
|
|
|
435
435
|
&& table !== 'schedules' && table !== 'sessions'
|
|
436
436
|
&& table !== 'provider_configs'
|
|
437
437
|
&& table !== 'runtime_runs' && table !== 'runtime_run_events'
|
|
438
|
+
&& table !== 'wallets'
|
|
438
439
|
) {
|
|
439
440
|
return { value, changed: false }
|
|
440
441
|
}
|
|
@@ -484,6 +485,8 @@ function normalizeStoredRecordInner(
|
|
|
484
485
|
delete agent.platformAssignScope
|
|
485
486
|
delete agent.subAgentIds
|
|
486
487
|
agent.sandboxConfig = normalizeAgentSandboxConfig(agent.sandboxConfig)
|
|
488
|
+
// Default executeConfig — null means not configured (falls back to defaults in execute.ts)
|
|
489
|
+
if (agent.executeConfig === undefined) agent.executeConfig = null
|
|
487
490
|
// Default proactiveMemory to true for existing agents
|
|
488
491
|
if (agent.proactiveMemory === undefined) agent.proactiveMemory = true
|
|
489
492
|
if (!Array.isArray(agent.capabilities)) agent.capabilities = []
|
|
@@ -571,6 +574,13 @@ function normalizeStoredRecordInner(
|
|
|
571
574
|
return normalizeStoredScheduleRecord(value, loadItem)
|
|
572
575
|
}
|
|
573
576
|
|
|
577
|
+
if (table === 'wallets') {
|
|
578
|
+
const wallet = value as StoredObject
|
|
579
|
+
if (wallet.chain !== 'base') wallet.chain = 'base'
|
|
580
|
+
if (typeof wallet.createdAt !== 'number') wallet.createdAt = Date.now()
|
|
581
|
+
return wallet
|
|
582
|
+
}
|
|
583
|
+
|
|
574
584
|
// sessions
|
|
575
585
|
const session = value as StoredObject
|
|
576
586
|
// Migrate legacy 'orchestrated' → 'delegated'
|