@swarmclawai/swarmclaw 1.2.4 → 1.2.6
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 +14 -0
- package/bin/daemon-cmd.js +169 -0
- package/bin/server-cmd.js +3 -0
- package/bin/swarmclaw.js +11 -0
- package/package.json +17 -16
- package/src/app/api/agents/[id]/clone/route.ts +3 -32
- package/src/app/api/agents/[id]/route.ts +6 -158
- package/src/app/api/agents/[id]/status/route.ts +2 -3
- package/src/app/api/agents/[id]/thread/route.ts +4 -17
- package/src/app/api/agents/bulk/route.ts +5 -47
- package/src/app/api/agents/route.ts +5 -119
- package/src/app/api/agents/trash/route.ts +13 -24
- package/src/app/api/auth/route.ts +3 -9
- package/src/app/api/autonomy/estop/route.ts +5 -5
- package/src/app/api/chatrooms/[id]/chat/route.ts +11 -5
- package/src/app/api/chatrooms/[id]/route.ts +23 -2
- package/src/app/api/chatrooms/route.ts +13 -2
- package/src/app/api/chats/[id]/clear/route.ts +2 -13
- package/src/app/api/chats/[id]/deploy/route.ts +2 -3
- package/src/app/api/chats/[id]/edit-resend/route.ts +7 -13
- package/src/app/api/chats/[id]/mailbox/route.ts +6 -8
- package/src/app/api/chats/[id]/queue/route.ts +17 -64
- package/src/app/api/chats/[id]/retry/route.ts +4 -22
- package/src/app/api/chats/[id]/route.ts +10 -138
- package/src/app/api/chats/heartbeat/route.ts +2 -1
- package/src/app/api/chats/migrate-messages/route.ts +7 -0
- package/src/app/api/chats/route.ts +13 -134
- package/src/app/api/connectors/[id]/access/route.ts +12 -229
- package/src/app/api/connectors/[id]/doctor/route.ts +1 -1
- package/src/app/api/connectors/[id]/health/route.ts +12 -39
- package/src/app/api/connectors/[id]/route.ts +14 -122
- package/src/app/api/connectors/[id]/webhook/route.ts +1 -1
- package/src/app/api/connectors/doctor/route.ts +1 -1
- package/src/app/api/connectors/route.ts +12 -70
- package/src/app/api/credentials/[id]/route.ts +2 -4
- package/src/app/api/credentials/route.ts +10 -19
- package/src/app/api/daemon/health-check/route.ts +3 -4
- package/src/app/api/daemon/route.ts +10 -8
- package/src/app/api/documents/route.ts +11 -10
- package/src/app/api/external-agents/route.ts +3 -3
- package/src/app/api/gateways/[id]/health/route.ts +2 -3
- package/src/app/api/gateways/[id]/route.ts +7 -122
- package/src/app/api/gateways/route.ts +3 -103
- package/src/app/api/mcp-servers/[id]/tools/route.ts +5 -5
- package/src/app/api/openclaw/dashboard-url/route.ts +8 -16
- package/src/app/api/openclaw/directory/route.ts +2 -2
- package/src/app/api/openclaw/history/route.ts +3 -5
- package/src/app/api/providers/[id]/route.test.ts +49 -0
- package/src/app/api/providers/ollama/route.ts +6 -5
- package/src/app/api/schedules/[id]/route.ts +14 -108
- package/src/app/api/schedules/[id]/run/route.ts +6 -67
- package/src/app/api/schedules/route.ts +9 -51
- package/src/app/api/settings/route.ts +4 -3
- package/src/app/api/setup/check-provider/route.ts +23 -1
- package/src/app/api/setup/openclaw-device/route.ts +2 -2
- package/src/app/api/system/status/route.ts +2 -2
- package/src/app/api/tasks/[id]/route.ts +16 -202
- package/src/app/api/tasks/bulk/route.ts +5 -86
- package/src/app/api/tasks/metrics/route.ts +2 -1
- package/src/app/api/tasks/route.ts +11 -171
- package/src/app/api/upload/route.ts +1 -1
- package/src/app/api/uploads/[filename]/route.ts +1 -1
- package/src/app/api/uploads/route.ts +1 -1
- package/src/app/api/webhooks/[id]/history/route.ts +2 -2
- package/src/app/layout.tsx +9 -6
- package/src/app/protocols/page.tsx +71 -89
- package/src/app/tasks/page.tsx +32 -32
- package/src/cli/index.js +1 -0
- package/src/cli/spec.js +1 -0
- package/src/components/agents/agent-sheet.tsx +5 -5
- package/src/components/auth/setup-wizard/index.tsx +4 -4
- package/src/components/auth/setup-wizard/step-agents.tsx +1 -1
- package/src/components/auth/setup-wizard/step-connect.tsx +1 -1
- package/src/components/auth/setup-wizard/utils.ts +1 -1
- package/src/components/chatrooms/chatroom-sheet.tsx +16 -276
- package/src/components/connectors/connector-list.tsx +26 -40
- package/src/components/connectors/connector-sheet.tsx +95 -149
- package/src/components/gateways/gateway-sheet.tsx +61 -110
- package/src/components/layout/live-query-sync.tsx +121 -0
- package/src/components/protocols/structured-session-launcher.tsx +24 -45
- package/src/components/providers/app-query-provider.tsx +17 -0
- package/src/components/providers/provider-list.tsx +60 -61
- package/src/components/providers/provider-sheet.tsx +74 -56
- package/src/components/skills/skill-list.tsx +5 -18
- package/src/components/skills/skill-sheet.tsx +21 -20
- package/src/components/skills/skills-workspace.tsx +48 -87
- package/src/components/tasks/task-card.tsx +20 -13
- package/src/components/tasks/task-column.tsx +22 -7
- package/src/components/tasks/task-list.tsx +8 -11
- package/src/components/tasks/task-sheet.tsx +111 -103
- package/src/features/agents/queries.ts +20 -0
- package/src/features/chatrooms/queries.ts +20 -0
- package/src/features/chats/queries.ts +27 -0
- package/src/features/connectors/queries.ts +145 -0
- package/src/features/credentials/queries.ts +37 -0
- package/src/features/extensions/queries.ts +26 -0
- package/src/features/external-agents/queries.ts +36 -0
- package/src/features/gateways/queries.ts +274 -0
- package/src/features/missions/queries.ts +23 -0
- package/src/features/projects/queries.ts +20 -0
- package/src/features/protocols/queries.ts +149 -0
- package/src/features/providers/queries.ts +142 -0
- package/src/features/settings/queries.ts +20 -0
- package/src/features/skills/queries.ts +182 -0
- package/src/features/tasks/queries.ts +189 -0
- package/src/hooks/use-ws.ts +3 -2
- package/src/lib/app/api-client.ts +2 -2
- package/src/lib/providers/index.test.ts +108 -0
- package/src/lib/providers/index.ts +38 -15
- package/src/lib/query/client.ts +17 -0
- package/src/lib/server/agents/agent-runtime-config.ts +1 -1
- package/src/lib/server/agents/agent-service.ts +429 -0
- package/src/lib/server/agents/agent-thread-session.ts +6 -5
- package/src/lib/server/agents/autonomy-contract.ts +1 -4
- package/src/lib/server/agents/delegation-advisory.test.ts +206 -0
- package/src/lib/server/agents/delegation-advisory.ts +251 -0
- package/src/lib/server/agents/main-agent-loop.ts +98 -40
- package/src/lib/server/agents/subagent-runtime.ts +12 -0
- package/src/lib/server/autonomy/supervisor-reflection.test.ts +20 -1
- package/src/lib/server/autonomy/supervisor-reflection.ts +39 -19
- package/src/lib/server/build-llm.ts +7 -15
- package/src/lib/server/capability-router.test.ts +70 -1
- package/src/lib/server/capability-router.ts +24 -99
- package/src/lib/server/chat-execution/chat-execution-utils.ts +0 -15
- package/src/lib/server/chat-execution/chat-streaming-utils.ts +2 -4
- package/src/lib/server/chat-execution/chat-turn-finalization.ts +77 -12
- package/src/lib/server/chat-execution/chat-turn-partial-persistence.ts +4 -4
- package/src/lib/server/chat-execution/chat-turn-preflight.ts +2 -2
- package/src/lib/server/chat-execution/chat-turn-preparation.ts +41 -17
- package/src/lib/server/chat-execution/chat-turn-stream-execution.ts +4 -2
- package/src/lib/server/chat-execution/chat-turn-tool-routing.test.ts +45 -0
- package/src/lib/server/chat-execution/chat-turn-tool-routing.ts +48 -17
- package/src/lib/server/chat-execution/continuation-evaluator.ts +4 -1
- package/src/lib/server/chat-execution/direct-memory-intent.test.ts +9 -0
- package/src/lib/server/chat-execution/direct-memory-intent.ts +12 -2
- package/src/lib/server/chat-execution/message-classifier.test.ts +35 -23
- package/src/lib/server/chat-execution/message-classifier.ts +74 -32
- package/src/lib/server/chat-execution/prompt-builder.test.ts +29 -0
- package/src/lib/server/chat-execution/prompt-builder.ts +37 -2
- package/src/lib/server/chat-execution/prompt-sections.test.ts +56 -0
- package/src/lib/server/chat-execution/prompt-sections.ts +193 -0
- package/src/lib/server/chat-execution/stream-agent-chat.ts +63 -7
- package/src/lib/server/chat-execution/stream-continuation.test.ts +36 -0
- package/src/lib/server/chat-execution/stream-continuation.ts +28 -13
- package/src/lib/server/chatrooms/chatroom-agent-signals.ts +26 -18
- package/src/lib/server/chatrooms/chatroom-helpers.ts +19 -18
- package/src/lib/server/chatrooms/chatroom-repository.ts +16 -0
- package/src/lib/server/chatrooms/chatroom-routing.test.ts +96 -0
- package/src/lib/server/chatrooms/chatroom-routing.ts +207 -53
- package/src/lib/server/chatrooms/mailbox-utils.ts +4 -2
- package/src/lib/server/chatrooms/session-mailbox.ts +50 -40
- package/src/lib/server/chats/chat-session-service.ts +410 -0
- package/src/lib/server/connectors/access.ts +1 -1
- package/src/lib/server/connectors/commands.ts +7 -6
- package/src/lib/server/connectors/connector-inbound.ts +14 -7
- package/src/lib/server/connectors/connector-outbound.ts +16 -11
- package/src/lib/server/connectors/connector-service.ts +453 -0
- package/src/lib/server/connectors/delivery.ts +17 -12
- package/src/lib/server/connectors/inbound-audio-transcription.ts +5 -14
- package/src/lib/server/connectors/media.ts +1 -1
- package/src/lib/server/connectors/response-media.ts +1 -1
- package/src/lib/server/connectors/session-consolidation.ts +11 -7
- package/src/lib/server/connectors/session.ts +9 -7
- package/src/lib/server/connectors/voice-note.ts +2 -1
- package/src/lib/server/context-manager.ts +20 -1
- package/src/lib/server/cost.ts +2 -3
- package/src/lib/server/credentials/credential-repository.ts +43 -4
- package/src/lib/server/credentials/credential-service.ts +112 -0
- package/src/lib/server/daemon/admin-metadata.ts +64 -0
- package/src/lib/server/daemon/controller.ts +577 -0
- package/src/lib/server/daemon/daemon-runtime.ts +352 -0
- package/src/lib/server/daemon/daemon-status-repository.ts +63 -0
- package/src/lib/server/daemon/types.ts +101 -0
- package/src/lib/server/embeddings.ts +3 -9
- package/src/lib/server/eval/agent-regression.ts +3 -2
- package/src/lib/server/eval/runner.ts +2 -2
- package/src/lib/server/execution-brief.test.ts +167 -0
- package/src/lib/server/execution-brief.ts +295 -0
- package/src/lib/server/execution-engine/chat-turn.ts +9 -0
- package/src/lib/server/execution-engine/import-boundary.test.ts +44 -0
- package/src/lib/server/execution-engine/index.ts +35 -0
- package/src/lib/server/execution-engine/task-attempt.ts +303 -0
- package/src/lib/server/execution-engine/types.ts +33 -0
- package/src/lib/server/gateways/gateway-profile-repository.ts +47 -3
- package/src/lib/server/gateways/gateway-profile-service.ts +200 -0
- package/src/lib/server/memory/session-archive-memory.ts +12 -10
- package/src/lib/server/messages/message-repository.ts +330 -0
- package/src/lib/server/missions/mission-service/core.ts +8 -6
- package/src/lib/server/openclaw/agent-resolver.ts +2 -3
- package/src/lib/server/openclaw/doctor.ts +1 -1
- package/src/lib/server/openclaw/gateway.test.ts +10 -1
- package/src/lib/server/openclaw/gateway.ts +5 -14
- package/src/lib/server/openclaw/health.ts +3 -11
- package/src/lib/server/openclaw/sync.ts +8 -6
- package/src/lib/server/persistence/storage-context.ts +3 -0
- package/src/lib/server/protocols/protocol-agent-turn.ts +25 -17
- package/src/lib/server/protocols/protocol-normalization.ts +1 -1
- package/src/lib/server/protocols/protocol-queries.ts +13 -7
- package/src/lib/server/protocols/protocol-run-lifecycle.ts +16 -20
- package/src/lib/server/protocols/protocol-run-repository.ts +81 -0
- package/src/lib/server/protocols/protocol-step-processors.ts +23 -31
- package/src/lib/server/protocols/protocol-swarm.ts +8 -8
- package/src/lib/server/protocols/protocol-template-repository.ts +42 -0
- package/src/lib/server/protocols/protocol-templates.ts +4 -2
- package/src/lib/server/protocols/protocol-types.ts +10 -7
- package/src/lib/server/provider-endpoint.ts +7 -12
- package/src/lib/server/provider-model-discovery.ts +2 -11
- package/src/lib/server/query-expansion.ts +5 -6
- package/src/lib/server/run-context.test.ts +365 -0
- package/src/lib/server/run-context.ts +367 -0
- package/src/lib/server/runtime/heartbeat-service.ts +7 -5
- package/src/lib/server/runtime/queue/core.ts +61 -190
- package/src/lib/server/runtime/run-ledger.ts +8 -0
- package/src/lib/server/runtime/session-run-manager/drain.ts +2 -2
- package/src/lib/server/runtime/session-run-manager/enqueue.ts +6 -0
- package/src/lib/server/runtime/session-run-manager/state.ts +4 -0
- package/src/lib/server/schedules/schedule-route-service.ts +230 -0
- package/src/lib/server/service-result.ts +16 -0
- package/src/lib/server/session-note.ts +2 -3
- package/src/lib/server/session-reset-policy.ts +4 -3
- package/src/lib/server/session-tools/connector.ts +9 -6
- package/src/lib/server/session-tools/context-mgmt.ts +58 -9
- package/src/lib/server/session-tools/crud.ts +162 -10
- package/src/lib/server/session-tools/delegate.ts +1 -1
- package/src/lib/server/session-tools/manage-tasks.test.ts +152 -0
- package/src/lib/server/session-tools/memory.ts +6 -4
- package/src/lib/server/session-tools/session-info.test.ts +56 -0
- package/src/lib/server/session-tools/session-info.ts +119 -12
- package/src/lib/server/session-tools/skill-runtime.ts +3 -1
- package/src/lib/server/session-tools/skills.ts +15 -15
- package/src/lib/server/session-tools/subagent.test.ts +115 -1
- package/src/lib/server/session-tools/subagent.ts +125 -7
- package/src/lib/server/session-tools/team-context.ts +4 -3
- package/src/lib/server/session-tools/wallet.ts +0 -58
- package/src/lib/server/sessions/session-lineage.ts +55 -0
- package/src/lib/server/sessions/session-repository.ts +2 -2
- package/src/lib/server/skills/learned-skills.ts +24 -23
- package/src/lib/server/skills/runtime-skill-resolver.ts +2 -1
- package/src/lib/server/skills/skill-repository.ts +136 -13
- package/src/lib/server/skills/skill-suggestions.ts +25 -28
- package/src/lib/server/storage-normalization.test.ts +44 -267
- package/src/lib/server/storage-normalization.ts +75 -0
- package/src/lib/server/storage.ts +19 -0
- package/src/lib/server/structured-extract.ts +3 -14
- package/src/lib/server/tasks/task-followups.ts +16 -11
- package/src/lib/server/tasks/task-result.test.ts +25 -29
- package/src/lib/server/tasks/task-result.ts +5 -9
- package/src/lib/server/tasks/task-route-service.ts +449 -0
- package/src/lib/server/text-normalization.ts +41 -0
- package/src/lib/server/tool-planning.ts +6 -42
- package/src/lib/server/upload-path.ts +5 -0
- package/src/lib/server/working-state/extraction.ts +614 -0
- package/src/lib/server/working-state/normalization.ts +866 -0
- package/src/lib/server/working-state/prompt.ts +60 -0
- package/src/lib/server/working-state/repository.ts +38 -0
- package/src/lib/server/working-state/service.test.ts +253 -0
- package/src/lib/server/working-state/service.ts +293 -0
- package/src/lib/validation/schemas.ts +1 -0
- package/src/lib/ws-client.ts +3 -3
- package/src/stores/slices/task-slice.ts +1 -4
- package/src/stores/use-chatroom-store.ts +2 -2
- package/src/types/index.ts +277 -12
|
@@ -5,13 +5,20 @@ import { genId } from '@/lib/id'
|
|
|
5
5
|
import type { Agent, Session, Skill, SkillSuggestion } from '@/types'
|
|
6
6
|
import { errorMessage } from '@/lib/shared-utils'
|
|
7
7
|
import {
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
loadAgent,
|
|
9
|
+
} from '@/lib/server/agents/agent-repository'
|
|
10
|
+
import {
|
|
11
|
+
loadSession,
|
|
12
|
+
} from '@/lib/server/sessions/session-repository'
|
|
13
|
+
import { getMessages, getRecentMessages } from '@/lib/server/messages/message-repository'
|
|
14
|
+
import {
|
|
15
|
+
loadSkill,
|
|
16
|
+
loadSkillSuggestion,
|
|
10
17
|
loadSkillSuggestions,
|
|
11
18
|
loadSkills,
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
} from '@/lib/server/
|
|
19
|
+
saveSkill,
|
|
20
|
+
upsertSkillSuggestion,
|
|
21
|
+
} from '@/lib/server/skills/skill-repository'
|
|
15
22
|
import { buildLLM, type GenerationModelPreference } from '@/lib/server/build-llm'
|
|
16
23
|
import { notify } from '@/lib/server/ws-hub'
|
|
17
24
|
import { resolveAgentRouteCandidates } from '@/lib/server/agents/agent-runtime-config'
|
|
@@ -91,7 +98,7 @@ function ensureHeading(name: string, content: string): string {
|
|
|
91
98
|
}
|
|
92
99
|
|
|
93
100
|
export function buildSessionTranscript(session: Session, maxMessages = DEFAULT_TRANSCRIPT_MESSAGES): string {
|
|
94
|
-
const messages =
|
|
101
|
+
const messages = getRecentMessages(session.id, maxMessages)
|
|
95
102
|
const lines: string[] = []
|
|
96
103
|
for (const message of messages) {
|
|
97
104
|
if (!message || message.suppressed) continue
|
|
@@ -109,9 +116,8 @@ export function buildSessionTranscript(session: Session, maxMessages = DEFAULT_T
|
|
|
109
116
|
}
|
|
110
117
|
|
|
111
118
|
function getSessionMessageCount(session: Session): number {
|
|
112
|
-
return
|
|
113
|
-
|
|
114
|
-
: 0
|
|
119
|
+
return getMessages(session.id)
|
|
120
|
+
.filter((message) => message && !message.suppressed && (message.text || message.toolEvents?.length)).length
|
|
115
121
|
}
|
|
116
122
|
|
|
117
123
|
function buildSuggestionPrompt(params: {
|
|
@@ -234,11 +240,9 @@ export async function createSkillSuggestionFromSession(
|
|
|
234
240
|
sessionId: string,
|
|
235
241
|
options?: { generateText?: (prompt: string) => Promise<string> },
|
|
236
242
|
): Promise<SkillSuggestion> {
|
|
237
|
-
const
|
|
238
|
-
const session = sessions[sessionId] as unknown as Session | undefined
|
|
243
|
+
const session = loadSession(sessionId)
|
|
239
244
|
if (!session) throw new Error(`Session "${sessionId}" not found.`)
|
|
240
|
-
const
|
|
241
|
-
const agent = session.agentId ? agents[session.agentId] : null
|
|
245
|
+
const agent = session.agentId ? loadAgent(session.agentId) : null
|
|
242
246
|
|
|
243
247
|
const transcript = buildSessionTranscript(session)
|
|
244
248
|
const sourceMessageCount = getSessionMessageCount(session)
|
|
@@ -331,18 +335,16 @@ export async function createSkillSuggestionFromSession(
|
|
|
331
335
|
updatedAt: now,
|
|
332
336
|
}
|
|
333
337
|
|
|
334
|
-
|
|
335
|
-
saveSkillSuggestions(suggestions)
|
|
338
|
+
upsertSkillSuggestion(suggestion.id, suggestion)
|
|
336
339
|
notify('skill_suggestions')
|
|
337
340
|
return suggestion
|
|
338
341
|
}
|
|
339
342
|
|
|
340
343
|
export function materializeSkillSuggestion(id: string): { suggestion: SkillSuggestion; skill: Skill } {
|
|
341
|
-
const
|
|
342
|
-
const suggestion = suggestions[id]
|
|
344
|
+
const suggestion = loadSkillSuggestion(id)
|
|
343
345
|
if (!suggestion) throw new Error(`Skill suggestion "${id}" not found.`)
|
|
344
346
|
if (suggestion.status === 'approved' && suggestion.createdSkillId) {
|
|
345
|
-
const existing =
|
|
347
|
+
const existing = loadSkill(suggestion.createdSkillId)
|
|
346
348
|
if (existing) return { suggestion, skill: existing }
|
|
347
349
|
}
|
|
348
350
|
|
|
@@ -370,8 +372,7 @@ export function materializeSkillSuggestion(id: string): { suggestion: SkillSugge
|
|
|
370
372
|
approvedAt: now,
|
|
371
373
|
updatedAt: now,
|
|
372
374
|
}
|
|
373
|
-
|
|
374
|
-
saveSkillSuggestions(suggestions)
|
|
375
|
+
upsertSkillSuggestion(id, approved)
|
|
375
376
|
notify('skill_suggestions')
|
|
376
377
|
return { suggestion: approved, skill: existingSkill }
|
|
377
378
|
}
|
|
@@ -399,8 +400,7 @@ export function materializeSkillSuggestion(id: string): { suggestion: SkillSugge
|
|
|
399
400
|
updatedAt: now,
|
|
400
401
|
}
|
|
401
402
|
|
|
402
|
-
|
|
403
|
-
saveSkills(skills)
|
|
403
|
+
saveSkill(skill.id, skill)
|
|
404
404
|
clearDiscoveredSkillsCache()
|
|
405
405
|
|
|
406
406
|
const approved: SkillSuggestion = {
|
|
@@ -410,16 +410,14 @@ export function materializeSkillSuggestion(id: string): { suggestion: SkillSugge
|
|
|
410
410
|
approvedAt: now,
|
|
411
411
|
updatedAt: now,
|
|
412
412
|
}
|
|
413
|
-
|
|
414
|
-
saveSkillSuggestions(suggestions)
|
|
413
|
+
upsertSkillSuggestion(id, approved)
|
|
415
414
|
notify('skills')
|
|
416
415
|
notify('skill_suggestions')
|
|
417
416
|
return { suggestion: approved, skill }
|
|
418
417
|
}
|
|
419
418
|
|
|
420
419
|
export function rejectSkillSuggestion(id: string): SkillSuggestion {
|
|
421
|
-
const
|
|
422
|
-
const suggestion = suggestions[id]
|
|
420
|
+
const suggestion = loadSkillSuggestion(id)
|
|
423
421
|
if (!suggestion) throw new Error(`Skill suggestion "${id}" not found.`)
|
|
424
422
|
const rejected: SkillSuggestion = {
|
|
425
423
|
...suggestion,
|
|
@@ -427,8 +425,7 @@ export function rejectSkillSuggestion(id: string): SkillSuggestion {
|
|
|
427
425
|
rejectedAt: Date.now(),
|
|
428
426
|
updatedAt: Date.now(),
|
|
429
427
|
}
|
|
430
|
-
|
|
431
|
-
saveSkillSuggestions(suggestions)
|
|
428
|
+
upsertSkillSuggestion(id, rejected)
|
|
432
429
|
notify('skill_suggestions')
|
|
433
430
|
return rejected
|
|
434
431
|
}
|
|
@@ -1,270 +1,47 @@
|
|
|
1
1
|
import assert from 'node:assert/strict'
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
assert.equal(result.delegationEnabled, true)
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
it('coordinator always has delegation enabled', () => {
|
|
50
|
-
const agent = { id: 'a1', name: 'Test', role: 'coordinator', delegationEnabled: false } as Record<string, unknown>
|
|
51
|
-
const result = normalizeStoredRecord('agents', agent, noopLoader).value as Record<string, unknown>
|
|
52
|
-
assert.equal(result.delegationEnabled, true)
|
|
53
|
-
})
|
|
54
|
-
|
|
55
|
-
it('migrates legacy platformAssignScope', () => {
|
|
56
|
-
const agent = { id: 'a1', name: 'Test', platformAssignScope: 'all' } as Record<string, unknown>
|
|
57
|
-
const result = normalizeStoredRecord('agents', agent, noopLoader).value as Record<string, unknown>
|
|
58
|
-
assert.equal(result.delegationEnabled, true)
|
|
59
|
-
assert.equal(result.platformAssignScope, undefined)
|
|
60
|
-
})
|
|
61
|
-
|
|
62
|
-
it('migrates legacy subAgentIds to delegationTargetAgentIds', () => {
|
|
63
|
-
const agent = { id: 'a1', name: 'Test', subAgentIds: ['b1', 'b2'] } as Record<string, unknown>
|
|
64
|
-
const result = normalizeStoredRecord('agents', agent, noopLoader).value as Record<string, unknown>
|
|
65
|
-
assert.deepEqual(result.delegationTargetAgentIds, ['b1', 'b2'])
|
|
66
|
-
assert.equal(result.delegationTargetMode, 'selected')
|
|
67
|
-
assert.equal(result.subAgentIds, undefined)
|
|
68
|
-
})
|
|
69
|
-
|
|
70
|
-
it('deletes legacy plugins field', () => {
|
|
71
|
-
const agent = { id: 'a1', name: 'Test', plugins: ['old'] } as Record<string, unknown>
|
|
72
|
-
const result = normalizeStoredRecord('agents', agent, noopLoader).value as Record<string, unknown>
|
|
73
|
-
assert.equal(result.plugins, undefined)
|
|
74
|
-
})
|
|
75
|
-
})
|
|
76
|
-
|
|
77
|
-
// ---- Session normalization ----
|
|
78
|
-
describe('sessions', () => {
|
|
79
|
-
it('type migration orchestrated → delegated', () => {
|
|
80
|
-
const session = { id: 's1', sessionType: 'orchestrated' } as Record<string, unknown>
|
|
81
|
-
const result = normalizeStoredRecord('sessions', session, noopLoader).value as Record<string, unknown>
|
|
82
|
-
assert.equal(result.sessionType, 'delegated')
|
|
83
|
-
})
|
|
84
|
-
|
|
85
|
-
it('defaults invalid sessionType to human', () => {
|
|
86
|
-
const session = { id: 's1', sessionType: 'bogus' } as Record<string, unknown>
|
|
87
|
-
const result = normalizeStoredRecord('sessions', session, noopLoader).value as Record<string, unknown>
|
|
88
|
-
assert.equal(result.sessionType, 'human')
|
|
89
|
-
})
|
|
90
|
-
|
|
91
|
-
it('detects shortcut session from id prefix', () => {
|
|
92
|
-
const session = { id: 'agent-thread-abc', agentId: 'a1', sessionType: 'human' } as Record<string, unknown>
|
|
93
|
-
const result = normalizeStoredRecord('sessions', session, noopLoader).value as Record<string, unknown>
|
|
94
|
-
assert.equal(result.shortcutForAgentId, 'a1')
|
|
95
|
-
})
|
|
96
|
-
|
|
97
|
-
it('normalizes capabilities', () => {
|
|
98
|
-
const session = { id: 's1', sessionType: 'human', tools: ['web'] } as Record<string, unknown>
|
|
99
|
-
const result = normalizeStoredRecord('sessions', session, noopLoader).value as Record<string, unknown>
|
|
100
|
-
assert.ok(Array.isArray(result.tools))
|
|
101
|
-
assert.ok(Array.isArray(result.extensions))
|
|
102
|
-
})
|
|
103
|
-
|
|
104
|
-
it('deletes legacy plugins and mainLoopState', () => {
|
|
105
|
-
const session = { id: 's1', sessionType: 'human', plugins: ['old'], mainLoopState: {} } as Record<string, unknown>
|
|
106
|
-
const result = normalizeStoredRecord('sessions', session, noopLoader).value as Record<string, unknown>
|
|
107
|
-
assert.equal(result.plugins, undefined)
|
|
108
|
-
assert.equal(result.mainLoopState, undefined)
|
|
109
|
-
})
|
|
110
|
-
})
|
|
111
|
-
|
|
112
|
-
// ---- Schedule normalization ----
|
|
113
|
-
describe('schedules', () => {
|
|
114
|
-
it('type resolution from scheduleType', () => {
|
|
115
|
-
const schedule = { id: 'sch1', scheduleType: 'cron', status: 'active' } as Record<string, unknown>
|
|
116
|
-
const result = normalizeStoredRecord('schedules', schedule, noopLoader).value as Record<string, unknown>
|
|
117
|
-
assert.equal(result.scheduleType, 'cron')
|
|
118
|
-
})
|
|
119
|
-
|
|
120
|
-
it('legacy type field migrated', () => {
|
|
121
|
-
const schedule = { id: 'sch1', type: 'once', status: 'active' } as Record<string, unknown>
|
|
122
|
-
const result = normalizeStoredRecord('schedules', schedule, noopLoader).value as Record<string, unknown>
|
|
123
|
-
assert.equal(result.scheduleType, 'once')
|
|
124
|
-
assert.equal(result.type, undefined)
|
|
125
|
-
})
|
|
126
|
-
|
|
127
|
-
it('invalid status defaults to active', () => {
|
|
128
|
-
const schedule = { id: 'sch1', status: 'bogus' } as Record<string, unknown>
|
|
129
|
-
const result = normalizeStoredRecord('schedules', schedule, noopLoader).value as Record<string, unknown>
|
|
130
|
-
assert.equal(result.status, 'active')
|
|
131
|
-
})
|
|
132
|
-
|
|
133
|
-
it('timestamp parsing from number', () => {
|
|
134
|
-
const schedule = { id: 'sch1', status: 'active', lastRunAt: 1700000000000 } as Record<string, unknown>
|
|
135
|
-
const result = normalizeStoredRecord('schedules', schedule, noopLoader).value as Record<string, unknown>
|
|
136
|
-
assert.equal(result.lastRunAt, 1700000000000)
|
|
137
|
-
})
|
|
138
|
-
})
|
|
139
|
-
|
|
140
|
-
// ---- Task normalization ----
|
|
141
|
-
describe('tasks', () => {
|
|
142
|
-
it('subtaskIds default to empty array', () => {
|
|
143
|
-
const task = { id: 't1', title: 'Test' } as Record<string, unknown>
|
|
144
|
-
const result = normalizeStoredRecord('tasks', task, noopLoader).value as Record<string, unknown>
|
|
145
|
-
assert.deepEqual(result.subtaskIds, [])
|
|
146
|
-
})
|
|
147
|
-
|
|
148
|
-
it('preserves existing subtaskIds', () => {
|
|
149
|
-
const task = { id: 't1', title: 'Test', subtaskIds: ['t2'] } as Record<string, unknown>
|
|
150
|
-
const result = normalizeStoredRecord('tasks', task, noopLoader).value as Record<string, unknown>
|
|
151
|
-
assert.deepEqual(result.subtaskIds, ['t2'])
|
|
152
|
-
})
|
|
153
|
-
|
|
154
|
-
it('removes missionSummary field', () => {
|
|
155
|
-
const task = { id: 't1', missionSummary: 'old' } as Record<string, unknown>
|
|
156
|
-
const result = normalizeStoredRecord('tasks', task, noopLoader).value as Record<string, unknown>
|
|
157
|
-
assert.equal(result.missionSummary, undefined)
|
|
158
|
-
})
|
|
159
|
-
})
|
|
160
|
-
|
|
161
|
-
describe('provider_configs', () => {
|
|
162
|
-
it('defaults legacy custom provider configs to enabled with normalized fields', () => {
|
|
163
|
-
const providerConfig = {
|
|
164
|
-
id: 'custom-llama',
|
|
165
|
-
name: ' Llama.cpp ',
|
|
166
|
-
type: 'custom',
|
|
167
|
-
baseUrl: ' http://localhost:8080/v1/ ',
|
|
168
|
-
models: [' llama-3.1-70b ', 'llama-3.1-70b', ''],
|
|
169
|
-
} as Record<string, unknown>
|
|
170
|
-
const result = normalizeStoredRecord('provider_configs', providerConfig, noopLoader).value as Record<string, unknown>
|
|
171
|
-
assert.equal(result.name, 'Llama.cpp')
|
|
172
|
-
assert.equal(result.baseUrl, 'http://localhost:8080/v1/')
|
|
173
|
-
assert.deepEqual(result.models, ['llama-3.1-70b'])
|
|
174
|
-
assert.equal(result.requiresApiKey, true)
|
|
175
|
-
assert.equal(result.isEnabled, true)
|
|
176
|
-
assert.equal(result.credentialId, null)
|
|
177
|
-
})
|
|
178
|
-
|
|
179
|
-
it('normalizes builtin override configs without treating them as custom providers', () => {
|
|
180
|
-
const providerConfig = {
|
|
181
|
-
id: 'openai',
|
|
182
|
-
type: 'builtin',
|
|
183
|
-
isEnabled: false,
|
|
184
|
-
} as Record<string, unknown>
|
|
185
|
-
const result = normalizeStoredRecord('provider_configs', providerConfig, noopLoader).value as Record<string, unknown>
|
|
186
|
-
assert.equal(result.type, 'builtin')
|
|
187
|
-
assert.equal(result.name, 'Built-in Provider')
|
|
188
|
-
assert.equal(result.baseUrl, '')
|
|
189
|
-
assert.deepEqual(result.models, [])
|
|
190
|
-
assert.equal(result.requiresApiKey, true)
|
|
191
|
-
assert.equal(result.isEnabled, false)
|
|
192
|
-
assert.equal(result.credentialId, null)
|
|
193
|
-
})
|
|
194
|
-
|
|
195
|
-
it('defaults createdAt and updatedAt for legacy records missing timestamps', () => {
|
|
196
|
-
const providerConfig = {
|
|
197
|
-
id: 'custom-old',
|
|
198
|
-
name: 'Old Provider',
|
|
199
|
-
type: 'custom',
|
|
200
|
-
baseUrl: 'http://localhost:8080/v1',
|
|
201
|
-
models: ['model-a'],
|
|
202
|
-
} as Record<string, unknown>
|
|
203
|
-
const result = normalizeStoredRecord('provider_configs', providerConfig, noopLoader).value as Record<string, unknown>
|
|
204
|
-
assert.equal(typeof result.createdAt, 'number')
|
|
205
|
-
assert.equal(typeof result.updatedAt, 'number')
|
|
206
|
-
assert.ok((result.createdAt as number) > 0)
|
|
207
|
-
assert.equal(result.updatedAt, result.createdAt)
|
|
208
|
-
})
|
|
209
|
-
})
|
|
210
|
-
|
|
211
|
-
// ---- Mission normalization ----
|
|
212
|
-
describe('missions', () => {
|
|
213
|
-
it('defaults status/phase/sourceRef', () => {
|
|
214
|
-
const mission = { id: 'm1' } as Record<string, unknown>
|
|
215
|
-
const result = normalizeStoredRecord('missions', mission, noopLoader).value as Record<string, unknown>
|
|
216
|
-
assert.equal(result.status, 'active')
|
|
217
|
-
assert.equal(result.phase, 'planning')
|
|
218
|
-
assert.deepEqual(result.sourceRef, { kind: 'manual' })
|
|
219
|
-
})
|
|
220
|
-
|
|
221
|
-
it('preserves valid status and phase', () => {
|
|
222
|
-
const mission = { id: 'm1', status: 'waiting', phase: 'executing' } as Record<string, unknown>
|
|
223
|
-
const result = normalizeStoredRecord('missions', mission, noopLoader).value as Record<string, unknown>
|
|
224
|
-
assert.equal(result.status, 'waiting')
|
|
225
|
-
assert.equal(result.phase, 'executing')
|
|
226
|
-
})
|
|
227
|
-
|
|
228
|
-
it('sourceRef from sessionId', () => {
|
|
229
|
-
const mission = { id: 'm1', sessionId: 'sess-1' } as Record<string, unknown>
|
|
230
|
-
const result = normalizeStoredRecord('missions', mission, noopLoader).value as Record<string, unknown>
|
|
231
|
-
assert.deepEqual(result.sourceRef, { kind: 'chat', sessionId: 'sess-1' })
|
|
232
|
-
})
|
|
233
|
-
|
|
234
|
-
it('waitState defaults', () => {
|
|
235
|
-
const mission = {
|
|
236
|
-
id: 'm1',
|
|
237
|
-
waitState: { kind: 'approval', reason: 'Needs sign-off' },
|
|
238
|
-
} as Record<string, unknown>
|
|
239
|
-
const result = normalizeStoredRecord('missions', mission, noopLoader).value as Record<string, unknown>
|
|
240
|
-
const ws = result.waitState as Record<string, unknown>
|
|
241
|
-
assert.equal(ws.kind, 'approval')
|
|
242
|
-
assert.equal(ws.reason, 'Needs sign-off')
|
|
243
|
-
})
|
|
244
|
-
})
|
|
245
|
-
|
|
246
|
-
// ---- DelegationJob normalization ----
|
|
247
|
-
describe('delegation_jobs', () => {
|
|
248
|
-
it('missionId cleanup', () => {
|
|
249
|
-
const job = { id: 'j1', missionId: ' m1 ' } as Record<string, unknown>
|
|
250
|
-
const result = normalizeStoredRecord('delegation_jobs', job, noopLoader).value as Record<string, unknown>
|
|
251
|
-
assert.equal(result.missionId, 'm1')
|
|
252
|
-
})
|
|
253
|
-
|
|
254
|
-
it('empty missionId is deleted', () => {
|
|
255
|
-
const job = { id: 'j1', missionId: ' ' } as Record<string, unknown>
|
|
256
|
-
const result = normalizeStoredRecord('delegation_jobs', job, noopLoader).value as Record<string, unknown>
|
|
257
|
-
assert.equal(result.missionId, undefined)
|
|
258
|
-
})
|
|
259
|
-
})
|
|
260
|
-
|
|
261
|
-
// ---- Unknown collection ----
|
|
262
|
-
describe('unknown collection', () => {
|
|
263
|
-
it('passes through unchanged', () => {
|
|
264
|
-
const data = { id: 'x1', foo: 'bar' }
|
|
265
|
-
const result = normalizeStoredRecord('widgets', data, noopLoader)
|
|
266
|
-
assert.deepEqual(result.value, data)
|
|
267
|
-
assert.equal(result.changed, false)
|
|
268
|
-
})
|
|
2
|
+
import { describe, it } from 'node:test'
|
|
3
|
+
|
|
4
|
+
import { normalizeStoredRecord } from '@/lib/server/storage-normalization'
|
|
5
|
+
|
|
6
|
+
const loadItem = () => null
|
|
7
|
+
|
|
8
|
+
describe('storage normalization for runtime execution records', () => {
|
|
9
|
+
it('backfills execution metadata on legacy runtime runs', () => {
|
|
10
|
+
const { value, changed } = normalizeStoredRecord('runtime_runs', {
|
|
11
|
+
id: 'run_1',
|
|
12
|
+
sessionId: 'sess_1',
|
|
13
|
+
source: 'task',
|
|
14
|
+
internal: false,
|
|
15
|
+
mode: 'followup',
|
|
16
|
+
status: 'queued',
|
|
17
|
+
messagePreview: 'Build the feature',
|
|
18
|
+
queuedAt: 123,
|
|
19
|
+
}, loadItem)
|
|
20
|
+
|
|
21
|
+
const record = value as Record<string, unknown>
|
|
22
|
+
assert.equal(changed, true)
|
|
23
|
+
assert.equal(record.kind, 'session_turn')
|
|
24
|
+
assert.equal(record.ownerType, 'session')
|
|
25
|
+
assert.equal(record.ownerId, 'sess_1')
|
|
26
|
+
assert.equal(record.parentExecutionId, null)
|
|
27
|
+
assert.equal(record.recoveryPolicy, 'restart_recoverable')
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it('backfills execution metadata on legacy runtime run events', () => {
|
|
31
|
+
const { value, changed } = normalizeStoredRecord('runtime_run_events', {
|
|
32
|
+
id: 'evt_1',
|
|
33
|
+
runId: 'run_1',
|
|
34
|
+
sessionId: 'sess_1',
|
|
35
|
+
timestamp: 123,
|
|
36
|
+
phase: 'status',
|
|
37
|
+
event: { t: 'md', text: '{}' },
|
|
38
|
+
}, loadItem)
|
|
39
|
+
|
|
40
|
+
const record = value as Record<string, unknown>
|
|
41
|
+
assert.equal(changed, true)
|
|
42
|
+
assert.equal(record.kind, 'session_turn')
|
|
43
|
+
assert.equal(record.ownerType, 'session')
|
|
44
|
+
assert.equal(record.ownerId, 'sess_1')
|
|
45
|
+
assert.equal(record.parentExecutionId, null)
|
|
269
46
|
})
|
|
270
47
|
})
|
|
@@ -369,6 +369,47 @@ function normalizeStoredDelegationJobRecord(value: unknown): unknown {
|
|
|
369
369
|
return job
|
|
370
370
|
}
|
|
371
371
|
|
|
372
|
+
function normalizeStoredRuntimeRunRecord(value: unknown): unknown {
|
|
373
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) return value
|
|
374
|
+
const run = value as StoredObject
|
|
375
|
+
|
|
376
|
+
if (typeof run.kind !== 'string' || !run.kind.trim()) run.kind = 'session_turn'
|
|
377
|
+
if (run.ownerType === undefined) run.ownerType = 'session'
|
|
378
|
+
if (run.ownerId === undefined) {
|
|
379
|
+
const sessionId = typeof run.sessionId === 'string' && run.sessionId.trim() ? run.sessionId.trim() : ''
|
|
380
|
+
run.ownerId = sessionId || null
|
|
381
|
+
}
|
|
382
|
+
if (run.parentExecutionId === undefined) run.parentExecutionId = null
|
|
383
|
+
if (run.recoveryPolicy === undefined) {
|
|
384
|
+
const source = typeof run.source === 'string' ? run.source.trim().toLowerCase() : ''
|
|
385
|
+
run.recoveryPolicy = source === 'heartbeat'
|
|
386
|
+
|| source === 'heartbeat-wake'
|
|
387
|
+
|| source === 'schedule'
|
|
388
|
+
|| source === 'task'
|
|
389
|
+
|| source === 'delegation'
|
|
390
|
+
|| source === 'subagent'
|
|
391
|
+
? 'restart_recoverable'
|
|
392
|
+
: 'ephemeral'
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
return run
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
function normalizeStoredRuntimeRunEventRecord(value: unknown): unknown {
|
|
399
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) return value
|
|
400
|
+
const event = value as StoredObject
|
|
401
|
+
|
|
402
|
+
if (typeof event.kind !== 'string' || !event.kind.trim()) event.kind = 'session_turn'
|
|
403
|
+
if (event.ownerType === undefined) event.ownerType = 'session'
|
|
404
|
+
if (event.ownerId === undefined) {
|
|
405
|
+
const sessionId = typeof event.sessionId === 'string' && event.sessionId.trim() ? event.sessionId.trim() : ''
|
|
406
|
+
event.ownerId = sessionId || null
|
|
407
|
+
}
|
|
408
|
+
if (event.parentExecutionId === undefined) event.parentExecutionId = null
|
|
409
|
+
|
|
410
|
+
return event
|
|
411
|
+
}
|
|
412
|
+
|
|
372
413
|
// --- Main dispatch function ---
|
|
373
414
|
|
|
374
415
|
export interface NormalizationResult {
|
|
@@ -393,6 +434,7 @@ export function normalizeStoredRecord(
|
|
|
393
434
|
&& table !== 'mission_events' && table !== 'delegation_jobs'
|
|
394
435
|
&& table !== 'schedules' && table !== 'sessions'
|
|
395
436
|
&& table !== 'provider_configs'
|
|
437
|
+
&& table !== 'runtime_runs' && table !== 'runtime_run_events'
|
|
396
438
|
) {
|
|
397
439
|
return { value, changed: false }
|
|
398
440
|
}
|
|
@@ -517,6 +559,14 @@ function normalizeStoredRecordInner(
|
|
|
517
559
|
return normalizeStoredDelegationJobRecord(value)
|
|
518
560
|
}
|
|
519
561
|
|
|
562
|
+
if (table === 'runtime_runs') {
|
|
563
|
+
return normalizeStoredRuntimeRunRecord(value)
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
if (table === 'runtime_run_events') {
|
|
567
|
+
return normalizeStoredRuntimeRunEventRecord(value)
|
|
568
|
+
}
|
|
569
|
+
|
|
520
570
|
if (table === 'schedules') {
|
|
521
571
|
return normalizeStoredScheduleRecord(value, loadItem)
|
|
522
572
|
}
|
|
@@ -547,11 +597,36 @@ function normalizeStoredRecordInner(
|
|
|
547
597
|
if ('plugins' in session) delete session.plugins
|
|
548
598
|
if ('mainLoopState' in session) delete session.mainLoopState
|
|
549
599
|
if ('missionSummary' in session) delete session.missionSummary
|
|
600
|
+
// Messages are now stored in session_messages table — ensure default empty array
|
|
601
|
+
if (!Array.isArray(session.messages)) session.messages = []
|
|
602
|
+
// Default messageCount for pre-migration blobs
|
|
603
|
+
if (typeof session.messageCount !== 'number') {
|
|
604
|
+
session.messageCount = (session.messages as unknown[]).length
|
|
605
|
+
}
|
|
550
606
|
// Default geminiSessionId for new field
|
|
551
607
|
if (session.geminiSessionId === undefined) session.geminiSessionId = null
|
|
552
608
|
// Default injectedMemoryIds for proactive recall dedup
|
|
553
609
|
if (!session.injectedMemoryIds || typeof session.injectedMemoryIds !== 'object') {
|
|
554
610
|
session.injectedMemoryIds = {}
|
|
555
611
|
}
|
|
612
|
+
// Validate runContext if present — leave null/undefined alone (created on demand)
|
|
613
|
+
if (session.runContext != null) {
|
|
614
|
+
if (typeof session.runContext !== 'object' || Array.isArray(session.runContext)) {
|
|
615
|
+
session.runContext = null
|
|
616
|
+
} else {
|
|
617
|
+
const rc = session.runContext as Record<string, unknown>
|
|
618
|
+
if (typeof rc.objective !== 'string' && rc.objective !== null) rc.objective = null
|
|
619
|
+
if (!Array.isArray(rc.constraints)) rc.constraints = []
|
|
620
|
+
if (!Array.isArray(rc.keyFacts)) rc.keyFacts = []
|
|
621
|
+
if (!Array.isArray(rc.discoveries)) rc.discoveries = []
|
|
622
|
+
if (!Array.isArray(rc.failedApproaches)) rc.failedApproaches = []
|
|
623
|
+
if (!Array.isArray(rc.currentPlan)) rc.currentPlan = []
|
|
624
|
+
if (!Array.isArray(rc.completedSteps)) rc.completedSteps = []
|
|
625
|
+
if (!Array.isArray(rc.blockers)) rc.blockers = []
|
|
626
|
+
if (typeof rc.parentContext !== 'string' && rc.parentContext !== null) rc.parentContext = null
|
|
627
|
+
if (typeof rc.updatedAt !== 'number') rc.updatedAt = Date.now()
|
|
628
|
+
if (typeof rc.version !== 'number') rc.version = 0
|
|
629
|
+
}
|
|
630
|
+
}
|
|
556
631
|
return session
|
|
557
632
|
}
|
|
@@ -100,6 +100,9 @@ export function withTransaction<T>(fn: () => T): T {
|
|
|
100
100
|
return wrapped()
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
+
/** Internal: raw database handle for specialized repositories (e.g. message-repository). */
|
|
104
|
+
export function getDb(): InstanceType<typeof Database> { return db }
|
|
105
|
+
|
|
103
106
|
type StoredObject = Record<string, unknown>
|
|
104
107
|
type StoredSessionRecord = Session
|
|
105
108
|
type StoredAgentRecord = Agent
|
|
@@ -157,6 +160,8 @@ const COLLECTIONS = [
|
|
|
157
160
|
'provider_health',
|
|
158
161
|
'swarm_snapshots',
|
|
159
162
|
'main_loop_states',
|
|
163
|
+
'working_states',
|
|
164
|
+
'daemon_status',
|
|
160
165
|
] as const
|
|
161
166
|
|
|
162
167
|
export type StorageCollection = (typeof COLLECTIONS)[number]
|
|
@@ -180,6 +185,14 @@ db.exec(`CREATE TABLE IF NOT EXISTS runtime_locks (
|
|
|
180
185
|
updated_at INTEGER NOT NULL
|
|
181
186
|
)`)
|
|
182
187
|
|
|
188
|
+
// Relational message storage — messages extracted from session blobs (Phase 1)
|
|
189
|
+
db.exec(`CREATE TABLE IF NOT EXISTS session_messages (
|
|
190
|
+
session_id TEXT NOT NULL,
|
|
191
|
+
seq INTEGER NOT NULL,
|
|
192
|
+
data TEXT NOT NULL,
|
|
193
|
+
PRIMARY KEY (session_id, seq)
|
|
194
|
+
) WITHOUT ROWID`)
|
|
195
|
+
|
|
183
196
|
// --- Internal normalize helper that binds the loadItem dependency ---
|
|
184
197
|
function normalize(table: string, value: unknown): NormalizationResult {
|
|
185
198
|
return normalizeStoredRecord(table, value, loadCollectionItem)
|
|
@@ -1594,6 +1607,12 @@ export const loadPersistedMainLoopState = mainLoopStatesStore.loadItem
|
|
|
1594
1607
|
export const upsertPersistedMainLoopState = mainLoopStatesStore.upsert
|
|
1595
1608
|
export const deletePersistedMainLoopState = mainLoopStatesStore.deleteItem
|
|
1596
1609
|
|
|
1610
|
+
// --- Working States ---
|
|
1611
|
+
const workingStatesStore = createCollectionStore('working_states')
|
|
1612
|
+
export const loadPersistedWorkingState = workingStatesStore.loadItem
|
|
1613
|
+
export const upsertPersistedWorkingState = workingStatesStore.upsert
|
|
1614
|
+
export const deletePersistedWorkingState = workingStatesStore.deleteItem
|
|
1615
|
+
|
|
1597
1616
|
export function getSessionMessages(sessionId: string): Message[] {
|
|
1598
1617
|
const session = loadSession(sessionId)
|
|
1599
1618
|
return Array.isArray(session?.messages) ? session.messages : []
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Session } from '@/types'
|
|
2
2
|
import { getProvider, streamChatWithFailover } from '@/lib/providers'
|
|
3
|
-
import {
|
|
3
|
+
import { requireCredentialSecret, resolveCredentialSecret } from './credentials/credential-service'
|
|
4
4
|
import { extractDocumentArtifact, type DocumentArtifact } from './document-utils'
|
|
5
5
|
|
|
6
6
|
type JsonSchemaLike = Record<string, unknown>
|
|
@@ -31,21 +31,10 @@ function resolveApiKey(session: ExtractionSession): string | null {
|
|
|
31
31
|
if (!provider) throw new Error(`Unknown provider: ${session.provider}`)
|
|
32
32
|
if (provider.requiresApiKey) {
|
|
33
33
|
if (!session.credentialId) throw new Error('No API key configured for this session')
|
|
34
|
-
|
|
35
|
-
const cred = creds[session.credentialId]
|
|
36
|
-
if (!cred?.encryptedKey) throw new Error('API key not found. Please add one in Settings.')
|
|
37
|
-
return decryptKey(cred.encryptedKey)
|
|
34
|
+
return requireCredentialSecret(session.credentialId, 'API key not found. Please add one in Settings.')
|
|
38
35
|
}
|
|
39
36
|
if (provider.optionalApiKey && session.credentialId) {
|
|
40
|
-
|
|
41
|
-
const cred = creds[session.credentialId]
|
|
42
|
-
if (cred?.encryptedKey) {
|
|
43
|
-
try {
|
|
44
|
-
return decryptKey(cred.encryptedKey)
|
|
45
|
-
} catch {
|
|
46
|
-
return null
|
|
47
|
-
}
|
|
48
|
-
}
|
|
37
|
+
return resolveCredentialSecret(session.credentialId)
|
|
49
38
|
}
|
|
50
39
|
return null
|
|
51
40
|
}
|