@swarmclawai/swarmclaw 0.6.7 → 0.6.8
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 +24 -6
- package/package.json +1 -1
- package/src/app/api/agents/route.ts +1 -0
- package/src/app/api/chatrooms/[id]/chat/route.ts +4 -0
- package/src/app/api/eval/run/route.ts +37 -0
- package/src/app/api/eval/scenarios/route.ts +24 -0
- package/src/app/api/eval/suite/route.ts +29 -0
- package/src/app/api/memory/graph/route.ts +46 -0
- package/src/app/api/sessions/[id]/checkpoints/route.ts +31 -0
- package/src/app/api/sessions/[id]/restore/route.ts +36 -0
- package/src/app/api/souls/[id]/route.ts +65 -0
- package/src/app/api/souls/route.ts +70 -0
- package/src/app/api/tasks/[id]/route.ts +5 -0
- package/src/app/api/tasks/route.ts +2 -0
- package/src/app/api/usage/route.ts +9 -2
- package/src/cli/index.js +24 -0
- package/src/components/agents/agent-sheet.tsx +27 -6
- package/src/components/agents/soul-library-picker.tsx +84 -13
- package/src/components/chat/activity-moment.tsx +2 -0
- package/src/components/chat/checkpoint-timeline.tsx +112 -0
- package/src/components/chat/message-list.tsx +19 -3
- package/src/components/chat/session-debug-panel.tsx +106 -84
- package/src/components/chat/task-approval-card.tsx +78 -0
- package/src/components/chat/tool-call-bubble.tsx +3 -0
- package/src/components/connectors/connector-sheet.tsx +8 -1
- package/src/components/home/home-view.tsx +39 -15
- package/src/components/layout/app-layout.tsx +18 -2
- package/src/components/memory/memory-browser.tsx +73 -45
- package/src/components/memory/memory-graph-view.tsx +203 -0
- package/src/components/plugins/plugin-list.tsx +1 -1
- package/src/components/schedules/schedule-sheet.tsx +9 -2
- package/src/components/shared/hint-tip.tsx +31 -0
- package/src/components/shared/settings/section-runtime-loop.tsx +5 -4
- package/src/components/tasks/approvals-panel.tsx +120 -0
- package/src/components/usage/metrics-dashboard.tsx +25 -3
- package/src/lib/server/chat-execution.ts +96 -12
- package/src/lib/server/chatroom-helpers.ts +63 -5
- package/src/lib/server/chatroom-orchestration.ts +74 -0
- package/src/lib/server/context-manager.ts +132 -50
- package/src/lib/server/daemon-state.ts +70 -1
- package/src/lib/server/eval/runner.ts +126 -0
- package/src/lib/server/eval/scenarios.ts +218 -0
- package/src/lib/server/eval/scorer.ts +96 -0
- package/src/lib/server/eval/store.ts +37 -0
- package/src/lib/server/eval/types.ts +48 -0
- package/src/lib/server/execution-log.ts +12 -8
- package/src/lib/server/guardian.ts +34 -0
- package/src/lib/server/heartbeat-service.ts +53 -1
- package/src/lib/server/langgraph-checkpoint.ts +10 -0
- package/src/lib/server/link-understanding.ts +55 -0
- package/src/lib/server/main-agent-loop.ts +114 -15
- package/src/lib/server/memory-db.ts +18 -7
- package/src/lib/server/mmr.ts +73 -0
- package/src/lib/server/orchestrator-lg.ts +3 -0
- package/src/lib/server/plugins.ts +44 -22
- package/src/lib/server/query-expansion.ts +57 -0
- package/src/lib/server/queue.ts +27 -0
- package/src/lib/server/session-run-manager.ts +21 -1
- package/src/lib/server/session-tools/http.ts +19 -9
- package/src/lib/server/session-tools/index.ts +34 -0
- package/src/lib/server/session-tools/memory.ts +39 -11
- package/src/lib/server/session-tools/schedule.ts +43 -0
- package/src/lib/server/session-tools/web.ts +35 -11
- package/src/lib/server/storage.ts +12 -0
- package/src/lib/server/stream-agent-chat.ts +57 -8
- package/src/lib/server/tool-capability-policy.ts +1 -0
- package/src/lib/server/tool-retry.ts +62 -0
- package/src/lib/server/transcript-repair.ts +72 -0
- package/src/lib/setup-defaults.ts +1 -0
- package/src/lib/tool-definitions.ts +1 -0
- package/src/lib/validation/schemas.ts +1 -0
- package/src/lib/view-routes.ts +1 -0
- package/src/types/index.ts +34 -3
|
@@ -153,6 +153,8 @@ const COLLECTIONS = [
|
|
|
153
153
|
'wallet_balance_history',
|
|
154
154
|
'moderation_logs',
|
|
155
155
|
'connector_health',
|
|
156
|
+
'souls',
|
|
157
|
+
'benchmarks',
|
|
156
158
|
] as const
|
|
157
159
|
|
|
158
160
|
for (const table of COLLECTIONS) {
|
|
@@ -624,6 +626,16 @@ export function saveSchedules(s: Record<string, any>) {
|
|
|
624
626
|
saveCollection('schedules', s)
|
|
625
627
|
}
|
|
626
628
|
|
|
629
|
+
// --- Souls ---
|
|
630
|
+
export const loadSouls = () => loadCollection('souls')
|
|
631
|
+
export const saveSouls = (s: Parameters<typeof saveCollection>[1]) => saveCollection('souls', s)
|
|
632
|
+
export const deleteSoul = (id: string) => deleteCollectionItem('souls', id)
|
|
633
|
+
|
|
634
|
+
// --- Benchmarks ---
|
|
635
|
+
export const loadBenchmarks = () => loadCollection('benchmarks')
|
|
636
|
+
export const saveBenchmarks = (b: Parameters<typeof saveCollection>[1]) => saveCollection('benchmarks', b)
|
|
637
|
+
export const deleteBenchmark = (id: string) => deleteCollectionItem('benchmarks', id)
|
|
638
|
+
|
|
627
639
|
// --- Tasks ---
|
|
628
640
|
export function loadTasks(): Record<string, any> {
|
|
629
641
|
return loadCollection('tasks')
|
|
@@ -59,6 +59,7 @@ function buildToolCapabilityLines(enabledTools: string[], opts?: { platformAssig
|
|
|
59
59
|
if (enabledTools.includes('manage_agents')) lines.push('- I can create and configure other agents (`manage_agents`) — spin up specialists when a task calls for it.')
|
|
60
60
|
if (enabledTools.includes('manage_tasks')) lines.push('- I can manage tasks (`manage_tasks`) — create plans, track progress, and stay organized over time.')
|
|
61
61
|
if (enabledTools.includes('manage_schedules')) lines.push('- I can set up schedules (`manage_schedules`) for recurring work or future follow-ups.')
|
|
62
|
+
if (enabledTools.includes('schedule_wake')) lines.push('- I can set a conversational timer (`schedule_wake`) to remind myself to check back on something later in this chat.')
|
|
62
63
|
if (enabledTools.includes('manage_documents')) lines.push('- I can store and search documents (`manage_documents`) for long-term knowledge and reference.')
|
|
63
64
|
if (enabledTools.includes('manage_webhooks')) lines.push('- I can register webhooks (`manage_webhooks`) so external events can trigger my work automatically.')
|
|
64
65
|
if (enabledTools.includes('manage_skills')) lines.push('- I can manage reusable skills (`manage_skills`) — building blocks I can learn and apply.')
|
|
@@ -77,12 +78,36 @@ function buildToolCapabilityLines(enabledTools: string[], opts?: { platformAssig
|
|
|
77
78
|
return lines
|
|
78
79
|
}
|
|
79
80
|
|
|
81
|
+
/** Detect whether a user message is a broad, high-level goal that benefits from decomposition. */
|
|
82
|
+
function isBroadGoal(text: string): boolean {
|
|
83
|
+
if (text.length < 50) return false
|
|
84
|
+
// Messages with code fences, file paths, or numbered steps are already structured
|
|
85
|
+
if (/```/.test(text)) return false
|
|
86
|
+
if (/\/(src|lib|app|pages|components|api)\//.test(text)) return false
|
|
87
|
+
if (/^\s*\d+[.)]\s/m.test(text)) return false
|
|
88
|
+
// Short direct questions aren't broad goals
|
|
89
|
+
if (text.length < 80 && text.endsWith('?')) return false
|
|
90
|
+
return true
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const GOAL_DECOMPOSITION_BLOCK = [
|
|
94
|
+
'## Goal Decomposition',
|
|
95
|
+
'When you receive a broad, open-ended goal:',
|
|
96
|
+
'1. Break it into 3-7 concrete, sequentially-executable subtasks before taking action.',
|
|
97
|
+
'2. If manage_tasks is available, create a task for each subtask to track progress.',
|
|
98
|
+
'3. Output your plan in a [MAIN_LOOP_PLAN] JSON line: {"steps":["step1","step2",...],"current_step":"step1"}',
|
|
99
|
+
'4. Execute the first subtask immediately — do not stop after planning.',
|
|
100
|
+
'5. After each subtask, update progress and move to the next.',
|
|
101
|
+
].join('\n')
|
|
102
|
+
|
|
80
103
|
function buildAgenticExecutionPolicy(opts: {
|
|
81
104
|
enabledTools: string[]
|
|
82
105
|
loopMode: 'bounded' | 'ongoing'
|
|
83
106
|
heartbeatPrompt: string
|
|
84
107
|
heartbeatIntervalSec: number
|
|
85
108
|
platformAssignScope?: 'self' | 'all'
|
|
109
|
+
userMessage?: string
|
|
110
|
+
hasExistingPlan?: boolean
|
|
86
111
|
}) {
|
|
87
112
|
const hasTooling = opts.enabledTools.length > 0
|
|
88
113
|
const toolLines = buildToolCapabilityLines(opts.enabledTools, { platformAssignScope: opts.platformAssignScope })
|
|
@@ -192,6 +217,8 @@ function buildAgenticExecutionPolicy(opts: {
|
|
|
192
217
|
? `Expected heartbeat cadence is roughly every ${opts.heartbeatIntervalSec} seconds while ongoing work is active.`
|
|
193
218
|
: '',
|
|
194
219
|
toolLines.length ? 'What I can do:\n' + toolLines.join('\n') : '',
|
|
220
|
+
// Inject goal decomposition instructions for broad goals without existing plans
|
|
221
|
+
(opts.userMessage && !opts.hasExistingPlan && isBroadGoal(opts.userMessage)) ? GOAL_DECOMPOSITION_BLOCK : '',
|
|
195
222
|
].filter(Boolean).join('\n')
|
|
196
223
|
}
|
|
197
224
|
|
|
@@ -204,6 +231,7 @@ export interface StreamAgentChatResult {
|
|
|
204
231
|
}
|
|
205
232
|
|
|
206
233
|
export async function streamAgentChat(opts: StreamAgentChatOpts): Promise<StreamAgentChatResult> {
|
|
234
|
+
const startTs = Date.now()
|
|
207
235
|
const { session, message, imagePath, attachedFiles, apiKey, systemPrompt, write, history, fallbackCredentialIds, signal } = opts
|
|
208
236
|
const sessionToolsWithImplicitProcess = Array.from(new Set([
|
|
209
237
|
...(session.tools || []),
|
|
@@ -385,15 +413,17 @@ export async function streamAgentChat(opts: StreamAgentChatOpts): Promise<Stream
|
|
|
385
413
|
'- When I learn something that corrects old knowledge, update or remove the old memory',
|
|
386
414
|
].join('\n'))
|
|
387
415
|
|
|
388
|
-
// Pre-compaction memory flush: nudge agent to
|
|
416
|
+
// Pre-compaction memory flush & Personality Evolution: nudge agent to reflect when conversation is long
|
|
389
417
|
const msgCount = history.filter(m => m.role === 'user' || m.role === 'assistant').length
|
|
390
418
|
if (msgCount > 20) {
|
|
419
|
+
const canEditSelf = (session.tools || []).includes('manage_agents')
|
|
391
420
|
stateModifierParts.push([
|
|
392
|
-
'##
|
|
393
|
-
'This conversation is getting long and I might lose older context soon.
|
|
394
|
-
'important I\'ve learned, decided, or discovered to my memory now
|
|
395
|
-
|
|
396
|
-
|
|
421
|
+
'## Reflection & Consolidation Reminder',
|
|
422
|
+
'This conversation is getting long and I might lose older context soon.',
|
|
423
|
+
'1. **Memory:** I should save anything important I\'ve learned, decided, or discovered to my memory now. Only what matters, not every detail.',
|
|
424
|
+
canEditSelf ? `2. **Personality Evolution:** I should reflect on this conversation. Have my boundaries, tone, or relationship with the user evolved? If so, I MUST use \`manage_agents\` (action: update, id: "${session.agentId}") to update my \`soul\` field with these new learnings.` : '',
|
|
425
|
+
'If there\'s nothing worth saving or updating, carry on.',
|
|
426
|
+
].filter(Boolean).join('\n'))
|
|
397
427
|
}
|
|
398
428
|
} catch {
|
|
399
429
|
// If memory context fails to load, continue without blocking the run.
|
|
@@ -449,7 +479,7 @@ export async function streamAgentChat(opts: StreamAgentChatOpts): Promise<Stream
|
|
|
449
479
|
'web_search', 'web_fetch', 'browser', 'memory',
|
|
450
480
|
'claude_code', 'codex_cli', 'opencode_cli',
|
|
451
481
|
'sandbox', 'create_document', 'create_spreadsheet', 'http_request', 'git', 'wallet',
|
|
452
|
-
'manage_agents', 'manage_tasks', 'manage_schedules', 'manage_skills',
|
|
482
|
+
'manage_agents', 'manage_tasks', 'manage_schedules', 'schedule_wake', 'manage_skills',
|
|
453
483
|
'manage_documents', 'manage_webhooks', 'manage_connectors', 'manage_sessions', 'manage_secrets',
|
|
454
484
|
]
|
|
455
485
|
const disabled = allToolIds.filter((t) => !enabledSet.has(t))
|
|
@@ -475,6 +505,9 @@ export async function streamAgentChat(opts: StreamAgentChatOpts): Promise<Stream
|
|
|
475
505
|
)
|
|
476
506
|
}
|
|
477
507
|
|
|
508
|
+
// Check for existing plan in mainLoopState to skip decomposition injection
|
|
509
|
+
const hasExistingPlan = Array.isArray(session.mainLoopState?.planSteps) && session.mainLoopState.planSteps.length > 0
|
|
510
|
+
|
|
478
511
|
stateModifierParts.push(
|
|
479
512
|
buildAgenticExecutionPolicy({
|
|
480
513
|
enabledTools: sessionToolsWithImplicitProcess,
|
|
@@ -482,10 +515,12 @@ export async function streamAgentChat(opts: StreamAgentChatOpts): Promise<Stream
|
|
|
482
515
|
heartbeatPrompt,
|
|
483
516
|
heartbeatIntervalSec,
|
|
484
517
|
platformAssignScope: agentPlatformAssignScope,
|
|
518
|
+
userMessage: message,
|
|
519
|
+
hasExistingPlan,
|
|
485
520
|
}),
|
|
486
521
|
)
|
|
487
522
|
|
|
488
|
-
|
|
523
|
+
let stateModifier = stateModifierParts.join('\n\n')
|
|
489
524
|
|
|
490
525
|
const { tools, cleanup } = await buildSessionTools(session.cwd, sessionToolsWithImplicitProcess, {
|
|
491
526
|
agentId: session.agentId,
|
|
@@ -613,6 +648,19 @@ export async function streamAgentChat(opts: StreamAgentChatOpts): Promise<Stream
|
|
|
613
648
|
// Context manager failure — continue with full history
|
|
614
649
|
}
|
|
615
650
|
|
|
651
|
+
// Context degradation warning: prepend warning to system prompt when nearing limits
|
|
652
|
+
try {
|
|
653
|
+
const { getContextDegradationWarning, estimateTokens: estTokens } = await import('./context-manager')
|
|
654
|
+
const sysTokens = estTokens(stateModifier)
|
|
655
|
+
const warning = getContextDegradationWarning(effectiveHistory, sysTokens, session.provider, session.model)
|
|
656
|
+
if (warning) {
|
|
657
|
+
stateModifierParts.unshift(warning)
|
|
658
|
+
stateModifier = stateModifierParts.join('\n\n')
|
|
659
|
+
}
|
|
660
|
+
} catch {
|
|
661
|
+
// Warning failure is non-critical
|
|
662
|
+
}
|
|
663
|
+
|
|
616
664
|
// Apply context-clear boundary: slice from most recent context-clear marker
|
|
617
665
|
let contextStart = 0
|
|
618
666
|
for (let i = effectiveHistory.length - 1; i >= 0; i--) {
|
|
@@ -836,6 +884,7 @@ export async function streamAgentChat(opts: StreamAgentChatOpts): Promise<Stream
|
|
|
836
884
|
totalTokens,
|
|
837
885
|
estimatedCost: cost,
|
|
838
886
|
timestamp: Date.now(),
|
|
887
|
+
durationMs: Date.now() - startTs,
|
|
839
888
|
}
|
|
840
889
|
appendUsage(session.id, usageRecord)
|
|
841
890
|
// Send usage metadata to client
|
|
@@ -53,6 +53,7 @@ const TOOL_DESCRIPTORS: Record<string, ToolDescriptor> = {
|
|
|
53
53
|
manage_agents: { categories: ['platform'], concreteTools: ['manage_agents'] },
|
|
54
54
|
manage_tasks: { categories: ['platform'], concreteTools: ['manage_tasks'] },
|
|
55
55
|
manage_schedules: { categories: ['platform'], concreteTools: ['manage_schedules'] },
|
|
56
|
+
schedule_wake: { categories: ['platform'], concreteTools: ['schedule_wake'] },
|
|
56
57
|
manage_skills: { categories: ['platform'], concreteTools: ['manage_skills'] },
|
|
57
58
|
manage_documents: { categories: ['platform'], concreteTools: ['manage_documents'] },
|
|
58
59
|
manage_webhooks: { categories: ['platform', 'network'], concreteTools: ['manage_webhooks'] },
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structured retry with exponential backoff for transient tool failures.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export interface RetryOptions {
|
|
6
|
+
maxAttempts?: number
|
|
7
|
+
backoffMs?: number
|
|
8
|
+
retryable?: RegExp[]
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const DEFAULT_RETRYABLE: RegExp[] = [
|
|
12
|
+
/timeout/i,
|
|
13
|
+
/ECONNRESET/i,
|
|
14
|
+
/ENOTFOUND/i,
|
|
15
|
+
/429/,
|
|
16
|
+
/503/,
|
|
17
|
+
/rate.?limit/i,
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
const DEFAULT_MAX_ATTEMPTS = 3
|
|
21
|
+
const DEFAULT_BACKOFF_MS = 2000
|
|
22
|
+
|
|
23
|
+
function isRetryableError(error: string, patterns: RegExp[]): boolean {
|
|
24
|
+
return patterns.some((p) => p.test(error))
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function sleep(ms: number): Promise<void> {
|
|
28
|
+
return new Promise((resolve) => setTimeout(resolve, ms))
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Wraps a tool handler function with retry logic for transient failures.
|
|
33
|
+
* The wrapped function must return a string (tool output).
|
|
34
|
+
* Retries only when the returned string matches a retryable pattern
|
|
35
|
+
* (tool handlers typically return error strings rather than throwing).
|
|
36
|
+
*/
|
|
37
|
+
export async function withRetry<TArgs>(
|
|
38
|
+
fn: (args: TArgs) => Promise<string>,
|
|
39
|
+
args: TArgs,
|
|
40
|
+
opts?: RetryOptions,
|
|
41
|
+
): Promise<string> {
|
|
42
|
+
const maxAttempts = opts?.maxAttempts ?? DEFAULT_MAX_ATTEMPTS
|
|
43
|
+
const backoffMs = opts?.backoffMs ?? DEFAULT_BACKOFF_MS
|
|
44
|
+
const retryable = opts?.retryable ?? DEFAULT_RETRYABLE
|
|
45
|
+
|
|
46
|
+
let lastResult = ''
|
|
47
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
48
|
+
lastResult = await fn(args)
|
|
49
|
+
|
|
50
|
+
// Only retry if the result looks like a retryable error
|
|
51
|
+
if (attempt < maxAttempts && isRetryableError(lastResult, retryable)) {
|
|
52
|
+
const delay = backoffMs * Math.pow(2, attempt - 1)
|
|
53
|
+
console.warn(
|
|
54
|
+
`[tool-retry] Attempt ${attempt}/${maxAttempts} matched retryable pattern, retrying in ${delay}ms`,
|
|
55
|
+
)
|
|
56
|
+
await sleep(delay)
|
|
57
|
+
continue
|
|
58
|
+
}
|
|
59
|
+
return lastResult
|
|
60
|
+
}
|
|
61
|
+
return lastResult
|
|
62
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { Message } from '@/types'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Repairs a conversation transcript by ensuring that tool events remain associated
|
|
5
|
+
* with their parent assistant messages during pruning or manipulation.
|
|
6
|
+
*
|
|
7
|
+
* In SwarmClaw, toolEvents are nested within the Message object, so "orphaning"
|
|
8
|
+
* is less of a structural risk than in OpenClaw, but we still need to ensure
|
|
9
|
+
* consistency during context management.
|
|
10
|
+
*/
|
|
11
|
+
export function repairTranscriptConsistency(messages: Message[]): Message[] {
|
|
12
|
+
// SwarmClaw specific: ensure that 'system' messages like [Context Summary]
|
|
13
|
+
// are preserved correctly and that nested toolEvents are valid.
|
|
14
|
+
return messages.map(m => {
|
|
15
|
+
if (m.role === 'assistant' && m.toolEvents) {
|
|
16
|
+
// Filter out empty or malformed tool events that might cause LLM confusion
|
|
17
|
+
const validTools = m.toolEvents.filter(t => t.name && t.input)
|
|
18
|
+
if (validTools.length !== m.toolEvents.length) {
|
|
19
|
+
return { ...m, toolEvents: validTools }
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return m
|
|
23
|
+
})
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Checks for and repairs common transcript issues that cause LLM provider errors.
|
|
28
|
+
* (e.g. consecutive user messages, trailing assistant messages without text).
|
|
29
|
+
*/
|
|
30
|
+
export function finalProviderTranscriptSanityCheck(messages: Message[]): Message[] {
|
|
31
|
+
if (messages.length === 0) return []
|
|
32
|
+
|
|
33
|
+
const out: Message[] = []
|
|
34
|
+
for (let i = 0; i < messages.length; i++) {
|
|
35
|
+
const m = messages[i]
|
|
36
|
+
|
|
37
|
+
// 1. Skip messages marked as suppressed
|
|
38
|
+
if (m.suppressed) continue
|
|
39
|
+
|
|
40
|
+
// 2. Prevent consecutive messages of same role (some providers are strict)
|
|
41
|
+
const prev = out.at(-1)
|
|
42
|
+
if (prev && prev.role === m.role) {
|
|
43
|
+
if (m.role === 'user') {
|
|
44
|
+
// Merge consecutive user messages
|
|
45
|
+
prev.text = `${prev.text}\n\n${m.text}`
|
|
46
|
+
if (m.imagePath) prev.imagePath = m.imagePath
|
|
47
|
+
if (m.imageUrl) prev.imageUrl = m.imageUrl
|
|
48
|
+
continue
|
|
49
|
+
} else {
|
|
50
|
+
// Assistant consecutive? Keep the one with tool events or the longer one
|
|
51
|
+
const mTools = m.toolEvents?.length || 0
|
|
52
|
+
const pTools = prev.toolEvents?.length || 0
|
|
53
|
+
if (mTools > pTools || m.text.length > prev.text.length) {
|
|
54
|
+
out[out.length - 1] = m
|
|
55
|
+
}
|
|
56
|
+
continue
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
out.push(m)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// 3. Ensure the transcript doesn't end with an empty assistant message
|
|
64
|
+
if (out.length > 0 && out.at(-1)?.role === 'assistant') {
|
|
65
|
+
const last = out.at(-1)!
|
|
66
|
+
if (!last.text.trim() && (!last.toolEvents || last.toolEvents.length === 0)) {
|
|
67
|
+
out.pop()
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return out
|
|
72
|
+
}
|
|
@@ -31,6 +31,7 @@ export const PLATFORM_TOOLS: ToolDefinition[] = [
|
|
|
31
31
|
{ id: 'manage_agents', label: 'Agents', description: 'Create, edit, and delete agents' },
|
|
32
32
|
{ id: 'manage_tasks', label: 'Tasks', description: 'Create, edit, and delete tasks' },
|
|
33
33
|
{ id: 'manage_schedules', label: 'Schedules', description: 'Create, edit, and delete schedules' },
|
|
34
|
+
{ id: 'schedule_wake', label: 'Reminders', description: 'Schedule a proactive wake event in the current chat' },
|
|
34
35
|
{ id: 'manage_skills', label: 'Skills', description: 'Create, edit, and delete skills' },
|
|
35
36
|
{ id: 'manage_documents', label: 'Documents', description: 'Upload, search, and delete indexed documents' },
|
|
36
37
|
{ id: 'manage_webhooks', label: 'Webhooks', description: 'Register webhooks that trigger agent workflows' },
|
|
@@ -14,6 +14,7 @@ export const AgentCreateSchema = z.object({
|
|
|
14
14
|
capabilities: z.array(z.string()).optional().default([]),
|
|
15
15
|
thinkingLevel: z.string().optional(),
|
|
16
16
|
soul: z.string().optional(),
|
|
17
|
+
autoRecovery: z.boolean().optional().default(false),
|
|
17
18
|
})
|
|
18
19
|
|
|
19
20
|
export const ConnectorCreateSchema = z.object({
|
package/src/lib/view-routes.ts
CHANGED
package/src/types/index.ts
CHANGED
|
@@ -75,6 +75,8 @@ export interface Session {
|
|
|
75
75
|
heartbeatIntervalSec?: number | null
|
|
76
76
|
heartbeatTarget?: 'last' | 'none' | string | null
|
|
77
77
|
lastAutoMemoryAt?: number | null
|
|
78
|
+
lastHeartbeatText?: string | null
|
|
79
|
+
lastHeartbeatSentAt?: number | null
|
|
78
80
|
mainLoopState?: {
|
|
79
81
|
goal?: string | null
|
|
80
82
|
goalContract?: GoalContract | null
|
|
@@ -102,6 +104,8 @@ export interface Session {
|
|
|
102
104
|
note: string
|
|
103
105
|
status?: 'idle' | 'progress' | 'blocked' | 'ok'
|
|
104
106
|
}>
|
|
107
|
+
missionTokens?: number
|
|
108
|
+
missionCostUsd?: number
|
|
105
109
|
followupChainCount?: number
|
|
106
110
|
metaMissCount?: number
|
|
107
111
|
workingMemoryNotes?: string[]
|
|
@@ -116,6 +120,11 @@ export interface Session {
|
|
|
116
120
|
queuedCount?: number
|
|
117
121
|
currentRunId?: string | null
|
|
118
122
|
conversationTone?: string
|
|
123
|
+
emoji?: string
|
|
124
|
+
creature?: string
|
|
125
|
+
vibe?: string
|
|
126
|
+
theme?: string
|
|
127
|
+
avatar?: string
|
|
119
128
|
canvasContent?: string | null
|
|
120
129
|
}
|
|
121
130
|
|
|
@@ -148,6 +157,7 @@ export interface UsageRecord {
|
|
|
148
157
|
totalTokens: number
|
|
149
158
|
estimatedCost: number
|
|
150
159
|
timestamp: number
|
|
160
|
+
durationMs?: number
|
|
151
161
|
}
|
|
152
162
|
|
|
153
163
|
// --- Plugin System ---
|
|
@@ -155,15 +165,27 @@ export interface UsageRecord {
|
|
|
155
165
|
export interface PluginHooks {
|
|
156
166
|
beforeAgentStart?: (ctx: { session: Session; message: string }) => Promise<void> | void
|
|
157
167
|
afterAgentComplete?: (ctx: { session: Session; response: string }) => Promise<void> | void
|
|
158
|
-
beforeToolExec?: (ctx: { toolName: string; input:
|
|
159
|
-
afterToolExec?: (ctx: { toolName: string; input:
|
|
168
|
+
beforeToolExec?: (ctx: { toolName: string; input: Record<string, unknown> | null }) => Promise<Record<string, unknown> | void> | Record<string, unknown> | void
|
|
169
|
+
afterToolExec?: (ctx: { toolName: string; input: Record<string, unknown> | null; output: string }) => Promise<void> | void
|
|
160
170
|
onMessage?: (ctx: { session: Session; message: Message }) => Promise<void> | void
|
|
171
|
+
|
|
172
|
+
// Orchestration & Swarm Hooks
|
|
173
|
+
onTaskComplete?: (ctx: { taskId: string; result: unknown }) => Promise<void> | void
|
|
174
|
+
onAgentDelegation?: (ctx: { sourceAgentId: string; targetAgentId: string; task: string }) => Promise<void> | void
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export interface PluginToolDef {
|
|
178
|
+
name: string
|
|
179
|
+
description: string
|
|
180
|
+
parameters: Record<string, unknown>
|
|
181
|
+
execute: (args: Record<string, unknown>, ctx: { session: Session; message: string }) => Promise<string | object> | string | object
|
|
161
182
|
}
|
|
162
183
|
|
|
163
184
|
export interface Plugin {
|
|
164
185
|
name: string
|
|
165
186
|
description?: string
|
|
166
187
|
hooks: PluginHooks
|
|
188
|
+
tools?: PluginToolDef[]
|
|
167
189
|
}
|
|
168
190
|
|
|
169
191
|
export interface PluginMeta {
|
|
@@ -233,6 +255,11 @@ export interface Agent {
|
|
|
233
255
|
name: string
|
|
234
256
|
description: string
|
|
235
257
|
soul?: string
|
|
258
|
+
emoji?: string
|
|
259
|
+
creature?: string
|
|
260
|
+
vibe?: string
|
|
261
|
+
theme?: string
|
|
262
|
+
avatar?: string
|
|
236
263
|
systemPrompt: string
|
|
237
264
|
provider: ProviderType
|
|
238
265
|
model: string
|
|
@@ -273,9 +300,12 @@ export interface Agent {
|
|
|
273
300
|
openclawAllowedSkills?: string[]
|
|
274
301
|
walletId?: string | null
|
|
275
302
|
monthlyBudget?: number | null
|
|
303
|
+
autoRecovery?: boolean
|
|
304
|
+
|
|
276
305
|
budgetAction?: 'warn' | 'block'
|
|
277
306
|
/** Runtime-enriched: current month's spend. Populated by GET /api/agents when monthlyBudget is set. */
|
|
278
307
|
monthlySpend?: number
|
|
308
|
+
maxFollowupChain?: number
|
|
279
309
|
createdAt: number
|
|
280
310
|
updatedAt: number
|
|
281
311
|
}
|
|
@@ -408,7 +438,7 @@ export interface MemoryEntry {
|
|
|
408
438
|
}
|
|
409
439
|
|
|
410
440
|
export type SessionType = 'human' | 'orchestrated'
|
|
411
|
-
export type AppView = 'home' | 'agents' | 'chatrooms' | 'schedules' | 'memory' | 'tasks' | 'secrets' | 'providers' | 'skills' | 'connectors' | 'webhooks' | 'mcp_servers' | 'knowledge' | 'plugins' | 'usage' | 'wallets' | 'runs' | 'logs' | 'settings' | 'projects' | 'activity'
|
|
441
|
+
export type AppView = 'home' | 'agents' | 'chatrooms' | 'schedules' | 'memory' | 'tasks' | 'approvals' | 'secrets' | 'providers' | 'skills' | 'connectors' | 'webhooks' | 'mcp_servers' | 'knowledge' | 'plugins' | 'usage' | 'wallets' | 'runs' | 'logs' | 'settings' | 'projects' | 'activity'
|
|
412
442
|
|
|
413
443
|
// --- Chatrooms ---
|
|
414
444
|
|
|
@@ -581,6 +611,7 @@ export interface AppSettings {
|
|
|
581
611
|
legacyOrchestratorMaxTurns?: number
|
|
582
612
|
ongoingLoopMaxIterations?: number
|
|
583
613
|
ongoingLoopMaxRuntimeMinutes?: number
|
|
614
|
+
maxFollowupChain?: number
|
|
584
615
|
shellCommandTimeoutSec?: number
|
|
585
616
|
claudeCodeTimeoutSec?: number
|
|
586
617
|
cliProcessTimeoutSec?: number
|