@swarmclawai/swarmclaw 1.2.0 → 1.2.2
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 +19 -0
- package/package.json +5 -2
- package/skills/coding-agent/SKILL.md +111 -0
- package/skills/github/SKILL.md +140 -0
- package/skills/nano-banana-pro/SKILL.md +62 -0
- package/skills/nano-banana-pro/scripts/generate_image.py +235 -0
- package/skills/nano-pdf/SKILL.md +53 -0
- package/skills/openai-image-gen/SKILL.md +78 -0
- package/skills/openai-image-gen/scripts/gen.py +328 -0
- package/skills/resourceful-problem-solving/SKILL.md +49 -0
- package/skills/skill-creator/SKILL.md +147 -0
- package/skills/skill-creator/scripts/init_skill.py +378 -0
- package/skills/skill-creator/scripts/quick_validate.py +159 -0
- package/skills/summarize/SKILL.md +77 -0
- package/src/app/api/auth/route.ts +20 -5
- package/src/app/api/chats/[id]/deploy/route.ts +11 -6
- package/src/app/api/chats/[id]/devserver/route.ts +17 -20
- package/src/app/api/chats/[id]/messages/route.ts +15 -11
- package/src/app/api/chats/[id]/route.ts +9 -10
- package/src/app/api/chats/[id]/stop/route.ts +5 -7
- package/src/app/api/chats/messages-route.test.ts +8 -6
- package/src/app/api/chats/route.ts +9 -10
- package/src/app/api/credentials/[id]/route.ts +4 -1
- package/src/app/api/extensions/marketplace/route.ts +5 -2
- package/src/app/api/ip/route.ts +2 -2
- package/src/app/api/memory/maintenance/route.ts +5 -2
- package/src/app/api/preview-server/route.ts +15 -12
- package/src/app/api/projects/[id]/route.ts +7 -46
- package/src/app/api/system/status/route.ts +11 -0
- package/src/app/api/upload/route.ts +4 -1
- package/src/cli/index.js +7 -0
- package/src/cli/spec.js +1 -0
- package/src/components/agents/agent-files-editor.tsx +44 -32
- package/src/components/agents/personality-builder.tsx +13 -7
- package/src/components/agents/trash-list.tsx +1 -1
- package/src/components/chat/chat-area.tsx +45 -23
- package/src/components/chat/message-bubble.test.ts +35 -0
- package/src/components/chat/message-bubble.tsx +20 -9
- package/src/components/chat/message-list.tsx +62 -42
- package/src/components/chat/swarm-status-card.tsx +10 -3
- package/src/components/input/chat-input.tsx +34 -14
- package/src/components/layout/daemon-indicator.tsx +7 -8
- package/src/components/layout/update-banner.tsx +8 -13
- package/src/components/logs/log-list.tsx +1 -1
- package/src/components/memory/memory-card.tsx +3 -1
- package/src/components/org-chart/org-chart-view.tsx +4 -0
- package/src/components/projects/project-list.tsx +4 -2
- package/src/components/projects/tabs/overview-tab.tsx +3 -2
- package/src/components/secrets/secret-sheet.tsx +1 -1
- package/src/components/secrets/secrets-list.tsx +1 -1
- package/src/components/shared/agent-switch-dialog.tsx +12 -6
- package/src/components/shared/dir-browser.tsx +22 -18
- package/src/components/skills/skill-sheet.tsx +2 -3
- package/src/components/tasks/task-list.tsx +1 -1
- package/src/components/tasks/task-sheet.tsx +1 -1
- package/src/hooks/use-openclaw-gateway.ts +46 -27
- package/src/instrumentation.ts +10 -7
- package/src/lib/chat/assistant-render-id.ts +3 -0
- package/src/lib/chat/chat-streaming-state.test.ts +42 -3
- package/src/lib/chat/chat-streaming-state.ts +20 -8
- package/src/lib/chat/chat.ts +18 -2
- package/src/lib/chat/queued-message-queue.test.ts +23 -1
- package/src/lib/chat/queued-message-queue.ts +11 -2
- package/src/lib/providers/anthropic.ts +6 -3
- package/src/lib/providers/claude-cli.ts +9 -3
- package/src/lib/providers/cli-utils.test.ts +124 -0
- package/src/lib/providers/cli-utils.ts +15 -0
- package/src/lib/providers/codex-cli.ts +9 -3
- package/src/lib/providers/gemini-cli.ts +6 -2
- package/src/lib/providers/index.ts +4 -1
- package/src/lib/providers/ollama.ts +5 -2
- package/src/lib/providers/openai.ts +8 -5
- package/src/lib/providers/opencode-cli.ts +6 -2
- package/src/lib/server/activity/activity-log.ts +21 -0
- package/src/lib/server/agents/agent-availability.test.ts +10 -5
- package/src/lib/server/agents/agent-cascade.ts +79 -59
- package/src/lib/server/agents/agent-registry.ts +23 -4
- package/src/lib/server/agents/agent-repository.ts +90 -0
- package/src/lib/server/agents/delegation-job-repository.ts +53 -0
- package/src/lib/server/agents/delegation-jobs.ts +11 -4
- package/src/lib/server/agents/guardian-checkpoint-repository.ts +35 -0
- package/src/lib/server/agents/guardian.ts +2 -2
- package/src/lib/server/agents/main-agent-loop.ts +14 -6
- package/src/lib/server/agents/main-loop-state-repository.ts +38 -0
- package/src/lib/server/agents/subagent-runtime.ts +9 -6
- package/src/lib/server/agents/subagent-swarm.ts +3 -2
- package/src/lib/server/agents/task-session.ts +3 -4
- package/src/lib/server/approvals/approval-repository.ts +30 -0
- package/src/lib/server/autonomy/supervisor-incident-repository.ts +42 -0
- package/src/lib/server/autonomy/supervisor-reflection.ts +14 -1
- package/src/lib/server/chat-execution/chat-execution-types.ts +38 -0
- package/src/lib/server/chat-execution/chat-execution-utils.ts +1 -1
- package/src/lib/server/chat-execution/chat-execution.ts +84 -1914
- package/src/lib/server/chat-execution/chat-turn-finalization.ts +620 -0
- package/src/lib/server/chat-execution/chat-turn-partial-persistence.ts +221 -0
- package/src/lib/server/chat-execution/chat-turn-preflight.ts +133 -0
- package/src/lib/server/chat-execution/chat-turn-preparation.ts +817 -0
- package/src/lib/server/chat-execution/chat-turn-stream-execution.ts +296 -0
- package/src/lib/server/chat-execution/chat-turn-tool-routing.ts +5 -5
- package/src/lib/server/chat-execution/continuation-evaluator.ts +4 -3
- package/src/lib/server/chat-execution/continuation-limits.ts +6 -3
- package/src/lib/server/chat-execution/message-classifier.test.ts +329 -0
- package/src/lib/server/chat-execution/message-classifier.ts +5 -2
- package/src/lib/server/chat-execution/post-stream-finalization.ts +5 -2
- package/src/lib/server/chat-execution/prompt-builder.ts +22 -1
- package/src/lib/server/chat-execution/prompt-sections.ts +55 -13
- package/src/lib/server/chat-execution/response-completeness.ts +5 -2
- package/src/lib/server/chat-execution/situational-awareness.ts +12 -7
- package/src/lib/server/chat-execution/stream-agent-chat.ts +58 -25
- package/src/lib/server/chatrooms/chatroom-memory-bridge.ts +6 -3
- package/src/lib/server/chatrooms/chatroom-repository.ts +32 -0
- package/src/lib/server/connectors/bluebubbles.ts +7 -4
- package/src/lib/server/connectors/connector-inbound.ts +16 -13
- package/src/lib/server/connectors/connector-lifecycle.ts +11 -8
- package/src/lib/server/connectors/connector-outbound.ts +6 -3
- package/src/lib/server/connectors/connector-repository.ts +58 -0
- package/src/lib/server/connectors/discord.ts +10 -7
- package/src/lib/server/connectors/email.ts +17 -14
- package/src/lib/server/connectors/googlechat.ts +7 -4
- package/src/lib/server/connectors/inbound-audio-transcription.ts +5 -2
- package/src/lib/server/connectors/matrix.ts +6 -3
- package/src/lib/server/connectors/openclaw.ts +20 -17
- package/src/lib/server/connectors/outbox.ts +4 -1
- package/src/lib/server/connectors/runtime-state.test.ts +117 -0
- package/src/lib/server/connectors/runtime-state.ts +19 -0
- package/src/lib/server/connectors/session-consolidation.ts +5 -2
- package/src/lib/server/connectors/signal.ts +9 -6
- package/src/lib/server/connectors/slack.ts +13 -10
- package/src/lib/server/connectors/teams.ts +8 -5
- package/src/lib/server/connectors/telegram.ts +15 -12
- package/src/lib/server/connectors/whatsapp.ts +32 -29
- package/src/lib/server/credentials/credential-repository.ts +7 -0
- package/src/lib/server/embeddings.ts +4 -1
- package/src/lib/server/gateways/gateway-profile-repository.ts +4 -0
- package/src/lib/server/link-understanding.ts +4 -1
- package/src/lib/server/memory/memory-abstract.test.ts +59 -0
- package/src/lib/server/memory/memory-abstract.ts +59 -0
- package/src/lib/server/memory/memory-db.ts +40 -14
- package/src/lib/server/missions/mission-repository.ts +74 -0
- package/src/lib/server/missions/mission-service/actions.ts +6 -0
- package/src/lib/server/missions/mission-service/bindings.ts +9 -0
- package/src/lib/server/missions/mission-service/context.ts +4 -0
- package/src/lib/server/missions/mission-service/core.ts +2269 -0
- package/src/lib/server/missions/mission-service/queries.ts +12 -0
- package/src/lib/server/missions/mission-service/recovery.ts +5 -0
- package/src/lib/server/missions/mission-service/ticks.ts +9 -0
- package/src/lib/server/missions/mission-service.test.ts +9 -2
- package/src/lib/server/missions/mission-service.ts +6 -2263
- package/src/lib/server/openclaw/gateway.ts +8 -5
- package/src/lib/server/persistence/repository-utils.ts +154 -0
- package/src/lib/server/persistence/storage-context.ts +51 -0
- package/src/lib/server/persistence/transaction.ts +1 -0
- package/src/lib/server/project-utils.ts +13 -0
- package/src/lib/server/projects/project-repository.ts +36 -0
- package/src/lib/server/projects/project-service.ts +79 -0
- package/src/lib/server/protocols/protocol-agent-turn.ts +5 -2
- package/src/lib/server/protocols/protocol-normalization.test.ts +6 -4
- package/src/lib/server/protocols/protocol-run-lifecycle.ts +5 -2
- package/src/lib/server/protocols/protocol-step-helpers.ts +4 -1
- package/src/lib/server/provider-health.ts +18 -0
- package/src/lib/server/query-expansion.ts +4 -1
- package/src/lib/server/runtime/alert-dispatch.ts +8 -7
- package/src/lib/server/runtime/daemon-policy.ts +1 -1
- package/src/lib/server/runtime/daemon-state/core.ts +1570 -0
- package/src/lib/server/runtime/daemon-state/health.ts +6 -0
- package/src/lib/server/runtime/daemon-state/policy.ts +7 -0
- package/src/lib/server/runtime/daemon-state/supervisor.ts +6 -0
- package/src/lib/server/runtime/daemon-state.test.ts +48 -0
- package/src/lib/server/runtime/daemon-state.ts +3 -1331
- package/src/lib/server/runtime/estop-repository.ts +4 -0
- package/src/lib/server/runtime/estop.ts +3 -1
- package/src/lib/server/runtime/heartbeat-service.test.ts +2 -2
- package/src/lib/server/runtime/heartbeat-service.ts +78 -34
- package/src/lib/server/runtime/heartbeat-wake.ts +6 -4
- package/src/lib/server/runtime/idle-window.ts +6 -3
- package/src/lib/server/runtime/network.ts +11 -0
- package/src/lib/server/runtime/orchestrator-events.ts +2 -2
- package/src/lib/server/runtime/perf.ts +4 -1
- package/src/lib/server/runtime/process-manager.ts +7 -4
- package/src/lib/server/runtime/queue/claims.ts +4 -0
- package/src/lib/server/runtime/queue/core.ts +2079 -0
- package/src/lib/server/runtime/queue/execution.ts +7 -0
- package/src/lib/server/runtime/queue/followups.ts +4 -0
- package/src/lib/server/runtime/queue/queries.ts +12 -0
- package/src/lib/server/runtime/queue/recovery.ts +7 -0
- package/src/lib/server/runtime/queue-recovery.test.ts +48 -13
- package/src/lib/server/runtime/queue-repository.ts +17 -0
- package/src/lib/server/runtime/queue.ts +5 -2058
- package/src/lib/server/runtime/run-ledger.ts +6 -5
- package/src/lib/server/runtime/run-repository.ts +73 -0
- package/src/lib/server/runtime/runtime-lock-repository.ts +8 -0
- package/src/lib/server/runtime/runtime-settings.ts +1 -1
- package/src/lib/server/runtime/runtime-state.ts +99 -0
- package/src/lib/server/runtime/scheduler.ts +13 -8
- package/src/lib/server/runtime/session-run-manager/cancellation.ts +157 -0
- package/src/lib/server/runtime/session-run-manager/drain.ts +246 -0
- package/src/lib/server/runtime/session-run-manager/enqueue.ts +287 -0
- package/src/lib/server/runtime/session-run-manager/queries.ts +117 -0
- package/src/lib/server/runtime/session-run-manager/recovery.ts +238 -0
- package/src/lib/server/runtime/session-run-manager/state.ts +441 -0
- package/src/lib/server/runtime/session-run-manager/types.ts +74 -0
- package/src/lib/server/runtime/session-run-manager.ts +72 -1374
- package/src/lib/server/runtime/watch-job-repository.ts +35 -0
- package/src/lib/server/runtime/watch-jobs.ts +3 -1
- package/src/lib/server/sandbox/bridge-auth-registry.ts +6 -0
- package/src/lib/server/sandbox/novnc-auth.ts +10 -0
- package/src/lib/server/schedules/schedule-repository.ts +42 -0
- package/src/lib/server/session-tools/context.ts +14 -0
- package/src/lib/server/session-tools/discovery.ts +9 -6
- package/src/lib/server/session-tools/index.ts +3 -1
- package/src/lib/server/session-tools/platform.ts +1 -1
- package/src/lib/server/session-tools/subagent.ts +23 -2
- package/src/lib/server/session-tools/wallet.ts +4 -1
- package/src/lib/server/sessions/session-repository.ts +85 -0
- package/src/lib/server/settings/settings-repository.ts +25 -0
- package/src/lib/server/skills/clawhub-client.ts +4 -1
- package/src/lib/server/skills/runtime-skill-resolver.ts +8 -2
- package/src/lib/server/skills/skill-discovery.test.ts +2 -2
- package/src/lib/server/skills/skill-discovery.ts +2 -2
- package/src/lib/server/skills/skill-eligibility.ts +6 -0
- package/src/lib/server/skills/skill-repository.ts +14 -0
- package/src/lib/server/solana.ts +6 -0
- package/src/lib/server/storage-auth.ts +5 -5
- package/src/lib/server/storage-normalization.ts +4 -0
- package/src/lib/server/storage.ts +32 -32
- package/src/lib/server/tasks/task-followups.ts +4 -1
- package/src/lib/server/tasks/task-repository.ts +54 -0
- package/src/lib/server/tool-loop-detection.ts +8 -3
- package/src/lib/server/tool-planning.ts +226 -0
- package/src/lib/server/tool-retry.ts +4 -3
- package/src/lib/server/usage/usage-repository.ts +30 -0
- package/src/lib/server/wallet/wallet-portfolio.ts +29 -0
- package/src/lib/server/webhooks/webhook-repository.ts +10 -0
- package/src/lib/server/ws-hub.ts +5 -2
- package/src/lib/strip-internal-metadata.test.ts +78 -37
- package/src/lib/strip-internal-metadata.ts +20 -6
- package/src/stores/use-approval-store.ts +7 -1
- package/src/stores/use-chat-store.test.ts +54 -0
- package/src/stores/use-chat-store.ts +26 -6
- package/src/types/index.ts +6 -0
- /package/{bundled-skills → skills}/google-workspace/SKILL.md +0 -0
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import fs from 'fs'
|
|
2
2
|
import path from 'path'
|
|
3
3
|
import crypto from 'crypto'
|
|
4
|
-
import os from 'os'
|
|
5
|
-
import type { ChildProcess } from 'node:child_process'
|
|
6
4
|
import Database from 'better-sqlite3'
|
|
7
5
|
|
|
8
6
|
import { perf } from '@/lib/server/runtime/perf'
|
|
7
|
+
import { log } from '@/lib/server/logger'
|
|
9
8
|
import { notify } from '@/lib/server/ws-hub'
|
|
9
|
+
|
|
10
|
+
const TAG = 'storage'
|
|
10
11
|
import { DATA_DIR, IS_BUILD_BOOTSTRAP, WORKSPACE_DIR } from './data-dir'
|
|
11
12
|
import { normalizeHeartbeatSettingFields } from '@/lib/runtime/heartbeat-defaults'
|
|
12
13
|
import { normalizeRuntimeSettingFields } from '@/lib/runtime/runtime-loop'
|
|
@@ -102,11 +103,6 @@ export function withTransaction<T>(fn: () => T): T {
|
|
|
102
103
|
type StoredObject = Record<string, unknown>
|
|
103
104
|
type StoredSessionRecord = Session
|
|
104
105
|
type StoredAgentRecord = Agent
|
|
105
|
-
type ActiveProcess = ChildProcess | {
|
|
106
|
-
runId?: string | null
|
|
107
|
-
source?: string
|
|
108
|
-
kill: (signal?: NodeJS.Signals | number) => boolean | void
|
|
109
|
-
}
|
|
110
106
|
|
|
111
107
|
// Collection tables (id → JSON blob)
|
|
112
108
|
const COLLECTIONS = [
|
|
@@ -264,8 +260,8 @@ function saveCollection(table: string, data: Record<string, unknown>) {
|
|
|
264
260
|
// partial collection instead of a full load-modify-save. This prevents
|
|
265
261
|
// accidental data wipes (e.g. tests calling saveCredentials with 1 item).
|
|
266
262
|
if (toDelete.length > 0 && next.size > 0 && toDelete.length > next.size) {
|
|
267
|
-
|
|
268
|
-
`
|
|
263
|
+
log.error(TAG,
|
|
264
|
+
`BLOCKED destructive saveCollection("${table}"): ` +
|
|
269
265
|
`would delete ${toDelete.length} rows but only upsert ${next.size}. ` +
|
|
270
266
|
`Use deleteCollectionItem() for explicit deletes or load-modify-save to update.`,
|
|
271
267
|
)
|
|
@@ -303,7 +299,18 @@ function deleteCollectionItem(table: string, id: string) {
|
|
|
303
299
|
db.prepare(`DELETE FROM ${table} WHERE id = ?`).run(id)
|
|
304
300
|
const cached = collectionCache.get(table)
|
|
305
301
|
if (cached) cached.delete(id)
|
|
302
|
+
invalidateDerivedCollectionCaches(table)
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function invalidateDerivedCollectionCaches(table: string): void {
|
|
306
306
|
factoryTtlCaches.get(table)?.invalidate()
|
|
307
|
+
if (table === 'sessions') {
|
|
308
|
+
getSessionsCache().invalidate()
|
|
309
|
+
return
|
|
310
|
+
}
|
|
311
|
+
if (table === 'agents') {
|
|
312
|
+
getAgentsCache().invalidate()
|
|
313
|
+
}
|
|
307
314
|
}
|
|
308
315
|
|
|
309
316
|
/**
|
|
@@ -319,7 +326,7 @@ function upsertCollectionItem(table: string, id: string, value: unknown) {
|
|
|
319
326
|
if (cached) {
|
|
320
327
|
cached.set(id, serialized)
|
|
321
328
|
}
|
|
322
|
-
|
|
329
|
+
invalidateDerivedCollectionCaches(table)
|
|
323
330
|
}
|
|
324
331
|
|
|
325
332
|
function loadCollectionItem(table: string, id: string): unknown | null {
|
|
@@ -353,7 +360,7 @@ function upsertCollectionItems(table: string, entries: Array<[string, unknown]>)
|
|
|
353
360
|
cached.set(id, serialized)
|
|
354
361
|
}
|
|
355
362
|
}
|
|
356
|
-
|
|
363
|
+
invalidateDerivedCollectionCaches(table)
|
|
357
364
|
}
|
|
358
365
|
|
|
359
366
|
export function loadStoredItem(table: StorageCollection, id: string): unknown | null {
|
|
@@ -537,7 +544,7 @@ const MIGRATION_FLAG = path.join(DATA_DIR, '.sqlite_migrated')
|
|
|
537
544
|
function migrateFromJson() {
|
|
538
545
|
if (fs.existsSync(MIGRATION_FLAG)) return
|
|
539
546
|
|
|
540
|
-
|
|
547
|
+
log.info(TAG, 'Migrating from JSON files to SQLite...')
|
|
541
548
|
|
|
542
549
|
const transaction = db.transaction(() => {
|
|
543
550
|
for (const [table, jsonPath] of Object.entries(JSON_FILES)) {
|
|
@@ -549,7 +556,7 @@ function migrateFromJson() {
|
|
|
549
556
|
for (const [id, val] of Object.entries(data)) {
|
|
550
557
|
ins.run(id, JSON.stringify(val))
|
|
551
558
|
}
|
|
552
|
-
|
|
559
|
+
log.info(TAG, `Migrated ${table}: ${Object.keys(data).length} records`)
|
|
553
560
|
}
|
|
554
561
|
} catch { /* skip malformed files */ }
|
|
555
562
|
}
|
|
@@ -562,7 +569,7 @@ function migrateFromJson() {
|
|
|
562
569
|
const data = JSON.parse(fs.readFileSync(settingsPath, 'utf8'))
|
|
563
570
|
if (data && Object.keys(data).length > 0) {
|
|
564
571
|
saveSingleton('settings', data)
|
|
565
|
-
|
|
572
|
+
log.info(TAG, 'Migrated settings')
|
|
566
573
|
}
|
|
567
574
|
} catch { /* skip */ }
|
|
568
575
|
}
|
|
@@ -574,7 +581,7 @@ function migrateFromJson() {
|
|
|
574
581
|
const data = JSON.parse(fs.readFileSync(queuePath, 'utf8'))
|
|
575
582
|
if (Array.isArray(data) && data.length > 0) {
|
|
576
583
|
saveSingleton('queue', data)
|
|
577
|
-
|
|
584
|
+
log.info(TAG, `Migrated queue: ${data.length} items`)
|
|
578
585
|
}
|
|
579
586
|
} catch { /* skip */ }
|
|
580
587
|
}
|
|
@@ -592,14 +599,14 @@ function migrateFromJson() {
|
|
|
592
599
|
}
|
|
593
600
|
}
|
|
594
601
|
}
|
|
595
|
-
|
|
602
|
+
log.info(TAG, 'Migrated usage records')
|
|
596
603
|
} catch { /* skip */ }
|
|
597
604
|
}
|
|
598
605
|
})
|
|
599
606
|
|
|
600
607
|
transaction()
|
|
601
608
|
fs.writeFileSync(MIGRATION_FLAG, new Date().toISOString())
|
|
602
|
-
|
|
609
|
+
log.info(TAG, 'Migration complete. JSON files preserved as backup.')
|
|
603
610
|
}
|
|
604
611
|
|
|
605
612
|
if (!IS_BUILD_BOOTSTRAP) {
|
|
@@ -1400,6 +1407,14 @@ export function appendUsage(sessionId: string, record: unknown) {
|
|
|
1400
1407
|
ins.run(sessionId, JSON.stringify(record))
|
|
1401
1408
|
}
|
|
1402
1409
|
|
|
1410
|
+
export function pruneOldUsage(maxAgeMs: number): number {
|
|
1411
|
+
const cutoff = Date.now() - maxAgeMs
|
|
1412
|
+
const result = db.prepare(
|
|
1413
|
+
`DELETE FROM usage WHERE CAST(COALESCE(json_extract(data, '$.timestamp'), 0) AS INTEGER) < ?`
|
|
1414
|
+
).run(cutoff)
|
|
1415
|
+
return result.changes
|
|
1416
|
+
}
|
|
1417
|
+
|
|
1403
1418
|
// --- Connectors ---
|
|
1404
1419
|
const connectorsStore = createCollectionStore('connectors', { ttlMs: 30_000 })
|
|
1405
1420
|
export const loadConnectors = connectorsStore.load
|
|
@@ -1427,21 +1442,6 @@ const webhooksStore = createCollectionStore('webhooks')
|
|
|
1427
1442
|
export const loadWebhooks = webhooksStore.load
|
|
1428
1443
|
export const saveWebhooks = webhooksStore.save
|
|
1429
1444
|
|
|
1430
|
-
// --- Active processes ---
|
|
1431
|
-
export const active = new Map<string, ActiveProcess>()
|
|
1432
|
-
export const devServers = new Map<string, { proc: ChildProcess; url: string }>()
|
|
1433
|
-
|
|
1434
|
-
// --- Utilities ---
|
|
1435
|
-
export function localIP(): string {
|
|
1436
|
-
for (const ifaces of Object.values(os.networkInterfaces())) {
|
|
1437
|
-
if (!ifaces) continue
|
|
1438
|
-
for (const i of ifaces) {
|
|
1439
|
-
if (i.family === 'IPv4' && !i.internal) return i.address
|
|
1440
|
-
}
|
|
1441
|
-
}
|
|
1442
|
-
return 'localhost'
|
|
1443
|
-
}
|
|
1444
|
-
|
|
1445
1445
|
// --- MCP Servers ---
|
|
1446
1446
|
const mcpServersStore = createCollectionStore('mcp_servers')
|
|
1447
1447
|
export const loadMcpServers = mcpServersStore.load
|
|
@@ -7,6 +7,9 @@ import { WORKSPACE_DIR } from '@/lib/server/data-dir'
|
|
|
7
7
|
import { loadConnectors, loadSessions, UPLOAD_DIR } from '@/lib/server/storage'
|
|
8
8
|
import { errorMessage } from '@/lib/shared-utils'
|
|
9
9
|
import { isMainSession } from '@/lib/server/agents/main-agent-loop'
|
|
10
|
+
import { log } from '@/lib/server/logger'
|
|
11
|
+
|
|
12
|
+
const TAG = 'task-followups'
|
|
10
13
|
|
|
11
14
|
export { normalizeWhatsappTarget }
|
|
12
15
|
|
|
@@ -507,7 +510,7 @@ export async function notifyConnectorTaskFollowups(params: {
|
|
|
507
510
|
})
|
|
508
511
|
} catch (err: unknown) {
|
|
509
512
|
const errMsg = errorMessage(err)
|
|
510
|
-
|
|
513
|
+
log.warn(TAG, `Failed task follow-up send on connector ${target.connectorId}: ${errMsg}`)
|
|
511
514
|
}
|
|
512
515
|
}
|
|
513
516
|
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { BoardTask } from '@/types'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
deleteTask as deleteStoredTask,
|
|
5
|
+
loadTask as loadStoredTask,
|
|
6
|
+
loadTasks as loadStoredTasks,
|
|
7
|
+
patchTask as patchStoredTask,
|
|
8
|
+
saveTasks as saveStoredTasks,
|
|
9
|
+
upsertTask as upsertStoredTask,
|
|
10
|
+
upsertTasks as upsertStoredTasks,
|
|
11
|
+
} from '@/lib/server/storage'
|
|
12
|
+
import { createRecordRepository } from '@/lib/server/persistence/repository-utils'
|
|
13
|
+
|
|
14
|
+
export const taskRepository = createRecordRepository<BoardTask>(
|
|
15
|
+
'tasks',
|
|
16
|
+
{
|
|
17
|
+
get(id) {
|
|
18
|
+
return loadStoredTask(id) as BoardTask | null
|
|
19
|
+
},
|
|
20
|
+
list() {
|
|
21
|
+
return loadStoredTasks() as Record<string, BoardTask>
|
|
22
|
+
},
|
|
23
|
+
upsert(id, value) {
|
|
24
|
+
upsertStoredTask(id, value as BoardTask)
|
|
25
|
+
},
|
|
26
|
+
upsertMany(entries) {
|
|
27
|
+
upsertStoredTasks(entries as Array<[string, BoardTask]>)
|
|
28
|
+
},
|
|
29
|
+
replace(data) {
|
|
30
|
+
saveStoredTasks(data as Record<string, BoardTask>)
|
|
31
|
+
},
|
|
32
|
+
patch(id, updater) {
|
|
33
|
+
return patchStoredTask(id, updater as (current: BoardTask | null) => BoardTask | null) as BoardTask | null
|
|
34
|
+
},
|
|
35
|
+
delete(id) {
|
|
36
|
+
deleteStoredTask(id)
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
export const getTask = (id: string) => taskRepository.get(id)
|
|
42
|
+
export const getTasks = (ids: string[]) => taskRepository.getMany(ids)
|
|
43
|
+
export const listTasks = () => taskRepository.list()
|
|
44
|
+
export const saveTask = (id: string, task: BoardTask | Record<string, unknown>) => taskRepository.upsert(id, task as BoardTask)
|
|
45
|
+
export const saveTaskMany = (entries: Array<[string, BoardTask | Record<string, unknown>]>) => taskRepository.upsertMany(entries as Array<[string, BoardTask]>)
|
|
46
|
+
export const replaceTasks = (items: Record<string, BoardTask | Record<string, unknown>>) => taskRepository.replace(items as Record<string, BoardTask>)
|
|
47
|
+
export const patchTask = (id: string, updater: (current: BoardTask | null) => BoardTask | null) => taskRepository.patch(id, updater)
|
|
48
|
+
export const deleteTask = (id: string) => taskRepository.delete(id)
|
|
49
|
+
|
|
50
|
+
export const loadTasks = listTasks
|
|
51
|
+
export const loadTask = getTask
|
|
52
|
+
export const saveTasks = replaceTasks
|
|
53
|
+
export const upsertTask = saveTask
|
|
54
|
+
export const upsertTasks = saveTaskMany
|
|
@@ -71,9 +71,9 @@ const DEFAULT_THRESHOLDS: LoopDetectionThresholds = {
|
|
|
71
71
|
pollCritical: 8,
|
|
72
72
|
pingPongWarn: 3,
|
|
73
73
|
pingPongCritical: 5,
|
|
74
|
-
circuitBreaker:
|
|
75
|
-
toolFrequencyWarn:
|
|
76
|
-
toolFrequencyCritical:
|
|
74
|
+
circuitBreaker: 15,
|
|
75
|
+
toolFrequencyWarn: 12,
|
|
76
|
+
toolFrequencyCritical: 25,
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
// ---------------------------------------------------------------------------
|
|
@@ -182,6 +182,11 @@ export class ToolLoopTracker {
|
|
|
182
182
|
this.keyCount.clear()
|
|
183
183
|
}
|
|
184
184
|
|
|
185
|
+
/** Partial reset: clear per-tool frequency counts but preserve circuit breaker and repeat history. */
|
|
186
|
+
resetFrequencyCounts(): void {
|
|
187
|
+
this.nameCount.clear()
|
|
188
|
+
}
|
|
189
|
+
|
|
185
190
|
/** Get the full call history (for diagnostics). */
|
|
186
191
|
getHistory(): ReadonlyArray<ToolCallRecord> {
|
|
187
192
|
return this.history
|
|
@@ -39,6 +39,7 @@ const CORE_TOOL_PLANNING: Record<string, ToolPlanningEntry[]> = {
|
|
|
39
39
|
capabilities: ['artifact.files'],
|
|
40
40
|
disciplineGuidance: [
|
|
41
41
|
'For `files`, include an explicit action whenever possible. Common patterns: `{"action":"list","dirPath":"."}`, `{"action":"read","filePath":"path/to/file.md"}`, and `{"action":"write","files":[{"path":"path/to/file.md","content":"..."}]}`.',
|
|
42
|
+
'Prefer a single write call with multiple files over writing one file at a time.',
|
|
42
43
|
],
|
|
43
44
|
requestMatchers: [],
|
|
44
45
|
},
|
|
@@ -49,6 +50,7 @@ const CORE_TOOL_PLANNING: Record<string, ToolPlanningEntry[]> = {
|
|
|
49
50
|
capabilities: ['runtime.shell'],
|
|
50
51
|
disciplineGuidance: [
|
|
51
52
|
'For `shell`, use `{"action":"execute","command":"..."}` for commands and `{"action":"status","processId":"..."}` or `{"action":"log","processId":"..."}` for long-lived processes.',
|
|
53
|
+
'Chain related commands in a single shell call using && to reduce round-trips. Avoid running the same build or test command repeatedly — if it fails, diagnose the error before retrying.',
|
|
52
54
|
],
|
|
53
55
|
requestMatchers: [],
|
|
54
56
|
},
|
|
@@ -59,6 +61,7 @@ const CORE_TOOL_PLANNING: Record<string, ToolPlanningEntry[]> = {
|
|
|
59
61
|
capabilities: [TOOL_CAPABILITY.researchSearch],
|
|
60
62
|
disciplineGuidance: [
|
|
61
63
|
'For `web_search`, use `{"query":"..."}` to research fresh information. For current events, breaking news, or "latest" requests, start with `web_search` before summarizing.',
|
|
64
|
+
'Gather 2-3 key sources, then synthesize. Do not search-read-search-read in a loop.',
|
|
62
65
|
],
|
|
63
66
|
requestMatchers: [
|
|
64
67
|
{
|
|
@@ -73,6 +76,7 @@ const CORE_TOOL_PLANNING: Record<string, ToolPlanningEntry[]> = {
|
|
|
73
76
|
capabilities: [TOOL_CAPABILITY.researchFetch],
|
|
74
77
|
disciplineGuidance: [
|
|
75
78
|
'For `web_fetch`, use `{"url":"https://..."}` to read a specific page or article after you know the URL.',
|
|
79
|
+
'Fetch the pages you need, then synthesize. Do not fetch-read-fetch-read in a loop.',
|
|
76
80
|
],
|
|
77
81
|
requestMatchers: [
|
|
78
82
|
{
|
|
@@ -91,6 +95,7 @@ const CORE_TOOL_PLANNING: Record<string, ToolPlanningEntry[]> = {
|
|
|
91
95
|
'For `browser`, when the task includes a literal URL, pass that exact URL string to `{"action":"navigate","url":"..."}`. Do not invent placeholder URLs like `[Your URL]`, `Example_URL`, or `MockMailPage_URL`.',
|
|
92
96
|
'For `browser` form work, prefer `{"action":"fill_form","fields":[{"element":"#email","value":"user@example.com"},{"element":"#password","value":"..."}]}`. A shorthand `form` object keyed by input id/name also works for simple forms.',
|
|
93
97
|
'Use `browser` when the user asks for screenshots, visual proof, page capture, PDFs, or a rendered view of a page. `navigate` alone is not a screenshot.',
|
|
98
|
+
'Limit browser navigations to what is needed. Each navigation is expensive. Plan your browser session: list the pages you need, visit each once, extract what you need.',
|
|
94
99
|
],
|
|
95
100
|
requestMatchers: [
|
|
96
101
|
{
|
|
@@ -117,6 +122,7 @@ const CORE_TOOL_PLANNING: Record<string, ToolPlanningEntry[]> = {
|
|
|
117
122
|
'For outbound delivery, inspect available channels with `connector_message_tool` using `{"action":"list_running"}` before claiming something cannot be sent.',
|
|
118
123
|
'Use `connector_message_tool` with `{"action":"send","message":"...","mediaPath":"..."}` for text/media and `{"action":"send_voice_note","voiceText":"..."}` for voice notes.',
|
|
119
124
|
'If no channel or recipient is configured, explain that connector/channel setup is missing rather than claiming the capability does not exist.',
|
|
125
|
+
'Check channel availability once with `list_running`, then send. Do not re-list channels between each message.',
|
|
120
126
|
],
|
|
121
127
|
requestMatchers: [
|
|
122
128
|
{
|
|
@@ -140,6 +146,7 @@ const CORE_TOOL_PLANNING: Record<string, ToolPlanningEntry[]> = {
|
|
|
140
146
|
capabilities: ['network.http'],
|
|
141
147
|
disciplineGuidance: [
|
|
142
148
|
'For `http_request`, send exact literal URLs from the task or from prior tool results. Keep JSON request bodies as raw JSON strings.',
|
|
149
|
+
'If an API call fails, inspect the error before retrying with the same request. Do not retry the same failing call in a loop.',
|
|
143
150
|
],
|
|
144
151
|
requestMatchers: [],
|
|
145
152
|
},
|
|
@@ -150,6 +157,7 @@ const CORE_TOOL_PLANNING: Record<string, ToolPlanningEntry[]> = {
|
|
|
150
157
|
capabilities: ['delivery.email'],
|
|
151
158
|
disciplineGuidance: [
|
|
152
159
|
'For `email`, send mail with `{"action":"send","to":"user@example.com","subject":"...","body":"..."}`. If delivery depends on SMTP setup, check `{"action":"status"}` before claiming success.',
|
|
160
|
+
'Compose the full message in one send call. Do not send partial drafts followed by corrections.',
|
|
153
161
|
],
|
|
154
162
|
requestMatchers: [],
|
|
155
163
|
},
|
|
@@ -162,6 +170,7 @@ const CORE_TOOL_PLANNING: Record<string, ToolPlanningEntry[]> = {
|
|
|
162
170
|
'For `google_workspace`, pass exact `gws` arguments in `{"args":[...]}` form. Prefer list/get/read commands first to confirm IDs and current state before mutating Drive, Docs, Sheets, Gmail, Calendar, or Chat resources.',
|
|
163
171
|
'Use `params` and `jsonInput` for `--params` / `--json` payloads instead of packing raw JSON blobs into the `args` array.',
|
|
164
172
|
'Do not call interactive `gws auth login` or `gws auth setup` from the agent. Use the extension settings or a pre-authenticated `gws` install.',
|
|
173
|
+
'Confirm resource IDs with a single list/get call before mutating. Do not repeatedly list the same resources between edits.',
|
|
165
174
|
],
|
|
166
175
|
requestMatchers: [
|
|
167
176
|
{
|
|
@@ -179,6 +188,223 @@ const CORE_TOOL_PLANNING: Record<string, ToolPlanningEntry[]> = {
|
|
|
179
188
|
'For `ask_human`, when a workflow needs a code, approval, or out-of-band value from a person, do not guess or keep re-submitting blank forms. Use `{"action":"request_input","question":"..."}` and, for durable pauses, `{"action":"wait_for_reply","correlationId":"..."}`.',
|
|
180
189
|
'Reuse the same `correlationId` from `request_input` when you call `wait_for_reply`. Once the durable wait returns active, stop the turn immediately and wait for the reply instead of calling `request_input` again.',
|
|
181
190
|
'Do not ask the same pending human question twice before the durable wait resumes unless the question materially changes.',
|
|
191
|
+
'Batch related questions into a single request rather than asking one question at a time.',
|
|
192
|
+
],
|
|
193
|
+
requestMatchers: [],
|
|
194
|
+
},
|
|
195
|
+
],
|
|
196
|
+
|
|
197
|
+
// --- Internal platform tools ---
|
|
198
|
+
|
|
199
|
+
manage_agents: [
|
|
200
|
+
{
|
|
201
|
+
toolName: 'manage_agents',
|
|
202
|
+
capabilities: ['platform.agents'],
|
|
203
|
+
disciplineGuidance: [
|
|
204
|
+
'List agents once at the start of a task, then work with specific agent IDs. Do not re-list between each action.',
|
|
205
|
+
],
|
|
206
|
+
requestMatchers: [],
|
|
207
|
+
},
|
|
208
|
+
],
|
|
209
|
+
manage_projects: [
|
|
210
|
+
{
|
|
211
|
+
toolName: 'manage_projects',
|
|
212
|
+
capabilities: ['platform.projects'],
|
|
213
|
+
disciplineGuidance: [
|
|
214
|
+
'List projects once to orient, then operate on specific project IDs. Do not re-list after each update.',
|
|
215
|
+
],
|
|
216
|
+
requestMatchers: [],
|
|
217
|
+
},
|
|
218
|
+
],
|
|
219
|
+
manage_tasks: [
|
|
220
|
+
{
|
|
221
|
+
toolName: 'manage_tasks',
|
|
222
|
+
capabilities: ['platform.tasks'],
|
|
223
|
+
disciplineGuidance: [
|
|
224
|
+
'Read the task list once, make your changes, then move on. Do not re-read the task list after every update.',
|
|
225
|
+
],
|
|
226
|
+
requestMatchers: [],
|
|
227
|
+
},
|
|
228
|
+
],
|
|
229
|
+
manage_schedules: [
|
|
230
|
+
{
|
|
231
|
+
toolName: 'manage_schedules',
|
|
232
|
+
capabilities: ['platform.schedules'],
|
|
233
|
+
disciplineGuidance: [
|
|
234
|
+
'List schedules once to check current state. Do not re-list after each modification.',
|
|
235
|
+
],
|
|
236
|
+
requestMatchers: [],
|
|
237
|
+
},
|
|
238
|
+
],
|
|
239
|
+
manage_skills: [
|
|
240
|
+
{
|
|
241
|
+
toolName: 'manage_skills',
|
|
242
|
+
capabilities: ['platform.skills'],
|
|
243
|
+
disciplineGuidance: [
|
|
244
|
+
'Use `recommend_for_task` to find a relevant skill efficiently. Do not repeatedly list or search skills between each action.',
|
|
245
|
+
],
|
|
246
|
+
requestMatchers: [],
|
|
247
|
+
},
|
|
248
|
+
],
|
|
249
|
+
manage_webhooks: [
|
|
250
|
+
{
|
|
251
|
+
toolName: 'manage_webhooks',
|
|
252
|
+
capabilities: ['platform.webhooks'],
|
|
253
|
+
disciplineGuidance: [
|
|
254
|
+
'List webhooks once for current state. Do not re-list after each change.',
|
|
255
|
+
],
|
|
256
|
+
requestMatchers: [],
|
|
257
|
+
},
|
|
258
|
+
],
|
|
259
|
+
manage_secrets: [
|
|
260
|
+
{
|
|
261
|
+
toolName: 'manage_secrets',
|
|
262
|
+
capabilities: ['platform.secrets'],
|
|
263
|
+
disciplineGuidance: [
|
|
264
|
+
'Store secrets directly. Use the `check` action (not `list`) to verify if a credential already exists before requesting a new one.',
|
|
265
|
+
],
|
|
266
|
+
requestMatchers: [],
|
|
267
|
+
},
|
|
268
|
+
],
|
|
269
|
+
manage_chatrooms: [
|
|
270
|
+
{
|
|
271
|
+
toolName: 'manage_chatrooms',
|
|
272
|
+
capabilities: ['platform.chatrooms'],
|
|
273
|
+
disciplineGuidance: [
|
|
274
|
+
'List chatrooms once to orient, then operate on specific IDs. Do not re-list after each message or update.',
|
|
275
|
+
],
|
|
276
|
+
requestMatchers: [],
|
|
277
|
+
},
|
|
278
|
+
],
|
|
279
|
+
manage_protocols: [
|
|
280
|
+
{
|
|
281
|
+
toolName: 'manage_protocols',
|
|
282
|
+
capabilities: ['platform.protocols'],
|
|
283
|
+
disciplineGuidance: [
|
|
284
|
+
'Read the protocol definition once, then execute steps. Do not re-read the protocol between each step.',
|
|
285
|
+
],
|
|
286
|
+
requestMatchers: [],
|
|
287
|
+
},
|
|
288
|
+
],
|
|
289
|
+
manage_platform: [
|
|
290
|
+
{
|
|
291
|
+
toolName: 'manage_platform',
|
|
292
|
+
capabilities: ['platform.umbrella'],
|
|
293
|
+
disciplineGuidance: [
|
|
294
|
+
'Prefer the direct `manage_*` tools (manage_agents, manage_tasks, etc.) when they are enabled. Use `manage_platform` only as a fallback when the specific tool is not available.',
|
|
295
|
+
],
|
|
296
|
+
requestMatchers: [],
|
|
297
|
+
},
|
|
298
|
+
],
|
|
299
|
+
spawn_subagent: [
|
|
300
|
+
{
|
|
301
|
+
toolName: 'spawn_subagent',
|
|
302
|
+
capabilities: ['delegation.subagent'],
|
|
303
|
+
disciplineGuidance: [
|
|
304
|
+
'Use `waitForCompletion: true` (the default) or `wait`/`wait_all` actions to await results. Do not poll `status` in a loop.',
|
|
305
|
+
'Batch related delegations — spawn multiple subagents at once if tasks are independent.',
|
|
306
|
+
'For multi-step or cross-domain work, delegate to a subagent rather than attempting everything in one long tool chain.',
|
|
307
|
+
],
|
|
308
|
+
requestMatchers: [],
|
|
309
|
+
},
|
|
310
|
+
],
|
|
311
|
+
delegate: [
|
|
312
|
+
{
|
|
313
|
+
toolName: 'delegate',
|
|
314
|
+
capabilities: ['delegation.cli'],
|
|
315
|
+
disciplineGuidance: [
|
|
316
|
+
'Give the delegate a complete task description in one call. Do not send incremental instructions across multiple delegation calls.',
|
|
317
|
+
],
|
|
318
|
+
requestMatchers: [],
|
|
319
|
+
},
|
|
320
|
+
],
|
|
321
|
+
manage_sessions: [
|
|
322
|
+
{
|
|
323
|
+
toolName: 'sessions_tool',
|
|
324
|
+
capabilities: ['platform.sessions'],
|
|
325
|
+
disciplineGuidance: [
|
|
326
|
+
'Check session identity once at the start. Do not re-query session info between each action.',
|
|
327
|
+
],
|
|
328
|
+
requestMatchers: [],
|
|
329
|
+
},
|
|
330
|
+
],
|
|
331
|
+
memory: [
|
|
332
|
+
{
|
|
333
|
+
toolName: 'memory_tool',
|
|
334
|
+
capabilities: ['memory.search', 'memory.store'],
|
|
335
|
+
disciplineGuidance: [
|
|
336
|
+
'Search memory once with a good query, then use the results. Do not run multiple overlapping searches for the same topic.',
|
|
337
|
+
'For stores and updates, write once with complete content. Do not read-back immediately after writing to confirm.',
|
|
338
|
+
],
|
|
339
|
+
requestMatchers: [],
|
|
340
|
+
},
|
|
341
|
+
],
|
|
342
|
+
context_mgmt: [
|
|
343
|
+
{
|
|
344
|
+
toolName: 'context_status',
|
|
345
|
+
capabilities: ['context.management'],
|
|
346
|
+
disciplineGuidance: [
|
|
347
|
+
'Check context status only when you suspect you are running low. Do not check after every tool call.',
|
|
348
|
+
],
|
|
349
|
+
requestMatchers: [],
|
|
350
|
+
},
|
|
351
|
+
],
|
|
352
|
+
monitor: [
|
|
353
|
+
{
|
|
354
|
+
toolName: 'monitor_tool',
|
|
355
|
+
capabilities: ['monitoring.watch'],
|
|
356
|
+
disciplineGuidance: [
|
|
357
|
+
'Prefer `wait_until`, `wait_for_http`, `wait_for_file`, or other `wait_for_*` shortcut actions — they create a durable wait that resumes your turn automatically. Avoid creating a watch with `create_watch` then polling `get_watch` in a loop.',
|
|
358
|
+
],
|
|
359
|
+
requestMatchers: [],
|
|
360
|
+
},
|
|
361
|
+
],
|
|
362
|
+
wallet: [
|
|
363
|
+
{
|
|
364
|
+
toolName: 'wallet_tool',
|
|
365
|
+
capabilities: [TOOL_CAPABILITY.walletInspect, TOOL_CAPABILITY.walletExecute],
|
|
366
|
+
disciplineGuidance: [
|
|
367
|
+
'Inspect wallet state once, then act. Use `simulate_transaction` to validate before executing. Do not re-inspect balances between each operation unless the operation changes them.',
|
|
368
|
+
],
|
|
369
|
+
requestMatchers: [],
|
|
370
|
+
},
|
|
371
|
+
],
|
|
372
|
+
image_gen: [
|
|
373
|
+
{
|
|
374
|
+
toolName: 'generate_image',
|
|
375
|
+
capabilities: ['media.image_generation'],
|
|
376
|
+
disciplineGuidance: [
|
|
377
|
+
'Describe the image fully in one generation call. Do not generate multiple variations unless the user asks for options.',
|
|
378
|
+
],
|
|
379
|
+
requestMatchers: [],
|
|
380
|
+
},
|
|
381
|
+
],
|
|
382
|
+
replicate: [
|
|
383
|
+
{
|
|
384
|
+
toolName: 'replicate',
|
|
385
|
+
capabilities: ['media.replicate'],
|
|
386
|
+
disciplineGuidance: [
|
|
387
|
+
'Submit the job with complete parameters in one call. Use `wait: true` for synchronous completion. If running async, let the built-in polling handle it — do not add your own polling loop on top.',
|
|
388
|
+
],
|
|
389
|
+
requestMatchers: [],
|
|
390
|
+
},
|
|
391
|
+
],
|
|
392
|
+
schedule_wake: [
|
|
393
|
+
{
|
|
394
|
+
toolName: 'schedule_wake',
|
|
395
|
+
capabilities: ['runtime.schedule'],
|
|
396
|
+
disciplineGuidance: [
|
|
397
|
+
'Schedule the wake once with the correct time. Do not reschedule repeatedly to adjust by small increments.',
|
|
398
|
+
],
|
|
399
|
+
requestMatchers: [],
|
|
400
|
+
},
|
|
401
|
+
],
|
|
402
|
+
mailbox: [
|
|
403
|
+
{
|
|
404
|
+
toolName: 'mailbox',
|
|
405
|
+
capabilities: ['delivery.mailbox'],
|
|
406
|
+
disciplineGuidance: [
|
|
407
|
+
'Use `search_messages` for targeted retrieval instead of listing all messages. Do not poll the inbox in a loop waiting for replies.',
|
|
182
408
|
],
|
|
183
409
|
requestMatchers: [],
|
|
184
410
|
},
|
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { sleep, jitteredBackoff } from '@/lib/shared-utils'
|
|
6
|
+
import { log } from '@/lib/server/logger'
|
|
7
|
+
|
|
8
|
+
const TAG = 'tool-retry'
|
|
6
9
|
|
|
7
10
|
export interface RetryOptions {
|
|
8
11
|
maxAttempts?: number
|
|
@@ -51,9 +54,7 @@ export async function withRetry<TArgs>(
|
|
|
51
54
|
if (attempt < maxAttempts && isRetryableError(lastResult, retryable)) {
|
|
52
55
|
await opts?.onRetry?.(attempt, lastResult)
|
|
53
56
|
const delay = jitteredBackoff(backoffMs, attempt - 1, backoffMs * 16)
|
|
54
|
-
|
|
55
|
-
`[tool-retry] Attempt ${attempt}/${maxAttempts} matched retryable pattern, retrying in ${delay}ms`,
|
|
56
|
-
)
|
|
57
|
+
log.warn(TAG, `Attempt ${attempt}/${maxAttempts} matched retryable pattern, retrying in ${delay}ms`)
|
|
57
58
|
await sleep(delay)
|
|
58
59
|
continue
|
|
59
60
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { UsageRecord } from '@/types'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
appendUsage as appendStoredUsage,
|
|
5
|
+
getUsageSpendSince as getStoredUsageSpendSince,
|
|
6
|
+
loadUsage as loadStoredUsage,
|
|
7
|
+
pruneOldUsage as pruneStoredUsage,
|
|
8
|
+
saveUsage as saveStoredUsage,
|
|
9
|
+
} from '@/lib/server/storage'
|
|
10
|
+
import { perf } from '@/lib/server/runtime/perf'
|
|
11
|
+
|
|
12
|
+
export function loadUsage(): Record<string, UsageRecord[]> {
|
|
13
|
+
return perf.measureSync('repository', 'usage.list', () => loadStoredUsage())
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function saveUsage(data: Record<string, UsageRecord[]>): void {
|
|
17
|
+
perf.measureSync('repository', 'usage.replace', () => saveStoredUsage(data), { count: Object.keys(data).length })
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function appendUsage(sessionId: string, record: unknown): void {
|
|
21
|
+
perf.measureSync('repository', 'usage.append', () => appendStoredUsage(sessionId, record), { sessionId })
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function getUsageSpendSince(minTimestamp: number): number {
|
|
25
|
+
return perf.measureSync('repository', 'usage.spendSince', () => getStoredUsageSpendSince(minTimestamp), { minTimestamp })
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function pruneOldUsage(maxAgeMs: number): number {
|
|
29
|
+
return perf.measureSync('repository', 'usage.prune', () => pruneStoredUsage(maxAgeMs), { maxAgeMs })
|
|
30
|
+
}
|
|
@@ -84,9 +84,37 @@ export interface GetWalletPortfolioOptions {
|
|
|
84
84
|
allowStale?: boolean
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
+
const PORTFOLIO_CACHE_MAX = 200
|
|
87
88
|
const portfolioCache = new Map<string, WalletPortfolioCacheEntry>()
|
|
88
89
|
const evmContractDiscoveryCache = new Map<string, EvmContractDiscoveryCacheEntry>()
|
|
89
90
|
|
|
91
|
+
function pruneExpiredPortfolioCaches(): void {
|
|
92
|
+
const now = Date.now()
|
|
93
|
+
for (const [key, entry] of portfolioCache) {
|
|
94
|
+
if (entry.expiresAt <= now) portfolioCache.delete(key)
|
|
95
|
+
}
|
|
96
|
+
for (const [key, entry] of evmContractDiscoveryCache) {
|
|
97
|
+
if (entry.expiresAt <= now) evmContractDiscoveryCache.delete(key)
|
|
98
|
+
}
|
|
99
|
+
// Hard cap as safety net
|
|
100
|
+
if (portfolioCache.size > PORTFOLIO_CACHE_MAX) {
|
|
101
|
+
const excess = portfolioCache.size - PORTFOLIO_CACHE_MAX
|
|
102
|
+
const iter = portfolioCache.keys()
|
|
103
|
+
for (let i = 0; i < excess; i++) {
|
|
104
|
+
const k = iter.next().value
|
|
105
|
+
if (k !== undefined) portfolioCache.delete(k)
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (evmContractDiscoveryCache.size > PORTFOLIO_CACHE_MAX) {
|
|
109
|
+
const excess = evmContractDiscoveryCache.size - PORTFOLIO_CACHE_MAX
|
|
110
|
+
const iter = evmContractDiscoveryCache.keys()
|
|
111
|
+
for (let i = 0; i < excess; i++) {
|
|
112
|
+
const k = iter.next().value
|
|
113
|
+
if (k !== undefined) evmContractDiscoveryCache.delete(k)
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
90
118
|
const KNOWN_SOLANA_TOKENS: Record<string, { symbol: string; name: string }> = {
|
|
91
119
|
EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v: { symbol: 'USDC', name: 'USD Coin' },
|
|
92
120
|
}
|
|
@@ -721,6 +749,7 @@ export async function getWalletPortfolio(wallet: AgentWallet, options?: GetWalle
|
|
|
721
749
|
label: `wallet portfolio ${wallet.id}`,
|
|
722
750
|
})
|
|
723
751
|
|
|
752
|
+
pruneExpiredPortfolioCaches()
|
|
724
753
|
portfolioCache.set(cacheKey, {
|
|
725
754
|
expiresAt: Date.now() + PORTFOLIO_CACHE_TTL_MS,
|
|
726
755
|
portfolio,
|