@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,515 @@
1
+ /**
2
+ * Sandbox Agent Definition
3
+ *
4
+ * Uses cloud Convex gateway for LLM calls to protect API keys.
5
+ * The sandbox calls back to the main cloud Convex for all LLM operations.
6
+ *
7
+ * Flow:
8
+ * 1. Sandbox receives prompt via HTTP
9
+ * 2. Agent calls cloud gateway with JWT auth
10
+ * 3. Cloud gateway calls OpenRouter with protected API key
11
+ * 4. Response returns through the chain
12
+ */
13
+
14
+ import { action, internalAction, query, mutation } from "../_generated/server";
15
+ import { api, internal } from "../_generated/api";
16
+ import { v } from "convex/values";
17
+ import type { ChainOfThoughtStep, StepStatus } from "../../../shared/chain-of-thought";
18
+ import { createStepId, getStepTypeForTool } from "../../../shared/chain-of-thought";
19
+ // Note: Zod 4 has native toJSONSchema() - don't need zod-to-json-schema
20
+
21
+ // Default model - used as fallback if no model passed via context
22
+ // The model should be passed from unified settings (convex/features/settings/models.ts)
23
+ // This fallback matches the "default" context in settings
24
+ const DEFAULT_MODEL = "anthropic/claude-sonnet-4";
25
+ const FALLBACK_MODELS = [
26
+ "anthropic/claude-3.5-sonnet",
27
+ "google/gemini-2.0-flash",
28
+ ];
29
+
30
+ // ============================================
31
+ // Cloud LLM Gateway
32
+ // ============================================
33
+
34
+ interface LLMMessage {
35
+ role: "system" | "user" | "assistant";
36
+ content: string;
37
+ }
38
+
39
+ interface ToolCall {
40
+ toolName: string;
41
+ args: Record<string, unknown>;
42
+ }
43
+
44
+ interface LLMResponse {
45
+ text: string;
46
+ toolCalls?: ToolCall[];
47
+ finishReason?: string;
48
+ }
49
+
50
+ /**
51
+ * OpenAI-format tool definition for the API
52
+ */
53
+ interface OpenAITool {
54
+ type: "function";
55
+ function: {
56
+ name: string;
57
+ description?: string;
58
+ parameters?: Record<string, any>;
59
+ };
60
+ }
61
+
62
+ /**
63
+ * Gateway configuration for cloud LLM calls
64
+ */
65
+ interface GatewayConfig {
66
+ convexUrl: string;
67
+ jwt: string;
68
+ }
69
+
70
+ // Module-level gateway config (set by startThread/continueThread)
71
+ let gatewayConfig: GatewayConfig | null = null;
72
+
73
+ // Module-level chain-of-thought steps for real-time UI (in-memory per sandbox session)
74
+ const chainOfThoughtSteps: Map<string, ChainOfThoughtStep[]> = new Map();
75
+
76
+ /** Emit a structured chain-of-thought step */
77
+ function emitStep(threadId: string, step: Omit<ChainOfThoughtStep, "id" | "timestamp">) {
78
+ if (!chainOfThoughtSteps.has(threadId)) {
79
+ chainOfThoughtSteps.set(threadId, []);
80
+ }
81
+ const fullStep = {
82
+ id: createStepId(),
83
+ timestamp: Date.now(),
84
+ ...step,
85
+ } as ChainOfThoughtStep;
86
+ chainOfThoughtSteps.get(threadId)!.push(fullStep);
87
+ return fullStep.id;
88
+ }
89
+
90
+ /** Update an existing step's status */
91
+ function updateStepStatus(threadId: string, stepId: string, status: StepStatus) {
92
+ const steps = chainOfThoughtSteps.get(threadId);
93
+ if (steps) {
94
+ const step = steps.find(s => s.id === stepId);
95
+ if (step) step.status = status;
96
+ }
97
+ }
98
+
99
+ /** Create a rich step from tool call and result */
100
+ function createToolStep(
101
+ toolName: string,
102
+ args: Record<string, unknown>,
103
+ result: unknown,
104
+ status: StepStatus
105
+ ): Omit<ChainOfThoughtStep, "id" | "timestamp"> {
106
+ const stepType = getStepTypeForTool(toolName);
107
+
108
+ switch (stepType) {
109
+ case "search": {
110
+ // Extract URLs from search results
111
+ const res = result as any;
112
+ const urls: Array<{ url: string; title?: string }> = [];
113
+ if (res?.results) {
114
+ for (const r of res.results.slice(0, 5)) {
115
+ if (r.url) urls.push({ url: r.url, title: r.title });
116
+ }
117
+ }
118
+ return {
119
+ type: "search",
120
+ status,
121
+ label: `Searching for ${(args as any).query || (args as any).username || "information"}`,
122
+ results: urls.length > 0 ? urls : undefined,
123
+ };
124
+ }
125
+
126
+ case "browser": {
127
+ const action = toolName.replace("browser_", "") as any;
128
+ return {
129
+ type: "browser",
130
+ status,
131
+ action: action === "open" ? "navigate" : action,
132
+ label: toolName === "browser_open"
133
+ ? `Navigating to ${(args as any).url}`
134
+ : toolName === "browser_screenshot"
135
+ ? "Taking screenshot"
136
+ : `Browser ${action}`,
137
+ url: (args as any).url,
138
+ screenshot: toolName === "browser_screenshot" ? (result as any)?.screenshot : undefined,
139
+ };
140
+ }
141
+
142
+ case "file": {
143
+ const operation = toolName.includes("read") ? "read"
144
+ : toolName.includes("edit") ? "edit"
145
+ : toolName.includes("pdf") ? "save"
146
+ : "write";
147
+ const path = (args as any).path || (args as any).filename || "file";
148
+ return {
149
+ type: "file",
150
+ status,
151
+ operation,
152
+ path,
153
+ label: operation === "read" ? `Reading ${path}`
154
+ : operation === "edit" ? `Editing ${path}`
155
+ : `Saving ${path}`,
156
+ };
157
+ }
158
+
159
+ default:
160
+ return {
161
+ type: "tool",
162
+ status,
163
+ toolName,
164
+ label: `Running ${toolName}`,
165
+ input: args,
166
+ output: result,
167
+ };
168
+ }
169
+ }
170
+
171
+ /**
172
+ * Call the cloud Convex gateway for LLM completions.
173
+ * This protects API keys by routing through the main cloud.
174
+ */
175
+ async function callCloudLLM(
176
+ messages: LLMMessage[],
177
+ options: {
178
+ model?: string;
179
+ tools?: Array<{ name: string; description: string; parameters: any }>;
180
+ maxTokens?: number;
181
+ temperature?: number;
182
+ } = {}
183
+ ): Promise<LLMResponse> {
184
+ // Use module-level config (set by action handlers) or fall back to env
185
+ const convexUrl = gatewayConfig?.convexUrl || process.env.CONVEX_URL;
186
+ const jwt = gatewayConfig?.jwt || process.env.SANDBOX_JWT;
187
+
188
+ if (!convexUrl) {
189
+ throw new Error("Gateway not configured: convexUrl missing. Pass gatewayConfig in context.");
190
+ }
191
+ if (!jwt) {
192
+ throw new Error("Gateway not configured: jwt missing. Pass gatewayConfig in context.");
193
+ }
194
+
195
+ // Convert simplified tool format to OpenAI format
196
+ const openAITools: OpenAITool[] | undefined = options.tools?.map((tool) => ({
197
+ type: "function" as const,
198
+ function: {
199
+ name: tool.name,
200
+ description: tool.description,
201
+ parameters: tool.parameters || { type: "object", properties: {} },
202
+ },
203
+ }));
204
+
205
+ // Debug: Log tools being sent
206
+ if (openAITools) {
207
+ console.log(`[lakitu LLM] Sending ${openAITools.length} tools to LLM`);
208
+ const bashTool = openAITools.find(t => t.function.name === 'bash');
209
+ if (bashTool) {
210
+ console.log(`[lakitu LLM] bash tool: ${JSON.stringify(bashTool)}`);
211
+ }
212
+ }
213
+
214
+ // No provider preference - let OpenRouter choose fastest
215
+
216
+ const response = await fetch(`${convexUrl}/agent/call`, {
217
+ method: "POST",
218
+ headers: {
219
+ "Content-Type": "application/json",
220
+ Authorization: `Bearer ${jwt}`,
221
+ },
222
+ body: JSON.stringify({
223
+ path: "services.OpenRouter.internal.chatCompletion",
224
+ args: {
225
+ model: options.model || DEFAULT_MODEL,
226
+ messages,
227
+ tools: openAITools,
228
+ maxTokens: options.maxTokens || 4096,
229
+ temperature: options.temperature,
230
+ },
231
+ }),
232
+ });
233
+
234
+ if (!response.ok) {
235
+ const error = await response.text();
236
+ throw new Error(`Cloud LLM call failed (${response.status}): ${error}`);
237
+ }
238
+
239
+ const result = await response.json();
240
+ if (!result.ok) {
241
+ throw new Error(`Cloud LLM error: ${result.error || JSON.stringify(result)}`);
242
+ }
243
+
244
+ const data = result.data;
245
+ const choice = data.choices?.[0];
246
+
247
+ // Debug: Log raw LLM response
248
+ console.log(`[lakitu LLM DEBUG] finish_reason: ${choice?.finish_reason}`);
249
+ console.log(`[lakitu LLM DEBUG] message keys: ${choice?.message ? Object.keys(choice.message).join(', ') : 'no message'}`);
250
+ console.log(`[lakitu LLM DEBUG] tool_calls present: ${!!choice?.message?.tool_calls}`);
251
+ if (choice?.message?.tool_calls) {
252
+ console.log(`[lakitu LLM DEBUG] tool_calls count: ${choice.message.tool_calls.length}`);
253
+ console.log(`[lakitu LLM DEBUG] tool_calls: ${JSON.stringify(choice.message.tool_calls).slice(0, 500)}`);
254
+ }
255
+ if (choice?.message?.content) {
256
+ console.log(`[lakitu LLM DEBUG] content (first 300 chars): ${choice.message.content.slice(0, 300)}`);
257
+ }
258
+
259
+ // Extract tool calls if present
260
+ const toolCalls = choice?.message?.tool_calls?.map((tc: any) => {
261
+ let args = {};
262
+ const rawArgs = tc.function?.arguments || tc.arguments;
263
+ if (typeof rawArgs === "string" && rawArgs.length > 0) {
264
+ try {
265
+ args = JSON.parse(rawArgs);
266
+ } catch (e) {
267
+ console.error(`[lakitu] Failed to parse tool args for ${tc.function?.name}: ${rawArgs}`);
268
+ args = {};
269
+ }
270
+ } else if (typeof rawArgs === "object" && rawArgs !== null) {
271
+ args = rawArgs;
272
+ }
273
+ return {
274
+ toolName: tc.function?.name || tc.name,
275
+ args,
276
+ };
277
+ });
278
+
279
+ return {
280
+ text: choice?.message?.content || "",
281
+ toolCalls: toolCalls?.length > 0 ? toolCalls : undefined,
282
+ finishReason: choice?.finish_reason,
283
+ };
284
+ }
285
+
286
+ // ============================================
287
+ // Types
288
+ // ============================================
289
+
290
+ interface AgentResult {
291
+ threadId: string;
292
+ text: string;
293
+ toolCalls?: Array<{
294
+ toolName: string;
295
+ args: Record<string, unknown>;
296
+ result: unknown;
297
+ }>;
298
+ }
299
+
300
+ // ============================================
301
+ // Thread Management
302
+ // ============================================
303
+
304
+ function createThreadId(): string {
305
+ return `thread_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
306
+ }
307
+
308
+ // ============================================
309
+ // Legacy Tool Execution Loop (DEPRECATED)
310
+ // ============================================
311
+
312
+ /**
313
+ * @deprecated Use startCodeExecThread instead. Legacy JSON tool calling is no longer supported.
314
+ */
315
+ async function runAgentLoop(
316
+ _ctx: any,
317
+ _systemPrompt: string,
318
+ _userPrompt: string,
319
+ _maxSteps: number = 10,
320
+ _threadId?: string
321
+ ): Promise<{ text: string; toolCalls: ToolCall[] }> {
322
+ throw new Error(
323
+ "Legacy tool calling mode is deprecated. Use startCodeExecThread instead, " +
324
+ "which uses the new KSA (Knowledge, Skills, Abilities) architecture with code execution."
325
+ );
326
+ }
327
+
328
+ // ============================================
329
+ // Agent Actions
330
+ // ============================================
331
+
332
+ /**
333
+ * Start a new agent thread
334
+ * @deprecated Use startCodeExecThread instead. Legacy JSON tool calling is no longer supported.
335
+ */
336
+ export const startThread = action({
337
+ args: {
338
+ prompt: v.string(),
339
+ context: v.optional(v.any()),
340
+ },
341
+ handler: async (_ctx, _args): Promise<AgentResult> => {
342
+ throw new Error(
343
+ "startThread is deprecated. Use startCodeExecThread instead, " +
344
+ "which uses the new KSA (Knowledge, Skills, Abilities) architecture with code execution."
345
+ );
346
+ },
347
+ });
348
+
349
+ /**
350
+ * Continue an existing thread
351
+ * @deprecated Use startCodeExecThread instead. Legacy JSON tool calling is no longer supported.
352
+ */
353
+ export const continueThread = action({
354
+ args: {
355
+ threadId: v.string(),
356
+ prompt: v.string(),
357
+ },
358
+ handler: async (_ctx, _args): Promise<AgentResult> => {
359
+ throw new Error(
360
+ "continueThread is deprecated. Use startCodeExecThread instead, " +
361
+ "which uses the new KSA (Knowledge, Skills, Abilities) architecture with code execution."
362
+ );
363
+ },
364
+ });
365
+
366
+ /**
367
+ * Run agent with timeout for chained execution
368
+ * @deprecated Use startCodeExecThread instead. Legacy JSON tool calling is no longer supported.
369
+ */
370
+ export const runWithTimeout = internalAction({
371
+ args: {
372
+ prompt: v.string(),
373
+ context: v.optional(v.any()),
374
+ timeoutMs: v.number(),
375
+ checkpointId: v.optional(v.id("checkpoints")),
376
+ },
377
+ handler: async (_ctx, _args) => {
378
+ throw new Error(
379
+ "runWithTimeout is deprecated. Use startCodeExecThread instead, " +
380
+ "which uses the new KSA (Knowledge, Skills, Abilities) architecture with code execution."
381
+ );
382
+ },
383
+ });
384
+
385
+ // ============================================
386
+ // Queries
387
+ // ============================================
388
+
389
+ export const getThreadMessages = query({
390
+ args: { threadId: v.string() },
391
+ handler: async (_ctx, _args): Promise<Array<{ role: string; content: string }>> => {
392
+ return [];
393
+ },
394
+ });
395
+
396
+ export const getStreamDeltas = query({
397
+ args: { threadId: v.string(), since: v.optional(v.number()) },
398
+ handler: async (_ctx, args): Promise<ChainOfThoughtStep[]> => {
399
+ const steps = chainOfThoughtSteps.get(args.threadId) || [];
400
+ const since = args.since || 0;
401
+ return steps.filter((s) => s.timestamp > since);
402
+ },
403
+ });
404
+
405
+ /** Get all chain-of-thought steps for a thread */
406
+ export const getChainOfThoughtSteps = query({
407
+ args: { threadId: v.string() },
408
+ handler: async (_ctx, args): Promise<ChainOfThoughtStep[]> => {
409
+ return chainOfThoughtSteps.get(args.threadId) || [];
410
+ },
411
+ });
412
+
413
+ // ============================================
414
+ // Code Execution Mode (NEW ARCHITECTURE)
415
+ // ============================================
416
+
417
+ import { runCodeExecLoop, getSteps } from "./codeExecLoop";
418
+ import { getCodeExecSystemPrompt, generateKSAInstructions } from "../prompts/codeExec";
419
+
420
+ /**
421
+ * Start a thread using code execution mode.
422
+ *
423
+ * This is the NEW architecture:
424
+ * - LLM generates TypeScript code
425
+ * - Code imports from skills/ and executes
426
+ * - No JSON tool calls
427
+ *
428
+ * Use this instead of startThread for the new code execution model.
429
+ */
430
+ export const startCodeExecThread = action({
431
+ args: {
432
+ prompt: v.string(),
433
+ context: v.optional(v.any()),
434
+ },
435
+ handler: async (ctx, args) => {
436
+ const threadId = `codeexec_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
437
+
438
+ // Extract gateway config, model, sessionId, allowedKSAs, skillConfigs, and intentSchema from context
439
+ const ctxObj = args.context as {
440
+ gatewayConfig?: { convexUrl: string; jwt: string };
441
+ cardId?: string;
442
+ cloudThreadId?: string; // Cloud thread ID for artifact uploads (different from sandbox-local threadId)
443
+ // Model config from unified settings
444
+ model?: string;
445
+ fallbackModels?: string[];
446
+ maxTokens?: number;
447
+ temperature?: number;
448
+ sessionId?: string; // For real-time log forwarding
449
+ allowedKSAs?: string[]; // KSAs allowed for this task
450
+ skillConfigs?: Record<string, Record<string, unknown>>; // Per-KSA configuration
451
+ intentSchema?: {
452
+ intent: { summary: string; objective: string; context: string[]; domain?: string };
453
+ ksas: { priority: string[]; secondary: string[]; notNeeded: string[]; reasoning: string };
454
+ plan: {
455
+ goals: Array<{ id: string; text: string; importance: string }>;
456
+ deliverables: Array<{ id: string; type: string; name: string; description: string }>;
457
+ steps: string[];
458
+ };
459
+ policy: { enabledKSAs: string[]; disabledKSAs: string[]; allowExternalCalls: boolean; requireApprovalFor?: string[] };
460
+ meta: { model: string; generatedAt: number; confidence: string; latencyMs?: number };
461
+ }; // Pre-analyzed intent schema for structured guidance
462
+ } | undefined;
463
+
464
+ if (!ctxObj?.gatewayConfig) {
465
+ throw new Error("gatewayConfig required for code execution mode");
466
+ }
467
+
468
+ // Log start
469
+ await ctx.runMutation(api.agent.decisions.log, {
470
+ threadId,
471
+ task: args.prompt,
472
+ decisionType: "tool_selection",
473
+ selectedTools: ["code_execution"],
474
+ reasoning: "Using code execution mode - agent will write and execute TypeScript",
475
+ expectedOutcome: "Agent will generate code that imports from skills/",
476
+ });
477
+
478
+ // Generate KSA instructions from skill configs (if any)
479
+ const ksaInstructions = ctxObj.skillConfigs
480
+ ? generateKSAInstructions(ctxObj.skillConfigs)
481
+ : "";
482
+
483
+ // Log if intent schema is present
484
+ if (ctxObj.intentSchema) {
485
+ console.log(
486
+ `[startCodeExecThread] Intent schema received: "${ctxObj.intentSchema.intent.summary}" (${ctxObj.intentSchema.meta.confidence} confidence)`
487
+ );
488
+ }
489
+
490
+ // Run the code execution loop with dynamic KSA documentation and intent schema
491
+ const systemPrompt = getCodeExecSystemPrompt({
492
+ allowedKSAs: ctxObj.allowedKSAs,
493
+ additions: ksaInstructions || undefined,
494
+ intentSchema: ctxObj.intentSchema as any, // Type is compatible
495
+ });
496
+ const result = await runCodeExecLoop(ctx, systemPrompt, args.prompt, ctxObj.gatewayConfig, {
497
+ threadId,
498
+ maxSteps: 10,
499
+ cardId: ctxObj.cardId,
500
+ cloudThreadId: ctxObj.cloudThreadId, // Cloud thread ID for artifact uploads
501
+ // Model config from unified settings (passed via context from cloud workflow)
502
+ model: ctxObj.model,
503
+ maxTokens: ctxObj.maxTokens,
504
+ temperature: ctxObj.temperature,
505
+ sessionId: ctxObj.sessionId, // Pass for real-time cloud log forwarding
506
+ });
507
+
508
+ return {
509
+ threadId,
510
+ text: result.text,
511
+ codeExecutions: result.codeExecutions,
512
+ chainOfThought: getSteps(threadId),
513
+ };
514
+ },
515
+ });