@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
|
@@ -236,15 +236,37 @@ export function resolveSandboxWorkdir(params: {
|
|
|
236
236
|
}
|
|
237
237
|
}
|
|
238
238
|
|
|
239
|
-
export
|
|
239
|
+
export function resolveSandboxSessionContext(params: {
|
|
240
240
|
config: AgentSandboxConfig | null | undefined
|
|
241
241
|
session?: Session | null
|
|
242
242
|
agentId?: string | null
|
|
243
243
|
sessionId?: string | null
|
|
244
244
|
workspaceDir: string
|
|
245
|
-
}):
|
|
245
|
+
}): SandboxSessionContext | null {
|
|
246
246
|
const status = resolveSandboxRuntimeStatus(params)
|
|
247
247
|
if (!status.sandboxed || !status.config || !status.scopeKey) return null
|
|
248
|
+
|
|
249
|
+
return {
|
|
250
|
+
containerName: `${status.config.containerPrefix}${slugifyScopeKey(status.scopeKey)}`.slice(0, 63),
|
|
251
|
+
containerWorkdir: status.config.workdir,
|
|
252
|
+
workspaceDir: params.workspaceDir,
|
|
253
|
+
workspaceAccess: status.config.workspaceAccess,
|
|
254
|
+
mode: status.mode,
|
|
255
|
+
scope: status.scope,
|
|
256
|
+
scopeKey: status.scopeKey,
|
|
257
|
+
config: status.config,
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
export async function ensureSessionSandbox(params: {
|
|
262
|
+
config: AgentSandboxConfig | null | undefined
|
|
263
|
+
session?: Session | null
|
|
264
|
+
agentId?: string | null
|
|
265
|
+
sessionId?: string | null
|
|
266
|
+
workspaceDir: string
|
|
267
|
+
}): Promise<SandboxSessionContext | null> {
|
|
268
|
+
const context = resolveSandboxSessionContext(params)
|
|
269
|
+
if (!context) return null
|
|
248
270
|
await maybePruneSandboxes(params.config)
|
|
249
271
|
|
|
250
272
|
const docker = detectDocker()
|
|
@@ -252,17 +274,16 @@ export async function ensureSessionSandbox(params: {
|
|
|
252
274
|
throw new Error('Sandbox is enabled but Docker is not available. Install Docker Desktop or disable the sandbox in agent settings.')
|
|
253
275
|
}
|
|
254
276
|
|
|
255
|
-
const containerName = `${status.config.containerPrefix}${slugifyScopeKey(status.scopeKey)}`.slice(0, 63)
|
|
256
277
|
const configHash = computeSandboxConfigHash({
|
|
257
|
-
config:
|
|
278
|
+
config: context.config,
|
|
258
279
|
workspaceDir: params.workspaceDir,
|
|
259
|
-
scopeKey:
|
|
280
|
+
scopeKey: context.scopeKey,
|
|
260
281
|
})
|
|
261
282
|
|
|
262
|
-
const current = await inspectDockerContainer(containerName)
|
|
263
|
-
const currentHash = current.exists ? await readDockerLabel(containerName, 'swarmclaw.configHash') : null
|
|
283
|
+
const current = await inspectDockerContainer(context.containerName)
|
|
284
|
+
const currentHash = current.exists ? await readDockerLabel(context.containerName, 'swarmclaw.configHash') : null
|
|
264
285
|
if (current.exists && currentHash && currentHash !== configHash) {
|
|
265
|
-
await execDocker(['rm', '-f', containerName], true)
|
|
286
|
+
await execDocker(['rm', '-f', context.containerName], true)
|
|
266
287
|
}
|
|
267
288
|
|
|
268
289
|
const next = current.exists && currentHash === configHash
|
|
@@ -271,37 +292,28 @@ export async function ensureSessionSandbox(params: {
|
|
|
271
292
|
|
|
272
293
|
if (!next.exists) {
|
|
273
294
|
await execDocker(buildSandboxCreateArgs({
|
|
274
|
-
containerName,
|
|
275
|
-
scopeKey:
|
|
295
|
+
containerName: context.containerName,
|
|
296
|
+
scopeKey: context.scopeKey,
|
|
276
297
|
configHash,
|
|
277
|
-
config:
|
|
298
|
+
config: context.config,
|
|
278
299
|
workspaceDir: params.workspaceDir,
|
|
279
300
|
}))
|
|
280
|
-
await execDocker(['start', containerName])
|
|
281
|
-
if (
|
|
282
|
-
await execDocker(['exec', '-i', containerName, '/bin/sh', '-lc',
|
|
301
|
+
await execDocker(['start', context.containerName])
|
|
302
|
+
if (context.config.setupCommand) {
|
|
303
|
+
await execDocker(['exec', '-i', context.containerName, '/bin/sh', '-lc', context.config.setupCommand])
|
|
283
304
|
}
|
|
284
305
|
} else if (!next.running) {
|
|
285
|
-
await execDocker(['start', containerName])
|
|
306
|
+
await execDocker(['start', context.containerName])
|
|
286
307
|
}
|
|
287
308
|
|
|
288
309
|
await upsertSandboxRegistryEntry({
|
|
289
|
-
containerName,
|
|
290
|
-
scopeKey:
|
|
310
|
+
containerName: context.containerName,
|
|
311
|
+
scopeKey: context.scopeKey,
|
|
291
312
|
createdAtMs: Date.now(),
|
|
292
313
|
lastUsedAtMs: Date.now(),
|
|
293
|
-
image:
|
|
314
|
+
image: context.config.image,
|
|
294
315
|
configHash,
|
|
295
316
|
})
|
|
296
317
|
|
|
297
|
-
return
|
|
298
|
-
containerName,
|
|
299
|
-
containerWorkdir: status.config.workdir,
|
|
300
|
-
workspaceDir: params.workspaceDir,
|
|
301
|
-
workspaceAccess: status.config.workspaceAccess,
|
|
302
|
-
mode: status.mode,
|
|
303
|
-
scope: status.scope,
|
|
304
|
-
scopeKey: status.scopeKey,
|
|
305
|
-
config: status.config,
|
|
306
|
-
}
|
|
318
|
+
return context
|
|
307
319
|
}
|
|
@@ -52,14 +52,12 @@ describe('durable wait surface', () => {
|
|
|
52
52
|
})
|
|
53
53
|
})
|
|
54
54
|
|
|
55
|
-
describe('
|
|
56
|
-
it('
|
|
57
|
-
const src = readToolSource('
|
|
58
|
-
assert.equal(src.includes('
|
|
59
|
-
assert.equal(src.includes('
|
|
60
|
-
assert.equal(src.includes('
|
|
61
|
-
// Extension registration removed — sandbox_exec is now provided by shell
|
|
62
|
-
assert.equal(src.includes('registerBuiltin'), false)
|
|
55
|
+
describe('execute surface', () => {
|
|
56
|
+
it('keeps just-bash as the sandboxed execution path with explicit host opt-in', () => {
|
|
57
|
+
const src = readToolSource('execute')
|
|
58
|
+
assert.equal(src.includes('just-bash'), true)
|
|
59
|
+
assert.equal(src.includes('normalizeAgentExecuteConfig'), true)
|
|
60
|
+
assert.equal(src.includes('persistent=true'), true)
|
|
63
61
|
})
|
|
64
62
|
})
|
|
65
63
|
|
|
@@ -81,7 +79,7 @@ describe('delegation job handles', () => {
|
|
|
81
79
|
|
|
82
80
|
it('scheduler and daemon recover the durable autonomy jobs', () => {
|
|
83
81
|
const schedulerSrc = fs.readFileSync(path.join(serverDir, 'runtime', 'scheduler.ts'), 'utf-8')
|
|
84
|
-
const daemonSrc = fs.readFileSync(path.join(serverDir, 'runtime', 'daemon-state.ts'), 'utf-8')
|
|
82
|
+
const daemonSrc = fs.readFileSync(path.join(serverDir, 'runtime', 'daemon-state', 'core.ts'), 'utf-8')
|
|
85
83
|
assert.equal(schedulerSrc.includes('processDueWatchJobs'), true)
|
|
86
84
|
assert.equal(daemonSrc.includes('recoverStaleDelegationJobs'), true)
|
|
87
85
|
})
|
|
@@ -190,7 +190,7 @@ export function findBinaryOnPath(binaryName: string): string | null {
|
|
|
190
190
|
const { spawnSync } = require('child_process')
|
|
191
191
|
const probe = isWindows
|
|
192
192
|
? spawnSync('where', [binaryName], { encoding: 'utf-8', timeout: 2000, stdio: 'pipe' })
|
|
193
|
-
: spawnSync('/bin/
|
|
193
|
+
: spawnSync(process.env.SHELL || '/bin/bash', ['-lc', `command -v ${binaryName} 2>/dev/null`], { encoding: 'utf-8', timeout: 2000 })
|
|
194
194
|
const resolved = (probe.stdout || '').trim() || null
|
|
195
195
|
binaryLookupCache.set(binaryName, { checkedAt: now, path: resolved })
|
|
196
196
|
return resolved
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Credential injection and secret redaction for agent code execution.
|
|
3
|
+
*
|
|
4
|
+
* Resolves an agent's configured credentials from the credential store,
|
|
5
|
+
* returns them as env vars for injection, and provides a redaction function
|
|
6
|
+
* to scrub secrets from execution output.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { loadCredentials, decryptKey } from '../storage'
|
|
10
|
+
import { log } from '../logger'
|
|
11
|
+
import type { Credential } from '@/types'
|
|
12
|
+
|
|
13
|
+
const TAG = 'credential-env'
|
|
14
|
+
|
|
15
|
+
export interface CredentialEnv {
|
|
16
|
+
/** Environment variables to inject (name → decrypted value) */
|
|
17
|
+
env: Record<string, string>
|
|
18
|
+
/** Raw secret values for redaction */
|
|
19
|
+
secrets: string[]
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Build credential environment variables for an agent execution.
|
|
24
|
+
*
|
|
25
|
+
* Each credential ID in the list is resolved from the credential store,
|
|
26
|
+
* decrypted, and mapped to an env var name derived from the credential's
|
|
27
|
+
* provider and name fields.
|
|
28
|
+
*
|
|
29
|
+
* Env var naming: `<PROVIDER>_API_KEY` for the primary key, or
|
|
30
|
+
* `<PROVIDER>_<NAME>` if a name is explicitly set. All uppercased,
|
|
31
|
+
* non-alphanumeric chars replaced with underscores.
|
|
32
|
+
*/
|
|
33
|
+
export function buildCredentialEnv(credentialIds: string[]): CredentialEnv {
|
|
34
|
+
if (!credentialIds.length) return { env: {}, secrets: [] }
|
|
35
|
+
|
|
36
|
+
const env: Record<string, string> = {}
|
|
37
|
+
const secrets: string[] = []
|
|
38
|
+
|
|
39
|
+
const allCredentials = loadCredentials() as Record<string, Credential & { encrypted?: string }>
|
|
40
|
+
|
|
41
|
+
for (const credId of credentialIds) {
|
|
42
|
+
const cred = allCredentials[credId]
|
|
43
|
+
if (!cred) {
|
|
44
|
+
log.warn(TAG, `Credential not found: ${credId}`)
|
|
45
|
+
continue
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Decrypt the stored key
|
|
49
|
+
const encrypted = cred.encrypted
|
|
50
|
+
if (!encrypted || typeof encrypted !== 'string') {
|
|
51
|
+
log.warn(TAG, `Credential has no encrypted value: ${credId}`)
|
|
52
|
+
continue
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
let value: string
|
|
56
|
+
try {
|
|
57
|
+
value = decryptKey(encrypted)
|
|
58
|
+
} catch (err: unknown) {
|
|
59
|
+
log.warn(TAG, `Failed to decrypt credential ${credId}`, { error: String(err) })
|
|
60
|
+
continue
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Derive env var name
|
|
64
|
+
const envVarName = deriveEnvVarName(cred.provider, cred.name)
|
|
65
|
+
env[envVarName] = value
|
|
66
|
+
secrets.push(value)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return { env, secrets }
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Derive an environment variable name from provider and credential name.
|
|
74
|
+
* e.g., provider="openai", name="default" → "OPENAI_API_KEY"
|
|
75
|
+
* e.g., provider="custom", name="my-service-token" → "CUSTOM_MY_SERVICE_TOKEN"
|
|
76
|
+
*/
|
|
77
|
+
function deriveEnvVarName(provider: string, name: string): string {
|
|
78
|
+
const sanitize = (s: string) => s.toUpperCase().replace(/[^A-Z0-9]+/g, '_').replace(/^_|_$/g, '')
|
|
79
|
+
|
|
80
|
+
const providerKey = sanitize(provider)
|
|
81
|
+
const nameKey = sanitize(name)
|
|
82
|
+
|
|
83
|
+
// If name is generic (default, primary, key, api-key, etc.), use PROVIDER_API_KEY
|
|
84
|
+
const genericNames = new Set(['DEFAULT', 'PRIMARY', 'KEY', 'API_KEY', 'APIKEY', ''])
|
|
85
|
+
if (genericNames.has(nameKey)) {
|
|
86
|
+
return `${providerKey}_API_KEY`
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return `${providerKey}_${nameKey}`
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Redact secret values from execution output.
|
|
94
|
+
*
|
|
95
|
+
* Scans the text for any injected secret values and replaces them
|
|
96
|
+
* with [REDACTED]. Only redacts secrets longer than 4 characters
|
|
97
|
+
* to avoid false positives on short strings.
|
|
98
|
+
*/
|
|
99
|
+
export function redactSecrets(text: string, secrets: string[]): string {
|
|
100
|
+
if (!secrets.length || !text) return text
|
|
101
|
+
|
|
102
|
+
let result = text
|
|
103
|
+
for (const secret of secrets) {
|
|
104
|
+
if (secret.length > 4) {
|
|
105
|
+
result = result.replaceAll(secret, '[REDACTED]')
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return result
|
|
109
|
+
}
|
|
@@ -38,7 +38,6 @@ import {
|
|
|
38
38
|
deriveTaskTitle,
|
|
39
39
|
prepareTaskCreation,
|
|
40
40
|
} from '@/lib/server/tasks/task-service'
|
|
41
|
-
import { ensureMissionForTask, enrichTaskWithMissionSummary } from '@/lib/server/missions/mission-service'
|
|
42
41
|
import { classifyMessage } from '@/lib/server/chat-execution/message-classifier'
|
|
43
42
|
import {
|
|
44
43
|
buildDelegationTaskProfile,
|
|
@@ -48,7 +47,7 @@ import {
|
|
|
48
47
|
import type { ToolBuildContext } from './context'
|
|
49
48
|
import { normalizeToolInputArgs } from './normalize-tool-args'
|
|
50
49
|
import type { BoardTask } from '@/types'
|
|
51
|
-
import { dedup } from '@/lib/shared-utils'
|
|
50
|
+
import { dedup, errorMessage } from '@/lib/shared-utils'
|
|
52
51
|
import { isDirectConnectorSession } from '../connectors/session-kind'
|
|
53
52
|
import { buildManageSkillsDescription, executeManageSkillsAction } from './skills'
|
|
54
53
|
import { isMainSession } from '@/lib/server/agents/main-agent-loop'
|
|
@@ -742,19 +741,6 @@ export function buildCrudTools(bctx: ToolBuildContext): StructuredToolInterface[
|
|
|
742
741
|
|
|
743
742
|
res.save(all)
|
|
744
743
|
if (toolKey === 'manage_tasks') {
|
|
745
|
-
const mission = ensureMissionForTask(entry as BoardTask, {
|
|
746
|
-
source: entry.sourceType === 'schedule'
|
|
747
|
-
? 'schedule'
|
|
748
|
-
: entry.sourceType === 'delegation'
|
|
749
|
-
? 'delegation'
|
|
750
|
-
: 'manual',
|
|
751
|
-
})
|
|
752
|
-
if (mission) {
|
|
753
|
-
responseEntry = enrichTaskWithMissionSummary({
|
|
754
|
-
...(responseEntry as BoardTask),
|
|
755
|
-
missionId: mission.id,
|
|
756
|
-
})
|
|
757
|
-
}
|
|
758
744
|
if (taskDelegationAdvisory && responseEntry && typeof responseEntry === 'object') {
|
|
759
745
|
responseEntry = {
|
|
760
746
|
...(responseEntry as Record<string, unknown>),
|
|
@@ -1149,8 +1135,8 @@ export function buildCrudTools(bctx: ToolBuildContext): StructuredToolInterface[
|
|
|
1149
1135
|
return JSON.stringify({ ok: true, taskId: id, claimedByAgentId: ctx?.agentId })
|
|
1150
1136
|
}
|
|
1151
1137
|
return `Unknown action "${action}". Valid: list, get, create, update, delete, claim_task`
|
|
1152
|
-
} catch (err:
|
|
1153
|
-
return `Error: ${err
|
|
1138
|
+
} catch (err: unknown) {
|
|
1139
|
+
return `Error: ${errorMessage(err)}`
|
|
1154
1140
|
}
|
|
1155
1141
|
},
|
|
1156
1142
|
{
|
|
@@ -21,7 +21,6 @@ import {
|
|
|
21
21
|
registerDelegationRuntime,
|
|
22
22
|
startDelegationJob,
|
|
23
23
|
} from '@/lib/server/agents/delegation-jobs'
|
|
24
|
-
import { loadSession } from '@/lib/server/sessions/session-repository'
|
|
25
24
|
import { markProviderFailure, markProviderSuccess } from '../provider-health'
|
|
26
25
|
import { loadRuntimeSettings } from '../runtime/runtime-settings'
|
|
27
26
|
import { getSessionDepth } from '../agents/subagent-runtime'
|
|
@@ -463,8 +462,6 @@ async function executeDelegateAction(args: Record<string, unknown>, bctx: Delega
|
|
|
463
462
|
const jobId = typeof normalized.jobId === 'string' ? normalized.jobId.trim() : ''
|
|
464
463
|
const waitForCompletion = normalized.waitForCompletion !== false && normalized.background !== true
|
|
465
464
|
const parentSessionId = resolveDelegateSessionId(bctx)
|
|
466
|
-
const parentMissionId = parentSessionId ? loadSession(parentSessionId)?.missionId || null : null
|
|
467
|
-
|
|
468
465
|
recoverStaleDelegationJobs()
|
|
469
466
|
|
|
470
467
|
if (action === 'status') {
|
|
@@ -505,7 +502,6 @@ async function executeDelegateAction(args: Record<string, unknown>, bctx: Delega
|
|
|
505
502
|
const job = createDelegationJob({
|
|
506
503
|
kind: 'delegate',
|
|
507
504
|
parentSessionId,
|
|
508
|
-
parentMissionId,
|
|
509
505
|
backend: requestedBackend,
|
|
510
506
|
task,
|
|
511
507
|
cwd: bctx.cwd || null,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { errorMessage } from '@/lib/shared-utils'
|
|
1
2
|
import { z } from 'zod'
|
|
2
3
|
import { tool, type StructuredToolInterface } from '@langchain/core/tools'
|
|
3
4
|
import fs from 'fs'
|
|
@@ -32,8 +33,8 @@ async function executeEditFile(args: { filePath: string; oldString: string; newS
|
|
|
32
33
|
const updated = content.replace(oldString, newString)
|
|
33
34
|
fs.writeFileSync(resolved, updated, 'utf-8')
|
|
34
35
|
return `Successfully updated ${filePath} (1 replacement made).`
|
|
35
|
-
} catch (err:
|
|
36
|
-
return `Error: ${err
|
|
36
|
+
} catch (err: unknown) {
|
|
37
|
+
return `Error: ${errorMessage(err)}`
|
|
37
38
|
}
|
|
38
39
|
}
|
|
39
40
|
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { describe, it } from 'node:test'
|
|
2
|
+
import assert from 'node:assert/strict'
|
|
3
|
+
import { redactSecrets, buildCredentialEnv } from './credential-env'
|
|
4
|
+
|
|
5
|
+
describe('credential-env', () => {
|
|
6
|
+
describe('redactSecrets', () => {
|
|
7
|
+
it('redacts secret values from text', () => {
|
|
8
|
+
const secrets = ['sk-abc123456789']
|
|
9
|
+
const text = 'Response: Bearer sk-abc123456789 was used'
|
|
10
|
+
const result = redactSecrets(text, secrets)
|
|
11
|
+
assert.equal(result, 'Response: Bearer [REDACTED] was used')
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
it('skips secrets shorter than 5 characters', () => {
|
|
15
|
+
const secrets = ['abc']
|
|
16
|
+
const text = 'Contains abc value'
|
|
17
|
+
const result = redactSecrets(text, secrets)
|
|
18
|
+
assert.equal(result, 'Contains abc value')
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
it('handles empty secrets list', () => {
|
|
22
|
+
const text = 'No secrets here'
|
|
23
|
+
const result = redactSecrets(text, [])
|
|
24
|
+
assert.equal(result, 'No secrets here')
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
it('handles empty text', () => {
|
|
28
|
+
const result = redactSecrets('', ['secret123'])
|
|
29
|
+
assert.equal(result, '')
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
it('redacts multiple occurrences', () => {
|
|
33
|
+
const secrets = ['mytoken12345']
|
|
34
|
+
const text = 'First: mytoken12345, Second: mytoken12345'
|
|
35
|
+
const result = redactSecrets(text, secrets)
|
|
36
|
+
assert.equal(result, 'First: [REDACTED], Second: [REDACTED]')
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
it('redacts multiple different secrets', () => {
|
|
40
|
+
const secrets = ['secret_one_value', 'secret_two_value']
|
|
41
|
+
const text = 'Key1=secret_one_value Key2=secret_two_value'
|
|
42
|
+
const result = redactSecrets(text, secrets)
|
|
43
|
+
assert.equal(result, 'Key1=[REDACTED] Key2=[REDACTED]')
|
|
44
|
+
})
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
describe('buildCredentialEnv', () => {
|
|
48
|
+
it('returns empty env for empty credential list', () => {
|
|
49
|
+
const result = buildCredentialEnv([])
|
|
50
|
+
assert.deepEqual(result, { env: {}, secrets: [] })
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it('handles non-existent credential IDs gracefully', () => {
|
|
54
|
+
const result = buildCredentialEnv(['nonexistent-id'])
|
|
55
|
+
assert.deepEqual(result, { env: {}, secrets: [] })
|
|
56
|
+
})
|
|
57
|
+
})
|
|
58
|
+
})
|