sparkecoder 0.1.24 → 0.1.26

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 (96) hide show
  1. package/dist/agent/index.d.ts +3 -4
  2. package/dist/agent/index.js +670 -470
  3. package/dist/agent/index.js.map +1 -1
  4. package/dist/cli.js +1990 -1022
  5. package/dist/cli.js.map +1 -1
  6. package/dist/db/index.d.ts +145 -122
  7. package/dist/db/index.js +276 -646
  8. package/dist/db/index.js.map +1 -1
  9. package/dist/{index-CwQ-PrZw.d.ts → index-CWO1mYcx.d.ts} +87 -16
  10. package/dist/index.d.ts +6 -9
  11. package/dist/index.js +1026 -888
  12. package/dist/index.js.map +1 -1
  13. package/dist/schema-NcQknWCg.d.ts +295 -0
  14. package/dist/{search-S0REHtvA.d.ts → search-DApagzr3.d.ts} +5 -5
  15. package/dist/server/index.js +1026 -888
  16. package/dist/server/index.js.map +1 -1
  17. package/dist/tools/index.d.ts +38 -4
  18. package/dist/tools/index.js +569 -291
  19. package/dist/tools/index.js.map +1 -1
  20. package/package.json +1 -7
  21. package/web/.next/BUILD_ID +1 -1
  22. package/web/.next/standalone/web/.next/BUILD_ID +1 -1
  23. package/web/.next/standalone/web/.next/build-manifest.json +2 -2
  24. package/web/.next/standalone/web/.next/prerender-manifest.json +3 -3
  25. package/web/.next/standalone/web/.next/server/app/_global-error.html +2 -2
  26. package/web/.next/standalone/web/.next/server/app/_global-error.rsc +1 -1
  27. package/web/.next/standalone/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  28. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  29. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  30. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  31. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  32. package/web/.next/standalone/web/.next/server/app/_not-found.html +1 -1
  33. package/web/.next/standalone/web/.next/server/app/_not-found.rsc +1 -1
  34. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  35. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  36. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  37. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  38. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  39. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  40. package/web/.next/standalone/web/.next/server/app/docs/installation.html +2 -2
  41. package/web/.next/standalone/web/.next/server/app/docs/installation.rsc +1 -1
  42. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_full.segment.rsc +1 -1
  43. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_head.segment.rsc +1 -1
  44. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_index.segment.rsc +1 -1
  45. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_tree.segment.rsc +1 -1
  46. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs/installation/__PAGE__.segment.rsc +1 -1
  47. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs/installation.segment.rsc +1 -1
  48. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs.segment.rsc +1 -1
  49. package/web/.next/standalone/web/.next/server/app/docs/skills.html +2 -2
  50. package/web/.next/standalone/web/.next/server/app/docs/skills.rsc +1 -1
  51. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_full.segment.rsc +1 -1
  52. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_head.segment.rsc +1 -1
  53. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_index.segment.rsc +1 -1
  54. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_tree.segment.rsc +1 -1
  55. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs/skills/__PAGE__.segment.rsc +1 -1
  56. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs/skills.segment.rsc +1 -1
  57. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs.segment.rsc +1 -1
  58. package/web/.next/standalone/web/.next/server/app/docs/tools.html +2 -2
  59. package/web/.next/standalone/web/.next/server/app/docs/tools.rsc +1 -1
  60. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_full.segment.rsc +1 -1
  61. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_head.segment.rsc +1 -1
  62. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_index.segment.rsc +1 -1
  63. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_tree.segment.rsc +1 -1
  64. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs/tools/__PAGE__.segment.rsc +1 -1
  65. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs/tools.segment.rsc +1 -1
  66. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs.segment.rsc +1 -1
  67. package/web/.next/standalone/web/.next/server/app/docs.html +2 -2
  68. package/web/.next/standalone/web/.next/server/app/docs.rsc +1 -1
  69. package/web/.next/standalone/web/.next/server/app/docs.segments/_full.segment.rsc +1 -1
  70. package/web/.next/standalone/web/.next/server/app/docs.segments/_head.segment.rsc +1 -1
  71. package/web/.next/standalone/web/.next/server/app/docs.segments/_index.segment.rsc +1 -1
  72. package/web/.next/standalone/web/.next/server/app/docs.segments/_tree.segment.rsc +1 -1
  73. package/web/.next/standalone/web/.next/server/app/docs.segments/docs/__PAGE__.segment.rsc +1 -1
  74. package/web/.next/standalone/web/.next/server/app/docs.segments/docs.segment.rsc +1 -1
  75. package/web/.next/standalone/web/.next/server/app/index.html +1 -1
  76. package/web/.next/standalone/web/.next/server/app/index.rsc +1 -1
  77. package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p/__PAGE__.segment.rsc +1 -1
  78. package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p.segment.rsc +1 -1
  79. package/web/.next/standalone/web/.next/server/app/index.segments/_full.segment.rsc +1 -1
  80. package/web/.next/standalone/web/.next/server/app/index.segments/_head.segment.rsc +1 -1
  81. package/web/.next/standalone/web/.next/server/app/index.segments/_index.segment.rsc +1 -1
  82. package/web/.next/standalone/web/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  83. package/web/.next/standalone/web/.next/server/pages/404.html +1 -1
  84. package/web/.next/standalone/web/.next/server/pages/500.html +2 -2
  85. package/web/.next/standalone/web/.next/server/server-reference-manifest.js +1 -1
  86. package/web/.next/standalone/web/.next/server/server-reference-manifest.json +1 -1
  87. package/dist/schema-BqpMl6XE.d.ts +0 -1614
  88. /package/web/.next/standalone/web/.next/static/{static/vMB9qfM9a7tfGQAYWRMr8 → gnPGpNaKfNwMKujKqMYAv}/_buildManifest.js +0 -0
  89. /package/web/.next/standalone/web/.next/static/{static/vMB9qfM9a7tfGQAYWRMr8 → gnPGpNaKfNwMKujKqMYAv}/_clientMiddlewareManifest.json +0 -0
  90. /package/web/.next/standalone/web/.next/static/{static/vMB9qfM9a7tfGQAYWRMr8 → gnPGpNaKfNwMKujKqMYAv}/_ssgManifest.js +0 -0
  91. /package/web/.next/standalone/web/.next/static/{vMB9qfM9a7tfGQAYWRMr8 → static/gnPGpNaKfNwMKujKqMYAv}/_buildManifest.js +0 -0
  92. /package/web/.next/standalone/web/.next/static/{vMB9qfM9a7tfGQAYWRMr8 → static/gnPGpNaKfNwMKujKqMYAv}/_clientMiddlewareManifest.json +0 -0
  93. /package/web/.next/standalone/web/.next/static/{vMB9qfM9a7tfGQAYWRMr8 → static/gnPGpNaKfNwMKujKqMYAv}/_ssgManifest.js +0 -0
  94. /package/web/.next/static/{vMB9qfM9a7tfGQAYWRMr8 → gnPGpNaKfNwMKujKqMYAv}/_buildManifest.js +0 -0
  95. /package/web/.next/static/{vMB9qfM9a7tfGQAYWRMr8 → gnPGpNaKfNwMKujKqMYAv}/_clientMiddlewareManifest.json +0 -0
  96. /package/web/.next/static/{vMB9qfM9a7tfGQAYWRMr8 → gnPGpNaKfNwMKujKqMYAv}/_ssgManifest.js +0 -0
@@ -10,7 +10,7 @@ var __export = (target, all) => {
10
10
 
11
11
  // src/config/types.ts
12
12
  import { z } from "zod";
13
- var ToolApprovalConfigSchema, SkillMetadataSchema, SessionConfigSchema, SparkcoderConfigSchema;
13
+ var ToolApprovalConfigSchema, SkillMetadataSchema, SessionConfigSchema, VectorGatewayConfigSchema, RemoteServerConfigSchema, SparkcoderConfigSchema;
14
14
  var init_types = __esm({
15
15
  "src/config/types.ts"() {
16
16
  "use strict";
@@ -35,6 +35,53 @@ var init_types = __esm({
35
35
  skillsDirectory: z.string().optional(),
36
36
  maxContextChars: z.number().optional().default(2e5)
37
37
  });
38
+ VectorGatewayConfigSchema = z.object({
39
+ // Redis URL for Vector Gateway (or use VECTOR_REDIS_URL env var)
40
+ redisUrl: z.string().optional(),
41
+ // HTTP URL for database operations (or use VECTOR_HTTP_URL env var)
42
+ httpUrl: z.string().optional(),
43
+ // Embedding model to use (default: text-embedding-3-small)
44
+ embeddingModel: z.string().default("gemini-embedding-001"),
45
+ // Custom namespace override (auto-generated from git remote if not set)
46
+ namespace: z.string().optional(),
47
+ // File patterns to include in indexing
48
+ include: z.array(z.string()).optional().default([
49
+ "**/*.ts",
50
+ "**/*.tsx",
51
+ "**/*.js",
52
+ "**/*.jsx",
53
+ "**/*.py",
54
+ "**/*.go",
55
+ "**/*.rs",
56
+ "**/*.java",
57
+ "**/*.md",
58
+ "**/*.mdx",
59
+ "**/*.txt"
60
+ ]),
61
+ // File patterns to exclude from indexing
62
+ exclude: z.array(z.string()).optional().default([
63
+ "**/node_modules/**",
64
+ "**/dist/**",
65
+ "**/build/**",
66
+ "**/.git/**",
67
+ "**/.next/**",
68
+ "**/*.min.js",
69
+ "**/*.bundle.js",
70
+ "**/pnpm-lock.yaml",
71
+ "**/package-lock.json",
72
+ "**/yarn.lock",
73
+ "**/.test-workspace/**",
74
+ "**/.semantic-test-workspace/**",
75
+ "**/.semantic-integration-test/**"
76
+ ])
77
+ }).optional();
78
+ RemoteServerConfigSchema = z.object({
79
+ // URL of the remote server (e.g., https://agent.sparkecode.com)
80
+ url: z.string().url().optional(),
81
+ // Auth key for the remote server (auto-generated on first use if not set)
82
+ // Can also be set via SPARKECODER_AUTH_KEY env var
83
+ authKey: z.string().optional()
84
+ }).optional();
38
85
  SparkcoderConfigSchema = z.object({
39
86
  // Default model to use (Vercel AI Gateway format)
40
87
  defaultModel: z.string().default("anthropic/claude-opus-4-5"),
@@ -68,8 +115,13 @@ var init_types = __esm({
68
115
  // If not set, defaults to http://{host}:{port}
69
116
  publicUrl: z.string().url().optional()
70
117
  }).default({ port: 3141, host: "127.0.0.1" }),
71
- // Database path
72
- databasePath: z.string().optional().default("./sparkecoder.db")
118
+ // Database path (used for local SQLite - ignored if remoteServer is configured)
119
+ databasePath: z.string().optional().default("./sparkecoder.db"),
120
+ // Remote server configuration (for centralized storage)
121
+ // If configured, uses remote MongoDB instead of local SQLite
122
+ remoteServer: RemoteServerConfigSchema,
123
+ // Vector Gateway configuration for semantic search
124
+ vectorGateway: VectorGatewayConfigSchema
73
125
  });
74
126
  }
75
127
  });
@@ -398,7 +450,7 @@ var init_skills = __esm({
398
450
  import {
399
451
  streamText as streamText2,
400
452
  generateText as generateText3,
401
- tool as tool9,
453
+ tool as tool10,
402
454
  stepCountIs as stepCountIs2
403
455
  } from "ai";
404
456
 
@@ -419,498 +471,278 @@ var SUBAGENT_MODELS = {
419
471
  };
420
472
 
421
473
  // src/agent/index.ts
422
- import { z as z10 } from "zod";
423
- import { nanoid as nanoid4 } from "nanoid";
424
-
425
- // src/db/index.ts
426
- import Database from "better-sqlite3";
427
- import { drizzle } from "drizzle-orm/better-sqlite3";
428
- import { eq, desc, and, sql } from "drizzle-orm";
429
- import { nanoid } from "nanoid";
430
-
431
- // src/db/schema.ts
432
- import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core";
433
- var sessions = sqliteTable("sessions", {
434
- id: text("id").primaryKey(),
435
- name: text("name"),
436
- workingDirectory: text("working_directory").notNull(),
437
- model: text("model").notNull(),
438
- status: text("status", { enum: ["active", "waiting", "completed", "error"] }).notNull().default("active"),
439
- config: text("config", { mode: "json" }).$type(),
440
- createdAt: integer("created_at", { mode: "timestamp" }).notNull().$defaultFn(() => /* @__PURE__ */ new Date()),
441
- updatedAt: integer("updated_at", { mode: "timestamp" }).notNull().$defaultFn(() => /* @__PURE__ */ new Date())
442
- });
443
- var messages = sqliteTable("messages", {
444
- id: text("id").primaryKey(),
445
- sessionId: text("session_id").notNull().references(() => sessions.id, { onDelete: "cascade" }),
446
- // Store the entire ModelMessage as JSON (role + content)
447
- modelMessage: text("model_message", { mode: "json" }).$type().notNull(),
448
- // Sequence number within session to maintain exact ordering
449
- sequence: integer("sequence").notNull().default(0),
450
- createdAt: integer("created_at", { mode: "timestamp" }).notNull().$defaultFn(() => /* @__PURE__ */ new Date())
451
- });
452
- var toolExecutions = sqliteTable("tool_executions", {
453
- id: text("id").primaryKey(),
454
- sessionId: text("session_id").notNull().references(() => sessions.id, { onDelete: "cascade" }),
455
- messageId: text("message_id").references(() => messages.id, { onDelete: "cascade" }),
456
- toolName: text("tool_name").notNull(),
457
- toolCallId: text("tool_call_id").notNull(),
458
- input: text("input", { mode: "json" }),
459
- output: text("output", { mode: "json" }),
460
- status: text("status", { enum: ["pending", "approved", "rejected", "completed", "error"] }).notNull().default("pending"),
461
- requiresApproval: integer("requires_approval", { mode: "boolean" }).notNull().default(false),
462
- error: text("error"),
463
- startedAt: integer("started_at", { mode: "timestamp" }).notNull().$defaultFn(() => /* @__PURE__ */ new Date()),
464
- completedAt: integer("completed_at", { mode: "timestamp" })
465
- });
466
- var todoItems = sqliteTable("todo_items", {
467
- id: text("id").primaryKey(),
468
- sessionId: text("session_id").notNull().references(() => sessions.id, { onDelete: "cascade" }),
469
- content: text("content").notNull(),
470
- status: text("status", { enum: ["pending", "in_progress", "completed", "cancelled"] }).notNull().default("pending"),
471
- order: integer("order").notNull().default(0),
472
- createdAt: integer("created_at", { mode: "timestamp" }).notNull().$defaultFn(() => /* @__PURE__ */ new Date()),
473
- updatedAt: integer("updated_at", { mode: "timestamp" }).notNull().$defaultFn(() => /* @__PURE__ */ new Date())
474
- });
475
- var loadedSkills = sqliteTable("loaded_skills", {
476
- id: text("id").primaryKey(),
477
- sessionId: text("session_id").notNull().references(() => sessions.id, { onDelete: "cascade" }),
478
- skillName: text("skill_name").notNull(),
479
- loadedAt: integer("loaded_at", { mode: "timestamp" }).notNull().$defaultFn(() => /* @__PURE__ */ new Date())
480
- });
481
- var terminals = sqliteTable("terminals", {
482
- id: text("id").primaryKey(),
483
- sessionId: text("session_id").notNull().references(() => sessions.id, { onDelete: "cascade" }),
484
- name: text("name"),
485
- // Optional friendly name (e.g., "dev-server")
486
- command: text("command").notNull(),
487
- // The command that was run
488
- cwd: text("cwd").notNull(),
489
- // Working directory
490
- pid: integer("pid"),
491
- // Process ID (null if not running)
492
- status: text("status", { enum: ["running", "stopped", "error"] }).notNull().default("running"),
493
- exitCode: integer("exit_code"),
494
- // Exit code if stopped
495
- error: text("error"),
496
- // Error message if status is 'error'
497
- createdAt: integer("created_at", { mode: "timestamp" }).notNull().$defaultFn(() => /* @__PURE__ */ new Date()),
498
- stoppedAt: integer("stopped_at", { mode: "timestamp" })
499
- });
500
- var activeStreams = sqliteTable("active_streams", {
501
- id: text("id").primaryKey(),
502
- sessionId: text("session_id").notNull().references(() => sessions.id, { onDelete: "cascade" }),
503
- streamId: text("stream_id").notNull().unique(),
504
- // Unique stream identifier
505
- status: text("status", { enum: ["active", "finished", "error"] }).notNull().default("active"),
506
- createdAt: integer("created_at", { mode: "timestamp" }).notNull().$defaultFn(() => /* @__PURE__ */ new Date()),
507
- finishedAt: integer("finished_at", { mode: "timestamp" })
508
- });
509
- var checkpoints = sqliteTable("checkpoints", {
510
- id: text("id").primaryKey(),
511
- sessionId: text("session_id").notNull().references(() => sessions.id, { onDelete: "cascade" }),
512
- // The message sequence number this checkpoint was created BEFORE
513
- // (i.e., the state before this user message was processed)
514
- messageSequence: integer("message_sequence").notNull(),
515
- // Optional git commit hash if in a git repo
516
- gitHead: text("git_head"),
517
- createdAt: integer("created_at", { mode: "timestamp" }).notNull().$defaultFn(() => /* @__PURE__ */ new Date())
518
- });
519
- var fileBackups = sqliteTable("file_backups", {
520
- id: text("id").primaryKey(),
521
- checkpointId: text("checkpoint_id").notNull().references(() => checkpoints.id, { onDelete: "cascade" }),
522
- sessionId: text("session_id").notNull().references(() => sessions.id, { onDelete: "cascade" }),
523
- // Relative path from working directory
524
- filePath: text("file_path").notNull(),
525
- // Original content (null means file didn't exist before)
526
- originalContent: text("original_content"),
527
- // Whether the file existed before this checkpoint
528
- existed: integer("existed", { mode: "boolean" }).notNull().default(true),
529
- createdAt: integer("created_at", { mode: "timestamp" }).notNull().$defaultFn(() => /* @__PURE__ */ new Date())
530
- });
531
- var subagentExecutions = sqliteTable("subagent_executions", {
532
- id: text("id").primaryKey(),
533
- sessionId: text("session_id").notNull().references(() => sessions.id, { onDelete: "cascade" }),
534
- toolCallId: text("tool_call_id").notNull(),
535
- // The tool call that spawned this subagent
536
- subagentType: text("subagent_type").notNull(),
537
- // e.g., 'search', 'analyze', etc.
538
- task: text("task").notNull(),
539
- // The task/query given to the subagent
540
- model: text("model").notNull(),
541
- // The model used (e.g., 'gemini-2.0-flash')
542
- status: text("status", { enum: ["running", "completed", "error", "cancelled"] }).notNull().default("running"),
543
- // Steps taken by the subagent (stored as JSON array)
544
- steps: text("steps", { mode: "json" }).$type().default([]),
545
- // Final result/output
546
- result: text("result", { mode: "json" }),
547
- error: text("error"),
548
- startedAt: integer("started_at", { mode: "timestamp" }).notNull().$defaultFn(() => /* @__PURE__ */ new Date()),
549
- completedAt: integer("completed_at", { mode: "timestamp" })
550
- });
474
+ import { z as z11 } from "zod";
475
+ import { nanoid as nanoid3 } from "nanoid";
551
476
 
552
- // src/db/index.ts
553
- var db = null;
554
- function getDb() {
555
- if (!db) {
556
- throw new Error("Database not initialized. Call initDatabase first.");
477
+ // src/db/remote.ts
478
+ var remoteServerUrl = null;
479
+ var authKey = null;
480
+ var DATE_FIELDS = ["createdAt", "updatedAt", "startedAt", "completedAt", "stoppedAt", "finishedAt", "loadedAt", "indexedAt", "lastFullIndex", "lastIncrementalIndex"];
481
+ function parseDates(obj) {
482
+ if (obj === null || obj === void 0) return obj;
483
+ if (Array.isArray(obj)) return obj.map(parseDates);
484
+ if (typeof obj !== "object") return obj;
485
+ const result = { ...obj };
486
+ for (const key of Object.keys(result)) {
487
+ if (DATE_FIELDS.includes(key) && typeof result[key] === "string") {
488
+ result[key] = new Date(result[key]);
489
+ } else if (typeof result[key] === "object") {
490
+ result[key] = parseDates(result[key]);
491
+ }
492
+ }
493
+ return result;
494
+ }
495
+ async function api(path, options = {}) {
496
+ if (!remoteServerUrl || !authKey) {
497
+ throw new Error("Remote database not initialized");
498
+ }
499
+ const url = `${remoteServerUrl}/db${path}`;
500
+ const init = {
501
+ method: options.method || "GET",
502
+ headers: {
503
+ "Content-Type": "application/json",
504
+ "Authorization": `Bearer ${authKey}`
505
+ }
506
+ };
507
+ if (options.body) {
508
+ init.body = JSON.stringify(options.body);
509
+ }
510
+ const response = await fetch(url, init);
511
+ if (!response.ok) {
512
+ const error = await response.json().catch(() => ({ error: "Unknown error" }));
513
+ throw new Error(error.error || `HTTP ${response.status}`);
514
+ }
515
+ const text = await response.text();
516
+ if (!text || text === "null") {
517
+ return null;
557
518
  }
558
- return db;
519
+ return parseDates(JSON.parse(text));
559
520
  }
560
- var sessionQueries = {
521
+ var remoteSessionQueries = {
561
522
  create(data) {
562
- const id = nanoid();
563
- const now = /* @__PURE__ */ new Date();
564
- const result = getDb().insert(sessions).values({
565
- id,
566
- ...data,
567
- createdAt: now,
568
- updatedAt: now
569
- }).returning().get();
570
- return result;
523
+ return api("/sessions", { method: "POST", body: data });
571
524
  },
572
525
  getById(id) {
573
- return getDb().select().from(sessions).where(eq(sessions.id, id)).get();
526
+ return api(`/sessions/${id}`).catch(() => void 0);
574
527
  },
575
528
  list(limit = 50, offset = 0) {
576
- return getDb().select().from(sessions).orderBy(desc(sessions.createdAt)).limit(limit).offset(offset).all();
529
+ return api(`/sessions?limit=${limit}&offset=${offset}`);
577
530
  },
578
531
  updateStatus(id, status) {
579
- return getDb().update(sessions).set({ status, updatedAt: /* @__PURE__ */ new Date() }).where(eq(sessions.id, id)).returning().get();
532
+ return api(`/sessions/${id}`, { method: "PATCH", body: { status } });
580
533
  },
581
534
  updateModel(id, model) {
582
- return getDb().update(sessions).set({ model, updatedAt: /* @__PURE__ */ new Date() }).where(eq(sessions.id, id)).returning().get();
535
+ return api(`/sessions/${id}`, { method: "PATCH", body: { model } });
583
536
  },
584
537
  update(id, updates) {
585
- return getDb().update(sessions).set({ ...updates, updatedAt: /* @__PURE__ */ new Date() }).where(eq(sessions.id, id)).returning().get();
538
+ return api(`/sessions/${id}`, { method: "PATCH", body: updates });
586
539
  },
587
540
  delete(id) {
588
- const result = getDb().delete(sessions).where(eq(sessions.id, id)).run();
589
- return result.changes > 0;
541
+ return api(`/sessions/${id}`, { method: "DELETE" }).then((r) => r?.success ?? false);
590
542
  }
591
543
  };
592
- var messageQueries = {
593
- /**
594
- * Get the next sequence number for a session
595
- */
596
- getNextSequence(sessionId) {
597
- const result = getDb().select({ maxSeq: sql`COALESCE(MAX(sequence), -1)` }).from(messages).where(eq(messages.sessionId, sessionId)).get();
598
- return (result?.maxSeq ?? -1) + 1;
544
+ var remoteMessageQueries = {
545
+ async getNextSequence(sessionId) {
546
+ const result = await api(`/messages/session/${sessionId}/next-sequence`);
547
+ return result.nextSequence;
599
548
  },
600
- /**
601
- * Create a single message from a ModelMessage
602
- */
603
549
  create(sessionId, modelMessage) {
604
- const id = nanoid();
605
- const sequence = this.getNextSequence(sessionId);
606
- const result = getDb().insert(messages).values({
607
- id,
608
- sessionId,
609
- modelMessage,
610
- sequence,
611
- createdAt: /* @__PURE__ */ new Date()
612
- }).returning().get();
613
- return result;
550
+ return api("/messages", { method: "POST", body: { sessionId, modelMessage } });
614
551
  },
615
- /**
616
- * Add multiple ModelMessages at once (from response.messages)
617
- * Maintains insertion order via sequence numbers
618
- */
619
552
  addMany(sessionId, modelMessages) {
620
- const results = [];
621
- let sequence = this.getNextSequence(sessionId);
622
- for (const msg of modelMessages) {
623
- const id = nanoid();
624
- const result = getDb().insert(messages).values({
625
- id,
626
- sessionId,
627
- modelMessage: msg,
628
- sequence,
629
- createdAt: /* @__PURE__ */ new Date()
630
- }).returning().get();
631
- results.push(result);
632
- sequence++;
633
- }
634
- return results;
553
+ return api("/messages/batch", { method: "POST", body: { sessionId, modelMessages } });
635
554
  },
636
- /**
637
- * Get all messages for a session as ModelMessage[]
638
- * Ordered by sequence to maintain exact insertion order
639
- */
640
555
  getBySession(sessionId) {
641
- return getDb().select().from(messages).where(eq(messages.sessionId, sessionId)).orderBy(messages.sequence).all();
556
+ return api(`/messages/session/${sessionId}`);
642
557
  },
643
- /**
644
- * Get ModelMessages directly (for passing to AI SDK)
645
- */
646
558
  getModelMessages(sessionId) {
647
- const messages2 = this.getBySession(sessionId);
648
- return messages2.map((m) => m.modelMessage);
559
+ return api(`/messages/session/${sessionId}/model-messages`);
649
560
  },
650
- getRecentBySession(sessionId, limit = 50) {
651
- return getDb().select().from(messages).where(eq(messages.sessionId, sessionId)).orderBy(desc(messages.sequence)).limit(limit).all().reverse();
561
+ async getRecentBySession(sessionId, limit = 50) {
562
+ const messages = await api(`/messages/session/${sessionId}`);
563
+ return messages.slice(-limit);
652
564
  },
653
- countBySession(sessionId) {
654
- const result = getDb().select({ count: sql`count(*)` }).from(messages).where(eq(messages.sessionId, sessionId)).get();
655
- return result?.count ?? 0;
565
+ async countBySession(sessionId) {
566
+ const result = await api(`/messages/session/${sessionId}/count`);
567
+ return result.count;
656
568
  },
657
- deleteBySession(sessionId) {
658
- const result = getDb().delete(messages).where(eq(messages.sessionId, sessionId)).run();
659
- return result.changes;
569
+ async deleteBySession(sessionId) {
570
+ const result = await api(`/messages/session/${sessionId}`, { method: "DELETE" });
571
+ return result.deleted;
660
572
  },
661
- /**
662
- * Delete all messages with sequence >= the given sequence number
663
- * (Used when reverting to a checkpoint)
664
- */
665
- deleteFromSequence(sessionId, fromSequence) {
666
- const result = getDb().delete(messages).where(
667
- and(
668
- eq(messages.sessionId, sessionId),
669
- sql`sequence >= ${fromSequence}`
670
- )
671
- ).run();
672
- return result.changes;
573
+ async deleteFromSequence(sessionId, fromSequence) {
574
+ const result = await api(
575
+ `/messages/session/${sessionId}/from-sequence/${fromSequence}`,
576
+ { method: "DELETE" }
577
+ );
578
+ return result.deleted;
673
579
  }
674
580
  };
675
- var toolExecutionQueries = {
581
+ var remoteToolExecutionQueries = {
676
582
  create(data) {
677
- const id = nanoid();
678
- const result = getDb().insert(toolExecutions).values({
679
- id,
680
- ...data,
681
- startedAt: /* @__PURE__ */ new Date()
682
- }).returning().get();
683
- return result;
583
+ return api("/tool-executions", { method: "POST", body: data });
684
584
  },
685
585
  getById(id) {
686
- return getDb().select().from(toolExecutions).where(eq(toolExecutions.id, id)).get();
586
+ return api(`/tool-executions/${id}`).catch(() => void 0);
687
587
  },
688
588
  getByToolCallId(toolCallId) {
689
- return getDb().select().from(toolExecutions).where(eq(toolExecutions.toolCallId, toolCallId)).get();
589
+ return api(`/tool-executions/by-tool-call-id/${toolCallId}`).catch(() => void 0);
690
590
  },
691
591
  getPendingApprovals(sessionId) {
692
- return getDb().select().from(toolExecutions).where(
693
- and(
694
- eq(toolExecutions.sessionId, sessionId),
695
- eq(toolExecutions.status, "pending"),
696
- eq(toolExecutions.requiresApproval, true)
697
- )
698
- ).all();
592
+ return api(`/tool-executions/session/${sessionId}/pending`);
699
593
  },
700
594
  approve(id) {
701
- return getDb().update(toolExecutions).set({ status: "approved" }).where(eq(toolExecutions.id, id)).returning().get();
595
+ return api(`/tool-executions/${id}`, { method: "PATCH", body: { status: "approved" } });
702
596
  },
703
597
  reject(id) {
704
- return getDb().update(toolExecutions).set({ status: "rejected" }).where(eq(toolExecutions.id, id)).returning().get();
598
+ return api(`/tool-executions/${id}`, { method: "PATCH", body: { status: "rejected" } });
705
599
  },
706
600
  complete(id, output, error) {
707
- return getDb().update(toolExecutions).set({
708
- status: error ? "error" : "completed",
709
- output,
710
- error,
711
- completedAt: /* @__PURE__ */ new Date()
712
- }).where(eq(toolExecutions.id, id)).returning().get();
601
+ return api(`/tool-executions/${id}`, {
602
+ method: "PATCH",
603
+ body: { status: error ? "error" : "completed", output, error }
604
+ });
713
605
  },
714
606
  getBySession(sessionId) {
715
- return getDb().select().from(toolExecutions).where(eq(toolExecutions.sessionId, sessionId)).orderBy(toolExecutions.startedAt).all();
607
+ return api(`/tool-executions/session/${sessionId}`);
716
608
  },
717
- /**
718
- * Delete all tool executions after a given timestamp
719
- * (Used when reverting to a checkpoint)
720
- */
721
- deleteAfterTime(sessionId, afterTime) {
722
- const result = getDb().delete(toolExecutions).where(
723
- and(
724
- eq(toolExecutions.sessionId, sessionId),
725
- sql`started_at > ${afterTime.getTime()}`
726
- )
727
- ).run();
728
- return result.changes;
609
+ async deleteAfterTime(sessionId, afterTime) {
610
+ const timestamp = afterTime instanceof Date ? afterTime.getTime() : new Date(afterTime).getTime();
611
+ const result = await api(
612
+ `/tool-executions/session/${sessionId}/after/${timestamp}`,
613
+ { method: "DELETE" }
614
+ );
615
+ return result.deleted;
729
616
  }
730
617
  };
731
- var todoQueries = {
618
+ var remoteTodoQueries = {
732
619
  create(data) {
733
- const id = nanoid();
734
- const now = /* @__PURE__ */ new Date();
735
- const result = getDb().insert(todoItems).values({
736
- id,
737
- ...data,
738
- createdAt: now,
739
- updatedAt: now
740
- }).returning().get();
741
- return result;
620
+ return api("/todos", { method: "POST", body: data });
742
621
  },
743
622
  createMany(sessionId, items) {
744
- const now = /* @__PURE__ */ new Date();
745
- const values = items.map((item, index) => ({
746
- id: nanoid(),
747
- sessionId,
748
- content: item.content,
749
- order: item.order ?? index,
750
- createdAt: now,
751
- updatedAt: now
752
- }));
753
- return getDb().insert(todoItems).values(values).returning().all();
623
+ return api("/todos/batch", { method: "POST", body: { sessionId, items } });
754
624
  },
755
625
  getBySession(sessionId) {
756
- return getDb().select().from(todoItems).where(eq(todoItems.sessionId, sessionId)).orderBy(todoItems.order).all();
626
+ return api(`/todos/session/${sessionId}`);
757
627
  },
758
628
  updateStatus(id, status) {
759
- return getDb().update(todoItems).set({ status, updatedAt: /* @__PURE__ */ new Date() }).where(eq(todoItems.id, id)).returning().get();
629
+ return api(`/todos/${id}`, { method: "PATCH", body: { status } });
760
630
  },
761
- delete(id) {
762
- const result = getDb().delete(todoItems).where(eq(todoItems.id, id)).run();
763
- return result.changes > 0;
631
+ async delete(id) {
632
+ const result = await api(`/todos/${id}`, { method: "DELETE" });
633
+ return result?.success ?? false;
764
634
  },
765
- clearSession(sessionId) {
766
- const result = getDb().delete(todoItems).where(eq(todoItems.sessionId, sessionId)).run();
767
- return result.changes;
635
+ async clearSession(sessionId) {
636
+ const result = await api(`/todos/session/${sessionId}`, { method: "DELETE" });
637
+ return result.deleted;
768
638
  }
769
639
  };
770
- var skillQueries = {
640
+ var remoteSkillQueries = {
771
641
  load(sessionId, skillName) {
772
- const id = nanoid();
773
- const result = getDb().insert(loadedSkills).values({
774
- id,
775
- sessionId,
776
- skillName,
777
- loadedAt: /* @__PURE__ */ new Date()
778
- }).returning().get();
779
- return result;
642
+ return api("/skills", { method: "POST", body: { sessionId, skillName } });
780
643
  },
781
644
  getBySession(sessionId) {
782
- return getDb().select().from(loadedSkills).where(eq(loadedSkills.sessionId, sessionId)).orderBy(loadedSkills.loadedAt).all();
645
+ return api(`/skills/session/${sessionId}`);
783
646
  },
784
- isLoaded(sessionId, skillName) {
785
- const result = getDb().select().from(loadedSkills).where(
786
- and(
787
- eq(loadedSkills.sessionId, sessionId),
788
- eq(loadedSkills.skillName, skillName)
789
- )
790
- ).get();
791
- return !!result;
647
+ async isLoaded(sessionId, skillName) {
648
+ const result = await api(`/skills/session/${sessionId}/is-loaded/${skillName}`);
649
+ return result.isLoaded;
792
650
  }
793
651
  };
794
- var fileBackupQueries = {
652
+ var remoteFileBackupQueries = {
795
653
  create(data) {
796
- const id = nanoid();
797
- const result = getDb().insert(fileBackups).values({
798
- id,
799
- checkpointId: data.checkpointId,
800
- sessionId: data.sessionId,
801
- filePath: data.filePath,
802
- originalContent: data.originalContent,
803
- existed: data.existed,
804
- createdAt: /* @__PURE__ */ new Date()
805
- }).returning().get();
806
- return result;
654
+ return api("/file-backups", { method: "POST", body: data });
807
655
  },
808
656
  getByCheckpoint(checkpointId) {
809
- return getDb().select().from(fileBackups).where(eq(fileBackups.checkpointId, checkpointId)).all();
657
+ return api(`/file-backups/checkpoint/${checkpointId}`);
810
658
  },
811
659
  getBySession(sessionId) {
812
- return getDb().select().from(fileBackups).where(eq(fileBackups.sessionId, sessionId)).orderBy(fileBackups.createdAt).all();
660
+ return api(`/file-backups/session/${sessionId}`);
813
661
  },
814
- /**
815
- * Get all file backups from a given checkpoint sequence onwards (inclusive)
816
- * (Used when reverting - need to restore these files)
817
- *
818
- * When reverting to checkpoint X, we need backups from checkpoint X and all later ones
819
- * because checkpoint X's backups represent the state BEFORE processing message X.
820
- */
821
662
  getFromSequence(sessionId, messageSequence) {
822
- const checkpointsFrom = getDb().select().from(checkpoints).where(
823
- and(
824
- eq(checkpoints.sessionId, sessionId),
825
- sql`message_sequence >= ${messageSequence}`
826
- )
827
- ).all();
828
- if (checkpointsFrom.length === 0) {
829
- return [];
830
- }
831
- const checkpointIds = checkpointsFrom.map((c) => c.id);
832
- const allBackups = [];
833
- for (const cpId of checkpointIds) {
834
- const backups = getDb().select().from(fileBackups).where(eq(fileBackups.checkpointId, cpId)).all();
835
- allBackups.push(...backups);
836
- }
837
- return allBackups;
663
+ return api(`/file-backups/session/${sessionId}/from-sequence/${messageSequence}`);
838
664
  },
839
- /**
840
- * Check if a file already has a backup in the current checkpoint
841
- */
842
- hasBackup(checkpointId, filePath) {
843
- const result = getDb().select().from(fileBackups).where(
844
- and(
845
- eq(fileBackups.checkpointId, checkpointId),
846
- eq(fileBackups.filePath, filePath)
847
- )
848
- ).get();
849
- return !!result;
665
+ async hasBackup(checkpointId, filePath) {
666
+ const result = await api(
667
+ `/file-backups/checkpoint/${checkpointId}/has-backup/${encodeURIComponent(filePath)}`
668
+ );
669
+ return result.hasBackup;
850
670
  },
851
- deleteBySession(sessionId) {
852
- const result = getDb().delete(fileBackups).where(eq(fileBackups.sessionId, sessionId)).run();
853
- return result.changes;
671
+ async deleteBySession(sessionId) {
672
+ const result = await api(`/file-backups/session/${sessionId}`, { method: "DELETE" });
673
+ return result.deleted;
854
674
  }
855
675
  };
856
- var subagentQueries = {
676
+ var remoteSubagentQueries = {
857
677
  create(data) {
858
- const id = nanoid();
859
- const result = getDb().insert(subagentExecutions).values({
860
- id,
861
- sessionId: data.sessionId,
862
- toolCallId: data.toolCallId,
863
- subagentType: data.subagentType,
864
- task: data.task,
865
- model: data.model,
866
- status: "running",
867
- steps: [],
868
- startedAt: /* @__PURE__ */ new Date()
869
- }).returning().get();
870
- return result;
678
+ return api("/subagents", { method: "POST", body: data });
871
679
  },
872
680
  getById(id) {
873
- return getDb().select().from(subagentExecutions).where(eq(subagentExecutions.id, id)).get();
681
+ return api(`/subagents/${id}`).catch(() => void 0);
874
682
  },
875
683
  getByToolCallId(toolCallId) {
876
- return getDb().select().from(subagentExecutions).where(eq(subagentExecutions.toolCallId, toolCallId)).get();
684
+ return api(`/subagents/by-tool-call-id/${toolCallId}`).catch(() => void 0);
877
685
  },
878
686
  getBySession(sessionId) {
879
- return getDb().select().from(subagentExecutions).where(eq(subagentExecutions.sessionId, sessionId)).orderBy(desc(subagentExecutions.startedAt)).all();
687
+ return api(`/subagents/session/${sessionId}`);
880
688
  },
881
689
  addStep(id, step) {
882
- const existing = this.getById(id);
883
- if (!existing) return void 0;
884
- const currentSteps = existing.steps || [];
885
- const newSteps = [...currentSteps, step];
886
- return getDb().update(subagentExecutions).set({ steps: newSteps }).where(eq(subagentExecutions.id, id)).returning().get();
690
+ return api(`/subagents/${id}/add-step`, { method: "POST", body: { step } }).catch(() => void 0);
887
691
  },
888
692
  complete(id, result) {
889
- return getDb().update(subagentExecutions).set({
890
- status: "completed",
891
- result,
892
- completedAt: /* @__PURE__ */ new Date()
893
- }).where(eq(subagentExecutions.id, id)).returning().get();
693
+ return api(`/subagents/${id}`, { method: "PATCH", body: { status: "completed", result } }).catch(() => void 0);
894
694
  },
895
695
  markError(id, error) {
896
- return getDb().update(subagentExecutions).set({
897
- status: "error",
898
- error,
899
- completedAt: /* @__PURE__ */ new Date()
900
- }).where(eq(subagentExecutions.id, id)).returning().get();
696
+ return api(`/subagents/${id}`, { method: "PATCH", body: { status: "error", error } }).catch(() => void 0);
901
697
  },
902
698
  cancel(id) {
903
- return getDb().update(subagentExecutions).set({
904
- status: "cancelled",
905
- completedAt: /* @__PURE__ */ new Date()
906
- }).where(eq(subagentExecutions.id, id)).returning().get();
699
+ return api(`/subagents/${id}`, { method: "PATCH", body: { status: "cancelled" } }).catch(() => void 0);
700
+ },
701
+ async deleteBySession(sessionId) {
702
+ const result = await api(`/subagents/session/${sessionId}`, { method: "DELETE" });
703
+ return result.deleted;
704
+ }
705
+ };
706
+ var remoteIndexStatusQueries = {
707
+ upsert(_db, data) {
708
+ return api("/index-status", {
709
+ method: "POST",
710
+ body: {
711
+ ...data,
712
+ lastFullIndex: data.lastFullIndex?.toISOString(),
713
+ lastIncrementalIndex: data.lastIncrementalIndex?.toISOString()
714
+ }
715
+ });
716
+ },
717
+ get(_db, namespace) {
718
+ return api(`/index-status/namespace/${namespace}`).then((r) => r ?? void 0);
719
+ },
720
+ async delete(_db, namespace) {
721
+ const result = await api(`/index-status/namespace/${namespace}`, { method: "DELETE" });
722
+ return result?.success ?? false;
907
723
  },
908
- deleteBySession(sessionId) {
909
- const result = getDb().delete(subagentExecutions).where(eq(subagentExecutions.sessionId, sessionId)).run();
910
- return result.changes;
724
+ list(_db) {
725
+ return api("/index-status");
911
726
  }
912
727
  };
913
728
 
729
+ // src/db/index.ts
730
+ var initialized = false;
731
+ function getDb() {
732
+ if (!initialized) {
733
+ throw new Error("Database not initialized. Call initDatabase first.");
734
+ }
735
+ return {};
736
+ }
737
+ var sessionQueries = remoteSessionQueries;
738
+ var messageQueries = remoteMessageQueries;
739
+ var toolExecutionQueries = remoteToolExecutionQueries;
740
+ var todoQueries = remoteTodoQueries;
741
+ var skillQueries = remoteSkillQueries;
742
+ var fileBackupQueries = remoteFileBackupQueries;
743
+ var subagentQueries = remoteSubagentQueries;
744
+ var indexStatusQueries = remoteIndexStatusQueries;
745
+
914
746
  // src/config/index.ts
915
747
  init_types();
916
748
  init_types();
@@ -978,8 +810,8 @@ function truncateOutput(output, maxChars = MAX_OUTPUT_CHARS) {
978
810
 
979
811
  ` + output.slice(-halfMax);
980
812
  }
981
- function calculateContextSize(messages2) {
982
- return messages2.reduce((total, msg) => {
813
+ function calculateContextSize(messages) {
814
+ return messages.reduce((total, msg) => {
983
815
  const content = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content);
984
816
  return total + content.length;
985
817
  }, 0);
@@ -991,7 +823,7 @@ import { promisify } from "util";
991
823
  import { mkdir, writeFile, readFile } from "fs/promises";
992
824
  import { existsSync as existsSync2, mkdirSync as mkdirSync2 } from "fs";
993
825
  import { join as join2 } from "path";
994
- import { nanoid as nanoid2 } from "nanoid";
826
+ import { nanoid } from "nanoid";
995
827
  var execAsync = promisify(exec);
996
828
  var SESSION_PREFIX = "spark_";
997
829
  var LOG_BASE_DIR = "sessions";
@@ -1011,7 +843,7 @@ async function isTmuxAvailable() {
1011
843
  }
1012
844
  }
1013
845
  function generateTerminalId() {
1014
- return "t" + nanoid2(9);
846
+ return "t" + nanoid(9);
1015
847
  }
1016
848
  function getSessionName(terminalId) {
1017
849
  return `${SESSION_PREFIX}${terminalId}`;
@@ -1615,7 +1447,7 @@ async function backupFile(sessionId, workingDirectory, filePath) {
1615
1447
  }
1616
1448
  const absolutePath = resolve3(workingDirectory, filePath);
1617
1449
  const relativePath = relative2(workingDirectory, absolutePath);
1618
- if (fileBackupQueries.hasBackup(manager.currentCheckpointId, relativePath)) {
1450
+ if (await fileBackupQueries.hasBackup(manager.currentCheckpointId, relativePath)) {
1619
1451
  return null;
1620
1452
  }
1621
1453
  let originalContent = null;
@@ -1628,7 +1460,7 @@ async function backupFile(sessionId, workingDirectory, filePath) {
1628
1460
  console.warn(`[checkpoint] Failed to read file for backup: ${error.message}`);
1629
1461
  }
1630
1462
  }
1631
- const backup = fileBackupQueries.create({
1463
+ const backup = await fileBackupQueries.create({
1632
1464
  checkpointId: manager.currentCheckpointId,
1633
1465
  sessionId,
1634
1466
  filePath: relativePath,
@@ -2385,7 +2217,7 @@ Best practices:
2385
2217
  error: "No items provided. Include at least one todo item."
2386
2218
  };
2387
2219
  }
2388
- const created = todoQueries.createMany(options.sessionId, items);
2220
+ const created = await todoQueries.createMany(options.sessionId, items);
2389
2221
  return {
2390
2222
  success: true,
2391
2223
  action: "add",
@@ -2394,7 +2226,7 @@ Best practices:
2394
2226
  };
2395
2227
  }
2396
2228
  case "list": {
2397
- const todos = todoQueries.getBySession(options.sessionId);
2229
+ const todos = await todoQueries.getBySession(options.sessionId);
2398
2230
  const stats = {
2399
2231
  total: todos.length,
2400
2232
  pending: todos.filter((t) => t.status === "pending").length,
@@ -2422,7 +2254,7 @@ Best practices:
2422
2254
  error: 'status is required for "mark" action'
2423
2255
  };
2424
2256
  }
2425
- const updated = todoQueries.updateStatus(todoId, status);
2257
+ const updated = await todoQueries.updateStatus(todoId, status);
2426
2258
  if (!updated) {
2427
2259
  return {
2428
2260
  success: false,
@@ -2436,7 +2268,7 @@ Best practices:
2436
2268
  };
2437
2269
  }
2438
2270
  case "clear": {
2439
- const count = todoQueries.clearSession(options.sessionId);
2271
+ const count = await todoQueries.clearSession(options.sessionId);
2440
2272
  return {
2441
2273
  success: true,
2442
2274
  action: "clear",
@@ -2510,7 +2342,7 @@ Once loaded, a skill's content will be available in the conversation context.`,
2510
2342
  error: 'skillName is required for "load" action'
2511
2343
  };
2512
2344
  }
2513
- if (skillQueries.isLoaded(options.sessionId, skillName)) {
2345
+ if (await skillQueries.isLoaded(options.sessionId, skillName)) {
2514
2346
  return {
2515
2347
  success: false,
2516
2348
  error: `Skill "${skillName}" is already loaded in this session`
@@ -2525,7 +2357,7 @@ Once loaded, a skill's content will be available in the conversation context.`,
2525
2357
  availableSkills: allSkills.map((s) => s.name)
2526
2358
  };
2527
2359
  }
2528
- skillQueries.load(options.sessionId, skillName);
2360
+ await skillQueries.load(options.sessionId, skillName);
2529
2361
  return {
2530
2362
  success: true,
2531
2363
  action: "load",
@@ -2753,7 +2585,7 @@ import {
2753
2585
  generateText,
2754
2586
  stepCountIs
2755
2587
  } from "ai";
2756
- import { nanoid as nanoid3 } from "nanoid";
2588
+ import { nanoid as nanoid2 } from "nanoid";
2757
2589
  var Subagent = class {
2758
2590
  /** Model to use (defaults to gemini-2.0-flash) */
2759
2591
  model;
@@ -2766,8 +2598,8 @@ var Subagent = class {
2766
2598
  * Parse the final result from the subagent's output.
2767
2599
  * Override this to structure the result for your subagent type.
2768
2600
  */
2769
- parseResult(text2, steps) {
2770
- return { text: text2, steps };
2601
+ parseResult(text, steps) {
2602
+ return { text, steps };
2771
2603
  }
2772
2604
  /**
2773
2605
  * Run the subagent with streaming progress updates
@@ -2775,7 +2607,7 @@ var Subagent = class {
2775
2607
  async run(options) {
2776
2608
  const { task, sessionId, toolCallId, onProgress, abortSignal } = options;
2777
2609
  const steps = [];
2778
- const execution = subagentQueries.create({
2610
+ const execution = await subagentQueries.create({
2779
2611
  sessionId,
2780
2612
  toolCallId,
2781
2613
  subagentType: this.type,
@@ -2784,12 +2616,12 @@ var Subagent = class {
2784
2616
  });
2785
2617
  const addStep = async (step) => {
2786
2618
  const fullStep = {
2787
- id: nanoid3(8),
2619
+ id: nanoid2(8),
2788
2620
  timestamp: Date.now(),
2789
2621
  ...step
2790
2622
  };
2791
2623
  steps.push(fullStep);
2792
- subagentQueries.addStep(execution.id, fullStep);
2624
+ await subagentQueries.addStep(execution.id, fullStep);
2793
2625
  await onProgress?.({
2794
2626
  type: "step",
2795
2627
  subagentId: execution.id,
@@ -2859,7 +2691,7 @@ var Subagent = class {
2859
2691
  }
2860
2692
  });
2861
2693
  const parsedResult = this.parseResult(result.text, steps);
2862
- subagentQueries.complete(execution.id, parsedResult);
2694
+ await subagentQueries.complete(execution.id, parsedResult);
2863
2695
  await onProgress?.({
2864
2696
  type: "complete",
2865
2697
  subagentId: execution.id,
@@ -2874,7 +2706,7 @@ var Subagent = class {
2874
2706
  };
2875
2707
  } catch (error) {
2876
2708
  const errorMessage = error.message || "Unknown error";
2877
- subagentQueries.markError(execution.id, errorMessage);
2709
+ await subagentQueries.markError(execution.id, errorMessage);
2878
2710
  await onProgress?.({
2879
2711
  type: "error",
2880
2712
  subagentId: execution.id,
@@ -3184,7 +3016,7 @@ Keep your responses concise and focused on actionable information.`;
3184
3016
  })
3185
3017
  };
3186
3018
  }
3187
- parseResult(text2, steps) {
3019
+ parseResult(text, steps) {
3188
3020
  const findings = [];
3189
3021
  let filesSearched = 0;
3190
3022
  let matchCount = 0;
@@ -3228,7 +3060,7 @@ Keep your responses concise and focused on actionable information.`;
3228
3060
  const query = steps.length > 0 ? steps.find((s) => s.type === "text")?.content || "" : "";
3229
3061
  return {
3230
3062
  query,
3231
- summary: text2,
3063
+ summary: text,
3232
3064
  findings: findings.slice(0, 20),
3233
3065
  // Limit findings
3234
3066
  filesSearched,
@@ -3359,9 +3191,362 @@ Context: ${context}` : query;
3359
3191
  });
3360
3192
  }
3361
3193
 
3194
+ // src/tools/semantic-search.ts
3195
+ import { tool as tool9 } from "ai";
3196
+ import { z as z10 } from "zod";
3197
+ import { existsSync as existsSync11, readFileSync as readFileSync4 } from "fs";
3198
+ import { join as join5 } from "path";
3199
+ import { minimatch as minimatch3 } from "minimatch";
3200
+
3201
+ // src/semantic/namespace.ts
3202
+ import { execSync } from "child_process";
3203
+ function getGitRemoteUrl(workingDirectory) {
3204
+ try {
3205
+ const result = execSync("git remote get-url origin", {
3206
+ cwd: workingDirectory,
3207
+ encoding: "utf-8",
3208
+ stdio: ["pipe", "pipe", "pipe"]
3209
+ });
3210
+ return result.trim();
3211
+ } catch {
3212
+ return null;
3213
+ }
3214
+ }
3215
+ function parseGitRemoteUrl(url) {
3216
+ const cleanUrl = url.replace(/\.git$/, "");
3217
+ const sshMatch = cleanUrl.match(/git@[^:]+:([^/]+)\/(.+)$/);
3218
+ if (sshMatch) {
3219
+ return { org: sshMatch[1], repo: sshMatch[2] };
3220
+ }
3221
+ const httpsMatch = cleanUrl.match(/https?:\/\/[^/]+\/([^/]+)\/(.+)$/);
3222
+ if (httpsMatch) {
3223
+ return { org: httpsMatch[1], repo: httpsMatch[2] };
3224
+ }
3225
+ const sshProtoMatch = cleanUrl.match(/ssh:\/\/[^/]+\/([^/]+)\/(.+)$/);
3226
+ if (sshProtoMatch) {
3227
+ return { org: sshProtoMatch[1], repo: sshProtoMatch[2] };
3228
+ }
3229
+ return null;
3230
+ }
3231
+ function sanitizeForNamespace(str) {
3232
+ return str.toLowerCase().replace(/[^a-z0-9]/g, "_").replace(/^_+|_+$/g, "").replace(/_+/g, "_");
3233
+ }
3234
+ async function getRepoNamespace(workingDirectory, configuredNamespace) {
3235
+ if (configuredNamespace) {
3236
+ return configuredNamespace;
3237
+ }
3238
+ const remoteUrl = getGitRemoteUrl(workingDirectory);
3239
+ if (!remoteUrl) {
3240
+ return null;
3241
+ }
3242
+ const parsed = parseGitRemoteUrl(remoteUrl);
3243
+ if (!parsed) {
3244
+ return null;
3245
+ }
3246
+ const org = sanitizeForNamespace(parsed.org);
3247
+ const repo = sanitizeForNamespace(parsed.repo);
3248
+ return `sparkecoder_${org}_${repo}`;
3249
+ }
3250
+
3251
+ // src/semantic/hasher.ts
3252
+ import { createHash } from "crypto";
3253
+
3254
+ // src/semantic/chunker.ts
3255
+ import { extname as extname6, basename as basename2 } from "path";
3256
+
3257
+ // src/semantic/client.ts
3258
+ var remoteServerUrl2 = null;
3259
+ var authKey2 = null;
3260
+ function initVectorClient(serverUrl, key) {
3261
+ remoteServerUrl2 = serverUrl.replace(/\/$/, "");
3262
+ authKey2 = key;
3263
+ }
3264
+ function isVectorClientConfigured() {
3265
+ return !!remoteServerUrl2 && !!authKey2;
3266
+ }
3267
+ async function vectorApi(path, options = {}) {
3268
+ if (!remoteServerUrl2 || !authKey2) {
3269
+ throw new Error("Vector client not initialized - remote server not configured");
3270
+ }
3271
+ const url = `${remoteServerUrl2}/vectors${path}`;
3272
+ const init = {
3273
+ method: options.method || "GET",
3274
+ headers: {
3275
+ "Content-Type": "application/json",
3276
+ "Authorization": `Bearer ${authKey2}`
3277
+ }
3278
+ };
3279
+ if (options.body) {
3280
+ init.body = JSON.stringify(options.body);
3281
+ }
3282
+ const response = await fetch(url, init);
3283
+ if (!response.ok) {
3284
+ const error = await response.json().catch(() => ({ error: "Unknown error" }));
3285
+ throw new Error(error.error || `HTTP ${response.status}`);
3286
+ }
3287
+ return response.json();
3288
+ }
3289
+ var remoteVectorClient = {
3290
+ embeddings: {
3291
+ /**
3292
+ * Create embeddings and store in vector DB
3293
+ */
3294
+ async createAndWait(texts, options) {
3295
+ return vectorApi("/embed", {
3296
+ method: "POST",
3297
+ body: {
3298
+ texts,
3299
+ namespace: options.namespace,
3300
+ embeddingModel: options.embeddingModel
3301
+ }
3302
+ });
3303
+ }
3304
+ },
3305
+ search: {
3306
+ /**
3307
+ * Query vectors using semantic search
3308
+ */
3309
+ async queryAndWait(query, options) {
3310
+ return vectorApi("/search", {
3311
+ method: "POST",
3312
+ body: {
3313
+ query,
3314
+ namespace: options.namespace,
3315
+ topK: options.topK || 10,
3316
+ embeddingModel: options.embeddingModel
3317
+ }
3318
+ });
3319
+ }
3320
+ },
3321
+ /**
3322
+ * Delete a namespace (if supported)
3323
+ */
3324
+ async deleteNamespace(namespace) {
3325
+ await vectorApi(`/namespace/${encodeURIComponent(namespace)}`, {
3326
+ method: "DELETE"
3327
+ });
3328
+ },
3329
+ /**
3330
+ * Close client (no-op for HTTP client)
3331
+ */
3332
+ async close() {
3333
+ }
3334
+ };
3335
+ function getVectorClient() {
3336
+ if (!isVectorClientConfigured()) {
3337
+ try {
3338
+ const config = getConfig();
3339
+ if (config.resolvedRemoteServer.url && config.resolvedRemoteServer.authKey) {
3340
+ initVectorClient(config.resolvedRemoteServer.url, config.resolvedRemoteServer.authKey);
3341
+ } else {
3342
+ return null;
3343
+ }
3344
+ } catch {
3345
+ return null;
3346
+ }
3347
+ }
3348
+ return remoteVectorClient;
3349
+ }
3350
+ async function closeVectorClient() {
3351
+ }
3352
+ function isVectorGatewayConfigured() {
3353
+ try {
3354
+ const config = getConfig();
3355
+ return !!(config.resolvedRemoteServer.url && config.resolvedRemoteServer.authKey);
3356
+ } catch {
3357
+ return false;
3358
+ }
3359
+ }
3360
+ function getEmbeddingModel() {
3361
+ try {
3362
+ const config = getConfig();
3363
+ return config.resolvedVectorGateway.embeddingModel;
3364
+ } catch {
3365
+ return "gemini-embedding-001";
3366
+ }
3367
+ }
3368
+
3369
+ // src/semantic/indexer.ts
3370
+ import { readFileSync as readFileSync3, statSync } from "fs";
3371
+ import { relative as relative7 } from "path";
3372
+ import { minimatch as minimatch2 } from "minimatch";
3373
+ var MAX_FILE_SIZE3 = 1024 * 1024;
3374
+ async function getIndexStatus(workingDirectory) {
3375
+ const config = getConfig();
3376
+ const namespace = await getRepoNamespace(
3377
+ workingDirectory,
3378
+ config.resolvedVectorGateway.namespace
3379
+ );
3380
+ const isConfigured = !!config.resolvedVectorGateway.redisUrl;
3381
+ if (!namespace) {
3382
+ return {
3383
+ namespace: "",
3384
+ totalChunks: 0,
3385
+ lastFullIndex: null,
3386
+ lastIncrementalIndex: null,
3387
+ isConfigured
3388
+ };
3389
+ }
3390
+ try {
3391
+ const db = getDb();
3392
+ const status = await indexStatusQueries.get(db, namespace);
3393
+ if (!status) {
3394
+ return {
3395
+ namespace,
3396
+ totalChunks: 0,
3397
+ lastFullIndex: null,
3398
+ lastIncrementalIndex: null,
3399
+ isConfigured
3400
+ };
3401
+ }
3402
+ return {
3403
+ namespace,
3404
+ totalChunks: status.totalChunks ?? 0,
3405
+ lastFullIndex: status.lastFullIndex ?? null,
3406
+ lastIncrementalIndex: status.lastIncrementalIndex ?? null,
3407
+ isConfigured
3408
+ };
3409
+ } catch {
3410
+ return {
3411
+ namespace,
3412
+ totalChunks: 0,
3413
+ lastFullIndex: null,
3414
+ lastIncrementalIndex: null,
3415
+ isConfigured
3416
+ };
3417
+ }
3418
+ }
3419
+ async function checkIndexExists(workingDirectory) {
3420
+ const status = await getIndexStatus(workingDirectory);
3421
+ return status.totalChunks > 0;
3422
+ }
3423
+
3424
+ // src/tools/semantic-search.ts
3425
+ var semanticSearchInputSchema = z10.object({
3426
+ query: z10.string().describe("Natural language search query describing what you want to find"),
3427
+ topK: z10.number().optional().default(10).describe("Number of results to return (default: 10, max: 50)"),
3428
+ filePattern: z10.string().optional().describe('Filter results by file glob pattern (e.g., "*.ts", "src/**/*.py")'),
3429
+ language: z10.string().optional().describe('Filter by programming language (e.g., "typescript", "python")')
3430
+ });
3431
+ function createSemanticSearchTool(options) {
3432
+ return tool9({
3433
+ description: `Search the codebase using semantic similarity. This tool finds code by understanding its meaning, not just matching text.
3434
+
3435
+ Use this tool when:
3436
+ - You need to understand how something works in the codebase
3437
+ - You're looking for code related to a concept (e.g., "authentication", "database queries")
3438
+ - You want to find implementations of features
3439
+ - The user asks "where is X?" or "how does Y work?"
3440
+
3441
+ This tool requires the repository to be indexed first with 'sparkecoder index'.
3442
+
3443
+ Returns matching code snippets with file paths, line numbers, and relevance scores.`,
3444
+ inputSchema: semanticSearchInputSchema,
3445
+ execute: async ({
3446
+ query,
3447
+ topK,
3448
+ filePattern,
3449
+ language
3450
+ }) => {
3451
+ const startTime = Date.now();
3452
+ try {
3453
+ const config = getConfig();
3454
+ const namespace = await getRepoNamespace(
3455
+ options.workingDirectory,
3456
+ config.resolvedVectorGateway.namespace
3457
+ );
3458
+ if (!namespace) {
3459
+ return {
3460
+ success: false,
3461
+ error: "Repository namespace not found. Ensure this is a git repository with a remote configured."
3462
+ };
3463
+ }
3464
+ const client = getVectorClient();
3465
+ if (!client) {
3466
+ return {
3467
+ success: false,
3468
+ error: 'Vector Gateway not configured. Run "sparkecoder index --status" for setup instructions.'
3469
+ };
3470
+ }
3471
+ try {
3472
+ const limitedTopK = Math.min(Math.max(1, topK), 50);
3473
+ const embeddingModel = getEmbeddingModel();
3474
+ const result = await client.search.queryAndWait(query, {
3475
+ namespace,
3476
+ topK: limitedTopK * 2,
3477
+ includeMetadata: true,
3478
+ embeddingModel
3479
+ });
3480
+ const matches = [];
3481
+ for (const match of result.matches) {
3482
+ const metadata = match.metadata;
3483
+ if (!metadata) continue;
3484
+ const filePath = metadata.filePath;
3485
+ const startLine = metadata.startLine;
3486
+ const endLine = metadata.endLine;
3487
+ const matchLanguage = metadata.language;
3488
+ const symbolName = metadata.symbolName;
3489
+ if (filePattern) {
3490
+ const matchesPattern = minimatch3(filePath, filePattern, { dot: true });
3491
+ if (!matchesPattern) continue;
3492
+ }
3493
+ if (language && matchLanguage !== language.toLowerCase()) {
3494
+ continue;
3495
+ }
3496
+ const fullPath = join5(options.workingDirectory, filePath);
3497
+ if (!existsSync11(fullPath)) {
3498
+ continue;
3499
+ }
3500
+ let snippet = "";
3501
+ try {
3502
+ const content = readFileSync4(fullPath, "utf-8");
3503
+ const lines = content.split("\n");
3504
+ const snippetLines = lines.slice(
3505
+ Math.max(0, startLine - 1),
3506
+ Math.min(lines.length, endLine)
3507
+ );
3508
+ snippet = snippetLines.join("\n");
3509
+ if (snippet.length > 500) {
3510
+ snippet = snippet.slice(0, 500) + "\n... (truncated)";
3511
+ }
3512
+ } catch {
3513
+ }
3514
+ matches.push({
3515
+ filePath,
3516
+ startLine,
3517
+ endLine,
3518
+ score: match.score,
3519
+ snippet,
3520
+ symbolName,
3521
+ language: matchLanguage
3522
+ });
3523
+ if (matches.length >= limitedTopK) {
3524
+ break;
3525
+ }
3526
+ }
3527
+ return {
3528
+ success: true,
3529
+ query,
3530
+ matches,
3531
+ totalMatches: matches.length,
3532
+ duration: Date.now() - startTime
3533
+ };
3534
+ } finally {
3535
+ await closeVectorClient();
3536
+ }
3537
+ } catch (error) {
3538
+ return {
3539
+ success: false,
3540
+ error: `Semantic search failed: ${error instanceof Error ? error.message : String(error)}`
3541
+ };
3542
+ }
3543
+ }
3544
+ });
3545
+ }
3546
+
3362
3547
  // src/tools/index.ts
3363
- function createTools(options) {
3364
- return {
3548
+ async function createTools(options) {
3549
+ const tools = {
3365
3550
  bash: createBashTool({
3366
3551
  workingDirectory: options.workingDirectory,
3367
3552
  sessionId: options.sessionId,
@@ -3393,6 +3578,20 @@ function createTools(options) {
3393
3578
  onProgress: options.onSearchProgress
3394
3579
  })
3395
3580
  };
3581
+ if (options.enableSemanticSearch !== false) {
3582
+ try {
3583
+ if (isVectorGatewayConfigured()) {
3584
+ const hasIndex = await checkIndexExists(options.workingDirectory);
3585
+ if (hasIndex) {
3586
+ tools.semantic_search = createSemanticSearchTool({
3587
+ workingDirectory: options.workingDirectory
3588
+ });
3589
+ }
3590
+ }
3591
+ } catch {
3592
+ }
3593
+ }
3594
+ return tools;
3396
3595
  }
3397
3596
 
3398
3597
  // src/agent/context.ts
@@ -3445,7 +3644,7 @@ async function buildSystemPrompt(options) {
3445
3644
  const skills = await loadAllSkills2(skillsDirectories);
3446
3645
  onDemandSkillsContext = formatSkillsForContext(skills);
3447
3646
  }
3448
- const todos = todoQueries.getBySession(sessionId);
3647
+ const todos = await todoQueries.getBySession(sessionId);
3449
3648
  const todosContext = formatTodosForContext(todos);
3450
3649
  const platform2 = process.platform === "win32" ? "Windows" : process.platform === "darwin" ? "macOS" : "Linux";
3451
3650
  const currentDate = (/* @__PURE__ */ new Date()).toLocaleDateString("en-US", { weekday: "long", year: "numeric", month: "long", day: "numeric" });
@@ -3687,7 +3886,7 @@ var ContextManager = class {
3687
3886
  * Returns ModelMessage[] that can be passed directly to streamText/generateText
3688
3887
  */
3689
3888
  async getMessages() {
3690
- let modelMessages = messageQueries.getModelMessages(this.sessionId);
3889
+ let modelMessages = await messageQueries.getModelMessages(this.sessionId);
3691
3890
  const contextSize = calculateContextSize(modelMessages);
3692
3891
  if (this.autoSummarize && contextSize > this.maxContextChars) {
3693
3892
  modelMessages = await this.summarizeContext(modelMessages);
@@ -3707,13 +3906,13 @@ ${this.summary}`
3707
3906
  /**
3708
3907
  * Summarize older messages to reduce context size
3709
3908
  */
3710
- async summarizeContext(messages2) {
3711
- if (messages2.length <= this.keepRecentMessages) {
3712
- return messages2;
3909
+ async summarizeContext(messages) {
3910
+ if (messages.length <= this.keepRecentMessages) {
3911
+ return messages;
3713
3912
  }
3714
- const splitIndex = messages2.length - this.keepRecentMessages;
3715
- const oldMessages = messages2.slice(0, splitIndex);
3716
- const recentMessages = messages2.slice(splitIndex);
3913
+ const splitIndex = messages.length - this.keepRecentMessages;
3914
+ const oldMessages = messages.slice(0, splitIndex);
3915
+ const recentMessages = messages.slice(splitIndex);
3717
3916
  const historyText = oldMessages.map((msg) => {
3718
3917
  const content = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content);
3719
3918
  return `[${msg.role}]: ${content}`;
@@ -3737,36 +3936,36 @@ ${this.summary}`
3737
3936
  * Add a user message to the context
3738
3937
  * Content can be a string or an array of content parts (for messages with images/files)
3739
3938
  */
3740
- addUserMessage(content) {
3939
+ async addUserMessage(content) {
3741
3940
  const userMessage = {
3742
3941
  role: "user",
3743
3942
  content
3744
3943
  };
3745
- messageQueries.create(this.sessionId, userMessage);
3944
+ await messageQueries.create(this.sessionId, userMessage);
3746
3945
  }
3747
3946
  /**
3748
3947
  * Add response messages from AI SDK directly
3749
3948
  * This is the preferred method - use result.response.messages from streamText/generateText
3750
3949
  */
3751
- addResponseMessages(messages2) {
3752
- messageQueries.addMany(this.sessionId, messages2);
3950
+ async addResponseMessages(messages) {
3951
+ await messageQueries.addMany(this.sessionId, messages);
3753
3952
  }
3754
3953
  /**
3755
3954
  * Get current context statistics
3756
3955
  */
3757
- getStats() {
3758
- const messages2 = messageQueries.getModelMessages(this.sessionId);
3956
+ async getStats() {
3957
+ const messages = await messageQueries.getModelMessages(this.sessionId);
3759
3958
  return {
3760
- messageCount: messages2.length,
3761
- contextChars: calculateContextSize(messages2),
3959
+ messageCount: messages.length,
3960
+ contextChars: calculateContextSize(messages),
3762
3961
  hasSummary: this.summary !== null
3763
3962
  };
3764
3963
  }
3765
3964
  /**
3766
3965
  * Clear all messages in the context
3767
3966
  */
3768
- clear() {
3769
- messageQueries.deleteBySession(this.sessionId);
3967
+ async clear() {
3968
+ await messageQueries.deleteBySession(this.sessionId);
3770
3969
  this.summary = null;
3771
3970
  }
3772
3971
  };
@@ -3786,7 +3985,7 @@ var Agent = class _Agent {
3786
3985
  /**
3787
3986
  * Create tools with optional progress callbacks
3788
3987
  */
3789
- createToolsWithCallbacks(options) {
3988
+ async createToolsWithCallbacks(options) {
3790
3989
  const config = getConfig();
3791
3990
  return createTools({
3792
3991
  sessionId: this.session.id,
@@ -3804,13 +4003,13 @@ var Agent = class _Agent {
3804
4003
  const config = getConfig();
3805
4004
  let session;
3806
4005
  if (options.sessionId) {
3807
- const existing = sessionQueries.getById(options.sessionId);
4006
+ const existing = await sessionQueries.getById(options.sessionId);
3808
4007
  if (!existing) {
3809
4008
  throw new Error(`Session not found: ${options.sessionId}`);
3810
4009
  }
3811
4010
  session = existing;
3812
4011
  } else {
3813
- session = sessionQueries.create({
4012
+ session = await sessionQueries.create({
3814
4013
  name: options.name,
3815
4014
  workingDirectory: options.workingDirectory || config.resolvedWorkingDirectory,
3816
4015
  model: options.model || config.defaultModel,
@@ -3823,7 +4022,7 @@ var Agent = class _Agent {
3823
4022
  keepRecentMessages: config.context?.keepRecentMessages || 10,
3824
4023
  autoSummarize: config.context?.autoSummarize ?? true
3825
4024
  });
3826
- const tools = createTools({
4025
+ const tools = await createTools({
3827
4026
  sessionId: session.id,
3828
4027
  workingDirectory: session.workingDirectory,
3829
4028
  skillsDirectories: config.resolvedSkillsDirectories
@@ -3899,7 +4098,7 @@ ${prompt}` });
3899
4098
  if (!options.skipSaveUserMessage) {
3900
4099
  this.context.addUserMessage(userContent);
3901
4100
  }
3902
- sessionQueries.updateStatus(this.session.id, "active");
4101
+ await sessionQueries.updateStatus(this.session.id, "active");
3903
4102
  const systemPrompt = await buildSystemPrompt({
3904
4103
  workingDirectory: this.session.workingDirectory,
3905
4104
  skillsDirectories: config.resolvedSkillsDirectories,
@@ -3908,14 +4107,14 @@ ${prompt}` });
3908
4107
  // TODO: Pass activeFiles from client for glob matching
3909
4108
  activeFiles: []
3910
4109
  });
3911
- const messages2 = await this.context.getMessages();
3912
- const tools = options.onToolProgress ? this.createToolsWithCallbacks({ onToolProgress: options.onToolProgress }) : this.baseTools;
4110
+ const messages = await this.context.getMessages();
4111
+ const tools = options.onToolProgress ? await this.createToolsWithCallbacks({ onToolProgress: options.onToolProgress }) : this.baseTools;
3913
4112
  const wrappedTools = this.wrapToolsWithApproval(options, tools);
3914
4113
  const useAnthropic = isAnthropicModel(this.session.model);
3915
4114
  const stream = streamText2({
3916
4115
  model: resolveModel(this.session.model),
3917
4116
  system: systemPrompt,
3918
- messages: messages2,
4117
+ messages,
3919
4118
  tools: wrappedTools,
3920
4119
  stopWhen: stepCountIs2(500),
3921
4120
  // Forward abort signal if provided
@@ -3963,14 +4162,14 @@ ${prompt}` });
3963
4162
  discoveredSkills: config.discoveredSkills,
3964
4163
  activeFiles: []
3965
4164
  });
3966
- const messages2 = await this.context.getMessages();
3967
- const tools = options.onToolProgress ? this.createToolsWithCallbacks({ onToolProgress: options.onToolProgress }) : this.baseTools;
4165
+ const messages = await this.context.getMessages();
4166
+ const tools = options.onToolProgress ? await this.createToolsWithCallbacks({ onToolProgress: options.onToolProgress }) : this.baseTools;
3968
4167
  const wrappedTools = this.wrapToolsWithApproval(options, tools);
3969
4168
  const useAnthropic = isAnthropicModel(this.session.model);
3970
4169
  const result = await generateText3({
3971
4170
  model: resolveModel(this.session.model),
3972
4171
  system: systemPrompt,
3973
- messages: messages2,
4172
+ messages,
3974
4173
  tools: wrappedTools,
3975
4174
  stopWhen: stepCountIs2(500),
3976
4175
  // Enable extended thinking/reasoning for models that support it
@@ -4003,11 +4202,11 @@ ${prompt}` });
4003
4202
  wrappedTools[name] = originalTool;
4004
4203
  continue;
4005
4204
  }
4006
- wrappedTools[name] = tool9({
4205
+ wrappedTools[name] = tool10({
4007
4206
  description: originalTool.description || "",
4008
- inputSchema: originalTool.inputSchema || z10.object({}),
4207
+ inputSchema: originalTool.inputSchema || z11.object({}),
4009
4208
  execute: async (input, toolOptions) => {
4010
- const toolCallId = toolOptions.toolCallId || nanoid4();
4209
+ const toolCallId = toolOptions.toolCallId || nanoid3();
4011
4210
  const execution = toolExecutionQueries.create({
4012
4211
  sessionId: this.session.id,
4013
4212
  toolName: name,
@@ -4016,19 +4215,20 @@ ${prompt}` });
4016
4215
  requiresApproval: true,
4017
4216
  status: "pending"
4018
4217
  });
4019
- this.pendingApprovals.set(toolCallId, execution);
4020
- options.onApprovalRequired?.(execution);
4021
- sessionQueries.updateStatus(this.session.id, "waiting");
4218
+ this.pendingApprovals.set(toolCallId, await execution);
4219
+ options.onApprovalRequired?.(await execution);
4220
+ await sessionQueries.updateStatus(this.session.id, "waiting");
4022
4221
  const approved = await new Promise((resolve9) => {
4023
4222
  approvalResolvers.set(toolCallId, { resolve: resolve9, sessionId: this.session.id });
4024
4223
  });
4025
4224
  const resolverData = approvalResolvers.get(toolCallId);
4026
4225
  approvalResolvers.delete(toolCallId);
4027
4226
  this.pendingApprovals.delete(toolCallId);
4227
+ const exec5 = await execution;
4028
4228
  if (!approved) {
4029
4229
  const reason = resolverData?.reason || "User rejected the tool execution";
4030
- toolExecutionQueries.reject(execution.id);
4031
- sessionQueries.updateStatus(this.session.id, "active");
4230
+ await toolExecutionQueries.reject(exec5.id);
4231
+ await sessionQueries.updateStatus(this.session.id, "active");
4032
4232
  return {
4033
4233
  status: "rejected",
4034
4234
  toolCallId,
@@ -4037,14 +4237,14 @@ ${prompt}` });
4037
4237
  message: `Tool "${name}" was rejected by the user. Reason: ${reason}`
4038
4238
  };
4039
4239
  }
4040
- toolExecutionQueries.approve(execution.id);
4041
- sessionQueries.updateStatus(this.session.id, "active");
4240
+ await toolExecutionQueries.approve(exec5.id);
4241
+ await sessionQueries.updateStatus(this.session.id, "active");
4042
4242
  try {
4043
4243
  const result = await originalTool.execute(input, toolOptions);
4044
- toolExecutionQueries.complete(execution.id, result);
4244
+ await toolExecutionQueries.complete(exec5.id, result);
4045
4245
  return result;
4046
4246
  } catch (error) {
4047
- toolExecutionQueries.complete(execution.id, null, error.message);
4247
+ await toolExecutionQueries.complete(exec5.id, null, error.message);
4048
4248
  throw error;
4049
4249
  }
4050
4250
  }
@@ -4067,36 +4267,36 @@ ${prompt}` });
4067
4267
  resolver.resolve(true);
4068
4268
  return { approved: true };
4069
4269
  }
4070
- const pendingFromDb = toolExecutionQueries.getPendingApprovals(this.session.id);
4270
+ const pendingFromDb = await toolExecutionQueries.getPendingApprovals(this.session.id);
4071
4271
  const execution = pendingFromDb.find((e) => e.toolCallId === toolCallId);
4072
4272
  if (!execution) {
4073
4273
  throw new Error(`No pending approval for tool call: ${toolCallId}`);
4074
4274
  }
4075
- toolExecutionQueries.approve(execution.id);
4275
+ await toolExecutionQueries.approve(execution.id);
4076
4276
  return { approved: true };
4077
4277
  }
4078
4278
  /**
4079
4279
  * Reject a pending tool execution
4080
4280
  */
4081
- reject(toolCallId, reason) {
4281
+ async reject(toolCallId, reason) {
4082
4282
  const resolver = approvalResolvers.get(toolCallId);
4083
4283
  if (resolver) {
4084
4284
  resolver.reason = reason;
4085
4285
  resolver.resolve(false);
4086
4286
  return { rejected: true };
4087
4287
  }
4088
- const pendingFromDb = toolExecutionQueries.getPendingApprovals(this.session.id);
4288
+ const pendingFromDb = await toolExecutionQueries.getPendingApprovals(this.session.id);
4089
4289
  const execution = pendingFromDb.find((e) => e.toolCallId === toolCallId);
4090
4290
  if (!execution) {
4091
4291
  throw new Error(`No pending approval for tool call: ${toolCallId}`);
4092
4292
  }
4093
- toolExecutionQueries.reject(execution.id);
4293
+ await toolExecutionQueries.reject(execution.id);
4094
4294
  return { rejected: true };
4095
4295
  }
4096
4296
  /**
4097
4297
  * Get pending approvals
4098
4298
  */
4099
- getPendingApprovals() {
4299
+ async getPendingApprovals() {
4100
4300
  return toolExecutionQueries.getPendingApprovals(this.session.id);
4101
4301
  }
4102
4302
  /**