@lakitu/sdk 0.1.0

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.
Files changed (111) hide show
  1. package/README.md +166 -0
  2. package/convex/_generated/api.d.ts +45 -0
  3. package/convex/_generated/api.js +23 -0
  4. package/convex/_generated/dataModel.d.ts +58 -0
  5. package/convex/_generated/server.d.ts +143 -0
  6. package/convex/_generated/server.js +93 -0
  7. package/convex/cloud/CLAUDE.md +238 -0
  8. package/convex/cloud/_generated/api.ts +84 -0
  9. package/convex/cloud/_generated/component.ts +861 -0
  10. package/convex/cloud/_generated/dataModel.ts +60 -0
  11. package/convex/cloud/_generated/server.ts +156 -0
  12. package/convex/cloud/convex.config.ts +16 -0
  13. package/convex/cloud/index.ts +29 -0
  14. package/convex/cloud/intentSchema/generate.ts +447 -0
  15. package/convex/cloud/intentSchema/index.ts +16 -0
  16. package/convex/cloud/intentSchema/types.ts +418 -0
  17. package/convex/cloud/ksaPolicy.ts +554 -0
  18. package/convex/cloud/mail.ts +92 -0
  19. package/convex/cloud/schema.ts +322 -0
  20. package/convex/cloud/utils/kanbanContext.ts +229 -0
  21. package/convex/cloud/workflows/agentBoard.ts +451 -0
  22. package/convex/cloud/workflows/agentPrompt.ts +272 -0
  23. package/convex/cloud/workflows/agentThread.ts +374 -0
  24. package/convex/cloud/workflows/compileSandbox.ts +146 -0
  25. package/convex/cloud/workflows/crudBoard.ts +217 -0
  26. package/convex/cloud/workflows/crudKSAs.ts +262 -0
  27. package/convex/cloud/workflows/crudLorobeads.ts +371 -0
  28. package/convex/cloud/workflows/crudSkills.ts +205 -0
  29. package/convex/cloud/workflows/crudThreads.ts +708 -0
  30. package/convex/cloud/workflows/lifecycleSandbox.ts +1396 -0
  31. package/convex/cloud/workflows/sandboxConvex.ts +1046 -0
  32. package/convex/sandbox/README.md +90 -0
  33. package/convex/sandbox/_generated/api.d.ts +2934 -0
  34. package/convex/sandbox/_generated/api.js +23 -0
  35. package/convex/sandbox/_generated/dataModel.d.ts +60 -0
  36. package/convex/sandbox/_generated/server.d.ts +143 -0
  37. package/convex/sandbox/_generated/server.js +93 -0
  38. package/convex/sandbox/actions/bash.ts +130 -0
  39. package/convex/sandbox/actions/browser.ts +282 -0
  40. package/convex/sandbox/actions/file.ts +336 -0
  41. package/convex/sandbox/actions/lsp.ts +325 -0
  42. package/convex/sandbox/actions/pdf.ts +119 -0
  43. package/convex/sandbox/agent/codeExecLoop.ts +535 -0
  44. package/convex/sandbox/agent/decisions.ts +284 -0
  45. package/convex/sandbox/agent/index.ts +515 -0
  46. package/convex/sandbox/agent/subagents.ts +651 -0
  47. package/convex/sandbox/brandResearch/index.ts +417 -0
  48. package/convex/sandbox/context/index.ts +7 -0
  49. package/convex/sandbox/context/session.ts +402 -0
  50. package/convex/sandbox/convex.config.ts +17 -0
  51. package/convex/sandbox/index.ts +51 -0
  52. package/convex/sandbox/nodeActions/codeExec.ts +130 -0
  53. package/convex/sandbox/planning/beads.ts +187 -0
  54. package/convex/sandbox/planning/index.ts +8 -0
  55. package/convex/sandbox/planning/sync.ts +194 -0
  56. package/convex/sandbox/prompts/codeExec.ts +852 -0
  57. package/convex/sandbox/prompts/modes.ts +231 -0
  58. package/convex/sandbox/prompts/system.ts +142 -0
  59. package/convex/sandbox/schema.ts +510 -0
  60. package/convex/sandbox/state/artifacts.ts +99 -0
  61. package/convex/sandbox/state/checkpoints.ts +341 -0
  62. package/convex/sandbox/state/files.ts +383 -0
  63. package/convex/sandbox/state/index.ts +10 -0
  64. package/convex/sandbox/state/verification.actions.ts +268 -0
  65. package/convex/sandbox/state/verification.ts +101 -0
  66. package/convex/sandbox/tsconfig.json +25 -0
  67. package/convex/sandbox/utils/codeExecHelpers.ts +52 -0
  68. package/dist/cli/commands/build.d.ts +19 -0
  69. package/dist/cli/commands/build.d.ts.map +1 -0
  70. package/dist/cli/commands/build.js +223 -0
  71. package/dist/cli/commands/init.d.ts +16 -0
  72. package/dist/cli/commands/init.d.ts.map +1 -0
  73. package/dist/cli/commands/init.js +148 -0
  74. package/dist/cli/commands/publish.d.ts +12 -0
  75. package/dist/cli/commands/publish.d.ts.map +1 -0
  76. package/dist/cli/commands/publish.js +33 -0
  77. package/dist/cli/index.d.ts +14 -0
  78. package/dist/cli/index.d.ts.map +1 -0
  79. package/dist/cli/index.js +40 -0
  80. package/dist/sdk/builders.d.ts +104 -0
  81. package/dist/sdk/builders.d.ts.map +1 -0
  82. package/dist/sdk/builders.js +214 -0
  83. package/dist/sdk/index.d.ts +29 -0
  84. package/dist/sdk/index.d.ts.map +1 -0
  85. package/dist/sdk/index.js +38 -0
  86. package/dist/sdk/types.d.ts +107 -0
  87. package/dist/sdk/types.d.ts.map +1 -0
  88. package/dist/sdk/types.js +6 -0
  89. package/ksa/README.md +263 -0
  90. package/ksa/_generated/REFERENCE.md +2954 -0
  91. package/ksa/_generated/registry.ts +257 -0
  92. package/ksa/_shared/configReader.ts +302 -0
  93. package/ksa/_shared/configSchemas.ts +649 -0
  94. package/ksa/_shared/gateway.ts +175 -0
  95. package/ksa/_shared/ksaBehaviors.ts +411 -0
  96. package/ksa/_shared/ksaProxy.ts +248 -0
  97. package/ksa/_shared/localDb.ts +302 -0
  98. package/ksa/index.ts +134 -0
  99. package/package.json +93 -0
  100. package/runtime/browser/agent-browser.ts +330 -0
  101. package/runtime/entrypoint.ts +194 -0
  102. package/runtime/lsp/manager.ts +366 -0
  103. package/runtime/pdf/pdf-generator.ts +50 -0
  104. package/runtime/pdf/renderer.ts +357 -0
  105. package/runtime/pdf/schema.ts +97 -0
  106. package/runtime/services/file-watcher.ts +191 -0
  107. package/template/build.ts +307 -0
  108. package/template/e2b/Dockerfile +69 -0
  109. package/template/e2b/e2b.toml +13 -0
  110. package/template/e2b/prebuild.sh +68 -0
  111. package/template/e2b/start.sh +14 -0
@@ -0,0 +1,651 @@
1
+ /**
2
+ * Subagents - Spawn and manage child agents
3
+ *
4
+ * Enables parallel work and task delegation through subagent spawning.
5
+ * Uses cloud gateway for LLM calls (same as main agent).
6
+ */
7
+
8
+ import {
9
+ mutation,
10
+ query,
11
+ internalMutation,
12
+ internalAction,
13
+ } from "../_generated/server";
14
+ import { internal } from "../_generated/api";
15
+ import { v } from "convex/values";
16
+
17
+ // Legacy tool system removed - subagents now use code execution mode
18
+ // createSubagentToolset is no longer available
19
+
20
+ // Default model for subagents - fast and cheap
21
+ // This should match the "subagent" context defaults in convex/features/settings/models.ts
22
+ // For customization, pass model via options when spawning subagents
23
+ const DEFAULT_SUBAGENT_MODEL = "google/gemini-2.0-flash";
24
+
25
+ // ============================================
26
+ // Progress Emission (to cloud thread)
27
+ // ============================================
28
+
29
+ interface ChainOfThoughtStep {
30
+ id: string;
31
+ type: "thinking" | "tool" | "search" | "file" | "complete" | "error";
32
+ label: string;
33
+ status: "active" | "complete" | "error";
34
+ details?: string;
35
+ }
36
+
37
+ /**
38
+ * Emit subagent progress to the cloud thread for UI display.
39
+ * This calls the cloud Convex gateway to update the thread messages.
40
+ */
41
+ async function emitProgress(
42
+ parentThreadId: string | undefined,
43
+ subagentId: string,
44
+ name: string,
45
+ task: string,
46
+ status: "spawning" | "running" | "completed" | "failed",
47
+ options?: {
48
+ progress?: number;
49
+ result?: string;
50
+ error?: string;
51
+ children?: ChainOfThoughtStep[];
52
+ }
53
+ ): Promise<void> {
54
+ if (!parentThreadId) return; // No parent thread to emit to
55
+
56
+ const convexUrl = process.env.CONVEX_URL;
57
+ const jwt = process.env.SANDBOX_JWT;
58
+
59
+ if (!convexUrl || !jwt) return; // Can't emit without credentials
60
+
61
+ try {
62
+ await fetch(`${convexUrl}/agent/call`, {
63
+ method: "POST",
64
+ headers: {
65
+ "Content-Type": "application/json",
66
+ Authorization: `Bearer ${jwt}`,
67
+ },
68
+ body: JSON.stringify({
69
+ path: "agent.workflows.crudThreads.emitSubagentProgress",
70
+ args: {
71
+ threadId: parentThreadId,
72
+ subagentId,
73
+ name,
74
+ task,
75
+ status,
76
+ progress: options?.progress,
77
+ result: options?.result,
78
+ error: options?.error,
79
+ children: options?.children,
80
+ },
81
+ }),
82
+ });
83
+ } catch (e) {
84
+ // Don't fail the subagent if progress emission fails
85
+ console.error("[subagent] Failed to emit progress:", e);
86
+ }
87
+ }
88
+
89
+ // ============================================
90
+ // Cloud LLM Gateway (shared with main agent)
91
+ // ============================================
92
+
93
+ interface LLMMessage {
94
+ role: "system" | "user" | "assistant";
95
+ content: string;
96
+ }
97
+
98
+ interface ToolCall {
99
+ toolName: string;
100
+ args: Record<string, unknown>;
101
+ }
102
+
103
+ interface LLMResponse {
104
+ text: string;
105
+ toolCalls?: ToolCall[];
106
+ finishReason?: string;
107
+ }
108
+
109
+ interface OpenAITool {
110
+ type: "function";
111
+ function: {
112
+ name: string;
113
+ description?: string;
114
+ parameters?: Record<string, any>;
115
+ };
116
+ }
117
+
118
+ /**
119
+ * Call the cloud Convex gateway for LLM completions.
120
+ */
121
+ async function callCloudLLM(
122
+ messages: LLMMessage[],
123
+ options: {
124
+ model?: string;
125
+ tools?: Array<{ name: string; description: string; parameters: any }>;
126
+ maxTokens?: number;
127
+ temperature?: number;
128
+ } = {}
129
+ ): Promise<LLMResponse> {
130
+ const convexUrl = process.env.CONVEX_URL;
131
+ const jwt = process.env.SANDBOX_JWT;
132
+
133
+ if (!convexUrl || !jwt) {
134
+ throw new Error("CONVEX_URL or SANDBOX_JWT not set");
135
+ }
136
+
137
+ const openAITools: OpenAITool[] | undefined = options.tools?.map((tool) => ({
138
+ type: "function" as const,
139
+ function: {
140
+ name: tool.name,
141
+ description: tool.description,
142
+ parameters: tool.parameters || { type: "object", properties: {} },
143
+ },
144
+ }));
145
+
146
+ const response = await fetch(`${convexUrl}/agent/call`, {
147
+ method: "POST",
148
+ headers: {
149
+ "Content-Type": "application/json",
150
+ Authorization: `Bearer ${jwt}`,
151
+ },
152
+ body: JSON.stringify({
153
+ path: "services.OpenRouter.internal.chatCompletion",
154
+ args: {
155
+ model: options.model || DEFAULT_SUBAGENT_MODEL,
156
+ messages,
157
+ tools: openAITools,
158
+ maxTokens: options.maxTokens || 4096,
159
+ temperature: options.temperature,
160
+ speedy: false,
161
+ },
162
+ }),
163
+ });
164
+
165
+ if (!response.ok) {
166
+ const error = await response.text();
167
+ throw new Error(`Cloud LLM call failed (${response.status}): ${error}`);
168
+ }
169
+
170
+ const result = await response.json();
171
+ if (!result.ok) {
172
+ throw new Error(`Cloud LLM error: ${result.error || JSON.stringify(result)}`);
173
+ }
174
+
175
+ const data = result.data;
176
+ const choice = data.choices?.[0];
177
+
178
+ const toolCalls = choice?.message?.tool_calls?.map((tc: any) => ({
179
+ toolName: tc.function?.name || tc.name,
180
+ args: typeof tc.function?.arguments === "string"
181
+ ? JSON.parse(tc.function.arguments)
182
+ : tc.function?.arguments || tc.arguments || {},
183
+ }));
184
+
185
+ return {
186
+ text: choice?.message?.content || "",
187
+ toolCalls: toolCalls?.length > 0 ? toolCalls : undefined,
188
+ finishReason: choice?.finish_reason,
189
+ };
190
+ }
191
+
192
+ // ============================================
193
+ // Types
194
+ // ============================================
195
+
196
+ interface SpawnResult {
197
+ subagentId: string;
198
+ status: "spawned";
199
+ }
200
+
201
+ interface ExecuteResult {
202
+ success: boolean;
203
+ error?: string;
204
+ }
205
+
206
+ // ============================================
207
+ // Helpers
208
+ // ============================================
209
+
210
+ function createSubagentId(): string {
211
+ return `subagent_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
212
+ }
213
+
214
+ // ============================================
215
+ // Actions
216
+ // ============================================
217
+
218
+ /**
219
+ * Spawn a new subagent
220
+ */
221
+ export const spawn = internalAction({
222
+ args: {
223
+ parentThreadId: v.optional(v.string()),
224
+ cloudThreadId: v.optional(v.string()), // Cloud thread ID for progress emission
225
+ name: v.string(),
226
+ task: v.string(),
227
+ tools: v.array(v.string()),
228
+ model: v.string(),
229
+ },
230
+ handler: async (ctx, args): Promise<SpawnResult> => {
231
+ const subagentId = createSubagentId();
232
+
233
+ // Emit spawning progress to cloud thread
234
+ await emitProgress(
235
+ args.cloudThreadId,
236
+ subagentId,
237
+ args.name,
238
+ args.task,
239
+ "spawning"
240
+ );
241
+
242
+ // Record subagent in database
243
+ await ctx.runMutation(internal.agent.subagents.internalRecordSubagent, {
244
+ threadId: subagentId,
245
+ parentThreadId: args.parentThreadId || "",
246
+ name: args.name,
247
+ task: args.task,
248
+ tools: args.tools,
249
+ model: args.model,
250
+ });
251
+
252
+ // Execute asynchronously using scheduler
253
+ await ctx.scheduler.runAfter(0, internal.agent.subagents.execute, {
254
+ threadId: subagentId,
255
+ cloudThreadId: args.cloudThreadId,
256
+ task: args.task,
257
+ tools: args.tools,
258
+ model: args.model,
259
+ name: args.name,
260
+ });
261
+
262
+ return { subagentId, status: "spawned" };
263
+ },
264
+ });
265
+
266
+ /**
267
+ * Execute subagent task with tool loop
268
+ * @deprecated Legacy tool system removed - use code execution mode instead
269
+ */
270
+ async function runSubagentLoop(
271
+ _ctx: any,
272
+ _systemPrompt: string,
273
+ _task: string,
274
+ _toolNames: string[],
275
+ _model: string,
276
+ _maxSteps: number = 5
277
+ ): Promise<{ text: string; toolCalls: ToolCall[] }> {
278
+ throw new Error(
279
+ "Legacy subagent tool system is deprecated. Use code execution mode with KSAs instead."
280
+ );
281
+ }
282
+
283
+ /**
284
+ * Execute subagent task with tool loop and progress emission
285
+ * @deprecated Legacy tool system removed - use code execution mode instead
286
+ */
287
+ async function runSubagentLoopWithProgress(
288
+ _ctx: any,
289
+ _systemPrompt: string,
290
+ _task: string,
291
+ _toolNames: string[],
292
+ _model: string,
293
+ _maxSteps: number = 5,
294
+ _onProgress?: (step: ChainOfThoughtStep) => Promise<void>
295
+ ): Promise<{ text: string; toolCalls: ToolCall[] }> {
296
+ throw new Error(
297
+ "Legacy subagent tool system is deprecated. Use code execution mode with KSAs instead."
298
+ );
299
+ }
300
+
301
+ /**
302
+ * Map tool names to step types for progress display
303
+ */
304
+ function getToolType(toolName: string): ChainOfThoughtStep["type"] {
305
+ const lowered = toolName.toLowerCase();
306
+ if (lowered.includes("search") || lowered.includes("web")) return "search";
307
+ if (lowered.includes("file") || lowered.includes("read") || lowered.includes("write")) return "file";
308
+ return "tool";
309
+ }
310
+
311
+ /**
312
+ * Execute subagent task
313
+ */
314
+ export const execute = internalAction({
315
+ args: {
316
+ threadId: v.string(),
317
+ cloudThreadId: v.optional(v.string()), // Cloud thread ID for progress emission
318
+ task: v.string(),
319
+ tools: v.array(v.string()),
320
+ model: v.string(),
321
+ name: v.string(),
322
+ },
323
+ handler: async (ctx, args): Promise<ExecuteResult> => {
324
+ const children: ChainOfThoughtStep[] = [];
325
+
326
+ try {
327
+ // Update status to running
328
+ await ctx.runMutation(internal.agent.subagents.updateStatus, {
329
+ threadId: args.threadId,
330
+ status: "running",
331
+ });
332
+
333
+ // Emit running progress to cloud thread
334
+ await emitProgress(
335
+ args.cloudThreadId,
336
+ args.threadId,
337
+ args.name,
338
+ args.task,
339
+ "running",
340
+ { children }
341
+ );
342
+
343
+ // Build system prompt for subagent
344
+ const systemPrompt = `You are ${args.name}, a specialized subagent.
345
+
346
+ Your task: ${args.task}
347
+
348
+ Guidelines:
349
+ - Focus only on the assigned task
350
+ - Be concise and efficient
351
+ - Report results clearly
352
+ - If blocked, explain why`;
353
+
354
+ // Execute using cloud gateway with progress callback
355
+ const onProgress = async (step: ChainOfThoughtStep) => {
356
+ children.push(step);
357
+ await emitProgress(
358
+ args.cloudThreadId,
359
+ args.threadId,
360
+ args.name,
361
+ args.task,
362
+ "running",
363
+ {
364
+ progress: Math.min(95, children.length * 15), // Approximate progress
365
+ children
366
+ }
367
+ );
368
+ };
369
+
370
+ const result = await runSubagentLoopWithProgress(
371
+ ctx,
372
+ systemPrompt,
373
+ args.task,
374
+ args.tools,
375
+ args.model || DEFAULT_SUBAGENT_MODEL,
376
+ 5,
377
+ onProgress
378
+ );
379
+
380
+ // Update with result
381
+ await ctx.runMutation(internal.agent.subagents.updateStatus, {
382
+ threadId: args.threadId,
383
+ status: "completed",
384
+ result: {
385
+ text: result.text,
386
+ toolCalls: result.toolCalls,
387
+ },
388
+ });
389
+
390
+ // Mark all children as complete and emit final progress
391
+ const completedChildren = children.map((c) => ({
392
+ ...c,
393
+ status: "complete" as const,
394
+ }));
395
+ completedChildren.push({
396
+ id: `${args.threadId}_complete`,
397
+ type: "complete",
398
+ label: "Task completed",
399
+ status: "complete",
400
+ details: result.text.slice(0, 100),
401
+ });
402
+
403
+ await emitProgress(
404
+ args.cloudThreadId,
405
+ args.threadId,
406
+ args.name,
407
+ args.task,
408
+ "completed",
409
+ { progress: 100, result: result.text, children: completedChildren }
410
+ );
411
+
412
+ return { success: true };
413
+ } catch (error: unknown) {
414
+ const errorMessage =
415
+ error instanceof Error ? error.message : String(error);
416
+
417
+ // Update with error
418
+ await ctx.runMutation(internal.agent.subagents.updateStatus, {
419
+ threadId: args.threadId,
420
+ status: "failed",
421
+ error: errorMessage,
422
+ });
423
+
424
+ // Emit failed progress
425
+ children.push({
426
+ id: `${args.threadId}_error`,
427
+ type: "error",
428
+ label: "Task failed",
429
+ status: "error",
430
+ details: errorMessage,
431
+ });
432
+
433
+ await emitProgress(
434
+ args.cloudThreadId,
435
+ args.threadId,
436
+ args.name,
437
+ args.task,
438
+ "failed",
439
+ { error: errorMessage, children }
440
+ );
441
+
442
+ return { success: false, error: errorMessage };
443
+ }
444
+ },
445
+ });
446
+
447
+ // ============================================
448
+ // Mutations
449
+ // ============================================
450
+
451
+ /**
452
+ * Internal: Record subagent in database
453
+ */
454
+ export const internalRecordSubagent = internalMutation({
455
+ args: {
456
+ threadId: v.string(),
457
+ parentThreadId: v.string(),
458
+ name: v.string(),
459
+ task: v.string(),
460
+ tools: v.array(v.string()),
461
+ model: v.string(),
462
+ },
463
+ handler: async (ctx, args) => {
464
+ return await ctx.db.insert("subagents", {
465
+ threadId: args.threadId,
466
+ parentThreadId: args.parentThreadId,
467
+ name: args.name,
468
+ task: args.task,
469
+ tools: args.tools,
470
+ model: args.model,
471
+ status: "pending",
472
+ createdAt: Date.now(),
473
+ });
474
+ },
475
+ });
476
+
477
+ /**
478
+ * Internal: Update subagent status
479
+ */
480
+ export const updateStatus = internalMutation({
481
+ args: {
482
+ threadId: v.string(),
483
+ status: v.string(),
484
+ result: v.optional(v.any()),
485
+ error: v.optional(v.string()),
486
+ },
487
+ handler: async (ctx, args) => {
488
+ const subagent = await ctx.db
489
+ .query("subagents")
490
+ .filter((q) => q.eq(q.field("threadId"), args.threadId))
491
+ .first();
492
+
493
+ if (!subagent) return;
494
+
495
+ const updates: Record<string, unknown> = {
496
+ status: args.status,
497
+ };
498
+
499
+ if (args.result) {
500
+ updates.result = args.result;
501
+ }
502
+
503
+ if (args.error) {
504
+ updates.error = args.error;
505
+ }
506
+
507
+ if (args.status === "completed" || args.status === "failed") {
508
+ updates.completedAt = Date.now();
509
+ }
510
+
511
+ await ctx.db.patch(subagent._id, updates);
512
+ },
513
+ });
514
+
515
+ /**
516
+ * Cancel a subagent
517
+ */
518
+ export const cancel = internalMutation({
519
+ args: {
520
+ subagentId: v.string(),
521
+ },
522
+ handler: async (ctx, args) => {
523
+ const subagent = await ctx.db
524
+ .query("subagents")
525
+ .filter((q) => q.eq(q.field("threadId"), args.subagentId))
526
+ .first();
527
+
528
+ if (!subagent) {
529
+ return { success: false, error: "Subagent not found" };
530
+ }
531
+
532
+ if (subagent.status === "completed" || subagent.status === "failed") {
533
+ return { success: false, error: "Subagent already finished" };
534
+ }
535
+
536
+ await ctx.db.patch(subagent._id, {
537
+ status: "failed",
538
+ error: "Cancelled by parent",
539
+ completedAt: Date.now(),
540
+ });
541
+
542
+ return { success: true };
543
+ },
544
+ });
545
+
546
+ // ============================================
547
+ // Queries
548
+ // ============================================
549
+
550
+ /**
551
+ * Get subagent status
552
+ */
553
+ export const getStatus = query({
554
+ args: {
555
+ subagentId: v.string(),
556
+ },
557
+ handler: async (ctx, args) => {
558
+ const subagent = await ctx.db
559
+ .query("subagents")
560
+ .filter((q) => q.eq(q.field("threadId"), args.subagentId))
561
+ .first();
562
+
563
+ if (!subagent) {
564
+ return { found: false, status: null };
565
+ }
566
+
567
+ return {
568
+ found: true,
569
+ status: subagent.status,
570
+ name: subagent.name,
571
+ task: subagent.task,
572
+ hasError: !!subagent.error,
573
+ };
574
+ },
575
+ });
576
+
577
+ /**
578
+ * Get subagent result
579
+ */
580
+ export const getResult = query({
581
+ args: {
582
+ subagentId: v.string(),
583
+ },
584
+ handler: async (ctx, args) => {
585
+ const subagent = await ctx.db
586
+ .query("subagents")
587
+ .filter((q) => q.eq(q.field("threadId"), args.subagentId))
588
+ .first();
589
+
590
+ if (!subagent) {
591
+ return { found: false };
592
+ }
593
+
594
+ if (subagent.status !== "completed" && subagent.status !== "failed") {
595
+ return {
596
+ found: true,
597
+ ready: false,
598
+ status: subagent.status,
599
+ };
600
+ }
601
+
602
+ return {
603
+ found: true,
604
+ ready: true,
605
+ status: subagent.status,
606
+ result: subagent.result,
607
+ error: subagent.error,
608
+ };
609
+ },
610
+ });
611
+
612
+ /**
613
+ * List subagents
614
+ */
615
+ export const list = query({
616
+ args: {
617
+ status: v.optional(
618
+ v.union(
619
+ v.literal("pending"),
620
+ v.literal("running"),
621
+ v.literal("completed"),
622
+ v.literal("failed")
623
+ )
624
+ ),
625
+ parentThreadId: v.optional(v.string()),
626
+ },
627
+ handler: async (ctx, args) => {
628
+ let q = ctx.db.query("subagents");
629
+
630
+ if (args.status) {
631
+ q = q.filter((q) => q.eq(q.field("status"), args.status));
632
+ }
633
+
634
+ if (args.parentThreadId) {
635
+ q = q.filter((q) =>
636
+ q.eq(q.field("parentThreadId"), args.parentThreadId)
637
+ );
638
+ }
639
+
640
+ const subagents = await q.order("desc").take(50);
641
+
642
+ return subagents.map((s) => ({
643
+ id: s.threadId,
644
+ name: s.name,
645
+ task: s.task,
646
+ status: s.status,
647
+ createdAt: s.createdAt,
648
+ completedAt: s.completedAt,
649
+ }));
650
+ },
651
+ });