@swarmclawai/swarmclaw 0.7.1 → 0.7.3
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 +155 -150
- package/package.json +1 -1
- package/src/app/api/agents/[id]/route.ts +26 -0
- package/src/app/api/agents/[id]/thread/route.ts +37 -9
- package/src/app/api/agents/route.ts +13 -2
- package/src/app/api/auth/route.ts +76 -7
- package/src/app/api/chatrooms/[id]/chat/route.ts +7 -2
- package/src/app/api/{sessions → chats}/[id]/browser/route.ts +5 -1
- package/src/app/api/{sessions → chats}/[id]/chat/route.ts +7 -3
- package/src/app/api/{sessions → chats}/[id]/checkpoints/route.ts +1 -1
- package/src/app/api/chats/[id]/main-loop/route.ts +13 -0
- package/src/app/api/{sessions → chats}/[id]/messages/route.ts +19 -13
- package/src/app/api/{sessions → chats}/[id]/restore/route.ts +1 -1
- package/src/app/api/{sessions → chats}/[id]/route.ts +22 -52
- package/src/app/api/{sessions → chats}/[id]/stop/route.ts +6 -1
- package/src/app/api/{sessions → chats}/route.ts +21 -7
- package/src/app/api/connectors/[id]/doctor/route.ts +26 -0
- package/src/app/api/connectors/doctor/route.ts +13 -0
- package/src/app/api/files/open/route.ts +16 -14
- package/src/app/api/memory/maintenance/route.ts +11 -1
- package/src/app/api/openclaw/agent-files/route.ts +27 -4
- package/src/app/api/openclaw/skills/route.ts +11 -3
- package/src/app/api/plugins/dependencies/route.ts +24 -0
- package/src/app/api/plugins/install/route.ts +15 -92
- package/src/app/api/plugins/route.ts +6 -26
- package/src/app/api/plugins/settings/route.ts +40 -0
- package/src/app/api/plugins/ui/route.ts +1 -0
- package/src/app/api/settings/route.ts +49 -7
- package/src/app/api/tasks/[id]/route.ts +15 -6
- package/src/app/api/tasks/bulk/route.ts +2 -2
- package/src/app/api/tasks/route.ts +9 -4
- package/src/app/api/usage/route.ts +30 -0
- package/src/app/api/webhooks/[id]/route.ts +8 -1
- package/src/app/page.tsx +9 -2
- package/src/cli/index.js +39 -33
- package/src/cli/index.ts +43 -49
- package/src/cli/spec.js +29 -27
- package/src/components/agents/agent-card.tsx +16 -13
- package/src/components/agents/agent-chat-list.tsx +104 -4
- package/src/components/agents/agent-list.tsx +54 -22
- package/src/components/agents/agent-sheet.tsx +209 -18
- package/src/components/agents/cron-job-form.tsx +3 -3
- package/src/components/agents/inspector-panel.tsx +110 -50
- package/src/components/auth/access-key-gate.tsx +36 -97
- package/src/components/auth/setup-wizard.tsx +5 -38
- package/src/components/chat/chat-area.tsx +39 -27
- package/src/components/{sessions/session-card.tsx → chat/chat-card.tsx} +7 -23
- package/src/components/chat/chat-header.tsx +299 -314
- package/src/components/{sessions/session-list.tsx → chat/chat-list.tsx} +11 -14
- package/src/components/chat/chat-tool-toggles.tsx +26 -17
- package/src/components/chat/checkpoint-timeline.tsx +4 -4
- package/src/components/chat/message-bubble.tsx +4 -1
- package/src/components/chat/message-list.tsx +5 -3
- package/src/components/chat/session-debug-panel.tsx +1 -1
- package/src/components/chat/tool-request-banner.tsx +3 -3
- package/src/components/chatrooms/agent-hover-card.tsx +3 -3
- package/src/components/chatrooms/chatroom-tool-request-banner.tsx +2 -2
- package/src/components/chatrooms/chatroom-view.tsx +347 -205
- package/src/components/connectors/connector-list.tsx +265 -127
- package/src/components/connectors/connector-sheet.tsx +218 -1
- package/src/components/home/home-view.tsx +129 -5
- package/src/components/layout/app-layout.tsx +392 -182
- package/src/components/layout/mobile-header.tsx +26 -8
- package/src/components/plugins/plugin-list.tsx +487 -254
- package/src/components/plugins/plugin-sheet.tsx +236 -13
- package/src/components/projects/project-detail.tsx +183 -0
- package/src/components/settings/gateway-connection-panel.tsx +1 -1
- package/src/components/shared/agent-picker-list.tsx +2 -2
- package/src/components/shared/command-palette.tsx +111 -25
- package/src/components/shared/settings/plugin-manager.tsx +20 -4
- package/src/components/shared/settings/section-capability-policy.tsx +105 -0
- package/src/components/shared/settings/section-heartbeat.tsx +78 -1
- package/src/components/shared/settings/section-orchestrator.tsx +3 -3
- package/src/components/shared/settings/section-providers.tsx +1 -1
- package/src/components/shared/settings/section-runtime-loop.tsx +5 -5
- package/src/components/shared/settings/section-secrets.tsx +6 -6
- package/src/components/shared/settings/section-user-preferences.tsx +1 -1
- package/src/components/shared/settings/section-voice.tsx +5 -1
- package/src/components/shared/settings/section-web-search.tsx +10 -2
- package/src/components/shared/settings/settings-page.tsx +244 -56
- package/src/components/tasks/approvals-panel.tsx +205 -18
- package/src/components/tasks/task-board.tsx +242 -46
- package/src/components/usage/metrics-dashboard.tsx +147 -1
- package/src/components/wallets/wallet-panel.tsx +17 -5
- package/src/components/webhooks/webhook-sheet.tsx +8 -8
- package/src/lib/auth.ts +17 -0
- package/src/lib/chat-streaming-state.test.ts +108 -0
- package/src/lib/chat-streaming-state.ts +108 -0
- package/src/lib/chat.ts +1 -1
- package/src/lib/{sessions.ts → chats.ts} +28 -18
- package/src/lib/openclaw-agent-id.test.ts +14 -0
- package/src/lib/openclaw-agent-id.ts +31 -0
- package/src/lib/providers/claude-cli.ts +1 -1
- package/src/lib/server/agent-assignment.test.ts +112 -0
- package/src/lib/server/agent-assignment.ts +169 -0
- package/src/lib/server/approval-connector-notify.test.ts +253 -0
- package/src/lib/server/approvals-auto-approve.test.ts +205 -0
- package/src/lib/server/approvals.ts +483 -75
- package/src/lib/server/autonomy-runtime.test.ts +341 -0
- package/src/lib/server/browser-state.test.ts +118 -0
- package/src/lib/server/browser-state.ts +123 -0
- package/src/lib/server/build-llm.test.ts +36 -0
- package/src/lib/server/build-llm.ts +11 -4
- package/src/lib/server/builtin-plugins.ts +34 -0
- package/src/lib/server/capability-router.ts +10 -8
- package/src/lib/server/chat-execution-heartbeat.test.ts +40 -0
- package/src/lib/server/chat-execution-tool-events.test.ts +134 -0
- package/src/lib/server/chat-execution.ts +285 -165
- package/src/lib/server/chatroom-health.test.ts +26 -0
- package/src/lib/server/chatroom-health.ts +2 -3
- package/src/lib/server/chatroom-helpers.test.ts +67 -2
- package/src/lib/server/chatroom-helpers.ts +48 -8
- package/src/lib/server/connectors/discord.ts +175 -11
- package/src/lib/server/connectors/doctor.test.ts +80 -0
- package/src/lib/server/connectors/doctor.ts +116 -0
- package/src/lib/server/connectors/manager.ts +948 -112
- package/src/lib/server/connectors/policy.test.ts +222 -0
- package/src/lib/server/connectors/policy.ts +452 -0
- package/src/lib/server/connectors/slack.ts +188 -9
- package/src/lib/server/connectors/telegram.ts +65 -15
- package/src/lib/server/connectors/thread-context.test.ts +44 -0
- package/src/lib/server/connectors/thread-context.ts +72 -0
- package/src/lib/server/connectors/types.ts +41 -11
- package/src/lib/server/cost.ts +34 -1
- package/src/lib/server/daemon-state.ts +61 -3
- package/src/lib/server/data-dir.ts +13 -0
- package/src/lib/server/delegation-jobs.test.ts +140 -0
- package/src/lib/server/delegation-jobs.ts +248 -0
- package/src/lib/server/document-utils.test.ts +47 -0
- package/src/lib/server/document-utils.ts +397 -0
- package/src/lib/server/heartbeat-service.ts +14 -40
- package/src/lib/server/heartbeat-source.test.ts +22 -0
- package/src/lib/server/heartbeat-source.ts +7 -0
- package/src/lib/server/identity-continuity.test.ts +77 -0
- package/src/lib/server/identity-continuity.ts +127 -0
- package/src/lib/server/mailbox-utils.ts +347 -0
- package/src/lib/server/main-agent-loop.ts +28 -1103
- package/src/lib/server/memory-db.ts +4 -6
- package/src/lib/server/memory-tiers.ts +40 -0
- package/src/lib/server/openclaw-agent-resolver.test.ts +70 -0
- package/src/lib/server/openclaw-agent-resolver.ts +128 -0
- package/src/lib/server/openclaw-exec-config.ts +5 -6
- package/src/lib/server/openclaw-skills-normalize.test.ts +56 -0
- package/src/lib/server/openclaw-skills-normalize.ts +136 -0
- package/src/lib/server/openclaw-sync.ts +3 -2
- package/src/lib/server/orchestrator-lg.ts +20 -9
- package/src/lib/server/orchestrator.ts +7 -7
- package/src/lib/server/playwright-proxy.mjs +27 -3
- package/src/lib/server/plugins.test.ts +207 -0
- package/src/lib/server/plugins.ts +927 -66
- package/src/lib/server/provider-health.ts +38 -6
- package/src/lib/server/queue.ts +13 -28
- package/src/lib/server/scheduler.ts +2 -0
- package/src/lib/server/session-archive-memory.test.ts +85 -0
- package/src/lib/server/session-archive-memory.ts +230 -0
- package/src/lib/server/session-mailbox.ts +8 -18
- package/src/lib/server/session-reset-policy.test.ts +99 -0
- package/src/lib/server/session-reset-policy.ts +311 -0
- package/src/lib/server/session-run-manager.ts +33 -82
- package/src/lib/server/session-tools/autonomy-tools.test.ts +105 -0
- package/src/lib/server/session-tools/calendar.ts +366 -0
- package/src/lib/server/session-tools/canvas.ts +1 -1
- package/src/lib/server/session-tools/chatroom.ts +4 -2
- package/src/lib/server/session-tools/connector.ts +114 -10
- package/src/lib/server/session-tools/context.ts +21 -5
- package/src/lib/server/session-tools/crawl.ts +447 -0
- package/src/lib/server/session-tools/crud.ts +74 -28
- package/src/lib/server/session-tools/delegate-fallback.test.ts +219 -0
- package/src/lib/server/session-tools/delegate.ts +497 -24
- package/src/lib/server/session-tools/discovery.ts +24 -6
- package/src/lib/server/session-tools/document.ts +283 -0
- package/src/lib/server/session-tools/edit_file.ts +4 -2
- package/src/lib/server/session-tools/email.ts +320 -0
- package/src/lib/server/session-tools/extract.ts +137 -0
- package/src/lib/server/session-tools/file-normalize.test.ts +93 -0
- package/src/lib/server/session-tools/file-send.test.ts +84 -1
- package/src/lib/server/session-tools/file.ts +241 -25
- package/src/lib/server/session-tools/git.ts +1 -1
- package/src/lib/server/session-tools/http.ts +1 -1
- package/src/lib/server/session-tools/human-loop.ts +227 -0
- package/src/lib/server/session-tools/image-gen.ts +380 -0
- package/src/lib/server/session-tools/index.ts +130 -50
- package/src/lib/server/session-tools/mailbox.ts +276 -0
- package/src/lib/server/session-tools/memory.ts +172 -3
- package/src/lib/server/session-tools/monitor.ts +151 -8
- package/src/lib/server/session-tools/normalize-tool-args.ts +17 -14
- package/src/lib/server/session-tools/openclaw-nodes.ts +1 -1
- package/src/lib/server/session-tools/openclaw-workspace.ts +1 -1
- package/src/lib/server/session-tools/platform-normalize.test.ts +142 -0
- package/src/lib/server/session-tools/platform.ts +148 -7
- package/src/lib/server/session-tools/plugin-creator.ts +89 -26
- package/src/lib/server/session-tools/primitive-tools.test.ts +257 -0
- package/src/lib/server/session-tools/replicate.ts +301 -0
- package/src/lib/server/session-tools/sample-ui.ts +1 -1
- package/src/lib/server/session-tools/sandbox.ts +4 -2
- package/src/lib/server/session-tools/schedule.ts +24 -12
- package/src/lib/server/session-tools/session-info.ts +43 -7
- package/src/lib/server/session-tools/session-tools-wiring.test.ts +31 -17
- package/src/lib/server/session-tools/shell.ts +5 -2
- package/src/lib/server/session-tools/subagent.ts +194 -28
- package/src/lib/server/session-tools/table.ts +587 -0
- package/src/lib/server/session-tools/wallet.ts +42 -12
- package/src/lib/server/session-tools/web-browser-config.test.ts +39 -0
- package/src/lib/server/session-tools/web.ts +926 -91
- package/src/lib/server/storage.ts +255 -16
- package/src/lib/server/stream-agent-chat.ts +116 -268
- package/src/lib/server/structured-extract.test.ts +72 -0
- package/src/lib/server/structured-extract.ts +373 -0
- package/src/lib/server/task-mention.test.ts +16 -2
- package/src/lib/server/task-mention.ts +61 -10
- package/src/lib/server/tool-aliases.ts +66 -18
- package/src/lib/server/tool-capability-policy.test.ts +9 -9
- package/src/lib/server/tool-capability-policy.ts +38 -27
- package/src/lib/server/tool-retry.ts +2 -0
- package/src/lib/server/watch-jobs.test.ts +173 -0
- package/src/lib/server/watch-jobs.ts +532 -0
- package/src/lib/server/ws-hub.ts +5 -3
- package/src/lib/tool-definitions.ts +4 -0
- package/src/lib/validation/schemas.test.ts +26 -0
- package/src/lib/validation/schemas.ts +10 -1
- package/src/lib/ws-client.ts +14 -12
- package/src/proxy.ts +5 -5
- package/src/stores/use-app-store.ts +5 -11
- package/src/stores/use-chat-store.ts +38 -9
- package/src/types/index.ts +352 -47
- package/src/app/api/sessions/[id]/main-loop/route.ts +0 -94
- package/src/components/sessions/new-session-sheet.tsx +0 -253
- package/src/lib/server/main-session.ts +0 -24
- package/src/lib/server/session-run-manager.test.ts +0 -23
- /package/src/app/api/{sessions → chats}/[id]/clear/route.ts +0 -0
- /package/src/app/api/{sessions → chats}/[id]/deploy/route.ts +0 -0
- /package/src/app/api/{sessions → chats}/[id]/devserver/route.ts +0 -0
- /package/src/app/api/{sessions → chats}/[id]/edit-resend/route.ts +0 -0
- /package/src/app/api/{sessions → chats}/[id]/fork/route.ts +0 -0
- /package/src/app/api/{sessions → chats}/[id]/mailbox/route.ts +0 -0
- /package/src/app/api/{sessions → chats}/[id]/retry/route.ts +0 -0
- /package/src/app/api/{sessions → chats}/heartbeat/route.ts +0 -0
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
import { tool, type StructuredToolInterface } from '@langchain/core/tools'
|
|
3
|
+
import type { Plugin, PluginHooks } from '@/types'
|
|
4
|
+
import { getPluginManager } from '../plugins'
|
|
5
|
+
import { normalizeToolInputArgs } from './normalize-tool-args'
|
|
6
|
+
import type { ToolBuildContext } from './context'
|
|
7
|
+
|
|
8
|
+
interface ReplicateConfig {
|
|
9
|
+
apiToken: string
|
|
10
|
+
defaultModel: string
|
|
11
|
+
pollingIntervalMs: number
|
|
12
|
+
timeoutMs: number
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function getConfig(): ReplicateConfig {
|
|
16
|
+
const ps = getPluginManager().getPluginSettings('replicate')
|
|
17
|
+
return {
|
|
18
|
+
apiToken: (ps.apiToken as string) || '',
|
|
19
|
+
defaultModel: (ps.defaultModel as string) || '',
|
|
20
|
+
pollingIntervalMs: Number(ps.pollingIntervalMs) || 2000,
|
|
21
|
+
timeoutMs: Number(ps.timeoutMs) || 120000,
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const API_BASE = 'https://api.replicate.com/v1'
|
|
26
|
+
|
|
27
|
+
async function replicateRequest(
|
|
28
|
+
method: string,
|
|
29
|
+
path: string,
|
|
30
|
+
token: string,
|
|
31
|
+
body?: unknown,
|
|
32
|
+
extraHeaders?: Record<string, string>,
|
|
33
|
+
): Promise<{ ok: boolean; data?: unknown; error?: string }> {
|
|
34
|
+
const headers: Record<string, string> = {
|
|
35
|
+
Authorization: `Bearer ${token}`,
|
|
36
|
+
'Content-Type': 'application/json',
|
|
37
|
+
...extraHeaders,
|
|
38
|
+
}
|
|
39
|
+
const init: RequestInit = {
|
|
40
|
+
method,
|
|
41
|
+
headers,
|
|
42
|
+
signal: AbortSignal.timeout(15_000),
|
|
43
|
+
}
|
|
44
|
+
if (body && method !== 'GET' && method !== 'DELETE') {
|
|
45
|
+
init.body = JSON.stringify(body)
|
|
46
|
+
}
|
|
47
|
+
const res = await fetch(`${API_BASE}${path}`, init)
|
|
48
|
+
if (!res.ok) {
|
|
49
|
+
const errText = await res.text().catch(() => '')
|
|
50
|
+
return { ok: false, error: `Replicate ${res.status}: ${errText.slice(0, 400)}` }
|
|
51
|
+
}
|
|
52
|
+
const data = await res.json()
|
|
53
|
+
return { ok: true, data }
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function formatPrediction(p: Record<string, unknown>): Record<string, unknown> {
|
|
57
|
+
return {
|
|
58
|
+
id: p.id,
|
|
59
|
+
model: p.model,
|
|
60
|
+
status: p.status,
|
|
61
|
+
output: p.output,
|
|
62
|
+
error: p.error,
|
|
63
|
+
logs: typeof p.logs === 'string' ? p.logs.slice(-500) : undefined,
|
|
64
|
+
metrics: p.metrics,
|
|
65
|
+
created_at: p.created_at,
|
|
66
|
+
started_at: p.started_at,
|
|
67
|
+
completed_at: p.completed_at,
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async function pollPrediction(token: string, predictionId: string, cfg: ReplicateConfig): Promise<Record<string, unknown>> {
|
|
72
|
+
const deadline = Date.now() + cfg.timeoutMs
|
|
73
|
+
while (Date.now() < deadline) {
|
|
74
|
+
await new Promise((r) => setTimeout(r, cfg.pollingIntervalMs))
|
|
75
|
+
const r = await replicateRequest('GET', `/predictions/${predictionId}`, token)
|
|
76
|
+
if (!r.ok) return { status: 'failed', error: r.error }
|
|
77
|
+
const prediction = r.data as Record<string, unknown>
|
|
78
|
+
if (prediction.status === 'succeeded' || prediction.status === 'failed' || prediction.status === 'canceled') {
|
|
79
|
+
return prediction
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return { status: 'failed', error: 'Prediction timed out.' }
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async function executeReplicate(args: Record<string, unknown>): Promise<string> {
|
|
86
|
+
const normalized = normalizeToolInputArgs(args)
|
|
87
|
+
const action = String(normalized.action || 'run')
|
|
88
|
+
const cfg = getConfig()
|
|
89
|
+
|
|
90
|
+
if (!cfg.apiToken) {
|
|
91
|
+
return 'Error: Replicate API token not configured. Ask the user to add it in Plugin Settings > Replicate.'
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
try {
|
|
95
|
+
switch (action) {
|
|
96
|
+
case 'run': {
|
|
97
|
+
const model = String(normalized.model || cfg.defaultModel || '').trim()
|
|
98
|
+
if (!model) return 'Error: "model" is required (e.g. "stability-ai/sdxl", "meta/llama-2-70b-chat").'
|
|
99
|
+
|
|
100
|
+
const input = (normalized.input as Record<string, unknown>) || {}
|
|
101
|
+
if (typeof normalized.prompt === 'string') {
|
|
102
|
+
input.prompt = normalized.prompt
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const version = typeof normalized.version === 'string' ? normalized.version.trim() : undefined
|
|
106
|
+
|
|
107
|
+
// Build request body
|
|
108
|
+
const body: Record<string, unknown> = { input }
|
|
109
|
+
if (version) {
|
|
110
|
+
body.version = version
|
|
111
|
+
} else {
|
|
112
|
+
body.model = model
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Try sync mode first (Prefer: wait blocks up to 60s)
|
|
116
|
+
const r = await replicateRequest('POST', '/predictions', cfg.apiToken, body, { Prefer: 'wait' })
|
|
117
|
+
if (!r.ok) return `Error: ${r.error}`
|
|
118
|
+
|
|
119
|
+
let prediction = r.data as Record<string, unknown>
|
|
120
|
+
|
|
121
|
+
// If sync didn't complete, poll
|
|
122
|
+
if (prediction.status !== 'succeeded' && prediction.status !== 'failed' && prediction.status !== 'canceled') {
|
|
123
|
+
prediction = await pollPrediction(cfg.apiToken, String(prediction.id), cfg)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (prediction.status === 'failed') {
|
|
127
|
+
return `Prediction failed: ${prediction.error || 'unknown error'}`
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return JSON.stringify(formatPrediction(prediction))
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
case 'get': {
|
|
134
|
+
const predictionId = String(normalized.predictionId || normalized.id || '').trim()
|
|
135
|
+
if (!predictionId) return 'Error: "predictionId" is required.'
|
|
136
|
+
|
|
137
|
+
const r = await replicateRequest('GET', `/predictions/${predictionId}`, cfg.apiToken)
|
|
138
|
+
if (!r.ok) return `Error: ${r.error}`
|
|
139
|
+
return JSON.stringify(formatPrediction(r.data as Record<string, unknown>))
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
case 'cancel': {
|
|
143
|
+
const predictionId = String(normalized.predictionId || normalized.id || '').trim()
|
|
144
|
+
if (!predictionId) return 'Error: "predictionId" is required.'
|
|
145
|
+
|
|
146
|
+
const r = await replicateRequest('POST', `/predictions/${predictionId}/cancel`, cfg.apiToken)
|
|
147
|
+
if (!r.ok) return `Error: ${r.error}`
|
|
148
|
+
return `Prediction ${predictionId} canceled.`
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
case 'get_model': {
|
|
152
|
+
const model = String(normalized.model || '').trim()
|
|
153
|
+
if (!model) return 'Error: "model" is required (e.g. "stability-ai/sdxl").'
|
|
154
|
+
|
|
155
|
+
const r = await replicateRequest('GET', `/models/${model}`, cfg.apiToken)
|
|
156
|
+
if (!r.ok) return `Error: ${r.error}`
|
|
157
|
+
const data = r.data as Record<string, unknown>
|
|
158
|
+
return JSON.stringify({
|
|
159
|
+
owner: data.owner,
|
|
160
|
+
name: data.name,
|
|
161
|
+
description: data.description,
|
|
162
|
+
visibility: data.visibility,
|
|
163
|
+
url: data.url,
|
|
164
|
+
latest_version: data.latest_version ? {
|
|
165
|
+
id: (data.latest_version as Record<string, unknown>).id,
|
|
166
|
+
created_at: (data.latest_version as Record<string, unknown>).created_at,
|
|
167
|
+
} : null,
|
|
168
|
+
run_count: data.run_count,
|
|
169
|
+
})
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
case 'search': {
|
|
173
|
+
const query = String(normalized.query || normalized.search || '').trim()
|
|
174
|
+
const cursor = typeof normalized.cursor === 'string' ? normalized.cursor : undefined
|
|
175
|
+
const params = new URLSearchParams()
|
|
176
|
+
if (query) params.set('query', query)
|
|
177
|
+
if (cursor) params.set('cursor', cursor)
|
|
178
|
+
const suffix = params.toString() ? `?${params}` : ''
|
|
179
|
+
const r = await replicateRequest('GET', `/models${suffix}`, cfg.apiToken)
|
|
180
|
+
if (!r.ok) return `Error: ${r.error}`
|
|
181
|
+
const data = r.data as Record<string, unknown>
|
|
182
|
+
const results = (data.results as Record<string, unknown>[]) ?? []
|
|
183
|
+
const items = results.slice(0, 20).map((m) => ({
|
|
184
|
+
owner: m.owner,
|
|
185
|
+
name: m.name,
|
|
186
|
+
description: typeof m.description === 'string' ? m.description.slice(0, 120) : '',
|
|
187
|
+
run_count: m.run_count,
|
|
188
|
+
url: m.url,
|
|
189
|
+
}))
|
|
190
|
+
return JSON.stringify({
|
|
191
|
+
models: items,
|
|
192
|
+
next_cursor: data.next ? (data.next as string).split('cursor=')[1]?.split('&')[0] : null,
|
|
193
|
+
})
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
case 'status': {
|
|
197
|
+
return JSON.stringify({
|
|
198
|
+
configured: true,
|
|
199
|
+
hasToken: !!cfg.apiToken,
|
|
200
|
+
defaultModel: cfg.defaultModel || '(none)',
|
|
201
|
+
timeoutMs: cfg.timeoutMs,
|
|
202
|
+
})
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
default:
|
|
206
|
+
return `Error: Unknown action "${action}". Use: run, get, cancel, get_model, search, status.`
|
|
207
|
+
}
|
|
208
|
+
} catch (err: unknown) {
|
|
209
|
+
return `Error: ${err instanceof Error ? err.message : String(err)}`
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const ReplicatePlugin: Plugin = {
|
|
214
|
+
name: 'Replicate',
|
|
215
|
+
enabledByDefault: false,
|
|
216
|
+
description: 'Run any AI model on Replicate — image generation, LLMs, audio, video, and more. Search models, create predictions, check status.',
|
|
217
|
+
hooks: {
|
|
218
|
+
getCapabilityDescription: () =>
|
|
219
|
+
'I can run any AI model on Replicate using `replicate`. This includes image generation (SDXL, Flux), language models, audio/video processing, and thousands more. I can search for models, run predictions, and check their status.',
|
|
220
|
+
} as PluginHooks,
|
|
221
|
+
tools: [
|
|
222
|
+
{
|
|
223
|
+
name: 'replicate',
|
|
224
|
+
description: 'Run AI models on Replicate. Actions: run (create and wait for prediction), get (check prediction status), cancel (stop a prediction), get_model (model details), search (find models), status (check config).',
|
|
225
|
+
parameters: {
|
|
226
|
+
type: 'object',
|
|
227
|
+
properties: {
|
|
228
|
+
action: { type: 'string', enum: ['run', 'get', 'cancel', 'get_model', 'search', 'status'], description: 'Action to perform' },
|
|
229
|
+
model: { type: 'string', description: 'Model identifier (e.g. "stability-ai/sdxl", "meta/llama-2-70b-chat"). Required for run/get_model.' },
|
|
230
|
+
version: { type: 'string', description: 'Optional specific model version hash for run.' },
|
|
231
|
+
input: { type: 'object', description: 'Model input parameters as key-value pairs (for run). Varies by model.' },
|
|
232
|
+
prompt: { type: 'string', description: 'Shorthand: sets input.prompt for models that accept a prompt (for run).' },
|
|
233
|
+
predictionId: { type: 'string', description: 'Prediction ID (for get/cancel).' },
|
|
234
|
+
query: { type: 'string', description: 'Search query (for search).' },
|
|
235
|
+
cursor: { type: 'string', description: 'Pagination cursor (for search).' },
|
|
236
|
+
},
|
|
237
|
+
required: ['action'],
|
|
238
|
+
},
|
|
239
|
+
execute: async (args) => executeReplicate(args),
|
|
240
|
+
},
|
|
241
|
+
],
|
|
242
|
+
ui: {
|
|
243
|
+
settingsFields: [
|
|
244
|
+
{
|
|
245
|
+
key: 'apiToken',
|
|
246
|
+
label: 'API Token',
|
|
247
|
+
type: 'secret',
|
|
248
|
+
required: true,
|
|
249
|
+
placeholder: 'r8_...',
|
|
250
|
+
help: 'Your Replicate API token. Find it at replicate.com/account/api-tokens.',
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
key: 'defaultModel',
|
|
254
|
+
label: 'Default Model',
|
|
255
|
+
type: 'text',
|
|
256
|
+
placeholder: 'stability-ai/sdxl',
|
|
257
|
+
help: 'Default model to use when none is specified. Format: owner/model-name.',
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
key: 'timeoutMs',
|
|
261
|
+
label: 'Timeout (ms)',
|
|
262
|
+
type: 'number',
|
|
263
|
+
defaultValue: 120000,
|
|
264
|
+
help: 'Maximum time to wait for a prediction (default: 120000ms / 2 minutes).',
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
key: 'pollingIntervalMs',
|
|
268
|
+
label: 'Polling Interval (ms)',
|
|
269
|
+
type: 'number',
|
|
270
|
+
defaultValue: 2000,
|
|
271
|
+
help: 'How often to poll for prediction results when sync mode times out (default: 2000ms).',
|
|
272
|
+
},
|
|
273
|
+
],
|
|
274
|
+
},
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
getPluginManager().registerBuiltin('replicate', ReplicatePlugin)
|
|
278
|
+
|
|
279
|
+
export function buildReplicateTools(bctx: ToolBuildContext): StructuredToolInterface[] {
|
|
280
|
+
if (!bctx.hasPlugin('replicate')) return []
|
|
281
|
+
|
|
282
|
+
return [
|
|
283
|
+
tool(
|
|
284
|
+
async (args) => executeReplicate(args),
|
|
285
|
+
{
|
|
286
|
+
name: 'replicate',
|
|
287
|
+
description: ReplicatePlugin.tools![0].description,
|
|
288
|
+
schema: z.object({
|
|
289
|
+
action: z.enum(['run', 'get', 'cancel', 'get_model', 'search', 'status']).describe('Action to perform'),
|
|
290
|
+
model: z.string().optional().describe('Model identifier (e.g. "stability-ai/sdxl")'),
|
|
291
|
+
version: z.string().optional().describe('Specific model version hash'),
|
|
292
|
+
input: z.record(z.string(), z.unknown()).optional().describe('Model input parameters'),
|
|
293
|
+
prompt: z.string().optional().describe('Shorthand for input.prompt'),
|
|
294
|
+
predictionId: z.string().optional().describe('Prediction ID (for get/cancel)'),
|
|
295
|
+
query: z.string().optional().describe('Search query (for search)'),
|
|
296
|
+
cursor: z.string().optional().describe('Pagination cursor (for search)'),
|
|
297
|
+
}),
|
|
298
|
+
},
|
|
299
|
+
),
|
|
300
|
+
]
|
|
301
|
+
}
|
|
@@ -80,7 +80,7 @@ const SampleUIPlugin: Plugin = {
|
|
|
80
80
|
getPluginManager().registerBuiltin('sample_ui', SampleUIPlugin)
|
|
81
81
|
|
|
82
82
|
export function buildSampleUITools(bctx: any) {
|
|
83
|
-
if (!bctx.
|
|
83
|
+
if (!bctx.hasPlugin('sample_ui')) return []
|
|
84
84
|
return [
|
|
85
85
|
tool(
|
|
86
86
|
async (args) => SampleUIPlugin.tools![0].execute(args as any, bctx),
|
|
@@ -143,7 +143,9 @@ async function executeListRuntimes() {
|
|
|
143
143
|
const SandboxPlugin: Plugin = {
|
|
144
144
|
name: 'Core Sandbox',
|
|
145
145
|
description: 'Secure isolated code execution for JS, TS, and Python.',
|
|
146
|
-
hooks: {
|
|
146
|
+
hooks: {
|
|
147
|
+
getCapabilityDescription: () => 'I can run code in a sandbox (`sandbox_exec`) — JS/TS via Deno or Python, in an isolated environment. I get stdout, stderr, and any files created.',
|
|
148
|
+
} as PluginHooks,
|
|
147
149
|
tools: [
|
|
148
150
|
{
|
|
149
151
|
name: 'sandbox_exec',
|
|
@@ -174,7 +176,7 @@ getPluginManager().registerBuiltin('sandbox', SandboxPlugin)
|
|
|
174
176
|
* Legacy Bridge
|
|
175
177
|
*/
|
|
176
178
|
export function buildSandboxTools(bctx: ToolBuildContext): StructuredToolInterface[] {
|
|
177
|
-
if (!bctx.
|
|
179
|
+
if (!bctx.hasPlugin('sandbox')) return []
|
|
178
180
|
const tools: StructuredToolInterface[] = []
|
|
179
181
|
|
|
180
182
|
tools.push(
|
|
@@ -6,6 +6,7 @@ import type { ToolBuildContext } from './context'
|
|
|
6
6
|
import type { Plugin, PluginHooks } from '@/types'
|
|
7
7
|
import { getPluginManager } from '../plugins'
|
|
8
8
|
import { normalizeToolInputArgs } from './normalize-tool-args'
|
|
9
|
+
import { createWatchJob } from '../watch-jobs'
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* Core Schedule Execution Logic
|
|
@@ -15,7 +16,7 @@ async function executeScheduleWake(args: { delayMinutes: number; message: string
|
|
|
15
16
|
const delayMinutes = normalized.delayMinutes as number
|
|
16
17
|
const message = normalized.message as string
|
|
17
18
|
if (!context.sessionId) return 'Cannot schedule wake: no session context.'
|
|
18
|
-
if (delayMinutes < 0 || delayMinutes >
|
|
19
|
+
if (delayMinutes < 0 || delayMinutes > 43_200) return 'delayMinutes must be between 0 and 43200 (30 days).'
|
|
19
20
|
|
|
20
21
|
if (delayMinutes === 0) {
|
|
21
22
|
enqueueSystemEvent(context.sessionId, `[Scheduled Wake Event / Reminder] ${message}`)
|
|
@@ -23,15 +24,24 @@ async function executeScheduleWake(args: { delayMinutes: number; message: string
|
|
|
23
24
|
return 'Successfully scheduled an immediate wake event.'
|
|
24
25
|
}
|
|
25
26
|
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
32
|
-
|
|
27
|
+
const runAt = Date.now() + delayMinutes * 60 * 1000
|
|
28
|
+
const watch = await createWatchJob({
|
|
29
|
+
type: 'time',
|
|
30
|
+
sessionId: context.sessionId,
|
|
31
|
+
resumeMessage: message,
|
|
32
|
+
description: `Scheduled wake in ${delayMinutes} minutes`,
|
|
33
|
+
target: { source: 'schedule_wake' },
|
|
34
|
+
condition: {},
|
|
35
|
+
runAt,
|
|
36
|
+
})
|
|
33
37
|
|
|
34
|
-
return
|
|
38
|
+
return JSON.stringify({
|
|
39
|
+
ok: true,
|
|
40
|
+
jobId: watch.id,
|
|
41
|
+
delayMinutes,
|
|
42
|
+
runAt,
|
|
43
|
+
message,
|
|
44
|
+
})
|
|
35
45
|
}
|
|
36
46
|
|
|
37
47
|
/**
|
|
@@ -39,8 +49,10 @@ async function executeScheduleWake(args: { delayMinutes: number; message: string
|
|
|
39
49
|
*/
|
|
40
50
|
const SchedulePlugin: Plugin = {
|
|
41
51
|
name: 'Core Scheduler',
|
|
42
|
-
description: 'Schedule wake events and reminders for agents.',
|
|
43
|
-
hooks: {
|
|
52
|
+
description: 'Schedule durable wake events and reminders for agents.',
|
|
53
|
+
hooks: {
|
|
54
|
+
getCapabilityDescription: () => 'I can set a conversational timer (`schedule_wake`) to remind myself to check back on something later in this chat.',
|
|
55
|
+
} as PluginHooks,
|
|
44
56
|
tools: [
|
|
45
57
|
{
|
|
46
58
|
name: 'schedule_wake',
|
|
@@ -64,7 +76,7 @@ getPluginManager().registerBuiltin('schedule', SchedulePlugin)
|
|
|
64
76
|
* Legacy Bridge
|
|
65
77
|
*/
|
|
66
78
|
export function buildScheduleTools(bctx: ToolBuildContext): StructuredToolInterface[] {
|
|
67
|
-
if (!bctx.
|
|
79
|
+
if (!bctx.hasPlugin('schedule_wake')) return []
|
|
68
80
|
return [
|
|
69
81
|
tool(
|
|
70
82
|
async (args) => executeScheduleWake(args as any, { sessionId: bctx.ctx?.sessionId || undefined }),
|
|
@@ -33,6 +33,7 @@ async function executeSessionsAction(args: any, context: { sessionId?: string; a
|
|
|
33
33
|
const limit = normalized.limit as number | undefined
|
|
34
34
|
const agentId = (normalized.agentId ?? normalized.agent_id) as string | undefined
|
|
35
35
|
const name = normalized.name as string | undefined
|
|
36
|
+
const updates = normalized.updates as Record<string, unknown> | undefined
|
|
36
37
|
try {
|
|
37
38
|
const sessions = loadSessions()
|
|
38
39
|
if (action === 'list') {
|
|
@@ -51,14 +52,45 @@ async function executeSessionsAction(args: any, context: { sessionId?: string; a
|
|
|
51
52
|
const id = genId()
|
|
52
53
|
const now = Date.now()
|
|
53
54
|
sessions[id] = {
|
|
54
|
-
id, name: (name || `${agent.name}
|
|
55
|
+
id, name: (name || `${agent.name} Chat`).trim(), cwd: context.cwd, user: 'system',
|
|
55
56
|
provider: agent.provider, model: agent.model, credentialId: agent.credentialId || null,
|
|
56
|
-
messages: [], createdAt: now, lastActiveAt: now, sessionType: '
|
|
57
|
-
agentId: agent.id, parentSessionId: context.sessionId || undefined,
|
|
57
|
+
messages: [], createdAt: now, lastActiveAt: now, sessionType: 'human',
|
|
58
|
+
agentId: agent.id, parentSessionId: context.sessionId || undefined, plugins: agent.plugins || agent.tools || [],
|
|
58
59
|
}
|
|
59
60
|
saveSessions(sessions)
|
|
60
61
|
return JSON.stringify({ sessionId: id, name: agent.name })
|
|
61
62
|
}
|
|
63
|
+
if (action === 'update') {
|
|
64
|
+
const targetId = sessionId || context.sessionId || ''
|
|
65
|
+
if (!targetId) return 'sessionId required.'
|
|
66
|
+
const target = sessions[targetId]
|
|
67
|
+
if (!target) return 'Not found.'
|
|
68
|
+
const allowedKeys = new Set([
|
|
69
|
+
'thinkingLevel',
|
|
70
|
+
'connectorThinkLevel',
|
|
71
|
+
'sessionResetMode',
|
|
72
|
+
'sessionIdleTimeoutSec',
|
|
73
|
+
'sessionMaxAgeSec',
|
|
74
|
+
'sessionDailyResetAt',
|
|
75
|
+
'sessionResetTimezone',
|
|
76
|
+
'connectorSessionScope',
|
|
77
|
+
'connectorReplyMode',
|
|
78
|
+
'connectorThreadBinding',
|
|
79
|
+
'connectorGroupPolicy',
|
|
80
|
+
'connectorIdleTimeoutSec',
|
|
81
|
+
'connectorMaxAgeSec',
|
|
82
|
+
'identityState',
|
|
83
|
+
'provider',
|
|
84
|
+
'model',
|
|
85
|
+
])
|
|
86
|
+
const patch = updates && typeof updates === 'object' ? updates : {}
|
|
87
|
+
for (const [key, value] of Object.entries(patch)) {
|
|
88
|
+
if (!allowedKeys.has(key)) continue
|
|
89
|
+
target[key] = value
|
|
90
|
+
}
|
|
91
|
+
saveSessions(sessions)
|
|
92
|
+
return JSON.stringify({ sessionId: targetId, updated: Object.keys(patch).filter((key) => allowedKeys.has(key)) })
|
|
93
|
+
}
|
|
62
94
|
return `Unknown action "${action}".`
|
|
63
95
|
} catch (err: any) { return `Error: ${err.message}` }
|
|
64
96
|
}
|
|
@@ -69,7 +101,10 @@ async function executeSessionsAction(args: any, context: { sessionId?: string; a
|
|
|
69
101
|
const SessionInfoPlugin: Plugin = {
|
|
70
102
|
name: 'Core Session Info',
|
|
71
103
|
description: 'Identify current session context and manage other agent sessions.',
|
|
72
|
-
hooks: {
|
|
104
|
+
hooks: {
|
|
105
|
+
getCapabilityDescription: () => 'I can manage chat sessions (`manage_sessions`, `sessions_tool`, `whoami_tool`, `search_history_tool`) — check my identity, look up past conversations, message other sessions, and coordinate work.',
|
|
106
|
+
getOperatingGuidance: () => 'Inspect existing chats before creating duplicates.',
|
|
107
|
+
} as PluginHooks,
|
|
73
108
|
tools: [
|
|
74
109
|
{
|
|
75
110
|
name: 'whoami_tool',
|
|
@@ -83,11 +118,12 @@ const SessionInfoPlugin: Plugin = {
|
|
|
83
118
|
parameters: {
|
|
84
119
|
type: 'object',
|
|
85
120
|
properties: {
|
|
86
|
-
action: { type: 'string', enum: ['list', 'history', 'spawn', 'status', 'stop'] },
|
|
121
|
+
action: { type: 'string', enum: ['list', 'history', 'spawn', 'status', 'stop', 'update'] },
|
|
87
122
|
sessionId: { type: 'string' },
|
|
88
123
|
agentId: { type: 'string' },
|
|
89
124
|
message: { type: 'string' },
|
|
90
|
-
limit: { type: 'number' }
|
|
125
|
+
limit: { type: 'number' },
|
|
126
|
+
updates: { type: 'object' },
|
|
91
127
|
},
|
|
92
128
|
required: ['action']
|
|
93
129
|
},
|
|
@@ -102,7 +138,7 @@ getPluginManager().registerBuiltin('session_info', SessionInfoPlugin)
|
|
|
102
138
|
* Legacy Bridge
|
|
103
139
|
*/
|
|
104
140
|
export function buildSessionInfoTools(bctx: ToolBuildContext): StructuredToolInterface[] {
|
|
105
|
-
if (!bctx.
|
|
141
|
+
if (!bctx.hasPlugin('manage_sessions')) return []
|
|
106
142
|
return [
|
|
107
143
|
tool(
|
|
108
144
|
async () => executeWhoAmI({ sessionId: bctx.ctx?.sessionId || undefined, agentId: bctx.ctx?.agentId || undefined }),
|
|
@@ -24,6 +24,21 @@ describe('module exports', () => {
|
|
|
24
24
|
const mem = await import('./memory')
|
|
25
25
|
assert.equal(typeof mem.buildMemoryTools, 'function')
|
|
26
26
|
})
|
|
27
|
+
|
|
28
|
+
it('primitive tool builders are exported', async () => {
|
|
29
|
+
const document = await import('./document')
|
|
30
|
+
const extract = await import('./extract')
|
|
31
|
+
const table = await import('./table')
|
|
32
|
+
const crawl = await import('./crawl')
|
|
33
|
+
const mailbox = await import('./mailbox')
|
|
34
|
+
const humanLoop = await import('./human-loop')
|
|
35
|
+
assert.equal(typeof document.buildDocumentTools, 'function')
|
|
36
|
+
assert.equal(typeof extract.buildExtractTools, 'function')
|
|
37
|
+
assert.equal(typeof table.buildTableTools, 'function')
|
|
38
|
+
assert.equal(typeof crawl.buildCrawlTools, 'function')
|
|
39
|
+
assert.equal(typeof mailbox.buildMailboxTools, 'function')
|
|
40
|
+
assert.equal(typeof humanLoop.buildHumanLoopTools, 'function')
|
|
41
|
+
})
|
|
27
42
|
})
|
|
28
43
|
|
|
29
44
|
// ---------------------------------------------------------------------------
|
|
@@ -60,46 +75,45 @@ describe('buildSessionTools signature', () => {
|
|
|
60
75
|
})
|
|
61
76
|
|
|
62
77
|
// ---------------------------------------------------------------------------
|
|
63
|
-
// 4. Memory tool schema
|
|
78
|
+
// 4. Memory tool schema
|
|
64
79
|
// buildMemoryTools calls getMemoryDb() eagerly so we cannot invoke it
|
|
65
80
|
// without a real SQLite DB. Instead we read the source and verify the
|
|
66
|
-
// action enum
|
|
81
|
+
// declared action enum matches the current JSON schema definition.
|
|
67
82
|
// ---------------------------------------------------------------------------
|
|
68
83
|
describe('memory tool knowledge actions (source verification)', () => {
|
|
69
|
-
it('action enum in memory.ts includes
|
|
84
|
+
it('action enum in memory.ts includes the declared base actions', async () => {
|
|
70
85
|
const fs = await import('fs')
|
|
71
86
|
const src = fs.readFileSync(
|
|
72
87
|
new URL('./memory.ts', import.meta.url).pathname,
|
|
73
88
|
'utf-8',
|
|
74
89
|
)
|
|
75
90
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
assert.ok(enumMatch, 'Should find a z.enum() for the action field')
|
|
91
|
+
const enumMatch = src.match(/action:\s*\{\s*type:\s*'string',\s*enum:\s*\[([^\]]+)\]/s)
|
|
92
|
+
assert.ok(enumMatch, 'Should find the action enum in the memory tool schema')
|
|
79
93
|
|
|
80
94
|
const enumBody = enumMatch![1]
|
|
81
|
-
|
|
82
|
-
|
|
95
|
+
const expectedActions = ['store', 'get', 'search', 'list', 'delete']
|
|
96
|
+
for (const action of expectedActions) {
|
|
97
|
+
assert.ok(
|
|
98
|
+
enumBody.includes(`'${action}'`),
|
|
99
|
+
`action enum should include '${action}'`,
|
|
100
|
+
)
|
|
101
|
+
}
|
|
83
102
|
})
|
|
84
103
|
|
|
85
|
-
it('action enum
|
|
104
|
+
it('action enum does not advertise removed knowledge actions', async () => {
|
|
86
105
|
const fs = await import('fs')
|
|
87
106
|
const src = fs.readFileSync(
|
|
88
107
|
new URL('./memory.ts', import.meta.url).pathname,
|
|
89
108
|
'utf-8',
|
|
90
109
|
)
|
|
91
110
|
|
|
92
|
-
const enumMatch = src.match(/
|
|
111
|
+
const enumMatch = src.match(/action:\s*\{\s*type:\s*'string',\s*enum:\s*\[([^\]]+)\]/s)
|
|
93
112
|
assert.ok(enumMatch)
|
|
94
113
|
const enumBody = enumMatch![1]
|
|
95
114
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
assert.ok(
|
|
99
|
-
enumBody.includes(`'${action}'`),
|
|
100
|
-
`action enum should include '${action}'`,
|
|
101
|
-
)
|
|
102
|
-
}
|
|
115
|
+
assert.equal(enumBody.includes("'knowledge_store'"), false)
|
|
116
|
+
assert.equal(enumBody.includes("'knowledge_search'"), false)
|
|
103
117
|
})
|
|
104
118
|
})
|
|
105
119
|
|
|
@@ -162,7 +162,10 @@ async function executeShellAction(args: Record<string, unknown>, bctx: { cwd: st
|
|
|
162
162
|
const ShellPlugin: Plugin = {
|
|
163
163
|
name: 'Core Shell',
|
|
164
164
|
description: 'Execute shell commands and manage background processes.',
|
|
165
|
-
hooks: {
|
|
165
|
+
hooks: {
|
|
166
|
+
getCapabilityDescription: () => 'I can run shell commands (`execute_command`) — servers, installs, scripts, git, builds, anything. I can run things in the background for long-lived processes like dev servers.',
|
|
167
|
+
getOperatingGuidance: () => ['Shell: use `execute_command` for servers, installs, scripts, git. Use `background=true` for long-lived processes.', 'Verify servers with `process_tool` status/log and liveness probes before claiming success.', 'Resolve IPs/URLs via shell — never use placeholders. Retry path errors without workdir override.'],
|
|
168
|
+
} as PluginHooks,
|
|
166
169
|
tools: [
|
|
167
170
|
{
|
|
168
171
|
name: 'shell',
|
|
@@ -185,7 +188,7 @@ const ShellPlugin: Plugin = {
|
|
|
185
188
|
getPluginManager().registerBuiltin('shell', ShellPlugin)
|
|
186
189
|
|
|
187
190
|
export function buildShellTools(bctx: ToolBuildContext) {
|
|
188
|
-
if (!bctx.
|
|
191
|
+
if (!bctx.hasPlugin('shell')) return []
|
|
189
192
|
return [
|
|
190
193
|
tool(
|
|
191
194
|
async (args) => executeShellAction(args, { ...bctx.ctx, cwd: bctx.cwd }),
|