@swarmclawai/swarmclaw 1.2.4 → 1.2.5
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 +15 -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/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,7 +5,13 @@ 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/
|
|
8
|
+
import { loadAgents } from '@/lib/server/agents/agent-repository'
|
|
9
|
+
import { classifyMessage } from '@/lib/server/chat-execution/message-classifier'
|
|
10
|
+
import {
|
|
11
|
+
buildDelegationTaskProfile,
|
|
12
|
+
resolveBestDelegateTarget,
|
|
13
|
+
type DelegationWorkType,
|
|
14
|
+
} from '@/lib/server/agents/delegation-advisory'
|
|
9
15
|
import {
|
|
10
16
|
cancelDelegationJob,
|
|
11
17
|
getDelegationJob,
|
|
@@ -57,6 +63,9 @@ const subagentToolSchema = z.object({
|
|
|
57
63
|
action: z.enum(SUBAGENT_ACTIONS).optional(),
|
|
58
64
|
agentId: z.string().optional(),
|
|
59
65
|
message: z.string().optional(),
|
|
66
|
+
selectionMode: z.enum(['explicit', 'best_fit']).optional(),
|
|
67
|
+
workType: z.enum(['coding', 'research', 'writing', 'review', 'operations', 'general']).optional(),
|
|
68
|
+
requiredCapabilities: z.union([z.array(z.string()), z.string()]).optional(),
|
|
60
69
|
cwd: z.string().optional(),
|
|
61
70
|
shareBrowserProfile: z.boolean().optional(),
|
|
62
71
|
jobId: z.string().optional(),
|
|
@@ -88,6 +97,7 @@ export function resolveSubagentBrowserProfileId(
|
|
|
88
97
|
// ---------------------------------------------------------------------------
|
|
89
98
|
|
|
90
99
|
interface ActionContext {
|
|
100
|
+
agentId?: string
|
|
91
101
|
sessionId?: string
|
|
92
102
|
cwd: string
|
|
93
103
|
delegationTargetMode?: 'all' | 'selected'
|
|
@@ -155,10 +165,74 @@ export function coerceSubagentActionArgs(rawArgs: Record<string, unknown>): Reco
|
|
|
155
165
|
|
|
156
166
|
const parsedJobIds = parseJsonLike(coerced.jobIds)
|
|
157
167
|
if (Array.isArray(parsedJobIds)) coerced.jobIds = parsedJobIds
|
|
168
|
+
const parsedRequiredCapabilities = parseJsonLike(coerced.requiredCapabilities)
|
|
169
|
+
if (Array.isArray(parsedRequiredCapabilities)) coerced.requiredCapabilities = parsedRequiredCapabilities
|
|
158
170
|
|
|
159
171
|
return coerced
|
|
160
172
|
}
|
|
161
173
|
|
|
174
|
+
function normalizeWorkType(value: unknown): DelegationWorkType | null {
|
|
175
|
+
if (
|
|
176
|
+
value === 'coding'
|
|
177
|
+
|| value === 'research'
|
|
178
|
+
|| value === 'writing'
|
|
179
|
+
|| value === 'review'
|
|
180
|
+
|| value === 'operations'
|
|
181
|
+
|| value === 'general'
|
|
182
|
+
) {
|
|
183
|
+
return value
|
|
184
|
+
}
|
|
185
|
+
return null
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function normalizeStringList(value: unknown): string[] {
|
|
189
|
+
if (!Array.isArray(value)) return []
|
|
190
|
+
const seen = new Set<string>()
|
|
191
|
+
const out: string[] = []
|
|
192
|
+
for (const entry of value) {
|
|
193
|
+
const trimmed = typeof entry === 'string' ? entry.trim() : ''
|
|
194
|
+
if (!trimmed || seen.has(trimmed.toLowerCase())) continue
|
|
195
|
+
seen.add(trimmed.toLowerCase())
|
|
196
|
+
out.push(trimmed)
|
|
197
|
+
}
|
|
198
|
+
return out
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
async function resolveBestFitAgentSelection(
|
|
202
|
+
args: Record<string, unknown>,
|
|
203
|
+
ctx: ActionContext,
|
|
204
|
+
): Promise<{ agentId: string; workType: DelegationWorkType; requiredCapabilities: string[] } | null> {
|
|
205
|
+
const message = typeof args.message === 'string' ? args.message.trim() : ''
|
|
206
|
+
if (!message) return null
|
|
207
|
+
const explicitWorkType = normalizeWorkType(args.workType)
|
|
208
|
+
const explicitCapabilities = normalizeStringList(args.requiredCapabilities)
|
|
209
|
+
const classification = (!explicitWorkType && explicitCapabilities.length === 0 && ctx.sessionId)
|
|
210
|
+
? await classifyMessage({
|
|
211
|
+
sessionId: ctx.sessionId,
|
|
212
|
+
agentId: ctx.agentId || null,
|
|
213
|
+
message,
|
|
214
|
+
}).catch(() => null)
|
|
215
|
+
: null
|
|
216
|
+
const profile = buildDelegationTaskProfile({
|
|
217
|
+
classification,
|
|
218
|
+
workType: explicitWorkType,
|
|
219
|
+
requiredCapabilities: explicitCapabilities,
|
|
220
|
+
})
|
|
221
|
+
const selection = resolveBestDelegateTarget({
|
|
222
|
+
currentAgentId: ctx.agentId || null,
|
|
223
|
+
agents: loadAgents(),
|
|
224
|
+
profile,
|
|
225
|
+
delegationTargetMode: ctx.delegationTargetMode,
|
|
226
|
+
delegationTargetAgentIds: ctx.delegationTargetAgentIds,
|
|
227
|
+
})
|
|
228
|
+
if (!selection) return null
|
|
229
|
+
return {
|
|
230
|
+
agentId: selection.agentId,
|
|
231
|
+
workType: profile.workType,
|
|
232
|
+
requiredCapabilities: profile.requiredCapabilities,
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
162
236
|
function requireString(args: Record<string, unknown>, key: string): string {
|
|
163
237
|
const val = typeof args[key] === 'string' ? (args[key] as string).trim() : ''
|
|
164
238
|
if (!val) throw new Error(`${key} is required.`)
|
|
@@ -373,10 +447,21 @@ function handleSwarmCancel(args: Record<string, unknown>): string {
|
|
|
373
447
|
}
|
|
374
448
|
|
|
375
449
|
async function handleStart(args: Record<string, unknown>, ctx: ActionContext): Promise<string> {
|
|
376
|
-
const
|
|
450
|
+
const selectionMode = args.selectionMode === 'best_fit' ? 'best_fit' : 'explicit'
|
|
451
|
+
let agentId = (args.agentId ?? args.agent_id) as string | undefined
|
|
377
452
|
const message = args.message as string | undefined
|
|
378
|
-
if (!agentId) return 'Error: agentId is required.'
|
|
379
453
|
if (!message) return 'Error: message is required.'
|
|
454
|
+
let selectedProfile: { workType: DelegationWorkType; requiredCapabilities: string[] } | null = null
|
|
455
|
+
if (selectionMode === 'best_fit') {
|
|
456
|
+
const resolved = await resolveBestFitAgentSelection(args, ctx)
|
|
457
|
+
if (!resolved) return 'Error: no eligible delegate agent available for best_fit selection.'
|
|
458
|
+
agentId = resolved.agentId
|
|
459
|
+
selectedProfile = {
|
|
460
|
+
workType: resolved.workType,
|
|
461
|
+
requiredCapabilities: resolved.requiredCapabilities,
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
if (!agentId) return 'Error: agentId is required.'
|
|
380
465
|
const targetError = validateAllowedSubagentTarget(agentId, ctx)
|
|
381
466
|
if (targetError) return targetError
|
|
382
467
|
|
|
@@ -393,10 +478,13 @@ async function handleStart(args: Record<string, unknown>, ctx: ActionContext): P
|
|
|
393
478
|
return JSON.stringify({
|
|
394
479
|
jobId: handle.jobId,
|
|
395
480
|
status: 'running',
|
|
481
|
+
selectionMode,
|
|
396
482
|
agentId: handle.agentId,
|
|
397
483
|
agentName: handle.agentName,
|
|
398
484
|
sessionId: handle.sessionId,
|
|
399
485
|
lineageId: handle.lineageId,
|
|
486
|
+
workType: selectedProfile?.workType || null,
|
|
487
|
+
requiredCapabilities: selectedProfile?.requiredCapabilities || [],
|
|
400
488
|
})
|
|
401
489
|
}
|
|
402
490
|
|
|
@@ -404,6 +492,7 @@ async function handleStart(args: Record<string, unknown>, ctx: ActionContext): P
|
|
|
404
492
|
return JSON.stringify({
|
|
405
493
|
jobId: result.jobId,
|
|
406
494
|
status: result.status,
|
|
495
|
+
selectionMode,
|
|
407
496
|
agentId: result.agentId,
|
|
408
497
|
agentName: result.agentName,
|
|
409
498
|
sessionId: result.sessionId,
|
|
@@ -412,6 +501,8 @@ async function handleStart(args: Record<string, unknown>, ctx: ActionContext): P
|
|
|
412
501
|
depth: result.depth,
|
|
413
502
|
childCount: result.childCount,
|
|
414
503
|
durationMs: result.durationMs,
|
|
504
|
+
workType: selectedProfile?.workType || null,
|
|
505
|
+
requiredCapabilities: selectedProfile?.requiredCapabilities || [],
|
|
415
506
|
})
|
|
416
507
|
}
|
|
417
508
|
|
|
@@ -463,13 +554,13 @@ const SubagentExtension: Extension = {
|
|
|
463
554
|
description: 'Delegate tasks to other specialized agents with resumable job handles.',
|
|
464
555
|
hooks: {
|
|
465
556
|
getCapabilityDescription: () =>
|
|
466
|
-
'Delegate tasks to other agents (spawn_subagent). Single task: action "start"
|
|
557
|
+
'Delegate tasks to other agents (spawn_subagent). Single task: action "start" with `agentId`, or use `selectionMode:"best_fit"` with `message` plus optional `workType`/`requiredCapabilities`. '
|
|
467
558
|
+ 'Multiple independent tasks: action "batch" with a tasks array. '
|
|
468
559
|
+ 'Event-driven parallel with status tracking: action "swarm" with a tasks array. '
|
|
469
560
|
+ 'Background swarms return a swarmId you can pass to swarm_status, swarm_list, and swarm_cancel.',
|
|
470
561
|
getOperatingGuidance: () => [
|
|
471
562
|
'SUBAGENT DISPATCH RULES:',
|
|
472
|
-
'- Single task → action "start" with agentId + message
|
|
563
|
+
'- Single task → action "start" with `agentId` + `message`, or `selectionMode:"best_fit"` with `message` and optional `workType` / `requiredCapabilities`.',
|
|
473
564
|
'- 2+ independent tasks → action "batch" with tasks array [{agentId, message}, ...]. Use `executionMode:"serial"` when local models are rate-limited.',
|
|
474
565
|
'- Background coordination example → `{"action":"swarm","tasks":[...],"background":true}` and then read the returned `swarmId` before calling `swarm_status` or `swarm_cancel`.',
|
|
475
566
|
'- If your final answer depends on all delegated results, set `waitForCompletion:true` and do not summarize early.',
|
|
@@ -483,8 +574,9 @@ const SubagentExtension: Extension = {
|
|
|
483
574
|
{
|
|
484
575
|
name: 'spawn_subagent',
|
|
485
576
|
description: 'Delegate tasks to other agents. '
|
|
486
|
-
+ 'Actions: start (single agent), batch (2+ tasks via "tasks" array), swarm (multi-agent execution with status tracking via "tasks" array). '
|
|
577
|
+
+ 'Actions: start (single agent, either explicit `agentId` or `selectionMode:"best_fit"`), batch (2+ tasks via "tasks" array), swarm (multi-agent execution with status tracking via "tasks" array). '
|
|
487
578
|
+ 'Management: status, list, wait, wait_all, cancel, lineage, aggregate, swarm_status, swarm_list, swarm_cancel. '
|
|
579
|
+
+ 'In `best_fit` mode, provide `message` and optionally `workType` / `requiredCapabilities`; the runtime will choose the best allowed teammate and return the chosen agent in the tool output. '
|
|
488
580
|
+ 'For multiple independent tasks, prefer one `batch` or `swarm` call with tasks:[{agentId,message},...] over calling `start` repeatedly. '
|
|
489
581
|
+ 'When the final answer depends on every delegated result, keep waitForCompletion enabled so you can synthesize after all children finish. '
|
|
490
582
|
+ 'Use executionMode:"serial" to avoid rate limits on local models. '
|
|
@@ -495,6 +587,21 @@ const SubagentExtension: Extension = {
|
|
|
495
587
|
action: { type: 'string', enum: ['start', 'status', 'list', 'wait', 'wait_all', 'cancel', 'lineage', 'batch', 'aggregate', 'swarm', 'swarm_status', 'swarm_list', 'swarm_cancel'] },
|
|
496
588
|
agentId: { type: 'string' },
|
|
497
589
|
message: { type: 'string' },
|
|
590
|
+
selectionMode: {
|
|
591
|
+
type: 'string',
|
|
592
|
+
enum: ['explicit', 'best_fit'],
|
|
593
|
+
description: 'Use "explicit" to target `agentId` directly, or "best_fit" to let the runtime choose the best allowed delegate.',
|
|
594
|
+
},
|
|
595
|
+
workType: {
|
|
596
|
+
type: 'string',
|
|
597
|
+
enum: ['coding', 'research', 'writing', 'review', 'operations', 'general'],
|
|
598
|
+
description: 'Optional hint for `best_fit` selection.',
|
|
599
|
+
},
|
|
600
|
+
requiredCapabilities: {
|
|
601
|
+
type: 'array',
|
|
602
|
+
items: { type: 'string' },
|
|
603
|
+
description: 'Optional explicit capability requirements for `best_fit` selection.',
|
|
604
|
+
},
|
|
498
605
|
cwd: { type: 'string' },
|
|
499
606
|
shareBrowserProfile: {
|
|
500
607
|
type: 'boolean',
|
|
@@ -532,7 +639,17 @@ const SubagentExtension: Extension = {
|
|
|
532
639
|
},
|
|
533
640
|
required: []
|
|
534
641
|
},
|
|
535
|
-
execute: async (args, context) =>
|
|
642
|
+
execute: async (args, context) => {
|
|
643
|
+
const sessionAgentId = context.session.agentId || undefined
|
|
644
|
+
const sessionAgent = sessionAgentId ? loadAgents()[sessionAgentId] : null
|
|
645
|
+
return executeSubagentAction(args, {
|
|
646
|
+
agentId: sessionAgentId,
|
|
647
|
+
sessionId: context.session.id,
|
|
648
|
+
cwd: context.session.cwd || process.cwd(),
|
|
649
|
+
delegationTargetMode: sessionAgent?.delegationTargetMode,
|
|
650
|
+
delegationTargetAgentIds: sessionAgent?.delegationTargetAgentIds,
|
|
651
|
+
})
|
|
652
|
+
}
|
|
536
653
|
}
|
|
537
654
|
]
|
|
538
655
|
}
|
|
@@ -562,6 +679,7 @@ export function buildSubagentTools(bctx: ToolBuildContext): StructuredToolInterf
|
|
|
562
679
|
return [
|
|
563
680
|
tool(
|
|
564
681
|
async (args) => executeSubagentAction(args, {
|
|
682
|
+
agentId: bctx.ctx?.agentId || undefined,
|
|
565
683
|
sessionId: bctx.ctx?.sessionId || undefined,
|
|
566
684
|
cwd: bctx.cwd,
|
|
567
685
|
delegationTargetMode: bctx.ctx?.delegationTargetMode,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod'
|
|
2
2
|
import { tool, type StructuredToolInterface } from '@langchain/core/tools'
|
|
3
3
|
import { loadAgents, loadTasks, loadSessions } from '../storage'
|
|
4
|
+
import { getRecentMessages } from '@/lib/server/messages/message-repository'
|
|
4
5
|
import { resolveTeam, resolveReachableAgentIds } from '../agents/team-resolution'
|
|
5
6
|
import { getAgentDirectory } from '../agents/agent-registry'
|
|
6
7
|
import { normalizeToolInputArgs } from './normalize-tool-args'
|
|
@@ -139,12 +140,12 @@ async function formatPeerContext(agentId: string, peerId: string): Promise<strin
|
|
|
139
140
|
try {
|
|
140
141
|
const sessions = allSessions || loadSessions()
|
|
141
142
|
const peerSessions = Object.values(sessions)
|
|
142
|
-
.filter((s) => s.agentId === peerId &&
|
|
143
|
+
.filter((s) => s.agentId === peerId && (s.messageCount ?? 0) > 0)
|
|
143
144
|
.sort((a, b) => (b.lastActiveAt || 0) - (a.lastActiveAt || 0))
|
|
144
145
|
|
|
145
146
|
const latestSession = peerSessions.length > 0 ? peerSessions[0] : null
|
|
146
|
-
if (latestSession
|
|
147
|
-
const recentMessages = latestSession.
|
|
147
|
+
if (latestSession) {
|
|
148
|
+
const recentMessages = getRecentMessages(latestSession.id, 20)
|
|
148
149
|
.filter((m) => m.role === 'assistant' && Array.isArray(m.toolEvents) && m.toolEvents.length > 0)
|
|
149
150
|
.slice(-MAX_RECENT_ACTIVITY)
|
|
150
151
|
result.recentActivity = recentMessages.map((m) => {
|
|
@@ -1181,64 +1181,6 @@ const WalletExtension: Extension = {
|
|
|
1181
1181
|
'If quote or assembly APIs keep failing, stop venue-shopping and use `wallet_tool` action `call_contract` for direct read-only onchain state or quote reads.',
|
|
1182
1182
|
'Treat `wallet_tool` as a server-side wallet capability. It does not inject a browser wallet extension or complete third-party wallet-connect prompts for you.',
|
|
1183
1183
|
],
|
|
1184
|
-
requestMatchers: [
|
|
1185
|
-
{
|
|
1186
|
-
capability: TOOL_CAPABILITY.walletInspect,
|
|
1187
|
-
patterns: [
|
|
1188
|
-
'wallet',
|
|
1189
|
-
'balance',
|
|
1190
|
-
'address',
|
|
1191
|
-
'fund',
|
|
1192
|
-
'transfer',
|
|
1193
|
-
'send',
|
|
1194
|
-
'deposit',
|
|
1195
|
-
'withdraw',
|
|
1196
|
-
'swap',
|
|
1197
|
-
'bridge',
|
|
1198
|
-
'onchain',
|
|
1199
|
-
'token',
|
|
1200
|
-
'gas',
|
|
1201
|
-
'usdc',
|
|
1202
|
-
'eth',
|
|
1203
|
-
'sol',
|
|
1204
|
-
'solana',
|
|
1205
|
-
'ethereum',
|
|
1206
|
-
'arbitrum',
|
|
1207
|
-
'base',
|
|
1208
|
-
'wallet connect',
|
|
1209
|
-
'walletconnect',
|
|
1210
|
-
'dex',
|
|
1211
|
-
'erc-20',
|
|
1212
|
-
'spl',
|
|
1213
|
-
'trade on',
|
|
1214
|
-
'quote swap',
|
|
1215
|
-
],
|
|
1216
|
-
},
|
|
1217
|
-
{
|
|
1218
|
-
capability: TOOL_CAPABILITY.walletExecute,
|
|
1219
|
-
patterns: [
|
|
1220
|
-
'swap',
|
|
1221
|
-
'trade',
|
|
1222
|
-
'buy token',
|
|
1223
|
-
'sell token',
|
|
1224
|
-
'sign message',
|
|
1225
|
-
'sign typed data',
|
|
1226
|
-
'signature',
|
|
1227
|
-
'typed data',
|
|
1228
|
-
'eip-712',
|
|
1229
|
-
'sign transaction',
|
|
1230
|
-
'send transaction',
|
|
1231
|
-
'simulate transaction',
|
|
1232
|
-
'broadcast transaction',
|
|
1233
|
-
'contract call',
|
|
1234
|
-
'call contract',
|
|
1235
|
-
'read contract',
|
|
1236
|
-
'calldata',
|
|
1237
|
-
'approve token',
|
|
1238
|
-
'raw transaction',
|
|
1239
|
-
],
|
|
1240
|
-
},
|
|
1241
|
-
],
|
|
1242
1184
|
},
|
|
1243
1185
|
parameters: {
|
|
1244
1186
|
type: 'object',
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { Session } from '@/types'
|
|
2
|
+
import { loadSession } from '@/lib/server/sessions/session-repository'
|
|
3
|
+
|
|
4
|
+
export interface SessionLineageIds {
|
|
5
|
+
parentSessionId: string | null
|
|
6
|
+
rootSessionId: string | null
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function resolveSessionLineageIds(
|
|
10
|
+
session: Pick<Session, 'id' | 'parentSessionId'> | null | undefined,
|
|
11
|
+
opts?: {
|
|
12
|
+
loadSessionById?: (id: string) => Session | null
|
|
13
|
+
maxDepth?: number
|
|
14
|
+
},
|
|
15
|
+
): SessionLineageIds {
|
|
16
|
+
const parentSessionId = typeof session?.parentSessionId === 'string' && session.parentSessionId.trim()
|
|
17
|
+
? session.parentSessionId.trim()
|
|
18
|
+
: null
|
|
19
|
+
const currentSessionId = typeof session?.id === 'string' && session.id.trim()
|
|
20
|
+
? session.id.trim()
|
|
21
|
+
: null
|
|
22
|
+
|
|
23
|
+
if (!currentSessionId) {
|
|
24
|
+
return {
|
|
25
|
+
parentSessionId,
|
|
26
|
+
rootSessionId: parentSessionId,
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const loadSessionById = opts?.loadSessionById || loadSession
|
|
31
|
+
const maxDepth = typeof opts?.maxDepth === 'number' && opts.maxDepth > 0 ? opts.maxDepth : 25
|
|
32
|
+
const seen = new Set<string>([currentSessionId])
|
|
33
|
+
|
|
34
|
+
let rootSessionId = currentSessionId
|
|
35
|
+
let cursor = parentSessionId
|
|
36
|
+
let depth = 0
|
|
37
|
+
|
|
38
|
+
while (cursor && depth < maxDepth) {
|
|
39
|
+
if (seen.has(cursor)) break
|
|
40
|
+
seen.add(cursor)
|
|
41
|
+
rootSessionId = cursor
|
|
42
|
+
const parent = loadSessionById(cursor)
|
|
43
|
+
const nextParentId = typeof parent?.parentSessionId === 'string' && parent.parentSessionId.trim()
|
|
44
|
+
? parent.parentSessionId.trim()
|
|
45
|
+
: null
|
|
46
|
+
if (!nextParentId) break
|
|
47
|
+
cursor = nextParentId
|
|
48
|
+
depth += 1
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
parentSessionId,
|
|
53
|
+
rootSessionId,
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -3,7 +3,6 @@ import type { Message, Session } from '@/types'
|
|
|
3
3
|
import {
|
|
4
4
|
deleteSession as deleteStoredSession,
|
|
5
5
|
disableAllSessionHeartbeats as disableAllStoredSessionHeartbeats,
|
|
6
|
-
getSessionMessages as getStoredSessionMessages,
|
|
7
6
|
loadSession as loadStoredSession,
|
|
8
7
|
loadSessions as loadStoredSessions,
|
|
9
8
|
patchSession as patchStoredSession,
|
|
@@ -11,6 +10,7 @@ import {
|
|
|
11
10
|
upsertSession as upsertStoredSession,
|
|
12
11
|
} from '@/lib/server/storage'
|
|
13
12
|
import { createRecordRepository } from '@/lib/server/persistence/repository-utils'
|
|
13
|
+
import { getMessages } from '@/lib/server/messages/message-repository'
|
|
14
14
|
|
|
15
15
|
export const sessionRepository = createRecordRepository<Session>(
|
|
16
16
|
'sessions',
|
|
@@ -72,7 +72,7 @@ export function deleteSession(id: string): void {
|
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
export function getSessionMessages(sessionId: string): Message[] {
|
|
75
|
-
return
|
|
75
|
+
return getMessages(sessionId)
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
export function disableAllSessionHeartbeats(): number {
|
|
@@ -15,13 +15,15 @@ import type {
|
|
|
15
15
|
import { errorMessage } from '@/lib/shared-utils'
|
|
16
16
|
import { buildLLM } from '@/lib/server/build-llm'
|
|
17
17
|
import {
|
|
18
|
+
loadLearnedSkill,
|
|
18
19
|
loadLearnedSkills,
|
|
19
|
-
|
|
20
|
-
loadSessions,
|
|
20
|
+
loadRunReflection,
|
|
21
21
|
loadSkills,
|
|
22
|
-
|
|
22
|
+
upsertRunReflection,
|
|
23
23
|
upsertLearnedSkill,
|
|
24
|
-
} from '@/lib/server/
|
|
24
|
+
} from '@/lib/server/skills/skill-repository'
|
|
25
|
+
import { loadSession } from '@/lib/server/sessions/session-repository'
|
|
26
|
+
import { getMessages, getMessageCount } from '@/lib/server/messages/message-repository'
|
|
25
27
|
import { buildSessionTranscript } from './skill-suggestions'
|
|
26
28
|
import { normalizeSkillPayload } from './skills-normalize'
|
|
27
29
|
import { onNextIdleWindow } from '@/lib/server/runtime/idle-window'
|
|
@@ -169,7 +171,7 @@ function ensureHeading(name: string, content: string): string {
|
|
|
169
171
|
}
|
|
170
172
|
|
|
171
173
|
function collectRecentUserText(session: Session): string {
|
|
172
|
-
return session.
|
|
174
|
+
return getMessages(session.id)
|
|
173
175
|
.filter((message) => message?.role === 'user' && !message.suppressed && typeof message.text === 'string')
|
|
174
176
|
.slice(-3)
|
|
175
177
|
.map((message) => message.text)
|
|
@@ -263,7 +265,7 @@ function buildFailureSourceHash(input: {
|
|
|
263
265
|
|
|
264
266
|
function buildObservation(input: ObserveLearnedSkillRunInput, session: Session): Observation | null {
|
|
265
267
|
if (input.status === 'cancelled' || input.status === 'queued' || input.status === 'running') return null
|
|
266
|
-
if (
|
|
268
|
+
if (getMessageCount(session.id) < 2) return null
|
|
267
269
|
|
|
268
270
|
const toolEvents = Array.isArray(input.toolEvents) ? input.toolEvents : []
|
|
269
271
|
const sourceSnippet = trimText(buildSessionTranscript(session), 700)
|
|
@@ -307,7 +309,7 @@ function buildDraftPrompt(params: {
|
|
|
307
309
|
reflection: RunReflection | null
|
|
308
310
|
existingSkillNames: string[]
|
|
309
311
|
}): string {
|
|
310
|
-
const toolSummary = params.session.
|
|
312
|
+
const toolSummary = getMessages(params.session.id)
|
|
311
313
|
.flatMap((message) => message?.toolEvents || [])
|
|
312
314
|
.slice(-8)
|
|
313
315
|
.map((event) => `- ${event.name} (${event.error ? 'error' : 'ok'})`)
|
|
@@ -455,20 +457,20 @@ function appendReflectionLearnedSkillNotes(params: {
|
|
|
455
457
|
skillIds: string[]
|
|
456
458
|
}): void {
|
|
457
459
|
if (!params.reflection || (params.notes.length === 0 && params.skillIds.length === 0)) return
|
|
458
|
-
const
|
|
459
|
-
const current = reflections[params.reflection.id]
|
|
460
|
+
const current = loadRunReflection(params.reflection.id)
|
|
460
461
|
if (!current) return
|
|
461
|
-
current.
|
|
462
|
-
...
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
462
|
+
upsertRunReflection(current.id, {
|
|
463
|
+
...current,
|
|
464
|
+
learnedSkillNotes: Array.from(new Set([
|
|
465
|
+
...(current.learnedSkillNotes || []),
|
|
466
|
+
...params.notes,
|
|
467
|
+
])),
|
|
468
|
+
learnedSkillIds: Array.from(new Set([
|
|
469
|
+
...(current.learnedSkillIds || []),
|
|
470
|
+
...params.skillIds,
|
|
471
|
+
])),
|
|
472
|
+
updatedAt: Date.now(),
|
|
473
|
+
})
|
|
472
474
|
}
|
|
473
475
|
|
|
474
476
|
function matchesSelectedLearnedSkill(session: Session, skill: LearnedSkill): boolean {
|
|
@@ -599,8 +601,7 @@ export async function observeLearnedSkillRunOutcome(
|
|
|
599
601
|
): Promise<{ notes: string[]; skillIds: string[] }> {
|
|
600
602
|
const agentId = typeof input.agentId === 'string' ? input.agentId.trim() : ''
|
|
601
603
|
if (!agentId) return { notes: [], skillIds: [] }
|
|
602
|
-
const
|
|
603
|
-
const session = sessions[input.sessionId] as Session | undefined
|
|
604
|
+
const session = loadSession(input.sessionId)
|
|
604
605
|
if (!session) return { notes: [], skillIds: [] }
|
|
605
606
|
|
|
606
607
|
const observation = buildObservation(input, session)
|
|
@@ -732,7 +733,7 @@ export async function observeLearnedSkillRunOutcome(
|
|
|
732
733
|
|
|
733
734
|
if (validation.status === 'passed') {
|
|
734
735
|
const parent = target.parentSkillId
|
|
735
|
-
?
|
|
736
|
+
? loadLearnedSkill(target.parentSkillId)
|
|
736
737
|
: null
|
|
737
738
|
if (parent && parent.lifecycle === 'active') {
|
|
738
739
|
target.lifecycle = 'review_ready'
|
|
@@ -11,8 +11,9 @@ import type {
|
|
|
11
11
|
} from '@/types'
|
|
12
12
|
import { dedup, hmrSingleton } from '@/lib/shared-utils'
|
|
13
13
|
import { expandExtensionIds, getExtensionAliases, normalizeExtensionId } from '@/lib/server/tool-aliases'
|
|
14
|
-
import { loadLearnedSkills, loadSettings, loadSkills } from '@/lib/server/storage'
|
|
15
14
|
import { cosineSimilarity, getEmbedding } from '@/lib/server/embeddings'
|
|
15
|
+
import { loadSettings } from '@/lib/server/settings/settings-repository'
|
|
16
|
+
import { loadLearnedSkills, loadSkills } from '@/lib/server/skills/skill-repository'
|
|
16
17
|
import { discoverSkills, type DiscoveredSkill } from './skill-discovery'
|
|
17
18
|
import { evaluateSkillEligibility } from './skill-eligibility'
|
|
18
19
|
import {
|
|
@@ -1,14 +1,137 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
1
|
+
import type { LearnedSkill, RunReflection, Skill, SkillSuggestion } from '@/types'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
deleteLearnedSkill as deleteStoredLearnedSkill,
|
|
5
|
+
deleteSkill as deleteStoredSkill,
|
|
6
|
+
deleteSkillSuggestion as deleteStoredSkillSuggestion,
|
|
7
|
+
loadLearnedSkill as loadStoredLearnedSkill,
|
|
8
|
+
loadLearnedSkills as loadStoredLearnedSkills,
|
|
9
|
+
loadRunReflection as loadStoredRunReflection,
|
|
10
|
+
loadRunReflections as loadStoredRunReflections,
|
|
11
|
+
loadSkillSuggestion as loadStoredSkillSuggestion,
|
|
12
|
+
loadSkillSuggestions as loadStoredSkillSuggestions,
|
|
13
|
+
loadSkills as loadStoredSkills,
|
|
14
|
+
patchLearnedSkill as patchStoredLearnedSkill,
|
|
15
|
+
patchSkillSuggestion as patchStoredSkillSuggestion,
|
|
16
|
+
saveLearnedSkills as saveStoredLearnedSkills,
|
|
17
|
+
saveRunReflections as saveStoredRunReflections,
|
|
18
|
+
saveSkillSuggestions as saveStoredSkillSuggestions,
|
|
19
|
+
saveSkills as saveStoredSkills,
|
|
20
|
+
upsertLearnedSkill as upsertStoredLearnedSkill,
|
|
21
|
+
upsertRunReflection as upsertStoredRunReflection,
|
|
22
|
+
upsertSkillSuggestion as upsertStoredSkillSuggestion,
|
|
23
|
+
upsertStoredItem,
|
|
14
24
|
} from '@/lib/server/storage'
|
|
25
|
+
import { createRecordRepository } from '@/lib/server/persistence/repository-utils'
|
|
26
|
+
|
|
27
|
+
export const skillRepository = createRecordRepository<Skill>(
|
|
28
|
+
'skills',
|
|
29
|
+
{
|
|
30
|
+
get(id) {
|
|
31
|
+
return (loadStoredSkills() as Record<string, Skill>)[id] || null
|
|
32
|
+
},
|
|
33
|
+
list() {
|
|
34
|
+
return loadStoredSkills() as Record<string, Skill>
|
|
35
|
+
},
|
|
36
|
+
upsert(id, value) {
|
|
37
|
+
upsertStoredItem('skills', id, value)
|
|
38
|
+
},
|
|
39
|
+
replace(data) {
|
|
40
|
+
saveStoredSkills(data as Record<string, Skill>)
|
|
41
|
+
},
|
|
42
|
+
delete(id) {
|
|
43
|
+
deleteStoredSkill(id)
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
export const learnedSkillRepository = createRecordRepository<LearnedSkill>(
|
|
49
|
+
'learned-skills',
|
|
50
|
+
{
|
|
51
|
+
get(id) {
|
|
52
|
+
return loadStoredLearnedSkill(id) as LearnedSkill | null
|
|
53
|
+
},
|
|
54
|
+
list() {
|
|
55
|
+
return loadStoredLearnedSkills() as Record<string, LearnedSkill>
|
|
56
|
+
},
|
|
57
|
+
upsert(id, value) {
|
|
58
|
+
upsertStoredLearnedSkill(id, value as LearnedSkill)
|
|
59
|
+
},
|
|
60
|
+
replace(data) {
|
|
61
|
+
saveStoredLearnedSkills(data as Record<string, LearnedSkill>)
|
|
62
|
+
},
|
|
63
|
+
patch(id, updater) {
|
|
64
|
+
return patchStoredLearnedSkill(id, updater as (current: LearnedSkill | null) => LearnedSkill | null) as LearnedSkill | null
|
|
65
|
+
},
|
|
66
|
+
delete(id) {
|
|
67
|
+
deleteStoredLearnedSkill(id)
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
export const skillSuggestionRepository = createRecordRepository<SkillSuggestion>(
|
|
73
|
+
'skill-suggestions',
|
|
74
|
+
{
|
|
75
|
+
get(id) {
|
|
76
|
+
return loadStoredSkillSuggestion(id) as SkillSuggestion | null
|
|
77
|
+
},
|
|
78
|
+
list() {
|
|
79
|
+
return loadStoredSkillSuggestions() as Record<string, SkillSuggestion>
|
|
80
|
+
},
|
|
81
|
+
upsert(id, value) {
|
|
82
|
+
upsertStoredSkillSuggestion(id, value as SkillSuggestion)
|
|
83
|
+
},
|
|
84
|
+
replace(data) {
|
|
85
|
+
saveStoredSkillSuggestions(data as Record<string, SkillSuggestion>)
|
|
86
|
+
},
|
|
87
|
+
patch(id, updater) {
|
|
88
|
+
return patchStoredSkillSuggestion(id, updater as (current: SkillSuggestion | null) => SkillSuggestion | null) as SkillSuggestion | null
|
|
89
|
+
},
|
|
90
|
+
delete(id) {
|
|
91
|
+
deleteStoredSkillSuggestion(id)
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
export const runReflectionRepository = createRecordRepository<RunReflection>(
|
|
97
|
+
'run-reflections',
|
|
98
|
+
{
|
|
99
|
+
get(id) {
|
|
100
|
+
return loadStoredRunReflection(id) as RunReflection | null
|
|
101
|
+
},
|
|
102
|
+
list() {
|
|
103
|
+
return loadStoredRunReflections() as Record<string, RunReflection>
|
|
104
|
+
},
|
|
105
|
+
upsert(id, value) {
|
|
106
|
+
upsertStoredRunReflection(id, value as RunReflection)
|
|
107
|
+
},
|
|
108
|
+
replace(data) {
|
|
109
|
+
saveStoredRunReflections(data as Record<string, RunReflection>)
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
export const loadSkills = () => skillRepository.list()
|
|
115
|
+
export const loadSkill = (id: string) => skillRepository.get(id)
|
|
116
|
+
export const saveSkills = (items: Record<string, Skill | Record<string, unknown>>) => skillRepository.replace(items as Record<string, Skill>)
|
|
117
|
+
export const saveSkill = (id: string, value: Skill | Record<string, unknown>) => skillRepository.upsert(id, value as Skill)
|
|
118
|
+
export const deleteSkill = (id: string) => skillRepository.delete(id)
|
|
119
|
+
|
|
120
|
+
export const loadLearnedSkills = () => learnedSkillRepository.list()
|
|
121
|
+
export const loadLearnedSkill = (id: string) => learnedSkillRepository.get(id)
|
|
122
|
+
export const saveLearnedSkills = (items: Record<string, LearnedSkill | Record<string, unknown>>) => learnedSkillRepository.replace(items as Record<string, LearnedSkill>)
|
|
123
|
+
export const upsertLearnedSkill = (id: string, value: LearnedSkill | Record<string, unknown>) => learnedSkillRepository.upsert(id, value as LearnedSkill)
|
|
124
|
+
export const patchLearnedSkill = (id: string, updater: (current: LearnedSkill | null) => LearnedSkill | null) => learnedSkillRepository.patch(id, updater)
|
|
125
|
+
export const deleteLearnedSkill = (id: string) => learnedSkillRepository.delete(id)
|
|
126
|
+
|
|
127
|
+
export const loadSkillSuggestions = () => skillSuggestionRepository.list()
|
|
128
|
+
export const loadSkillSuggestion = (id: string) => skillSuggestionRepository.get(id)
|
|
129
|
+
export const saveSkillSuggestions = (items: Record<string, SkillSuggestion | Record<string, unknown>>) => skillSuggestionRepository.replace(items as Record<string, SkillSuggestion>)
|
|
130
|
+
export const upsertSkillSuggestion = (id: string, value: SkillSuggestion | Record<string, unknown>) => skillSuggestionRepository.upsert(id, value as SkillSuggestion)
|
|
131
|
+
export const patchSkillSuggestion = (id: string, updater: (current: SkillSuggestion | null) => SkillSuggestion | null) => skillSuggestionRepository.patch(id, updater)
|
|
132
|
+
export const deleteSkillSuggestion = (id: string) => skillSuggestionRepository.delete(id)
|
|
133
|
+
|
|
134
|
+
export const loadRunReflections = () => runReflectionRepository.list()
|
|
135
|
+
export const loadRunReflection = (id: string) => runReflectionRepository.get(id)
|
|
136
|
+
export const saveRunReflections = (items: Record<string, RunReflection | Record<string, unknown>>) => runReflectionRepository.replace(items as Record<string, RunReflection>)
|
|
137
|
+
export const upsertRunReflection = (id: string, value: RunReflection | Record<string, unknown>) => runReflectionRepository.upsert(id, value as RunReflection)
|