@lakitu/sdk 0.1.42 → 0.1.44

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.
@@ -266,38 +266,145 @@ export const spawn = internalAction({
266
266
  });
267
267
 
268
268
  /**
269
- * Execute subagent task with tool loop
270
- * @deprecated Legacy tool system removed - use code execution mode instead
269
+ * Run subagent code execution loop
270
+ * Uses the same pattern as the main agent - LLM generates code, we execute it.
271
271
  */
272
- async function runSubagentLoop(
273
- _ctx: any,
274
- _systemPrompt: string,
275
- _task: string,
276
- _toolNames: string[],
277
- _model: string,
278
- _maxSteps: number = 5
279
- ): Promise<{ text: string; toolCalls: ToolCall[] }> {
280
- throw new Error(
281
- "Legacy subagent tool system is deprecated. Use code execution mode with KSAs instead."
282
- );
283
- }
272
+ async function runSubagentCodeExecLoop(
273
+ ctx: any,
274
+ systemPrompt: string,
275
+ task: string,
276
+ model: string,
277
+ maxSteps: number = 5,
278
+ onProgress?: (step: ChainOfThoughtStep) => Promise<void>
279
+ ): Promise<{ text: string; codeExecutions: Array<{ code: string; output: string; success: boolean }> }> {
280
+ const messages: Array<{ role: "system" | "user" | "assistant"; content: string }> = [
281
+ { role: "system", content: systemPrompt },
282
+ { role: "user", content: `Task: ${task}\n\nYou have access to KSAs (Knowledge, Skills, Abilities) via imports from './ksa/'. Write TypeScript code to accomplish this task.\n\nRespond with JSON containing:\n- "thinking": Your reasoning\n- "code": TypeScript code to execute (or empty if done)\n- "response": Final result when task is complete` },
283
+ ];
284
+
285
+ const allExecutions: Array<{ code: string; output: string; success: boolean }> = [];
286
+ let finalText = "";
287
+
288
+ for (let step = 0; step < maxSteps; step++) {
289
+ // Call LLM via local sandbox
290
+ let llmResponse: string;
291
+ try {
292
+ const res = await fetch("http://localhost:3210/api/action", {
293
+ method: "POST",
294
+ headers: { "Content-Type": "application/json" },
295
+ body: JSON.stringify({
296
+ path: "agent/index:callLLM",
297
+ args: { messages, model },
298
+ format: "json",
299
+ }),
300
+ });
301
+ if (!res.ok) {
302
+ throw new Error(`LLM call failed: ${await res.text()}`);
303
+ }
304
+ const result = await res.json();
305
+ llmResponse = result.value?.text || result.value || "";
306
+ } catch (e: any) {
307
+ console.error("[subagent] LLM call failed:", e.message);
308
+ finalText = `Error: ${e.message}`;
309
+ break;
310
+ }
284
311
 
285
- /**
286
- * Execute subagent task with tool loop and progress emission
287
- * @deprecated Legacy tool system removed - use code execution mode instead
288
- */
289
- async function runSubagentLoopWithProgress(
290
- _ctx: any,
291
- _systemPrompt: string,
292
- _task: string,
293
- _toolNames: string[],
294
- _model: string,
295
- _maxSteps: number = 5,
296
- _onProgress?: (step: ChainOfThoughtStep) => Promise<void>
297
- ): Promise<{ text: string; toolCalls: ToolCall[] }> {
298
- throw new Error(
299
- "Legacy subagent tool system is deprecated. Use code execution mode with KSAs instead."
300
- );
312
+ // Parse JSON response
313
+ let action: { thinking?: string; code?: string; response?: string } = {};
314
+ try {
315
+ const jsonMatch = llmResponse.match(/\{[\s\S]*\}/);
316
+ if (jsonMatch) {
317
+ action = JSON.parse(jsonMatch[0]);
318
+ }
319
+ } catch {
320
+ // Try to extract code block directly
321
+ const codeMatch = llmResponse.match(/```(?:typescript|ts)?\s*([\s\S]*?)```/);
322
+ if (codeMatch) {
323
+ action = { code: codeMatch[1].trim() };
324
+ }
325
+ }
326
+
327
+ // Emit thinking progress
328
+ if (action.thinking && onProgress) {
329
+ await onProgress({
330
+ id: `step_${step}_thinking`,
331
+ type: "thinking",
332
+ label: action.thinking.slice(0, 150),
333
+ status: "complete",
334
+ });
335
+ }
336
+
337
+ // Check if done
338
+ if (action.response && !action.code) {
339
+ finalText = action.response;
340
+ break;
341
+ }
342
+
343
+ // Execute code
344
+ if (action.code && action.code.trim()) {
345
+ if (onProgress) {
346
+ await onProgress({
347
+ id: `step_${step}_code`,
348
+ type: "tool",
349
+ label: "Executing code...",
350
+ status: "active",
351
+ });
352
+ }
353
+
354
+ try {
355
+ const execResult = await ctx.runAction(internal.nodeActions.codeExec.execute, {
356
+ code: action.code,
357
+ timeoutMs: 60_000,
358
+ env: {
359
+ LOCAL_CONVEX_URL: "http://localhost:3210",
360
+ },
361
+ });
362
+
363
+ allExecutions.push({
364
+ code: action.code,
365
+ output: execResult.output,
366
+ success: execResult.success,
367
+ });
368
+
369
+ // Add to conversation
370
+ messages.push({
371
+ role: "assistant",
372
+ content: `Thinking: ${action.thinking || "..."}\n\n\`\`\`typescript\n${action.code}\n\`\`\``,
373
+ });
374
+ messages.push({
375
+ role: "user",
376
+ content: `Execution ${execResult.success ? "succeeded" : "failed"}:\n${execResult.output}\n\nContinue with JSON response.`,
377
+ });
378
+
379
+ if (onProgress) {
380
+ await onProgress({
381
+ id: `step_${step}_result`,
382
+ type: execResult.success ? "complete" : "error",
383
+ label: execResult.output.slice(0, 150),
384
+ status: execResult.success ? "complete" : "error",
385
+ });
386
+ }
387
+ } catch (e: any) {
388
+ allExecutions.push({
389
+ code: action.code,
390
+ output: e.message,
391
+ success: false,
392
+ });
393
+ messages.push({
394
+ role: "user",
395
+ content: `Execution error: ${e.message}\n\nTry a different approach.`,
396
+ });
397
+ }
398
+ } else if (!action.response) {
399
+ // No code and no response - try to continue
400
+ messages.push({
401
+ role: "user",
402
+ content: "Please provide either code to execute or a final response.",
403
+ });
404
+ }
405
+ }
406
+
407
+ return { text: finalText || "Subagent completed.", codeExecutions: allExecutions };
301
408
  }
302
409
 
303
410
  /**
@@ -369,11 +476,10 @@ Guidelines:
369
476
  );
370
477
  };
371
478
 
372
- const result = await runSubagentLoopWithProgress(
479
+ const result = await runSubagentCodeExecLoop(
373
480
  ctx,
374
481
  systemPrompt,
375
482
  args.task,
376
- args.tools,
377
483
  args.model || DEFAULT_SUBAGENT_MODEL,
378
484
  5,
379
485
  onProgress
@@ -385,7 +491,7 @@ Guidelines:
385
491
  status: "completed",
386
492
  result: {
387
493
  text: result.text,
388
- toolCalls: result.toolCalls,
494
+ codeExecutions: result.codeExecutions,
389
495
  },
390
496
  });
391
497
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lakitu/sdk",
3
- "version": "0.1.42",
3
+ "version": "0.1.44",
4
4
  "description": "Self-hosted AI agent framework for Convex + E2B with code execution",
5
5
  "type": "module",
6
6
  "main": "./dist/sdk/index.js",
@@ -1,55 +1,60 @@
1
1
  #!/bin/bash
2
- # Start convex-backend and deploy functions if needed
2
+ # Start convex-backend with pre-deployed functions
3
3
 
4
4
  STORAGE_DIR=/home/user/.convex/convex-backend-state/lakitu
5
5
  SQLITE_DB=$STORAGE_DIR/convex_local_backend.sqlite3
6
6
  MODULES_DIR=$STORAGE_DIR/modules
7
7
 
8
- # Start convex-backend in background
9
- convex-backend \
10
- "$SQLITE_DB" \
11
- --port 3210 \
12
- --site-proxy-port 3211 \
13
- --local-storage "$STORAGE_DIR" \
14
- --disable-beacon &
15
-
16
- BACKEND_PID=$!
17
-
18
- # Wait for backend to be ready
19
- for i in {1..30}; do
20
- if curl -s http://127.0.0.1:3210/version > /dev/null 2>&1; then
21
- echo "Backend ready"
22
- break
23
- fi
24
- sleep 1
25
- done
26
-
27
- # Deploy functions if not already deployed
8
+ # Deploy functions FIRST if not already deployed
28
9
  if [ ! -d "$MODULES_DIR" ] || [ -z "$(ls -A $MODULES_DIR 2>/dev/null)" ]; then
29
- echo "Deploying Convex functions..."
10
+ echo "Pre-deploying Convex functions..."
11
+
12
+ # Start temp backend on different port for deployment
13
+ convex-backend \
14
+ "$SQLITE_DB" \
15
+ --port 3209 \
16
+ --site-proxy-port 3208 \
17
+ --local-storage "$STORAGE_DIR" \
18
+ --disable-beacon &
19
+ TEMP_PID=$!
20
+
21
+ # Wait for temp backend
22
+ for i in {1..30}; do
23
+ if curl -s http://127.0.0.1:3209/version > /dev/null 2>&1; then
24
+ break
25
+ fi
26
+ sleep 1
27
+ done
28
+
29
+ # Deploy functions
30
30
  cd /home/user/lakitu
31
- export CONVEX_SELF_HOSTED_URL=http://127.0.0.1:3210
31
+ export CONVEX_SELF_HOSTED_URL=http://127.0.0.1:3209
32
32
  export CONVEX_SELF_HOSTED_ADMIN_KEY=0135d8598650f8f5cb0f30c34ec2e2bb62793bc28717c8eb6fb577996d50be5f4281b59181095065c5d0f86a2c31ddbe9b597ec62b47ded69782cd
33
33
 
34
- # Run deployment and wait for completion
35
34
  if npx convex dev --once --typecheck disable > /tmp/convex-deploy.log 2>&1; then
36
35
  echo "Functions deployed successfully"
37
36
  else
38
- echo "Function deployment failed, check /tmp/convex-deploy.log"
37
+ echo "Deploy failed:"
39
38
  cat /tmp/convex-deploy.log
40
39
  fi
41
40
 
42
- # Wait for modules to be ready (up to 30 seconds)
43
- echo "Waiting for modules..."
44
- for i in {1..30}; do
45
- if [ -d "$MODULES_DIR" ] && [ -n "$(ls -A $MODULES_DIR 2>/dev/null)" ]; then
46
- MODULE_COUNT=$(ls -1 $MODULES_DIR | wc -l)
47
- echo "Modules ready: $MODULE_COUNT files"
48
- break
49
- fi
50
- sleep 1
51
- done
41
+ # Stop temp backend
42
+ kill $TEMP_PID 2>/dev/null
43
+ sleep 1
44
+
45
+ # Verify deployment
46
+ if [ -d "$MODULES_DIR" ] && [ -n "$(ls -A $MODULES_DIR 2>/dev/null)" ]; then
47
+ MODULE_COUNT=$(ls -1 $MODULES_DIR | wc -l)
48
+ echo "Deployed $MODULE_COUNT modules"
49
+ else
50
+ echo "WARNING: No modules deployed"
51
+ fi
52
52
  fi
53
53
 
54
- # Wait for backend process
55
- wait $BACKEND_PID
54
+ # Now start the main backend on correct port
55
+ exec convex-backend \
56
+ "$SQLITE_DB" \
57
+ --port 3210 \
58
+ --site-proxy-port 3211 \
59
+ --local-storage "$STORAGE_DIR" \
60
+ --disable-beacon