@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.
- package/README.md +166 -0
- package/convex/_generated/api.d.ts +45 -0
- package/convex/_generated/api.js +23 -0
- package/convex/_generated/dataModel.d.ts +58 -0
- package/convex/_generated/server.d.ts +143 -0
- package/convex/_generated/server.js +93 -0
- package/convex/cloud/CLAUDE.md +238 -0
- package/convex/cloud/_generated/api.ts +84 -0
- package/convex/cloud/_generated/component.ts +861 -0
- package/convex/cloud/_generated/dataModel.ts +60 -0
- package/convex/cloud/_generated/server.ts +156 -0
- package/convex/cloud/convex.config.ts +16 -0
- package/convex/cloud/index.ts +29 -0
- package/convex/cloud/intentSchema/generate.ts +447 -0
- package/convex/cloud/intentSchema/index.ts +16 -0
- package/convex/cloud/intentSchema/types.ts +418 -0
- package/convex/cloud/ksaPolicy.ts +554 -0
- package/convex/cloud/mail.ts +92 -0
- package/convex/cloud/schema.ts +322 -0
- package/convex/cloud/utils/kanbanContext.ts +229 -0
- package/convex/cloud/workflows/agentBoard.ts +451 -0
- package/convex/cloud/workflows/agentPrompt.ts +272 -0
- package/convex/cloud/workflows/agentThread.ts +374 -0
- package/convex/cloud/workflows/compileSandbox.ts +146 -0
- package/convex/cloud/workflows/crudBoard.ts +217 -0
- package/convex/cloud/workflows/crudKSAs.ts +262 -0
- package/convex/cloud/workflows/crudLorobeads.ts +371 -0
- package/convex/cloud/workflows/crudSkills.ts +205 -0
- package/convex/cloud/workflows/crudThreads.ts +708 -0
- package/convex/cloud/workflows/lifecycleSandbox.ts +1396 -0
- package/convex/cloud/workflows/sandboxConvex.ts +1046 -0
- package/convex/sandbox/README.md +90 -0
- package/convex/sandbox/_generated/api.d.ts +2934 -0
- package/convex/sandbox/_generated/api.js +23 -0
- package/convex/sandbox/_generated/dataModel.d.ts +60 -0
- package/convex/sandbox/_generated/server.d.ts +143 -0
- package/convex/sandbox/_generated/server.js +93 -0
- package/convex/sandbox/actions/bash.ts +130 -0
- package/convex/sandbox/actions/browser.ts +282 -0
- package/convex/sandbox/actions/file.ts +336 -0
- package/convex/sandbox/actions/lsp.ts +325 -0
- package/convex/sandbox/actions/pdf.ts +119 -0
- package/convex/sandbox/agent/codeExecLoop.ts +535 -0
- package/convex/sandbox/agent/decisions.ts +284 -0
- package/convex/sandbox/agent/index.ts +515 -0
- package/convex/sandbox/agent/subagents.ts +651 -0
- package/convex/sandbox/brandResearch/index.ts +417 -0
- package/convex/sandbox/context/index.ts +7 -0
- package/convex/sandbox/context/session.ts +402 -0
- package/convex/sandbox/convex.config.ts +17 -0
- package/convex/sandbox/index.ts +51 -0
- package/convex/sandbox/nodeActions/codeExec.ts +130 -0
- package/convex/sandbox/planning/beads.ts +187 -0
- package/convex/sandbox/planning/index.ts +8 -0
- package/convex/sandbox/planning/sync.ts +194 -0
- package/convex/sandbox/prompts/codeExec.ts +852 -0
- package/convex/sandbox/prompts/modes.ts +231 -0
- package/convex/sandbox/prompts/system.ts +142 -0
- package/convex/sandbox/schema.ts +510 -0
- package/convex/sandbox/state/artifacts.ts +99 -0
- package/convex/sandbox/state/checkpoints.ts +341 -0
- package/convex/sandbox/state/files.ts +383 -0
- package/convex/sandbox/state/index.ts +10 -0
- package/convex/sandbox/state/verification.actions.ts +268 -0
- package/convex/sandbox/state/verification.ts +101 -0
- package/convex/sandbox/tsconfig.json +25 -0
- package/convex/sandbox/utils/codeExecHelpers.ts +52 -0
- package/dist/cli/commands/build.d.ts +19 -0
- package/dist/cli/commands/build.d.ts.map +1 -0
- package/dist/cli/commands/build.js +223 -0
- package/dist/cli/commands/init.d.ts +16 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +148 -0
- package/dist/cli/commands/publish.d.ts +12 -0
- package/dist/cli/commands/publish.d.ts.map +1 -0
- package/dist/cli/commands/publish.js +33 -0
- package/dist/cli/index.d.ts +14 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +40 -0
- package/dist/sdk/builders.d.ts +104 -0
- package/dist/sdk/builders.d.ts.map +1 -0
- package/dist/sdk/builders.js +214 -0
- package/dist/sdk/index.d.ts +29 -0
- package/dist/sdk/index.d.ts.map +1 -0
- package/dist/sdk/index.js +38 -0
- package/dist/sdk/types.d.ts +107 -0
- package/dist/sdk/types.d.ts.map +1 -0
- package/dist/sdk/types.js +6 -0
- package/ksa/README.md +263 -0
- package/ksa/_generated/REFERENCE.md +2954 -0
- package/ksa/_generated/registry.ts +257 -0
- package/ksa/_shared/configReader.ts +302 -0
- package/ksa/_shared/configSchemas.ts +649 -0
- package/ksa/_shared/gateway.ts +175 -0
- package/ksa/_shared/ksaBehaviors.ts +411 -0
- package/ksa/_shared/ksaProxy.ts +248 -0
- package/ksa/_shared/localDb.ts +302 -0
- package/ksa/index.ts +134 -0
- package/package.json +93 -0
- package/runtime/browser/agent-browser.ts +330 -0
- package/runtime/entrypoint.ts +194 -0
- package/runtime/lsp/manager.ts +366 -0
- package/runtime/pdf/pdf-generator.ts +50 -0
- package/runtime/pdf/renderer.ts +357 -0
- package/runtime/pdf/schema.ts +97 -0
- package/runtime/services/file-watcher.ts +191 -0
- package/template/build.ts +307 -0
- package/template/e2b/Dockerfile +69 -0
- package/template/e2b/e2b.toml +13 -0
- package/template/e2b/prebuild.sh +68 -0
- 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
|
+
});
|