@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
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { WatchJob } from '@/types'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
deleteWatchJob as deleteStoredWatchJob,
|
|
5
|
+
loadWatchJobs as loadStoredWatchJobs,
|
|
6
|
+
upsertWatchJob as upsertStoredWatchJob,
|
|
7
|
+
upsertWatchJobs as upsertStoredWatchJobs,
|
|
8
|
+
} from '@/lib/server/storage'
|
|
9
|
+
import { createRecordRepository } from '@/lib/server/persistence/repository-utils'
|
|
10
|
+
|
|
11
|
+
export const watchJobRepository = createRecordRepository<WatchJob>(
|
|
12
|
+
'watch-jobs',
|
|
13
|
+
{
|
|
14
|
+
get(id) {
|
|
15
|
+
return (loadStoredWatchJobs() as Record<string, WatchJob>)[id] || null
|
|
16
|
+
},
|
|
17
|
+
list() {
|
|
18
|
+
return loadStoredWatchJobs() as Record<string, WatchJob>
|
|
19
|
+
},
|
|
20
|
+
upsert(id, value) {
|
|
21
|
+
upsertStoredWatchJob(id, value as WatchJob)
|
|
22
|
+
},
|
|
23
|
+
upsertMany(entries) {
|
|
24
|
+
upsertStoredWatchJobs(entries as Array<[string, WatchJob]>)
|
|
25
|
+
},
|
|
26
|
+
delete(id) {
|
|
27
|
+
deleteStoredWatchJob(id)
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
export const loadWatchJobs = () => watchJobRepository.list()
|
|
33
|
+
export const upsertWatchJob = (id: string, value: WatchJob | Record<string, unknown>) => watchJobRepository.upsert(id, value as WatchJob)
|
|
34
|
+
export const upsertWatchJobs = (entries: Array<[string, WatchJob | Record<string, unknown>]>) => watchJobRepository.upsertMany(entries as Array<[string, WatchJob]>)
|
|
35
|
+
export const deleteWatchJob = (id: string) => watchJobRepository.delete(id)
|
|
@@ -5,7 +5,9 @@ import { genId } from '@/lib/id'
|
|
|
5
5
|
import type { MailboxEnvelope, WatchJob } from '@/types'
|
|
6
6
|
import { dispatchWake } from '@/lib/server/runtime/wake-dispatcher'
|
|
7
7
|
import { enqueueSystemEvent } from '@/lib/server/runtime/system-events'
|
|
8
|
-
import { loadApprovals
|
|
8
|
+
import { loadApprovals } from '@/lib/server/approvals/approval-repository'
|
|
9
|
+
import { loadTasks } from '@/lib/server/tasks/task-repository'
|
|
10
|
+
import { loadWatchJobs, upsertWatchJob, upsertWatchJobs } from '@/lib/server/runtime/watch-job-repository'
|
|
9
11
|
import { notify } from '@/lib/server/ws-hub'
|
|
10
12
|
import { fetchMailboxMessages, getMailboxHighwaterUid } from '@/lib/server/chatrooms/mailbox-utils'
|
|
11
13
|
import { errorMessage } from '@/lib/shared-utils'
|
|
@@ -3,10 +3,16 @@ type BridgeAuth = {
|
|
|
3
3
|
password?: string
|
|
4
4
|
}
|
|
5
5
|
|
|
6
|
+
const AUTH_BY_PORT_MAX = 200
|
|
6
7
|
const authByPort = new Map<number, BridgeAuth>()
|
|
7
8
|
|
|
8
9
|
export function setBridgeAuthForPort(port: number, auth: BridgeAuth): void {
|
|
9
10
|
if (!Number.isFinite(port) || port <= 0) return
|
|
11
|
+
// FIFO eviction at cap
|
|
12
|
+
if (!authByPort.has(port) && authByPort.size >= AUTH_BY_PORT_MAX) {
|
|
13
|
+
const firstKey = authByPort.keys().next().value
|
|
14
|
+
if (firstKey !== undefined) authByPort.delete(firstKey)
|
|
15
|
+
}
|
|
10
16
|
const token = typeof auth.token === 'string' ? auth.token.trim() : ''
|
|
11
17
|
const password = typeof auth.password === 'string' ? auth.password.trim() : ''
|
|
12
18
|
authByPort.set(port, {
|
|
@@ -15,12 +15,22 @@ export type NoVncObserverTokenPayload = {
|
|
|
15
15
|
password?: string
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
const NOVNC_TOKEN_MAX = 500
|
|
18
19
|
const noVncObserverTokens = new Map<string, NoVncObserverTokenEntry>()
|
|
19
20
|
|
|
20
21
|
function pruneExpiredObserverTokens(now: number): void {
|
|
21
22
|
for (const [token, entry] of noVncObserverTokens) {
|
|
22
23
|
if (entry.expiresAt <= now) noVncObserverTokens.delete(token)
|
|
23
24
|
}
|
|
25
|
+
// Hard cap as safety net
|
|
26
|
+
if (noVncObserverTokens.size > NOVNC_TOKEN_MAX) {
|
|
27
|
+
const excess = noVncObserverTokens.size - NOVNC_TOKEN_MAX
|
|
28
|
+
const iter = noVncObserverTokens.keys()
|
|
29
|
+
for (let i = 0; i < excess; i++) {
|
|
30
|
+
const k = iter.next().value
|
|
31
|
+
if (k !== undefined) noVncObserverTokens.delete(k)
|
|
32
|
+
}
|
|
33
|
+
}
|
|
24
34
|
}
|
|
25
35
|
|
|
26
36
|
export function isNoVncEnabled(params: { enableNoVnc: boolean; headless: boolean }): boolean {
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { Schedule } from '@/types'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
deleteSchedule as deleteStoredSchedule,
|
|
5
|
+
loadSchedule as loadStoredSchedule,
|
|
6
|
+
loadSchedules as loadStoredSchedules,
|
|
7
|
+
saveSchedules as saveStoredSchedules,
|
|
8
|
+
upsertSchedule as upsertStoredSchedule,
|
|
9
|
+
upsertSchedules as upsertStoredSchedules,
|
|
10
|
+
} from '@/lib/server/storage'
|
|
11
|
+
import { createRecordRepository } from '@/lib/server/persistence/repository-utils'
|
|
12
|
+
|
|
13
|
+
export const scheduleRepository = createRecordRepository<Schedule>(
|
|
14
|
+
'schedules',
|
|
15
|
+
{
|
|
16
|
+
get(id) {
|
|
17
|
+
return loadStoredSchedule(id) as Schedule | null
|
|
18
|
+
},
|
|
19
|
+
list() {
|
|
20
|
+
return loadStoredSchedules() as Record<string, Schedule>
|
|
21
|
+
},
|
|
22
|
+
upsert(id, value) {
|
|
23
|
+
upsertStoredSchedule(id, value as Schedule)
|
|
24
|
+
},
|
|
25
|
+
upsertMany(entries) {
|
|
26
|
+
upsertStoredSchedules(entries as Array<[string, Schedule]>)
|
|
27
|
+
},
|
|
28
|
+
replace(data) {
|
|
29
|
+
saveStoredSchedules(data as Record<string, Schedule>)
|
|
30
|
+
},
|
|
31
|
+
delete(id) {
|
|
32
|
+
deleteStoredSchedule(id)
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
export const loadSchedules = () => scheduleRepository.list()
|
|
38
|
+
export const loadSchedule = (id: string) => scheduleRepository.get(id)
|
|
39
|
+
export const saveSchedules = (items: Record<string, Schedule | Record<string, unknown>>) => scheduleRepository.replace(items as Record<string, Schedule>)
|
|
40
|
+
export const upsertSchedule = (id: string, value: Schedule | Record<string, unknown>) => scheduleRepository.upsert(id, value as Schedule)
|
|
41
|
+
export const upsertSchedules = (entries: Array<[string, Schedule | Record<string, unknown>]>) => scheduleRepository.upsertMany(entries as Array<[string, Schedule]>)
|
|
42
|
+
export const deleteSchedule = (id: string) => scheduleRepository.delete(id)
|
|
@@ -164,6 +164,7 @@ export function extractResumeIdentifier(text: string): string | null {
|
|
|
164
164
|
return null
|
|
165
165
|
}
|
|
166
166
|
|
|
167
|
+
const BINARY_LOOKUP_CACHE_MAX = 100
|
|
167
168
|
const binaryLookupCache = new Map<string, { checkedAt: number; path: string | null }>()
|
|
168
169
|
const BINARY_LOOKUP_TTL_MS = 30_000
|
|
169
170
|
const isWindows = process.platform === 'win32'
|
|
@@ -173,6 +174,19 @@ export function findBinaryOnPath(binaryName: string): string | null {
|
|
|
173
174
|
const cached = binaryLookupCache.get(binaryName)
|
|
174
175
|
if (cached && now - cached.checkedAt < BINARY_LOOKUP_TTL_MS) return cached.path
|
|
175
176
|
|
|
177
|
+
// Prune expired + cap
|
|
178
|
+
for (const [k, v] of binaryLookupCache) {
|
|
179
|
+
if (now - v.checkedAt > BINARY_LOOKUP_TTL_MS) binaryLookupCache.delete(k)
|
|
180
|
+
}
|
|
181
|
+
if (binaryLookupCache.size > BINARY_LOOKUP_CACHE_MAX) {
|
|
182
|
+
const excess = binaryLookupCache.size - BINARY_LOOKUP_CACHE_MAX
|
|
183
|
+
const iter = binaryLookupCache.keys()
|
|
184
|
+
for (let i = 0; i < excess; i++) {
|
|
185
|
+
const k = iter.next().value
|
|
186
|
+
if (k !== undefined) binaryLookupCache.delete(k)
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
176
190
|
const { spawnSync } = require('child_process')
|
|
177
191
|
const probe = isWindows
|
|
178
192
|
? spawnSync('where', [binaryName], { encoding: 'utf-8', timeout: 2000, stdio: 'pipe' })
|
|
@@ -11,6 +11,9 @@ import { loadSessions, patchAgent, patchSession } from '../storage'
|
|
|
11
11
|
import { inferExtensionPublisherSourceFromUrl } from '@/lib/extension-sources'
|
|
12
12
|
import { errorMessage } from '@/lib/shared-utils'
|
|
13
13
|
import { getEnabledCapabilityIds, isExternalExtensionId, normalizeCapabilitySelection } from '@/lib/capability-selection'
|
|
14
|
+
import { log } from '@/lib/server/logger'
|
|
15
|
+
|
|
16
|
+
const TAG = 'session-tools-discovery'
|
|
14
17
|
|
|
15
18
|
function grantCapabilitySelection(current: {
|
|
16
19
|
tools?: string[] | null
|
|
@@ -62,7 +65,7 @@ async function executeDiscoveryAction(args: Record<string, unknown>, bctx?: Tool
|
|
|
62
65
|
const q = typeof normalized.query === 'string' ? normalized.query : ''
|
|
63
66
|
const extensionId = explicitExtensionId || (action === 'request_access' ? q.trim() : '')
|
|
64
67
|
|
|
65
|
-
|
|
68
|
+
log.info(TAG, 'Executing action:', { action, query: q, extensionId })
|
|
66
69
|
|
|
67
70
|
try {
|
|
68
71
|
switch (action) {
|
|
@@ -87,7 +90,7 @@ async function executeDiscoveryAction(args: Record<string, unknown>, bctx?: Tool
|
|
|
87
90
|
const results: Record<string, unknown>[] = []
|
|
88
91
|
|
|
89
92
|
try {
|
|
90
|
-
|
|
93
|
+
log.info(TAG, 'Searching ClawHub...')
|
|
91
94
|
const hubResults = await searchClawHub(q)
|
|
92
95
|
if (hubResults && hubResults.skills) {
|
|
93
96
|
results.push(...hubResults.skills.map((s) => ({
|
|
@@ -101,11 +104,11 @@ async function executeDiscoveryAction(args: Record<string, unknown>, bctx?: Tool
|
|
|
101
104
|
})))
|
|
102
105
|
}
|
|
103
106
|
} catch (err: unknown) {
|
|
104
|
-
|
|
107
|
+
log.error(TAG, 'ClawHub search failed:', errorMessage(err))
|
|
105
108
|
}
|
|
106
109
|
|
|
107
110
|
try {
|
|
108
|
-
|
|
111
|
+
log.info(TAG, 'Searching SwarmClaw registry...')
|
|
109
112
|
const registryResults = new Map<string, Record<string, unknown>>()
|
|
110
113
|
const registries = [
|
|
111
114
|
{ url: 'https://swarmclaw.ai/registry/extensions.json', catalogSource: 'swarmclaw-site' },
|
|
@@ -133,7 +136,7 @@ async function executeDiscoveryAction(args: Record<string, unknown>, bctx?: Tool
|
|
|
133
136
|
}
|
|
134
137
|
results.push(...registryResults.values())
|
|
135
138
|
} catch (err: unknown) {
|
|
136
|
-
|
|
139
|
+
log.error(TAG, 'SC Registry search failed:', errorMessage(err))
|
|
137
140
|
}
|
|
138
141
|
|
|
139
142
|
if (results.length === 0) {
|
|
@@ -226,7 +229,7 @@ async function executeDiscoveryAction(args: Record<string, unknown>, bctx?: Tool
|
|
|
226
229
|
}
|
|
227
230
|
} catch (err: unknown) {
|
|
228
231
|
const msg = errorMessage(err)
|
|
229
|
-
|
|
232
|
+
log.error(TAG, 'executeDiscoveryAction failed:', msg)
|
|
230
233
|
return `Error: ${msg}`
|
|
231
234
|
}
|
|
232
235
|
}
|
|
@@ -57,6 +57,8 @@ import {
|
|
|
57
57
|
export type { ToolContext, SessionToolsResult }
|
|
58
58
|
export { sweepOrphanedBrowsers, cleanupSessionBrowser, getActiveBrowserCount, hasActiveBrowser }
|
|
59
59
|
|
|
60
|
+
const TAG = 'session-tools'
|
|
61
|
+
|
|
60
62
|
const DELEGATION_TOOL_NAMES = new Set([
|
|
61
63
|
'delegate',
|
|
62
64
|
'spawn_subagent',
|
|
@@ -455,7 +457,7 @@ export async function buildSessionTools(cwd: string, enabledExtensions: string[]
|
|
|
455
457
|
abortSignalRef,
|
|
456
458
|
}
|
|
457
459
|
} catch (err: any) {
|
|
458
|
-
|
|
460
|
+
log.error(TAG, 'buildSessionTools critical failure:', err.message)
|
|
459
461
|
throw err
|
|
460
462
|
}
|
|
461
463
|
}
|
|
@@ -226,7 +226,7 @@ const PlatformExtension: Extension = {
|
|
|
226
226
|
description: 'Unified management of agents, projects, tasks, schedules, skills, documents, and secrets.',
|
|
227
227
|
hooks: {
|
|
228
228
|
getCapabilityDescription: () => 'I can manage durable execution context across agents, projects, tasks, schedules, documents, skills, webhooks, connectors, sessions, and encrypted secrets.',
|
|
229
|
-
getOperatingGuidance: () => ['Use projects to hold longer-lived goals, objectives, and credential requirements.', 'Create/update tasks for long-lived goals to track progress.', 'Use schedules for follow-ups and heartbeat-style check-ins. Check existing schedules before creating new ones.', 'Inspect existing chats before creating duplicates.'],
|
|
229
|
+
getOperatingGuidance: () => ['Use projects to hold longer-lived goals, objectives, and credential requirements.', 'Create/update tasks for long-lived goals to track progress.', 'When work on a task is finished, update its status to completed with a concrete result summary. Cancel or archive tasks that are no longer relevant. Stale open tasks block project progress.', 'Use schedules for follow-ups and heartbeat-style check-ins. Check existing schedules before creating new ones.', 'Inspect existing chats before creating duplicates.'],
|
|
230
230
|
} as ExtensionHooks,
|
|
231
231
|
tools: [
|
|
232
232
|
{
|
|
@@ -5,6 +5,7 @@ import type { Extension, ExtensionHooks } from '@/types'
|
|
|
5
5
|
import { registerNativeCapability } from '../native-capabilities'
|
|
6
6
|
import { normalizeToolInputArgs } from './normalize-tool-args'
|
|
7
7
|
import { errorMessage, sleep } from '@/lib/shared-utils'
|
|
8
|
+
import { loadAgents } from '@/lib/server/storage'
|
|
8
9
|
import {
|
|
9
10
|
cancelDelegationJob,
|
|
10
11
|
getDelegationJob,
|
|
@@ -99,7 +100,12 @@ function validateAllowedSubagentTarget(agentId: string, ctx: ActionContext): str
|
|
|
99
100
|
? ctx.delegationTargetAgentIds.filter((entry): entry is string => typeof entry === 'string' && entry.trim().length > 0)
|
|
100
101
|
: []
|
|
101
102
|
if (allowedAgentIds.length === 0 || allowedAgentIds.includes(agentId)) return null
|
|
102
|
-
|
|
103
|
+
|
|
104
|
+
const agents = loadAgents()
|
|
105
|
+
const allowedNames = allowedAgentIds
|
|
106
|
+
.map(id => agents[id]?.name ? `${agents[id].name} [${id}]` : id)
|
|
107
|
+
.join(', ')
|
|
108
|
+
return `Error: agent "${agentId}" is not in your allowed delegation list. You may only delegate to: ${allowedNames}. Do not retry with this agent.`
|
|
103
109
|
}
|
|
104
110
|
|
|
105
111
|
function parseBooleanLike(value: unknown): boolean | unknown {
|
|
@@ -538,6 +544,21 @@ registerNativeCapability('subagent', SubagentExtension)
|
|
|
538
544
|
*/
|
|
539
545
|
export function buildSubagentTools(bctx: ToolBuildContext): StructuredToolInterface[] {
|
|
540
546
|
if (!bctx.ctx?.delegationEnabled || !bctx.hasExtension('spawn_subagent')) return []
|
|
547
|
+
|
|
548
|
+
let description = SubagentExtension.tools![0].description
|
|
549
|
+
if (bctx.ctx?.delegationTargetMode === 'selected') {
|
|
550
|
+
const allowedIds = (bctx.ctx.delegationTargetAgentIds || []).filter(
|
|
551
|
+
(id): id is string => typeof id === 'string' && id.trim().length > 0,
|
|
552
|
+
)
|
|
553
|
+
if (allowedIds.length > 0) {
|
|
554
|
+
const agents = loadAgents()
|
|
555
|
+
const allowedSummary = allowedIds
|
|
556
|
+
.map(id => agents[id]?.name ? `${agents[id].name} [${id}]` : id)
|
|
557
|
+
.join(', ')
|
|
558
|
+
description += ` DELEGATION RESTRICTED: You may ONLY delegate to these agents: ${allowedSummary}. Attempts to delegate to any other agent will be rejected.`
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
|
|
541
562
|
return [
|
|
542
563
|
tool(
|
|
543
564
|
async (args) => executeSubagentAction(args, {
|
|
@@ -548,7 +569,7 @@ export function buildSubagentTools(bctx: ToolBuildContext): StructuredToolInterf
|
|
|
548
569
|
}),
|
|
549
570
|
{
|
|
550
571
|
name: 'spawn_subagent',
|
|
551
|
-
description
|
|
572
|
+
description,
|
|
552
573
|
schema: subagentToolSchema
|
|
553
574
|
}
|
|
554
575
|
)
|
|
@@ -39,6 +39,9 @@ import {
|
|
|
39
39
|
simulateSolanaTransaction,
|
|
40
40
|
} from '../solana'
|
|
41
41
|
import { TOOL_CAPABILITY } from '../tool-planning'
|
|
42
|
+
import { log } from '@/lib/server/logger'
|
|
43
|
+
|
|
44
|
+
const TAG = 'wallet'
|
|
42
45
|
import { clearWalletPortfolioCache } from '@/lib/server/wallet/wallet-portfolio'
|
|
43
46
|
import {
|
|
44
47
|
createAgentWallet,
|
|
@@ -1042,7 +1045,7 @@ async function executeWalletAction(args: unknown, context: { agentId?: string |
|
|
|
1042
1045
|
} catch (err: unknown) {
|
|
1043
1046
|
const msg = errorMessage(err)
|
|
1044
1047
|
if (msg.includes('429') || msg.toLowerCase().includes('too many requests')) {
|
|
1045
|
-
|
|
1048
|
+
log.warn(TAG, 'Solana RPC rate-limited. Consider using a dedicated RPC endpoint (SOLANA_RPC_URL env var).')
|
|
1046
1049
|
}
|
|
1047
1050
|
return JSON.stringify({ error: msg })
|
|
1048
1051
|
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import type { Message, Session } from '@/types'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
deleteSession as deleteStoredSession,
|
|
5
|
+
disableAllSessionHeartbeats as disableAllStoredSessionHeartbeats,
|
|
6
|
+
getSessionMessages as getStoredSessionMessages,
|
|
7
|
+
loadSession as loadStoredSession,
|
|
8
|
+
loadSessions as loadStoredSessions,
|
|
9
|
+
patchSession as patchStoredSession,
|
|
10
|
+
saveSessions as replaceStoredSessions,
|
|
11
|
+
upsertSession as upsertStoredSession,
|
|
12
|
+
} from '@/lib/server/storage'
|
|
13
|
+
import { createRecordRepository } from '@/lib/server/persistence/repository-utils'
|
|
14
|
+
|
|
15
|
+
export const sessionRepository = createRecordRepository<Session>(
|
|
16
|
+
'sessions',
|
|
17
|
+
{
|
|
18
|
+
get(id) {
|
|
19
|
+
return loadStoredSession(id) as Session | null
|
|
20
|
+
},
|
|
21
|
+
list() {
|
|
22
|
+
return loadStoredSessions() as Record<string, Session>
|
|
23
|
+
},
|
|
24
|
+
upsert(id, value) {
|
|
25
|
+
upsertStoredSession(id, value as Session)
|
|
26
|
+
},
|
|
27
|
+
upsertMany(entries) {
|
|
28
|
+
replaceStoredSessions(Object.fromEntries(entries))
|
|
29
|
+
},
|
|
30
|
+
replace(data) {
|
|
31
|
+
replaceStoredSessions(data)
|
|
32
|
+
},
|
|
33
|
+
patch(id, updater) {
|
|
34
|
+
return patchStoredSession(id, updater as (current: Session | null) => Session | null) as Session | null
|
|
35
|
+
},
|
|
36
|
+
delete(id) {
|
|
37
|
+
deleteStoredSession(id)
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
export function listSessions(): Record<string, Session> {
|
|
43
|
+
return sessionRepository.list()
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function getSession(id: string): Session | null {
|
|
47
|
+
return sessionRepository.get(id)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function getSessions(ids: string[]): Record<string, Session> {
|
|
51
|
+
return sessionRepository.getMany(ids)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function saveSession(id: string, session: Session | Record<string, unknown>): void {
|
|
55
|
+
sessionRepository.upsert(id, session as Session)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function saveSessionMany(entries: Array<[string, Session | Record<string, unknown>]>): void {
|
|
59
|
+
sessionRepository.upsertMany(entries as Array<[string, Session]>)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function replaceSessions(sessions: Record<string, Session | Record<string, unknown>>): void {
|
|
63
|
+
sessionRepository.replace(sessions as Record<string, Session>)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function patchSession(id: string, updater: (current: Session | null) => Session | null): Session | null {
|
|
67
|
+
return sessionRepository.patch(id, updater)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function deleteSession(id: string): void {
|
|
71
|
+
sessionRepository.delete(id)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function getSessionMessages(sessionId: string): Message[] {
|
|
75
|
+
return getStoredSessionMessages(sessionId)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function disableAllSessionHeartbeats(): number {
|
|
79
|
+
return disableAllStoredSessionHeartbeats()
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export const loadSessions = listSessions
|
|
83
|
+
export const loadSession = getSession
|
|
84
|
+
export const saveSessions = replaceSessions
|
|
85
|
+
export const upsertSession = saveSession
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { AppSettings } from '@/types'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
loadPublicSettings as loadStoredPublicSettings,
|
|
5
|
+
loadSettings as loadStoredSettings,
|
|
6
|
+
saveSettings as saveStoredSettings,
|
|
7
|
+
} from '@/lib/server/storage'
|
|
8
|
+
import { createSingletonRepository } from '@/lib/server/persistence/repository-utils'
|
|
9
|
+
|
|
10
|
+
export const settingsRepository = createSingletonRepository<AppSettings>(
|
|
11
|
+
'settings',
|
|
12
|
+
{
|
|
13
|
+
get() {
|
|
14
|
+
return loadStoredSettings()
|
|
15
|
+
},
|
|
16
|
+
save(value) {
|
|
17
|
+
saveStoredSettings(value)
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
export const loadSettings = () => settingsRepository.get()
|
|
23
|
+
export const saveSettings = (value: AppSettings | Record<string, unknown>) => settingsRepository.save(value as AppSettings)
|
|
24
|
+
export const patchSettings = (updater: (current: AppSettings) => AppSettings | Record<string, unknown>) => settingsRepository.patch(updater as (current: AppSettings) => AppSettings)
|
|
25
|
+
export const loadPublicSettings = () => loadStoredPublicSettings()
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import type { ClawHubSkill } from '@/types'
|
|
2
2
|
import { errorMessage } from '@/lib/shared-utils'
|
|
3
|
+
import { log } from '@/lib/server/logger'
|
|
4
|
+
|
|
5
|
+
const TAG = 'clawhub-client'
|
|
3
6
|
|
|
4
7
|
export interface ClawHubSearchResult {
|
|
5
8
|
skills: ClawHubSkill[]
|
|
@@ -177,7 +180,7 @@ export async function searchClawHub(query: string, page = 1, limit = 20, cursor?
|
|
|
177
180
|
return { skills, total, page, nextCursor: data.nextCursor }
|
|
178
181
|
} catch (err: unknown) {
|
|
179
182
|
const error = errorMessage(err)
|
|
180
|
-
|
|
183
|
+
log.warn(TAG, 'search failed:', error)
|
|
181
184
|
return { skills: [], total: 0, page, error }
|
|
182
185
|
}
|
|
183
186
|
}
|
|
@@ -9,7 +9,7 @@ import type {
|
|
|
9
9
|
SkillRequirements,
|
|
10
10
|
SkillSecuritySummary,
|
|
11
11
|
} from '@/types'
|
|
12
|
-
import { dedup } from '@/lib/shared-utils'
|
|
12
|
+
import { dedup, hmrSingleton } from '@/lib/shared-utils'
|
|
13
13
|
import { expandExtensionIds, getExtensionAliases, normalizeExtensionId } from '@/lib/server/tool-aliases'
|
|
14
14
|
import { loadLearnedSkills, loadSettings, loadSkills } from '@/lib/server/storage'
|
|
15
15
|
import { cosineSimilarity, getEmbedding } from '@/lib/server/embeddings'
|
|
@@ -160,7 +160,8 @@ const SOURCE_PRIORITY: Record<RuntimeSkillSource, number> = {
|
|
|
160
160
|
}
|
|
161
161
|
|
|
162
162
|
const DEFAULT_RUNTIME_SKILL_TOP_K = 8
|
|
163
|
-
const
|
|
163
|
+
const SKILL_EMBEDDING_CACHE_MAX = 200
|
|
164
|
+
const embeddingCache = hmrSingleton('__swarmclaw_skill_embedding_cache__', () => new Map<string, Promise<number[] | null>>())
|
|
164
165
|
|
|
165
166
|
function normalizeKey(value: string | null | undefined): string {
|
|
166
167
|
return String(value || '')
|
|
@@ -859,6 +860,11 @@ async function getCachedSkillEmbedding(
|
|
|
859
860
|
const key = getSkillEmbeddingCacheKey(skill)
|
|
860
861
|
let cached = embeddingCache.get(key)
|
|
861
862
|
if (!cached) {
|
|
863
|
+
// FIFO eviction when cache exceeds cap
|
|
864
|
+
if (embeddingCache.size >= SKILL_EMBEDDING_CACHE_MAX) {
|
|
865
|
+
const firstKey = embeddingCache.keys().next().value
|
|
866
|
+
if (firstKey !== undefined) embeddingCache.delete(firstKey)
|
|
867
|
+
}
|
|
862
868
|
cached = embeddingResolver(buildSkillEmbeddingText(skill))
|
|
863
869
|
embeddingCache.set(key, cached)
|
|
864
870
|
}
|
|
@@ -5,14 +5,14 @@ import path from 'node:path'
|
|
|
5
5
|
import test from 'node:test'
|
|
6
6
|
import { clearDiscoveredSkillsCache, discoverSkills } from './skill-discovery'
|
|
7
7
|
|
|
8
|
-
test('discoverSkills includes tracked bundled skills from
|
|
8
|
+
test('discoverSkills includes tracked bundled skills from skills', () => {
|
|
9
9
|
const skills = discoverSkills({ cwd: path.join(process.cwd(), 'src') })
|
|
10
10
|
const googleWorkspaceSkill = skills.find((skill) => skill.name === 'google-workspace')
|
|
11
11
|
|
|
12
12
|
assert.ok(googleWorkspaceSkill)
|
|
13
13
|
assert.equal(googleWorkspaceSkill?.source, 'bundled')
|
|
14
14
|
assert.equal(
|
|
15
|
-
googleWorkspaceSkill?.sourcePath.endsWith(path.join('
|
|
15
|
+
googleWorkspaceSkill?.sourcePath.endsWith(path.join('skills', 'google-workspace', 'SKILL.md')),
|
|
16
16
|
true,
|
|
17
17
|
)
|
|
18
18
|
})
|
|
@@ -19,7 +19,7 @@ interface DiscoveryCache {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
const CACHE_TTL_MS = 5_000
|
|
22
|
-
const BUNDLED_SKILLS_DIR = path.join(process.cwd(), '
|
|
22
|
+
const BUNDLED_SKILLS_DIR = path.join(process.cwd(), 'skills')
|
|
23
23
|
const LEGACY_BUNDLED_SKILLS_DIR = path.join(DATA_DIR, 'skills')
|
|
24
24
|
|
|
25
25
|
let cache: DiscoveryCache | null = null
|
|
@@ -84,7 +84,7 @@ function scanLayer(
|
|
|
84
84
|
|
|
85
85
|
/**
|
|
86
86
|
* Discover skills from three layers:
|
|
87
|
-
* 1. Bundled: `
|
|
87
|
+
* 1. Bundled: `skills/` (tracked with the app)
|
|
88
88
|
* Legacy fallback: `data/skills/`
|
|
89
89
|
* 2. Workspace: `<swarmclaw-home>/skills/` (user-installed)
|
|
90
90
|
* 3. Project: `<cwd>/skills/` (project-local)
|
|
@@ -10,11 +10,17 @@ export interface SkillEligibilityResult {
|
|
|
10
10
|
reasons: string[]
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
const BINARY_CACHE_MAX = 200
|
|
13
14
|
const binaryCache = new Map<string, boolean>()
|
|
14
15
|
|
|
15
16
|
function hasBinary(name: string): boolean {
|
|
16
17
|
const cached = binaryCache.get(name)
|
|
17
18
|
if (cached !== undefined) return cached
|
|
19
|
+
// FIFO eviction at cap
|
|
20
|
+
if (binaryCache.size >= BINARY_CACHE_MAX) {
|
|
21
|
+
const firstKey = binaryCache.keys().next().value
|
|
22
|
+
if (firstKey !== undefined) binaryCache.delete(firstKey)
|
|
23
|
+
}
|
|
18
24
|
try {
|
|
19
25
|
execSync(`which ${name}`, { stdio: 'ignore', timeout: 2000 })
|
|
20
26
|
binaryCache.set(name, true)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export {
|
|
2
|
+
deleteLearnedSkill,
|
|
3
|
+
deleteSkill,
|
|
4
|
+
loadLearnedSkill,
|
|
5
|
+
loadLearnedSkills,
|
|
6
|
+
loadSkillSuggestions,
|
|
7
|
+
loadSkills,
|
|
8
|
+
patchLearnedSkill,
|
|
9
|
+
patchSkillSuggestion,
|
|
10
|
+
saveLearnedSkills,
|
|
11
|
+
saveSkills,
|
|
12
|
+
upsertLearnedSkill,
|
|
13
|
+
upsertSkillSuggestion,
|
|
14
|
+
} from '@/lib/server/storage'
|
package/src/lib/server/solana.ts
CHANGED
|
@@ -129,11 +129,17 @@ export function getSolanaExplorerUrl(cluster: SolanaCluster | string | null | un
|
|
|
129
129
|
return `https://explorer.solana.com/${prefix}/${value}${clusterSuffix}`
|
|
130
130
|
}
|
|
131
131
|
|
|
132
|
+
const CONNECTION_CACHE_MAX = 50
|
|
132
133
|
const connectionCache = new Map<string, Connection>()
|
|
133
134
|
|
|
134
135
|
function getCachedConnection(url: string): Connection {
|
|
135
136
|
let conn = connectionCache.get(url)
|
|
136
137
|
if (!conn) {
|
|
138
|
+
// FIFO eviction if cache exceeds cap
|
|
139
|
+
if (connectionCache.size >= CONNECTION_CACHE_MAX) {
|
|
140
|
+
const firstKey = connectionCache.keys().next().value
|
|
141
|
+
if (firstKey !== undefined) connectionCache.delete(firstKey)
|
|
142
|
+
}
|
|
137
143
|
conn = new Connection(url, {
|
|
138
144
|
commitment: 'confirmed',
|
|
139
145
|
disableRetryOnRateLimit: true,
|
|
@@ -3,6 +3,9 @@ import path from 'path'
|
|
|
3
3
|
import crypto from 'crypto'
|
|
4
4
|
|
|
5
5
|
import { DATA_DIR, IS_BUILD_BOOTSTRAP } from './data-dir'
|
|
6
|
+
import { log } from '@/lib/server/logger'
|
|
7
|
+
|
|
8
|
+
const TAG = 'storage-auth'
|
|
6
9
|
|
|
7
10
|
// --- .env loading ---
|
|
8
11
|
function loadEnv() {
|
|
@@ -24,7 +27,7 @@ if (!IS_BUILD_BOOTSTRAP && !process.env.CREDENTIAL_SECRET) {
|
|
|
24
27
|
const envPath = path.join(process.cwd(), '.env.local')
|
|
25
28
|
fs.appendFileSync(envPath, `\nCREDENTIAL_SECRET=${secret}\n`)
|
|
26
29
|
process.env.CREDENTIAL_SECRET = secret
|
|
27
|
-
|
|
30
|
+
log.info(TAG, 'Generated CREDENTIAL_SECRET in .env.local')
|
|
28
31
|
}
|
|
29
32
|
|
|
30
33
|
// Auto-generate ACCESS_KEY if missing (used for simple auth)
|
|
@@ -35,10 +38,7 @@ if (!IS_BUILD_BOOTSTRAP && !process.env.ACCESS_KEY) {
|
|
|
35
38
|
fs.appendFileSync(envPath, `\nACCESS_KEY=${key}\n`)
|
|
36
39
|
process.env.ACCESS_KEY = key
|
|
37
40
|
fs.writeFileSync(SETUP_FLAG, key)
|
|
38
|
-
|
|
39
|
-
console.log(` ACCESS KEY: ${key}`)
|
|
40
|
-
console.log(` Use this key to connect from the browser.`)
|
|
41
|
-
console.log(`${'='.repeat(50)}\n`)
|
|
41
|
+
log.info(TAG, `ACCESS KEY: ${key} — Use this key to connect from the browser.`)
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
export function getAccessKey(): string {
|
|
@@ -526,5 +526,9 @@ function normalizeStoredRecordInner(
|
|
|
526
526
|
if ('missionSummary' in session) delete session.missionSummary
|
|
527
527
|
// Default geminiSessionId for new field
|
|
528
528
|
if (session.geminiSessionId === undefined) session.geminiSessionId = null
|
|
529
|
+
// Default injectedMemoryIds for proactive recall dedup
|
|
530
|
+
if (!session.injectedMemoryIds || typeof session.injectedMemoryIds !== 'object') {
|
|
531
|
+
session.injectedMemoryIds = {}
|
|
532
|
+
}
|
|
529
533
|
return session
|
|
530
534
|
}
|