@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
|
@@ -8,6 +8,9 @@ import { getGatewayProfile, getGatewayProfiles, resolvePrimaryAgentRoute } from
|
|
|
8
8
|
import { isAgentDisabled } from '@/lib/server/agents/agent-availability'
|
|
9
9
|
import { errorMessage, hmrSingleton, jitteredBackoff } from '@/lib/shared-utils'
|
|
10
10
|
import type { Agent } from '@/types'
|
|
11
|
+
import { log } from '@/lib/server/logger'
|
|
12
|
+
|
|
13
|
+
const TAG = 'openclaw-gateway'
|
|
11
14
|
|
|
12
15
|
// --- Types ---
|
|
13
16
|
|
|
@@ -165,7 +168,7 @@ export class OpenClawGateway {
|
|
|
165
168
|
try {
|
|
166
169
|
const result = await wsConnect(this.wsUrl, this.token, true, 15_000)
|
|
167
170
|
if (!result.ok || !result.ws) {
|
|
168
|
-
|
|
171
|
+
log.error(TAG, 'Connect failed:', result.message)
|
|
169
172
|
this.scheduleReconnect()
|
|
170
173
|
return false
|
|
171
174
|
}
|
|
@@ -173,7 +176,7 @@ export class OpenClawGateway {
|
|
|
173
176
|
this.ws = result.ws
|
|
174
177
|
this._connected = true
|
|
175
178
|
this.consecutiveFailures = 0
|
|
176
|
-
|
|
179
|
+
log.info(TAG, 'Connected to gateway')
|
|
177
180
|
|
|
178
181
|
this.ws.on('message', (data) => {
|
|
179
182
|
try {
|
|
@@ -195,7 +198,7 @@ export class OpenClawGateway {
|
|
|
195
198
|
|
|
196
199
|
return true
|
|
197
200
|
} catch (err: unknown) {
|
|
198
|
-
|
|
201
|
+
log.error(TAG, 'Connect error:', errorMessage(err))
|
|
199
202
|
this.scheduleReconnect()
|
|
200
203
|
return false
|
|
201
204
|
}
|
|
@@ -213,7 +216,7 @@ export class OpenClawGateway {
|
|
|
213
216
|
this.ws = null
|
|
214
217
|
}
|
|
215
218
|
this._connected = false
|
|
216
|
-
|
|
219
|
+
log.info(TAG, 'Disconnected')
|
|
217
220
|
}
|
|
218
221
|
|
|
219
222
|
private scheduleReconnect() {
|
|
@@ -228,7 +231,7 @@ export class OpenClawGateway {
|
|
|
228
231
|
this.doConnect().catch(() => {})
|
|
229
232
|
}, delay)
|
|
230
233
|
if (this.consecutiveFailures === 1 || this.consecutiveFailures % 5 === 0) {
|
|
231
|
-
|
|
234
|
+
log.info(TAG, `${this.consecutiveFailures} consecutive failure${this.consecutiveFailures > 1 ? 's' : ''}, next retry in ${Math.round(delay / 1000)}s`)
|
|
232
235
|
}
|
|
233
236
|
}
|
|
234
237
|
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { perf } from '@/lib/server/runtime/perf'
|
|
2
|
+
|
|
3
|
+
export interface RecordRepository<
|
|
4
|
+
T,
|
|
5
|
+
ListOptions = void,
|
|
6
|
+
UpsertValue = T | Record<string, unknown>,
|
|
7
|
+
> {
|
|
8
|
+
get(id: string, options?: ListOptions): T | null
|
|
9
|
+
getMany(ids: string[], options?: ListOptions): Record<string, T>
|
|
10
|
+
list(options?: ListOptions): Record<string, T>
|
|
11
|
+
upsert(id: string, value: UpsertValue): void
|
|
12
|
+
upsertMany(entries: Array<[string, UpsertValue]>): void
|
|
13
|
+
patch(id: string, updater: (current: T | null) => T | null, options?: ListOptions): T | null
|
|
14
|
+
replace(data: Record<string, UpsertValue>): void
|
|
15
|
+
delete(id: string): void
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface RecordRepositoryOps<
|
|
19
|
+
T,
|
|
20
|
+
ListOptions = void,
|
|
21
|
+
UpsertValue = T | Record<string, unknown>,
|
|
22
|
+
> {
|
|
23
|
+
get(id: string, options?: ListOptions): T | null
|
|
24
|
+
list(options?: ListOptions): Record<string, T>
|
|
25
|
+
upsert(id: string, value: UpsertValue): void
|
|
26
|
+
upsertMany?: (entries: Array<[string, UpsertValue]>) => void
|
|
27
|
+
patch?: (id: string, updater: (current: T | null) => T | null) => T | null
|
|
28
|
+
replace?: (data: Record<string, UpsertValue>) => void
|
|
29
|
+
delete?: (id: string) => void
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface SingletonRepository<
|
|
33
|
+
T,
|
|
34
|
+
SaveValue = T | Record<string, unknown>,
|
|
35
|
+
> {
|
|
36
|
+
get(): T
|
|
37
|
+
save(value: SaveValue): void
|
|
38
|
+
patch(updater: (current: T) => SaveValue): T
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
interface SingletonRepositoryOps<
|
|
42
|
+
T,
|
|
43
|
+
SaveValue = T | Record<string, unknown>,
|
|
44
|
+
> {
|
|
45
|
+
get(): T
|
|
46
|
+
save(value: SaveValue): void
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function uniqueIds(ids: string[]): string[] {
|
|
50
|
+
const out: string[] = []
|
|
51
|
+
const seen = new Set<string>()
|
|
52
|
+
for (const id of ids) {
|
|
53
|
+
const normalized = typeof id === 'string' ? id.trim() : ''
|
|
54
|
+
if (!normalized || seen.has(normalized)) continue
|
|
55
|
+
seen.add(normalized)
|
|
56
|
+
out.push(normalized)
|
|
57
|
+
}
|
|
58
|
+
return out
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function createRecordRepository<
|
|
62
|
+
T,
|
|
63
|
+
ListOptions = void,
|
|
64
|
+
UpsertValue = T | Record<string, unknown>,
|
|
65
|
+
>(
|
|
66
|
+
name: string,
|
|
67
|
+
ops: RecordRepositoryOps<T, ListOptions, UpsertValue>,
|
|
68
|
+
): RecordRepository<T, ListOptions, UpsertValue> {
|
|
69
|
+
return {
|
|
70
|
+
get(id, options) {
|
|
71
|
+
return perf.measureSync('repository', `${name}.get`, () => ops.get(id, options), { id })
|
|
72
|
+
},
|
|
73
|
+
getMany(ids, options) {
|
|
74
|
+
return perf.measureSync('repository', `${name}.getMany`, () => {
|
|
75
|
+
const result: Record<string, T> = {}
|
|
76
|
+
for (const id of uniqueIds(ids)) {
|
|
77
|
+
const item = ops.get(id, options)
|
|
78
|
+
if (item) result[id] = item
|
|
79
|
+
}
|
|
80
|
+
return result
|
|
81
|
+
}, { count: ids.length })
|
|
82
|
+
},
|
|
83
|
+
list(options) {
|
|
84
|
+
return perf.measureSync('repository', `${name}.list`, () => ops.list(options))
|
|
85
|
+
},
|
|
86
|
+
upsert(id, value) {
|
|
87
|
+
perf.measureSync('repository', `${name}.upsert`, () => ops.upsert(id, value), { id })
|
|
88
|
+
},
|
|
89
|
+
upsertMany(entries) {
|
|
90
|
+
perf.measureSync('repository', `${name}.upsertMany`, () => {
|
|
91
|
+
if (ops.upsertMany) {
|
|
92
|
+
ops.upsertMany(entries)
|
|
93
|
+
return
|
|
94
|
+
}
|
|
95
|
+
for (const [id, value] of entries) ops.upsert(id, value)
|
|
96
|
+
}, { count: entries.length })
|
|
97
|
+
},
|
|
98
|
+
patch(id, updater, options) {
|
|
99
|
+
return perf.measureSync('repository', `${name}.patch`, () => {
|
|
100
|
+
if (ops.patch) return ops.patch(id, updater)
|
|
101
|
+
const current = ops.get(id, options)
|
|
102
|
+
const next = updater(current)
|
|
103
|
+
if (next === null) {
|
|
104
|
+
if (!ops.delete) return null
|
|
105
|
+
ops.delete(id)
|
|
106
|
+
return null
|
|
107
|
+
}
|
|
108
|
+
ops.upsert(id, next as UpsertValue)
|
|
109
|
+
return next
|
|
110
|
+
}, { id })
|
|
111
|
+
},
|
|
112
|
+
replace(data) {
|
|
113
|
+
perf.measureSync('repository', `${name}.replace`, () => {
|
|
114
|
+
if (ops.replace) {
|
|
115
|
+
ops.replace(data)
|
|
116
|
+
return
|
|
117
|
+
}
|
|
118
|
+
const entries = Object.entries(data)
|
|
119
|
+
if (ops.upsertMany) ops.upsertMany(entries)
|
|
120
|
+
else for (const [id, value] of entries) ops.upsert(id, value)
|
|
121
|
+
}, { count: Object.keys(data).length })
|
|
122
|
+
},
|
|
123
|
+
delete(id) {
|
|
124
|
+
perf.measureSync('repository', `${name}.delete`, () => {
|
|
125
|
+
if (!ops.delete) return
|
|
126
|
+
ops.delete(id)
|
|
127
|
+
}, { id })
|
|
128
|
+
},
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function createSingletonRepository<
|
|
133
|
+
T,
|
|
134
|
+
SaveValue = T | Record<string, unknown>,
|
|
135
|
+
>(
|
|
136
|
+
name: string,
|
|
137
|
+
ops: SingletonRepositoryOps<T, SaveValue>,
|
|
138
|
+
): SingletonRepository<T, SaveValue> {
|
|
139
|
+
return {
|
|
140
|
+
get() {
|
|
141
|
+
return perf.measureSync('repository', `${name}.get`, () => ops.get())
|
|
142
|
+
},
|
|
143
|
+
save(value) {
|
|
144
|
+
perf.measureSync('repository', `${name}.save`, () => ops.save(value))
|
|
145
|
+
},
|
|
146
|
+
patch(updater) {
|
|
147
|
+
return perf.measureSync('repository', `${name}.patch`, () => {
|
|
148
|
+
const next = updater(ops.get())
|
|
149
|
+
ops.save(next)
|
|
150
|
+
return ops.get()
|
|
151
|
+
})
|
|
152
|
+
},
|
|
153
|
+
}
|
|
154
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { withTransaction } from '@/lib/server/storage'
|
|
2
|
+
|
|
3
|
+
import { agentRepository } from '@/lib/server/agents/agent-repository'
|
|
4
|
+
import { approvalRepository } from '@/lib/server/approvals/approval-repository'
|
|
5
|
+
import { chatroomRepository } from '@/lib/server/chatrooms/chatroom-repository'
|
|
6
|
+
import { connectorRepository } from '@/lib/server/connectors/connector-repository'
|
|
7
|
+
import { missionEventRepository, missionRepository } from '@/lib/server/missions/mission-repository'
|
|
8
|
+
import { projectRepository } from '@/lib/server/projects/project-repository'
|
|
9
|
+
import { scheduleRepository } from '@/lib/server/schedules/schedule-repository'
|
|
10
|
+
import { sessionRepository } from '@/lib/server/sessions/session-repository'
|
|
11
|
+
import { settingsRepository } from '@/lib/server/settings/settings-repository'
|
|
12
|
+
import { taskRepository } from '@/lib/server/tasks/task-repository'
|
|
13
|
+
import { runEventRepository, runRepository } from '@/lib/server/runtime/run-repository'
|
|
14
|
+
|
|
15
|
+
export interface StorageTxContext {
|
|
16
|
+
agents: typeof agentRepository
|
|
17
|
+
approvals: typeof approvalRepository
|
|
18
|
+
chatrooms: typeof chatroomRepository
|
|
19
|
+
connectors: typeof connectorRepository
|
|
20
|
+
missions: typeof missionRepository
|
|
21
|
+
missionEvents: typeof missionEventRepository
|
|
22
|
+
projects: typeof projectRepository
|
|
23
|
+
runs: typeof runRepository
|
|
24
|
+
runEvents: typeof runEventRepository
|
|
25
|
+
schedules: typeof scheduleRepository
|
|
26
|
+
sessions: typeof sessionRepository
|
|
27
|
+
settings: typeof settingsRepository
|
|
28
|
+
tasks: typeof taskRepository
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function createStorageTxContext(): StorageTxContext {
|
|
32
|
+
return {
|
|
33
|
+
agents: agentRepository,
|
|
34
|
+
approvals: approvalRepository,
|
|
35
|
+
chatrooms: chatroomRepository,
|
|
36
|
+
connectors: connectorRepository,
|
|
37
|
+
missions: missionRepository,
|
|
38
|
+
missionEvents: missionEventRepository,
|
|
39
|
+
projects: projectRepository,
|
|
40
|
+
runs: runRepository,
|
|
41
|
+
runEvents: runEventRepository,
|
|
42
|
+
schedules: scheduleRepository,
|
|
43
|
+
sessions: sessionRepository,
|
|
44
|
+
settings: settingsRepository,
|
|
45
|
+
tasks: taskRepository,
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function withStorageTx<T>(fn: (ctx: StorageTxContext) => T): T {
|
|
50
|
+
return withTransaction(() => fn(createStorageTxContext()))
|
|
51
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { withTransaction } from '@/lib/server/storage'
|
|
@@ -93,6 +93,8 @@ export interface ProjectResourceSummary {
|
|
|
93
93
|
openTaskCount: number
|
|
94
94
|
queuedTaskCount: number
|
|
95
95
|
runningTaskCount: number
|
|
96
|
+
failedTaskCount: number
|
|
97
|
+
staleTaskCount: number
|
|
96
98
|
activeScheduleCount: number
|
|
97
99
|
secretCount: number
|
|
98
100
|
skillCount: number
|
|
@@ -118,6 +120,15 @@ export function summarizeProjectResources(projectId: string): ProjectResourceSum
|
|
|
118
120
|
const openTasks = tasks
|
|
119
121
|
.filter((task) => ['backlog', 'queued', 'running'].includes(String(task.status || '').toLowerCase()))
|
|
120
122
|
.sort(byUpdatedDesc)
|
|
123
|
+
const failedTasks = tasks.filter((task) => String(task.status || '').toLowerCase() === 'failed')
|
|
124
|
+
const now = Date.now()
|
|
125
|
+
const staleDays = 3 * 24 * 60 * 60 * 1000
|
|
126
|
+
const staleTasks = tasks.filter((task) => {
|
|
127
|
+
const status = String(task.status || '').toLowerCase()
|
|
128
|
+
if (['completed', 'cancelled', 'archived'].includes(status)) return false
|
|
129
|
+
const lastUpdate = Number(task.updatedAt || task.createdAt || 0)
|
|
130
|
+
return lastUpdate > 0 && (now - lastUpdate) > staleDays
|
|
131
|
+
})
|
|
121
132
|
const activeSchedules = schedules
|
|
122
133
|
.filter((schedule) => String(schedule.status || '').toLowerCase() === 'active')
|
|
123
134
|
.sort(byUpdatedDesc)
|
|
@@ -129,6 +140,8 @@ export function summarizeProjectResources(projectId: string): ProjectResourceSum
|
|
|
129
140
|
openTaskCount: openTasks.length,
|
|
130
141
|
queuedTaskCount: openTasks.filter((task) => task.status === 'queued').length,
|
|
131
142
|
runningTaskCount: openTasks.filter((task) => task.status === 'running').length,
|
|
143
|
+
failedTaskCount: failedTasks.length,
|
|
144
|
+
staleTaskCount: staleTasks.length,
|
|
132
145
|
activeScheduleCount: activeSchedules.length,
|
|
133
146
|
secretCount: secrets.length,
|
|
134
147
|
skillCount: skills.length,
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { Project } from '@/types'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
deleteProject as deleteStoredProject,
|
|
5
|
+
loadProjects as loadStoredProjects,
|
|
6
|
+
saveProjects as saveStoredProjects,
|
|
7
|
+
upsertStoredItem,
|
|
8
|
+
} from '@/lib/server/storage'
|
|
9
|
+
import { createRecordRepository } from '@/lib/server/persistence/repository-utils'
|
|
10
|
+
|
|
11
|
+
export const projectRepository = createRecordRepository<Project>(
|
|
12
|
+
'projects',
|
|
13
|
+
{
|
|
14
|
+
get(id) {
|
|
15
|
+
return (loadStoredProjects() as Record<string, Project>)[id] || null
|
|
16
|
+
},
|
|
17
|
+
list() {
|
|
18
|
+
return loadStoredProjects() as Record<string, Project>
|
|
19
|
+
},
|
|
20
|
+
upsert(id, value) {
|
|
21
|
+
upsertStoredItem('projects', id, value)
|
|
22
|
+
},
|
|
23
|
+
replace(data) {
|
|
24
|
+
saveStoredProjects(data)
|
|
25
|
+
},
|
|
26
|
+
delete(id) {
|
|
27
|
+
deleteStoredProject(id)
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
export const loadProjects = () => projectRepository.list()
|
|
33
|
+
export const loadProject = (id: string) => projectRepository.get(id)
|
|
34
|
+
export const saveProjects = (items: Record<string, Project | Record<string, unknown>>) => projectRepository.replace(items as Record<string, Project>)
|
|
35
|
+
export const upsertProject = (id: string, value: Project | Record<string, unknown>) => projectRepository.upsert(id, value as Project)
|
|
36
|
+
export const deleteProject = (id: string) => projectRepository.delete(id)
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type { Agent, BoardTask, Project, Schedule, Skill, StoredSecret } from '@/types'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
deleteProject as deleteStoredProject,
|
|
5
|
+
loadAgents,
|
|
6
|
+
loadProjects,
|
|
7
|
+
loadSchedules,
|
|
8
|
+
loadSecrets,
|
|
9
|
+
loadSkills,
|
|
10
|
+
loadTasks,
|
|
11
|
+
saveAgents,
|
|
12
|
+
saveProjects,
|
|
13
|
+
saveSchedules,
|
|
14
|
+
saveSecrets,
|
|
15
|
+
saveSkills,
|
|
16
|
+
saveTasks,
|
|
17
|
+
} from '@/lib/server/storage'
|
|
18
|
+
import { ensureProjectWorkspace, normalizeProjectPatchInput } from '@/lib/server/project-utils'
|
|
19
|
+
import { notify } from '@/lib/server/ws-hub'
|
|
20
|
+
|
|
21
|
+
type ProjectLinkedRecord = {
|
|
22
|
+
projectId?: string
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function clearProjectId<T extends ProjectLinkedRecord>(
|
|
26
|
+
projectId: string,
|
|
27
|
+
load: () => Record<string, T>,
|
|
28
|
+
save: (items: Record<string, T>) => void,
|
|
29
|
+
topic: string,
|
|
30
|
+
): void {
|
|
31
|
+
const items = load()
|
|
32
|
+
let changed = false
|
|
33
|
+
for (const item of Object.values(items)) {
|
|
34
|
+
if (item.projectId !== projectId) continue
|
|
35
|
+
item.projectId = undefined
|
|
36
|
+
changed = true
|
|
37
|
+
}
|
|
38
|
+
if (!changed) return
|
|
39
|
+
save(items)
|
|
40
|
+
notify(topic)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function getProject(id: string): Project | null {
|
|
44
|
+
return loadProjects()[id] || null
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function updateProject(id: string, input: Record<string, unknown>): Project | null {
|
|
48
|
+
const projects = loadProjects()
|
|
49
|
+
const existing = projects[id]
|
|
50
|
+
if (!existing) return null
|
|
51
|
+
|
|
52
|
+
const patch = normalizeProjectPatchInput(input)
|
|
53
|
+
const nextProject: Project = {
|
|
54
|
+
...existing,
|
|
55
|
+
...patch,
|
|
56
|
+
id,
|
|
57
|
+
updatedAt: Date.now(),
|
|
58
|
+
}
|
|
59
|
+
projects[id] = nextProject
|
|
60
|
+
saveProjects(projects)
|
|
61
|
+
ensureProjectWorkspace(id, nextProject.name)
|
|
62
|
+
notify('projects')
|
|
63
|
+
return nextProject
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function deleteProjectAndDetachReferences(id: string): boolean {
|
|
67
|
+
if (!getProject(id)) return false
|
|
68
|
+
|
|
69
|
+
deleteStoredProject(id)
|
|
70
|
+
notify('projects')
|
|
71
|
+
|
|
72
|
+
clearProjectId<Agent>(id, loadAgents, saveAgents, 'agents')
|
|
73
|
+
clearProjectId<BoardTask>(id, loadTasks, saveTasks, 'tasks')
|
|
74
|
+
clearProjectId<Schedule>(id, loadSchedules, saveSchedules, 'schedules')
|
|
75
|
+
clearProjectId<Skill>(id, loadSkills, saveSkills, 'skills')
|
|
76
|
+
clearProjectId<StoredSecret>(id, loadSecrets, saveSecrets, 'secrets')
|
|
77
|
+
|
|
78
|
+
return true
|
|
79
|
+
}
|
|
@@ -4,7 +4,10 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { HumanMessage } from '@langchain/core/messages'
|
|
6
6
|
import { z } from 'zod'
|
|
7
|
+
import { log } from '@/lib/server/logger'
|
|
7
8
|
import { genId } from '@/lib/id'
|
|
9
|
+
|
|
10
|
+
const TAG = 'protocol-agent-turn'
|
|
8
11
|
import type {
|
|
9
12
|
Agent,
|
|
10
13
|
Chatroom,
|
|
@@ -230,7 +233,7 @@ export async function defaultExecuteAgentTurn(params: {
|
|
|
230
233
|
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
231
234
|
if (attempt > 0) {
|
|
232
235
|
const delay = BASE_DELAY_MS * Math.pow(2, attempt - 1)
|
|
233
|
-
|
|
236
|
+
log.warn(TAG, `retrying agent turn for ${params.agentId} (attempt ${attempt + 1}/${MAX_RETRIES + 1}, waiting ${delay}ms)`)
|
|
234
237
|
await new Promise((resolve) => setTimeout(resolve, delay))
|
|
235
238
|
}
|
|
236
239
|
try {
|
|
@@ -271,7 +274,7 @@ export async function defaultExecuteAgentTurn(params: {
|
|
|
271
274
|
const msg = errorMessage(err)
|
|
272
275
|
const isRetryable = /\b(401|429|5\d{2}|timeout|ECONNR|ETIMEDOUT|ENOTFOUND|socket hang up|fetch failed)\b/i.test(msg)
|
|
273
276
|
if (!isRetryable || attempt >= MAX_RETRIES) throw err
|
|
274
|
-
|
|
277
|
+
log.warn(TAG, `transient LLM error for agent ${params.agentId}: ${msg}`)
|
|
275
278
|
}
|
|
276
279
|
}
|
|
277
280
|
throw lastError
|
|
@@ -3,13 +3,15 @@ import { after, before, describe, it } from 'node:test'
|
|
|
3
3
|
|
|
4
4
|
let mod: typeof import('@/lib/server/protocols/protocol-normalization')
|
|
5
5
|
|
|
6
|
+
const savedBuildMode = process.env.SWARMCLAW_BUILD_MODE
|
|
6
7
|
before(async () => {
|
|
7
8
|
process.env.SWARMCLAW_BUILD_MODE = '1'
|
|
8
9
|
mod = await import('@/lib/server/protocols/protocol-normalization')
|
|
9
10
|
})
|
|
10
11
|
|
|
11
12
|
after(() => {
|
|
12
|
-
delete process.env.SWARMCLAW_BUILD_MODE
|
|
13
|
+
if (savedBuildMode === undefined) delete process.env.SWARMCLAW_BUILD_MODE
|
|
14
|
+
else process.env.SWARMCLAW_BUILD_MODE = savedBuildMode
|
|
13
15
|
})
|
|
14
16
|
|
|
15
17
|
describe('protocol-normalization', () => {
|
|
@@ -26,8 +28,8 @@ describe('protocol-normalization', () => {
|
|
|
26
28
|
})
|
|
27
29
|
|
|
28
30
|
it('artifact_exists with artifactKind', () => {
|
|
29
|
-
const result = mod.normalizeCondition({ type: 'artifact_exists', artifactKind: '
|
|
30
|
-
assert.deepEqual(result, { type: 'artifact_exists', artifactKind: '
|
|
31
|
+
const result = mod.normalizeCondition({ type: 'artifact_exists', artifactKind: 'summary' })
|
|
32
|
+
assert.deepEqual(result, { type: 'artifact_exists', artifactKind: 'summary' })
|
|
31
33
|
})
|
|
32
34
|
|
|
33
35
|
it('artifact_exists without artifactKind', () => {
|
|
@@ -53,7 +55,7 @@ describe('protocol-normalization', () => {
|
|
|
53
55
|
type: 'all',
|
|
54
56
|
conditions: [
|
|
55
57
|
{ type: 'summary_exists' },
|
|
56
|
-
{ type: 'artifact_exists', artifactKind: '
|
|
58
|
+
{ type: 'artifact_exists', artifactKind: 'notes' },
|
|
57
59
|
],
|
|
58
60
|
})
|
|
59
61
|
assert.equal(result!.type, 'all')
|
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
* Protocol run lifecycle: create/run/action, scheduling/recovery, launch helpers.
|
|
3
3
|
* Groups G10 + G18 + G19 from protocol-service.ts
|
|
4
4
|
*/
|
|
5
|
+
import { log } from '@/lib/server/logger'
|
|
5
6
|
import { genId } from '@/lib/id'
|
|
7
|
+
|
|
8
|
+
const TAG = 'protocol-run-lifecycle'
|
|
6
9
|
import type {
|
|
7
10
|
ProtocolRun,
|
|
8
11
|
ProtocolRunConfig,
|
|
@@ -58,7 +61,7 @@ export function requestProtocolRunExecution(runId: string, deps?: ProtocolRunDep
|
|
|
58
61
|
setTimeout(() => {
|
|
59
62
|
void runProtocolRun(normalizedId, deps)
|
|
60
63
|
.catch((err: unknown) => {
|
|
61
|
-
|
|
64
|
+
log.warn(TAG, `execution failed for ${normalizedId}: ${errorMessage(err)}`)
|
|
62
65
|
})
|
|
63
66
|
.finally(() => {
|
|
64
67
|
protocolExecutionState.pendingRunIds.delete(normalizedId)
|
|
@@ -300,7 +303,7 @@ export function createProtocolRun(input: CreateProtocolRunInput, deps?: Protocol
|
|
|
300
303
|
export async function runProtocolRun(runId: string, deps?: ProtocolRunDeps): Promise<ProtocolRun | null> {
|
|
301
304
|
const release = acquireProtocolLease(runId)
|
|
302
305
|
if (!release) {
|
|
303
|
-
|
|
306
|
+
log.warn(TAG, `could not acquire lease for run ${runId}, another execution may be active`)
|
|
304
307
|
return loadProtocolRunById(runId)
|
|
305
308
|
}
|
|
306
309
|
try {
|
|
@@ -4,7 +4,10 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { z } from 'zod'
|
|
6
6
|
import { HumanMessage } from '@langchain/core/messages'
|
|
7
|
+
import { log } from '@/lib/server/logger'
|
|
7
8
|
import { genId } from '@/lib/id'
|
|
9
|
+
|
|
10
|
+
const TAG = 'protocol-step-helpers'
|
|
8
11
|
import type {
|
|
9
12
|
ProtocolBranchCase,
|
|
10
13
|
ProtocolConditionDefinition,
|
|
@@ -161,7 +164,7 @@ export function syncProtocolParentFromChildRun(runOrId: ProtocolRun | string, de
|
|
|
161
164
|
const subflowState = parent.subflowState?.[child.parentStepId]
|
|
162
165
|
if (subflowState && subflowState.childRunId === child.id) {
|
|
163
166
|
if (typeof subflowMod.syncSubflowParentFromChildRun !== 'function') {
|
|
164
|
-
|
|
167
|
+
log.warn(TAG, 'syncSubflowParentFromChildRun not available (circular dep not yet resolved), skipping subflow sync')
|
|
165
168
|
return parent
|
|
166
169
|
}
|
|
167
170
|
return subflowMod.syncSubflowParentFromChildRun(child, parent, subflowState, deps)
|
|
@@ -14,15 +14,32 @@ interface ProviderHealthState {
|
|
|
14
14
|
const states: Map<string, ProviderHealthState> =
|
|
15
15
|
hmrSingleton('__swarmclaw_provider_health__', () => new Map<string, ProviderHealthState>())
|
|
16
16
|
|
|
17
|
+
const CLI_CHECK_CACHE_MAX = 100
|
|
17
18
|
const cliCheckCache = new Map<string, { at: number; ok: boolean }>()
|
|
18
19
|
const delegateReadyCache = new Map<string, { at: number; ok: boolean }>()
|
|
19
20
|
const CLI_CHECK_TTL_MS = 30_000
|
|
20
21
|
const isWindows = process.platform === 'win32'
|
|
21
22
|
|
|
23
|
+
function pruneTTLCache(cache: Map<string, { at: number }>, ttlMs: number, maxSize: number): void {
|
|
24
|
+
const now = Date.now()
|
|
25
|
+
for (const [k, v] of cache) {
|
|
26
|
+
if (now - v.at > ttlMs) cache.delete(k)
|
|
27
|
+
}
|
|
28
|
+
if (cache.size > maxSize) {
|
|
29
|
+
const excess = cache.size - maxSize
|
|
30
|
+
const iter = cache.keys()
|
|
31
|
+
for (let i = 0; i < excess; i++) {
|
|
32
|
+
const k = iter.next().value
|
|
33
|
+
if (k !== undefined) cache.delete(k)
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
22
38
|
function commandExists(binary: string): boolean {
|
|
23
39
|
const now = Date.now()
|
|
24
40
|
const cached = cliCheckCache.get(binary)
|
|
25
41
|
if (cached && now - cached.at < CLI_CHECK_TTL_MS) return cached.ok
|
|
42
|
+
pruneTTLCache(cliCheckCache, CLI_CHECK_TTL_MS, CLI_CHECK_CACHE_MAX)
|
|
26
43
|
const probe = isWindows
|
|
27
44
|
? spawnSync('where', [binary], { timeout: 2000, stdio: 'pipe' })
|
|
28
45
|
: spawnSync('/bin/zsh', ['-lc', `command -v ${binary} >/dev/null 2>&1`], { timeout: 2000 })
|
|
@@ -108,6 +125,7 @@ function delegateToolReady(delegateTool: DelegateTool): boolean {
|
|
|
108
125
|
const now = Date.now()
|
|
109
126
|
const cached = delegateReadyCache.get(delegateTool)
|
|
110
127
|
if (cached && now - cached.at < CLI_CHECK_TTL_MS) return cached.ok
|
|
128
|
+
pruneTTLCache(delegateReadyCache, CLI_CHECK_TTL_MS, CLI_CHECK_CACHE_MAX)
|
|
111
129
|
|
|
112
130
|
const binary = delegateBinary(delegateTool)
|
|
113
131
|
let ok = commandExists(binary)
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { loadAgents, loadSettings, loadCredentials, decryptKey } from './storage'
|
|
2
2
|
import { getProvider } from '../providers'
|
|
3
|
+
import { log } from '@/lib/server/logger'
|
|
4
|
+
|
|
5
|
+
const TAG = 'query-expansion'
|
|
3
6
|
|
|
4
7
|
/**
|
|
5
8
|
* Expands a single user query into multiple semantic search variants
|
|
@@ -58,7 +61,7 @@ Format your response as a simple newline-separated list. No numbering, no bullet
|
|
|
58
61
|
return [query, ...variants.slice(0, 3)]
|
|
59
62
|
}
|
|
60
63
|
} catch (err) {
|
|
61
|
-
|
|
64
|
+
log.error(TAG, 'Failed to expand query:', err)
|
|
62
65
|
}
|
|
63
66
|
|
|
64
67
|
return [query]
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
import { loadSettings } from '@/lib/server/
|
|
1
|
+
import { loadSettings } from '@/lib/server/settings/settings-repository'
|
|
2
2
|
import type { AppNotification } from '@/types'
|
|
3
3
|
import { errorMessage } from '@/lib/shared-utils'
|
|
4
|
+
import { log } from '@/lib/server/logger'
|
|
5
|
+
|
|
6
|
+
const TAG = 'alert-dispatch'
|
|
4
7
|
|
|
5
8
|
/** In-memory rate limiter: dedupKey → last dispatch timestamp */
|
|
6
9
|
const recentDispatches = new Map<string, number>()
|
|
@@ -23,11 +26,9 @@ export async function dispatchAlert(notification: AppNotification): Promise<void
|
|
|
23
26
|
if (lastSent && now - lastSent < DEDUP_WINDOW_MS) return
|
|
24
27
|
recentDispatches.set(dedupKey, now)
|
|
25
28
|
|
|
26
|
-
// Prune stale entries
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
if (now - ts > DEDUP_WINDOW_MS) recentDispatches.delete(key)
|
|
30
|
-
}
|
|
29
|
+
// Prune stale entries on every write to bound growth
|
|
30
|
+
for (const [key, ts] of recentDispatches) {
|
|
31
|
+
if (now - ts > DEDUP_WINDOW_MS) recentDispatches.delete(key)
|
|
31
32
|
}
|
|
32
33
|
|
|
33
34
|
const webhookType = settings.alertWebhookType || 'custom'
|
|
@@ -60,6 +61,6 @@ export async function dispatchAlert(notification: AppNotification): Promise<void
|
|
|
60
61
|
signal: AbortSignal.timeout(5000),
|
|
61
62
|
})
|
|
62
63
|
} catch (err: unknown) {
|
|
63
|
-
|
|
64
|
+
log.warn(TAG, 'Webhook delivery failed:', errorMessage(err))
|
|
64
65
|
}
|
|
65
66
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { DEFAULT_HEARTBEAT_INTERVAL_SEC } from '@/lib/runtime/heartbeat-defaults'
|
|
2
|
-
import { loadSettings } from '@/lib/server/
|
|
2
|
+
import { loadSettings } from '@/lib/server/settings/settings-repository'
|
|
3
3
|
import type { Session } from '@/types'
|
|
4
4
|
|
|
5
5
|
const SYNTHETIC_HEALTH_SESSION_USERS = new Set(['workbench', 'comparison-bench'])
|