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
package/dist/index.js CHANGED
@@ -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,730 +471,345 @@ 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
- var schema_exports = {};
433
- __export(schema_exports, {
434
- activeStreams: () => activeStreams,
435
- checkpoints: () => checkpoints,
436
- fileBackups: () => fileBackups,
437
- loadedSkills: () => loadedSkills,
438
- messages: () => messages,
439
- sessions: () => sessions,
440
- subagentExecutions: () => subagentExecutions,
441
- terminals: () => terminals,
442
- todoItems: () => todoItems,
443
- toolExecutions: () => toolExecutions
444
- });
445
- import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core";
446
- var sessions = sqliteTable("sessions", {
447
- id: text("id").primaryKey(),
448
- name: text("name"),
449
- workingDirectory: text("working_directory").notNull(),
450
- model: text("model").notNull(),
451
- status: text("status", { enum: ["active", "waiting", "completed", "error"] }).notNull().default("active"),
452
- config: text("config", { mode: "json" }).$type(),
453
- createdAt: integer("created_at", { mode: "timestamp" }).notNull().$defaultFn(() => /* @__PURE__ */ new Date()),
454
- updatedAt: integer("updated_at", { mode: "timestamp" }).notNull().$defaultFn(() => /* @__PURE__ */ new Date())
455
- });
456
- var messages = sqliteTable("messages", {
457
- id: text("id").primaryKey(),
458
- sessionId: text("session_id").notNull().references(() => sessions.id, { onDelete: "cascade" }),
459
- // Store the entire ModelMessage as JSON (role + content)
460
- modelMessage: text("model_message", { mode: "json" }).$type().notNull(),
461
- // Sequence number within session to maintain exact ordering
462
- sequence: integer("sequence").notNull().default(0),
463
- createdAt: integer("created_at", { mode: "timestamp" }).notNull().$defaultFn(() => /* @__PURE__ */ new Date())
464
- });
465
- var toolExecutions = sqliteTable("tool_executions", {
466
- id: text("id").primaryKey(),
467
- sessionId: text("session_id").notNull().references(() => sessions.id, { onDelete: "cascade" }),
468
- messageId: text("message_id").references(() => messages.id, { onDelete: "cascade" }),
469
- toolName: text("tool_name").notNull(),
470
- toolCallId: text("tool_call_id").notNull(),
471
- input: text("input", { mode: "json" }),
472
- output: text("output", { mode: "json" }),
473
- status: text("status", { enum: ["pending", "approved", "rejected", "completed", "error"] }).notNull().default("pending"),
474
- requiresApproval: integer("requires_approval", { mode: "boolean" }).notNull().default(false),
475
- error: text("error"),
476
- startedAt: integer("started_at", { mode: "timestamp" }).notNull().$defaultFn(() => /* @__PURE__ */ new Date()),
477
- completedAt: integer("completed_at", { mode: "timestamp" })
478
- });
479
- var todoItems = sqliteTable("todo_items", {
480
- id: text("id").primaryKey(),
481
- sessionId: text("session_id").notNull().references(() => sessions.id, { onDelete: "cascade" }),
482
- content: text("content").notNull(),
483
- status: text("status", { enum: ["pending", "in_progress", "completed", "cancelled"] }).notNull().default("pending"),
484
- order: integer("order").notNull().default(0),
485
- createdAt: integer("created_at", { mode: "timestamp" }).notNull().$defaultFn(() => /* @__PURE__ */ new Date()),
486
- updatedAt: integer("updated_at", { mode: "timestamp" }).notNull().$defaultFn(() => /* @__PURE__ */ new Date())
487
- });
488
- var loadedSkills = sqliteTable("loaded_skills", {
489
- id: text("id").primaryKey(),
490
- sessionId: text("session_id").notNull().references(() => sessions.id, { onDelete: "cascade" }),
491
- skillName: text("skill_name").notNull(),
492
- loadedAt: integer("loaded_at", { mode: "timestamp" }).notNull().$defaultFn(() => /* @__PURE__ */ new Date())
493
- });
494
- var terminals = sqliteTable("terminals", {
495
- id: text("id").primaryKey(),
496
- sessionId: text("session_id").notNull().references(() => sessions.id, { onDelete: "cascade" }),
497
- name: text("name"),
498
- // Optional friendly name (e.g., "dev-server")
499
- command: text("command").notNull(),
500
- // The command that was run
501
- cwd: text("cwd").notNull(),
502
- // Working directory
503
- pid: integer("pid"),
504
- // Process ID (null if not running)
505
- status: text("status", { enum: ["running", "stopped", "error"] }).notNull().default("running"),
506
- exitCode: integer("exit_code"),
507
- // Exit code if stopped
508
- error: text("error"),
509
- // Error message if status is 'error'
510
- createdAt: integer("created_at", { mode: "timestamp" }).notNull().$defaultFn(() => /* @__PURE__ */ new Date()),
511
- stoppedAt: integer("stopped_at", { mode: "timestamp" })
512
- });
513
- var activeStreams = sqliteTable("active_streams", {
514
- id: text("id").primaryKey(),
515
- sessionId: text("session_id").notNull().references(() => sessions.id, { onDelete: "cascade" }),
516
- streamId: text("stream_id").notNull().unique(),
517
- // Unique stream identifier
518
- status: text("status", { enum: ["active", "finished", "error"] }).notNull().default("active"),
519
- createdAt: integer("created_at", { mode: "timestamp" }).notNull().$defaultFn(() => /* @__PURE__ */ new Date()),
520
- finishedAt: integer("finished_at", { mode: "timestamp" })
521
- });
522
- var checkpoints = sqliteTable("checkpoints", {
523
- id: text("id").primaryKey(),
524
- sessionId: text("session_id").notNull().references(() => sessions.id, { onDelete: "cascade" }),
525
- // The message sequence number this checkpoint was created BEFORE
526
- // (i.e., the state before this user message was processed)
527
- messageSequence: integer("message_sequence").notNull(),
528
- // Optional git commit hash if in a git repo
529
- gitHead: text("git_head"),
530
- createdAt: integer("created_at", { mode: "timestamp" }).notNull().$defaultFn(() => /* @__PURE__ */ new Date())
531
- });
532
- var fileBackups = sqliteTable("file_backups", {
533
- id: text("id").primaryKey(),
534
- checkpointId: text("checkpoint_id").notNull().references(() => checkpoints.id, { onDelete: "cascade" }),
535
- sessionId: text("session_id").notNull().references(() => sessions.id, { onDelete: "cascade" }),
536
- // Relative path from working directory
537
- filePath: text("file_path").notNull(),
538
- // Original content (null means file didn't exist before)
539
- originalContent: text("original_content"),
540
- // Whether the file existed before this checkpoint
541
- existed: integer("existed", { mode: "boolean" }).notNull().default(true),
542
- createdAt: integer("created_at", { mode: "timestamp" }).notNull().$defaultFn(() => /* @__PURE__ */ new Date())
543
- });
544
- var subagentExecutions = sqliteTable("subagent_executions", {
545
- id: text("id").primaryKey(),
546
- sessionId: text("session_id").notNull().references(() => sessions.id, { onDelete: "cascade" }),
547
- toolCallId: text("tool_call_id").notNull(),
548
- // The tool call that spawned this subagent
549
- subagentType: text("subagent_type").notNull(),
550
- // e.g., 'search', 'analyze', etc.
551
- task: text("task").notNull(),
552
- // The task/query given to the subagent
553
- model: text("model").notNull(),
554
- // The model used (e.g., 'gemini-2.0-flash')
555
- status: text("status", { enum: ["running", "completed", "error", "cancelled"] }).notNull().default("running"),
556
- // Steps taken by the subagent (stored as JSON array)
557
- steps: text("steps", { mode: "json" }).$type().default([]),
558
- // Final result/output
559
- result: text("result", { mode: "json" }),
560
- error: text("error"),
561
- startedAt: integer("started_at", { mode: "timestamp" }).notNull().$defaultFn(() => /* @__PURE__ */ new Date()),
562
- completedAt: integer("completed_at", { mode: "timestamp" })
563
- });
564
-
565
- // src/db/index.ts
566
- var db = null;
567
- var sqlite = null;
568
- function initDatabase(dbPath) {
569
- sqlite = new Database(dbPath);
570
- sqlite.pragma("journal_mode = WAL");
571
- db = drizzle(sqlite, { schema: schema_exports });
572
- sqlite.exec(`
573
- CREATE TABLE IF NOT EXISTS sessions (
574
- id TEXT PRIMARY KEY,
575
- name TEXT,
576
- working_directory TEXT NOT NULL,
577
- model TEXT NOT NULL,
578
- status TEXT NOT NULL DEFAULT 'active',
579
- config TEXT,
580
- created_at INTEGER NOT NULL,
581
- updated_at INTEGER NOT NULL
582
- );
583
-
584
- CREATE TABLE IF NOT EXISTS messages (
585
- id TEXT PRIMARY KEY,
586
- session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
587
- model_message TEXT NOT NULL,
588
- sequence INTEGER NOT NULL DEFAULT 0,
589
- created_at INTEGER NOT NULL
590
- );
591
-
592
- CREATE TABLE IF NOT EXISTS tool_executions (
593
- id TEXT PRIMARY KEY,
594
- session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
595
- message_id TEXT REFERENCES messages(id) ON DELETE CASCADE,
596
- tool_name TEXT NOT NULL,
597
- tool_call_id TEXT NOT NULL,
598
- input TEXT,
599
- output TEXT,
600
- status TEXT NOT NULL DEFAULT 'pending',
601
- requires_approval INTEGER NOT NULL DEFAULT 0,
602
- error TEXT,
603
- started_at INTEGER NOT NULL,
604
- completed_at INTEGER
605
- );
606
-
607
- CREATE TABLE IF NOT EXISTS todo_items (
608
- id TEXT PRIMARY KEY,
609
- session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
610
- content TEXT NOT NULL,
611
- status TEXT NOT NULL DEFAULT 'pending',
612
- "order" INTEGER NOT NULL DEFAULT 0,
613
- created_at INTEGER NOT NULL,
614
- updated_at INTEGER NOT NULL
615
- );
616
-
617
- CREATE TABLE IF NOT EXISTS loaded_skills (
618
- id TEXT PRIMARY KEY,
619
- session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
620
- skill_name TEXT NOT NULL,
621
- loaded_at INTEGER NOT NULL
622
- );
623
-
624
- CREATE TABLE IF NOT EXISTS terminals (
625
- id TEXT PRIMARY KEY,
626
- session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
627
- name TEXT,
628
- command TEXT NOT NULL,
629
- cwd TEXT NOT NULL,
630
- pid INTEGER,
631
- status TEXT NOT NULL DEFAULT 'running',
632
- exit_code INTEGER,
633
- error TEXT,
634
- created_at INTEGER NOT NULL,
635
- stopped_at INTEGER
636
- );
637
-
638
- -- Table for tracking active streams (for resumable streams)
639
- CREATE TABLE IF NOT EXISTS active_streams (
640
- id TEXT PRIMARY KEY,
641
- session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
642
- stream_id TEXT NOT NULL UNIQUE,
643
- status TEXT NOT NULL DEFAULT 'active',
644
- created_at INTEGER NOT NULL,
645
- finished_at INTEGER
646
- );
647
-
648
- -- Checkpoints table - created before each user message
649
- CREATE TABLE IF NOT EXISTS checkpoints (
650
- id TEXT PRIMARY KEY,
651
- session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
652
- message_sequence INTEGER NOT NULL,
653
- git_head TEXT,
654
- created_at INTEGER NOT NULL
655
- );
656
-
657
- -- File backups table - stores original file content
658
- CREATE TABLE IF NOT EXISTS file_backups (
659
- id TEXT PRIMARY KEY,
660
- checkpoint_id TEXT NOT NULL REFERENCES checkpoints(id) ON DELETE CASCADE,
661
- session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
662
- file_path TEXT NOT NULL,
663
- original_content TEXT,
664
- existed INTEGER NOT NULL DEFAULT 1,
665
- created_at INTEGER NOT NULL
666
- );
667
-
668
- -- Subagent executions table - tracks subagent runs
669
- CREATE TABLE IF NOT EXISTS subagent_executions (
670
- id TEXT PRIMARY KEY,
671
- session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
672
- tool_call_id TEXT NOT NULL,
673
- subagent_type TEXT NOT NULL,
674
- task TEXT NOT NULL,
675
- model TEXT NOT NULL,
676
- status TEXT NOT NULL DEFAULT 'running',
677
- steps TEXT DEFAULT '[]',
678
- result TEXT,
679
- error TEXT,
680
- started_at INTEGER NOT NULL,
681
- completed_at INTEGER
682
- );
474
+ import { z as z11 } from "zod";
475
+ import { nanoid as nanoid3 } from "nanoid";
683
476
 
684
- CREATE INDEX IF NOT EXISTS idx_messages_session ON messages(session_id);
685
- CREATE INDEX IF NOT EXISTS idx_tool_executions_session ON tool_executions(session_id);
686
- CREATE INDEX IF NOT EXISTS idx_todo_items_session ON todo_items(session_id);
687
- CREATE INDEX IF NOT EXISTS idx_loaded_skills_session ON loaded_skills(session_id);
688
- CREATE INDEX IF NOT EXISTS idx_terminals_session ON terminals(session_id);
689
- CREATE INDEX IF NOT EXISTS idx_active_streams_session ON active_streams(session_id);
690
- CREATE INDEX IF NOT EXISTS idx_checkpoints_session ON checkpoints(session_id);
691
- CREATE INDEX IF NOT EXISTS idx_file_backups_checkpoint ON file_backups(checkpoint_id);
692
- CREATE INDEX IF NOT EXISTS idx_file_backups_session ON file_backups(session_id);
693
- CREATE INDEX IF NOT EXISTS idx_subagent_executions_session ON subagent_executions(session_id);
694
- CREATE INDEX IF NOT EXISTS idx_subagent_executions_tool_call ON subagent_executions(tool_call_id);
695
- `);
696
- return db;
477
+ // src/db/remote.ts
478
+ var remoteServerUrl = null;
479
+ var authKey = null;
480
+ function initRemoteDatabase(serverUrl, key) {
481
+ remoteServerUrl = serverUrl.replace(/\/$/, "");
482
+ authKey = key;
697
483
  }
698
- function getDb() {
699
- if (!db) {
700
- throw new Error("Database not initialized. Call initDatabase first.");
701
- }
702
- return db;
484
+ function closeRemoteDatabase() {
485
+ remoteServerUrl = null;
486
+ authKey = null;
703
487
  }
704
- function closeDatabase() {
705
- if (sqlite) {
706
- sqlite.close();
707
- sqlite = null;
708
- db = null;
488
+ var DATE_FIELDS = ["createdAt", "updatedAt", "startedAt", "completedAt", "stoppedAt", "finishedAt", "loadedAt", "indexedAt", "lastFullIndex", "lastIncrementalIndex"];
489
+ function parseDates(obj) {
490
+ if (obj === null || obj === void 0) return obj;
491
+ if (Array.isArray(obj)) return obj.map(parseDates);
492
+ if (typeof obj !== "object") return obj;
493
+ const result = { ...obj };
494
+ for (const key of Object.keys(result)) {
495
+ if (DATE_FIELDS.includes(key) && typeof result[key] === "string") {
496
+ result[key] = new Date(result[key]);
497
+ } else if (typeof result[key] === "object") {
498
+ result[key] = parseDates(result[key]);
499
+ }
500
+ }
501
+ return result;
502
+ }
503
+ async function api(path, options = {}) {
504
+ if (!remoteServerUrl || !authKey) {
505
+ throw new Error("Remote database not initialized");
506
+ }
507
+ const url = `${remoteServerUrl}/db${path}`;
508
+ const init = {
509
+ method: options.method || "GET",
510
+ headers: {
511
+ "Content-Type": "application/json",
512
+ "Authorization": `Bearer ${authKey}`
513
+ }
514
+ };
515
+ if (options.body) {
516
+ init.body = JSON.stringify(options.body);
709
517
  }
518
+ const response = await fetch(url, init);
519
+ if (!response.ok) {
520
+ const error = await response.json().catch(() => ({ error: "Unknown error" }));
521
+ throw new Error(error.error || `HTTP ${response.status}`);
522
+ }
523
+ const text = await response.text();
524
+ if (!text || text === "null") {
525
+ return null;
526
+ }
527
+ return parseDates(JSON.parse(text));
710
528
  }
711
- var sessionQueries = {
529
+ var remoteSessionQueries = {
712
530
  create(data) {
713
- const id = nanoid();
714
- const now = /* @__PURE__ */ new Date();
715
- const result = getDb().insert(sessions).values({
716
- id,
717
- ...data,
718
- createdAt: now,
719
- updatedAt: now
720
- }).returning().get();
721
- return result;
531
+ return api("/sessions", { method: "POST", body: data });
722
532
  },
723
533
  getById(id) {
724
- return getDb().select().from(sessions).where(eq(sessions.id, id)).get();
534
+ return api(`/sessions/${id}`).catch(() => void 0);
725
535
  },
726
536
  list(limit = 50, offset = 0) {
727
- return getDb().select().from(sessions).orderBy(desc(sessions.createdAt)).limit(limit).offset(offset).all();
537
+ return api(`/sessions?limit=${limit}&offset=${offset}`);
728
538
  },
729
539
  updateStatus(id, status) {
730
- return getDb().update(sessions).set({ status, updatedAt: /* @__PURE__ */ new Date() }).where(eq(sessions.id, id)).returning().get();
540
+ return api(`/sessions/${id}`, { method: "PATCH", body: { status } });
731
541
  },
732
542
  updateModel(id, model) {
733
- return getDb().update(sessions).set({ model, updatedAt: /* @__PURE__ */ new Date() }).where(eq(sessions.id, id)).returning().get();
543
+ return api(`/sessions/${id}`, { method: "PATCH", body: { model } });
734
544
  },
735
545
  update(id, updates) {
736
- return getDb().update(sessions).set({ ...updates, updatedAt: /* @__PURE__ */ new Date() }).where(eq(sessions.id, id)).returning().get();
546
+ return api(`/sessions/${id}`, { method: "PATCH", body: updates });
737
547
  },
738
548
  delete(id) {
739
- const result = getDb().delete(sessions).where(eq(sessions.id, id)).run();
740
- return result.changes > 0;
549
+ return api(`/sessions/${id}`, { method: "DELETE" }).then((r) => r?.success ?? false);
741
550
  }
742
551
  };
743
- var messageQueries = {
744
- /**
745
- * Get the next sequence number for a session
746
- */
747
- getNextSequence(sessionId) {
748
- const result = getDb().select({ maxSeq: sql`COALESCE(MAX(sequence), -1)` }).from(messages).where(eq(messages.sessionId, sessionId)).get();
749
- return (result?.maxSeq ?? -1) + 1;
552
+ var remoteMessageQueries = {
553
+ async getNextSequence(sessionId) {
554
+ const result = await api(`/messages/session/${sessionId}/next-sequence`);
555
+ return result.nextSequence;
750
556
  },
751
- /**
752
- * Create a single message from a ModelMessage
753
- */
754
557
  create(sessionId, modelMessage) {
755
- const id = nanoid();
756
- const sequence = this.getNextSequence(sessionId);
757
- const result = getDb().insert(messages).values({
758
- id,
759
- sessionId,
760
- modelMessage,
761
- sequence,
762
- createdAt: /* @__PURE__ */ new Date()
763
- }).returning().get();
764
- return result;
558
+ return api("/messages", { method: "POST", body: { sessionId, modelMessage } });
765
559
  },
766
- /**
767
- * Add multiple ModelMessages at once (from response.messages)
768
- * Maintains insertion order via sequence numbers
769
- */
770
560
  addMany(sessionId, modelMessages) {
771
- const results = [];
772
- let sequence = this.getNextSequence(sessionId);
773
- for (const msg of modelMessages) {
774
- const id = nanoid();
775
- const result = getDb().insert(messages).values({
776
- id,
777
- sessionId,
778
- modelMessage: msg,
779
- sequence,
780
- createdAt: /* @__PURE__ */ new Date()
781
- }).returning().get();
782
- results.push(result);
783
- sequence++;
784
- }
785
- return results;
561
+ return api("/messages/batch", { method: "POST", body: { sessionId, modelMessages } });
786
562
  },
787
- /**
788
- * Get all messages for a session as ModelMessage[]
789
- * Ordered by sequence to maintain exact insertion order
790
- */
791
563
  getBySession(sessionId) {
792
- return getDb().select().from(messages).where(eq(messages.sessionId, sessionId)).orderBy(messages.sequence).all();
564
+ return api(`/messages/session/${sessionId}`);
793
565
  },
794
- /**
795
- * Get ModelMessages directly (for passing to AI SDK)
796
- */
797
566
  getModelMessages(sessionId) {
798
- const messages2 = this.getBySession(sessionId);
799
- return messages2.map((m) => m.modelMessage);
567
+ return api(`/messages/session/${sessionId}/model-messages`);
800
568
  },
801
- getRecentBySession(sessionId, limit = 50) {
802
- return getDb().select().from(messages).where(eq(messages.sessionId, sessionId)).orderBy(desc(messages.sequence)).limit(limit).all().reverse();
569
+ async getRecentBySession(sessionId, limit = 50) {
570
+ const messages = await api(`/messages/session/${sessionId}`);
571
+ return messages.slice(-limit);
803
572
  },
804
- countBySession(sessionId) {
805
- const result = getDb().select({ count: sql`count(*)` }).from(messages).where(eq(messages.sessionId, sessionId)).get();
806
- return result?.count ?? 0;
573
+ async countBySession(sessionId) {
574
+ const result = await api(`/messages/session/${sessionId}/count`);
575
+ return result.count;
807
576
  },
808
- deleteBySession(sessionId) {
809
- const result = getDb().delete(messages).where(eq(messages.sessionId, sessionId)).run();
810
- return result.changes;
577
+ async deleteBySession(sessionId) {
578
+ const result = await api(`/messages/session/${sessionId}`, { method: "DELETE" });
579
+ return result.deleted;
811
580
  },
812
- /**
813
- * Delete all messages with sequence >= the given sequence number
814
- * (Used when reverting to a checkpoint)
815
- */
816
- deleteFromSequence(sessionId, fromSequence) {
817
- const result = getDb().delete(messages).where(
818
- and(
819
- eq(messages.sessionId, sessionId),
820
- sql`sequence >= ${fromSequence}`
821
- )
822
- ).run();
823
- return result.changes;
581
+ async deleteFromSequence(sessionId, fromSequence) {
582
+ const result = await api(
583
+ `/messages/session/${sessionId}/from-sequence/${fromSequence}`,
584
+ { method: "DELETE" }
585
+ );
586
+ return result.deleted;
824
587
  }
825
588
  };
826
- var toolExecutionQueries = {
589
+ var remoteToolExecutionQueries = {
827
590
  create(data) {
828
- const id = nanoid();
829
- const result = getDb().insert(toolExecutions).values({
830
- id,
831
- ...data,
832
- startedAt: /* @__PURE__ */ new Date()
833
- }).returning().get();
834
- return result;
591
+ return api("/tool-executions", { method: "POST", body: data });
835
592
  },
836
593
  getById(id) {
837
- return getDb().select().from(toolExecutions).where(eq(toolExecutions.id, id)).get();
594
+ return api(`/tool-executions/${id}`).catch(() => void 0);
838
595
  },
839
596
  getByToolCallId(toolCallId) {
840
- return getDb().select().from(toolExecutions).where(eq(toolExecutions.toolCallId, toolCallId)).get();
597
+ return api(`/tool-executions/by-tool-call-id/${toolCallId}`).catch(() => void 0);
841
598
  },
842
599
  getPendingApprovals(sessionId) {
843
- return getDb().select().from(toolExecutions).where(
844
- and(
845
- eq(toolExecutions.sessionId, sessionId),
846
- eq(toolExecutions.status, "pending"),
847
- eq(toolExecutions.requiresApproval, true)
848
- )
849
- ).all();
600
+ return api(`/tool-executions/session/${sessionId}/pending`);
850
601
  },
851
602
  approve(id) {
852
- return getDb().update(toolExecutions).set({ status: "approved" }).where(eq(toolExecutions.id, id)).returning().get();
603
+ return api(`/tool-executions/${id}`, { method: "PATCH", body: { status: "approved" } });
853
604
  },
854
605
  reject(id) {
855
- return getDb().update(toolExecutions).set({ status: "rejected" }).where(eq(toolExecutions.id, id)).returning().get();
606
+ return api(`/tool-executions/${id}`, { method: "PATCH", body: { status: "rejected" } });
856
607
  },
857
608
  complete(id, output, error) {
858
- return getDb().update(toolExecutions).set({
859
- status: error ? "error" : "completed",
860
- output,
861
- error,
862
- completedAt: /* @__PURE__ */ new Date()
863
- }).where(eq(toolExecutions.id, id)).returning().get();
609
+ return api(`/tool-executions/${id}`, {
610
+ method: "PATCH",
611
+ body: { status: error ? "error" : "completed", output, error }
612
+ });
864
613
  },
865
614
  getBySession(sessionId) {
866
- return getDb().select().from(toolExecutions).where(eq(toolExecutions.sessionId, sessionId)).orderBy(toolExecutions.startedAt).all();
615
+ return api(`/tool-executions/session/${sessionId}`);
867
616
  },
868
- /**
869
- * Delete all tool executions after a given timestamp
870
- * (Used when reverting to a checkpoint)
871
- */
872
- deleteAfterTime(sessionId, afterTime) {
873
- const result = getDb().delete(toolExecutions).where(
874
- and(
875
- eq(toolExecutions.sessionId, sessionId),
876
- sql`started_at > ${afterTime.getTime()}`
877
- )
878
- ).run();
879
- return result.changes;
617
+ async deleteAfterTime(sessionId, afterTime) {
618
+ const timestamp = afterTime instanceof Date ? afterTime.getTime() : new Date(afterTime).getTime();
619
+ const result = await api(
620
+ `/tool-executions/session/${sessionId}/after/${timestamp}`,
621
+ { method: "DELETE" }
622
+ );
623
+ return result.deleted;
880
624
  }
881
625
  };
882
- var todoQueries = {
626
+ var remoteTodoQueries = {
883
627
  create(data) {
884
- const id = nanoid();
885
- const now = /* @__PURE__ */ new Date();
886
- const result = getDb().insert(todoItems).values({
887
- id,
888
- ...data,
889
- createdAt: now,
890
- updatedAt: now
891
- }).returning().get();
892
- return result;
628
+ return api("/todos", { method: "POST", body: data });
893
629
  },
894
630
  createMany(sessionId, items) {
895
- const now = /* @__PURE__ */ new Date();
896
- const values = items.map((item, index) => ({
897
- id: nanoid(),
898
- sessionId,
899
- content: item.content,
900
- order: item.order ?? index,
901
- createdAt: now,
902
- updatedAt: now
903
- }));
904
- return getDb().insert(todoItems).values(values).returning().all();
631
+ return api("/todos/batch", { method: "POST", body: { sessionId, items } });
905
632
  },
906
633
  getBySession(sessionId) {
907
- return getDb().select().from(todoItems).where(eq(todoItems.sessionId, sessionId)).orderBy(todoItems.order).all();
634
+ return api(`/todos/session/${sessionId}`);
908
635
  },
909
636
  updateStatus(id, status) {
910
- return getDb().update(todoItems).set({ status, updatedAt: /* @__PURE__ */ new Date() }).where(eq(todoItems.id, id)).returning().get();
637
+ return api(`/todos/${id}`, { method: "PATCH", body: { status } });
911
638
  },
912
- delete(id) {
913
- const result = getDb().delete(todoItems).where(eq(todoItems.id, id)).run();
914
- return result.changes > 0;
639
+ async delete(id) {
640
+ const result = await api(`/todos/${id}`, { method: "DELETE" });
641
+ return result?.success ?? false;
915
642
  },
916
- clearSession(sessionId) {
917
- const result = getDb().delete(todoItems).where(eq(todoItems.sessionId, sessionId)).run();
918
- return result.changes;
643
+ async clearSession(sessionId) {
644
+ const result = await api(`/todos/session/${sessionId}`, { method: "DELETE" });
645
+ return result.deleted;
919
646
  }
920
647
  };
921
- var skillQueries = {
648
+ var remoteSkillQueries = {
922
649
  load(sessionId, skillName) {
923
- const id = nanoid();
924
- const result = getDb().insert(loadedSkills).values({
925
- id,
926
- sessionId,
927
- skillName,
928
- loadedAt: /* @__PURE__ */ new Date()
929
- }).returning().get();
930
- return result;
650
+ return api("/skills", { method: "POST", body: { sessionId, skillName } });
931
651
  },
932
652
  getBySession(sessionId) {
933
- return getDb().select().from(loadedSkills).where(eq(loadedSkills.sessionId, sessionId)).orderBy(loadedSkills.loadedAt).all();
653
+ return api(`/skills/session/${sessionId}`);
934
654
  },
935
- isLoaded(sessionId, skillName) {
936
- const result = getDb().select().from(loadedSkills).where(
937
- and(
938
- eq(loadedSkills.sessionId, sessionId),
939
- eq(loadedSkills.skillName, skillName)
940
- )
941
- ).get();
942
- return !!result;
655
+ async isLoaded(sessionId, skillName) {
656
+ const result = await api(`/skills/session/${sessionId}/is-loaded/${skillName}`);
657
+ return result.isLoaded;
943
658
  }
944
659
  };
945
- var activeStreamQueries = {
660
+ var remoteActiveStreamQueries = {
946
661
  create(sessionId, streamId) {
947
- const id = nanoid();
948
- const result = getDb().insert(activeStreams).values({
949
- id,
950
- sessionId,
951
- streamId,
952
- status: "active",
953
- createdAt: /* @__PURE__ */ new Date()
954
- }).returning().get();
955
- return result;
662
+ return api("/streams", { method: "POST", body: { sessionId, streamId } });
956
663
  },
957
664
  getBySessionId(sessionId) {
958
- return getDb().select().from(activeStreams).where(
959
- and(
960
- eq(activeStreams.sessionId, sessionId),
961
- eq(activeStreams.status, "active")
962
- )
963
- ).get();
665
+ return api(`/streams/session/${sessionId}`).then((r) => r ?? void 0);
964
666
  },
965
667
  getByStreamId(streamId) {
966
- return getDb().select().from(activeStreams).where(eq(activeStreams.streamId, streamId)).get();
668
+ return api(`/streams/by-stream-id/${streamId}`).catch(() => void 0);
967
669
  },
968
670
  finish(streamId) {
969
- return getDb().update(activeStreams).set({ status: "finished", finishedAt: /* @__PURE__ */ new Date() }).where(eq(activeStreams.streamId, streamId)).returning().get();
671
+ return api(`/streams/by-stream-id/${streamId}`, { method: "PATCH", body: { status: "finished" } });
970
672
  },
971
673
  markError(streamId) {
972
- return getDb().update(activeStreams).set({ status: "error", finishedAt: /* @__PURE__ */ new Date() }).where(eq(activeStreams.streamId, streamId)).returning().get();
674
+ return api(`/streams/by-stream-id/${streamId}`, { method: "PATCH", body: { status: "error" } });
973
675
  },
974
- deleteBySession(sessionId) {
975
- const result = getDb().delete(activeStreams).where(eq(activeStreams.sessionId, sessionId)).run();
976
- return result.changes;
676
+ async deleteBySession(sessionId) {
677
+ const result = await api(`/streams/session/${sessionId}`, { method: "DELETE" });
678
+ return result.deleted;
977
679
  }
978
680
  };
979
- var checkpointQueries = {
681
+ var remoteCheckpointQueries = {
980
682
  create(data) {
981
- const id = nanoid();
982
- const result = getDb().insert(checkpoints).values({
983
- id,
984
- sessionId: data.sessionId,
985
- messageSequence: data.messageSequence,
986
- gitHead: data.gitHead,
987
- createdAt: /* @__PURE__ */ new Date()
988
- }).returning().get();
989
- return result;
683
+ return api("/checkpoints", { method: "POST", body: data });
990
684
  },
991
685
  getById(id) {
992
- return getDb().select().from(checkpoints).where(eq(checkpoints.id, id)).get();
686
+ return api(`/checkpoints/${id}`).catch(() => void 0);
993
687
  },
994
688
  getBySession(sessionId) {
995
- return getDb().select().from(checkpoints).where(eq(checkpoints.sessionId, sessionId)).orderBy(checkpoints.messageSequence).all();
689
+ return api(`/checkpoints/session/${sessionId}`);
996
690
  },
997
691
  getByMessageSequence(sessionId, messageSequence) {
998
- return getDb().select().from(checkpoints).where(
999
- and(
1000
- eq(checkpoints.sessionId, sessionId),
1001
- eq(checkpoints.messageSequence, messageSequence)
1002
- )
1003
- ).get();
692
+ return api(`/checkpoints/session/${sessionId}/by-sequence/${messageSequence}`).then((r) => r ?? void 0);
1004
693
  },
1005
694
  getLatest(sessionId) {
1006
- return getDb().select().from(checkpoints).where(eq(checkpoints.sessionId, sessionId)).orderBy(desc(checkpoints.messageSequence)).limit(1).get();
695
+ return api(`/checkpoints/session/${sessionId}/latest`).then((r) => r ?? void 0);
1007
696
  },
1008
- /**
1009
- * Delete all checkpoints after a given sequence number
1010
- * (Used when reverting to a checkpoint)
1011
- */
1012
- deleteAfterSequence(sessionId, messageSequence) {
1013
- const result = getDb().delete(checkpoints).where(
1014
- and(
1015
- eq(checkpoints.sessionId, sessionId),
1016
- sql`message_sequence > ${messageSequence}`
1017
- )
1018
- ).run();
1019
- return result.changes;
697
+ async deleteAfterSequence(sessionId, messageSequence) {
698
+ const result = await api(
699
+ `/checkpoints/session/${sessionId}/after-sequence/${messageSequence}`,
700
+ { method: "DELETE" }
701
+ );
702
+ return result.deleted;
1020
703
  },
1021
- deleteBySession(sessionId) {
1022
- const result = getDb().delete(checkpoints).where(eq(checkpoints.sessionId, sessionId)).run();
1023
- return result.changes;
704
+ async deleteBySession(sessionId) {
705
+ const result = await api(`/checkpoints/session/${sessionId}`, { method: "DELETE" });
706
+ return result.deleted;
1024
707
  }
1025
708
  };
1026
- var fileBackupQueries = {
709
+ var remoteFileBackupQueries = {
1027
710
  create(data) {
1028
- const id = nanoid();
1029
- const result = getDb().insert(fileBackups).values({
1030
- id,
1031
- checkpointId: data.checkpointId,
1032
- sessionId: data.sessionId,
1033
- filePath: data.filePath,
1034
- originalContent: data.originalContent,
1035
- existed: data.existed,
1036
- createdAt: /* @__PURE__ */ new Date()
1037
- }).returning().get();
1038
- return result;
711
+ return api("/file-backups", { method: "POST", body: data });
1039
712
  },
1040
713
  getByCheckpoint(checkpointId) {
1041
- return getDb().select().from(fileBackups).where(eq(fileBackups.checkpointId, checkpointId)).all();
714
+ return api(`/file-backups/checkpoint/${checkpointId}`);
1042
715
  },
1043
716
  getBySession(sessionId) {
1044
- return getDb().select().from(fileBackups).where(eq(fileBackups.sessionId, sessionId)).orderBy(fileBackups.createdAt).all();
717
+ return api(`/file-backups/session/${sessionId}`);
1045
718
  },
1046
- /**
1047
- * Get all file backups from a given checkpoint sequence onwards (inclusive)
1048
- * (Used when reverting - need to restore these files)
1049
- *
1050
- * When reverting to checkpoint X, we need backups from checkpoint X and all later ones
1051
- * because checkpoint X's backups represent the state BEFORE processing message X.
1052
- */
1053
719
  getFromSequence(sessionId, messageSequence) {
1054
- const checkpointsFrom = getDb().select().from(checkpoints).where(
1055
- and(
1056
- eq(checkpoints.sessionId, sessionId),
1057
- sql`message_sequence >= ${messageSequence}`
1058
- )
1059
- ).all();
1060
- if (checkpointsFrom.length === 0) {
1061
- return [];
1062
- }
1063
- const checkpointIds = checkpointsFrom.map((c) => c.id);
1064
- const allBackups = [];
1065
- for (const cpId of checkpointIds) {
1066
- const backups = getDb().select().from(fileBackups).where(eq(fileBackups.checkpointId, cpId)).all();
1067
- allBackups.push(...backups);
1068
- }
1069
- return allBackups;
720
+ return api(`/file-backups/session/${sessionId}/from-sequence/${messageSequence}`);
1070
721
  },
1071
- /**
1072
- * Check if a file already has a backup in the current checkpoint
1073
- */
1074
- hasBackup(checkpointId, filePath) {
1075
- const result = getDb().select().from(fileBackups).where(
1076
- and(
1077
- eq(fileBackups.checkpointId, checkpointId),
1078
- eq(fileBackups.filePath, filePath)
1079
- )
1080
- ).get();
1081
- return !!result;
722
+ async hasBackup(checkpointId, filePath) {
723
+ const result = await api(
724
+ `/file-backups/checkpoint/${checkpointId}/has-backup/${encodeURIComponent(filePath)}`
725
+ );
726
+ return result.hasBackup;
1082
727
  },
1083
- deleteBySession(sessionId) {
1084
- const result = getDb().delete(fileBackups).where(eq(fileBackups.sessionId, sessionId)).run();
1085
- return result.changes;
728
+ async deleteBySession(sessionId) {
729
+ const result = await api(`/file-backups/session/${sessionId}`, { method: "DELETE" });
730
+ return result.deleted;
1086
731
  }
1087
732
  };
1088
- var subagentQueries = {
733
+ var remoteSubagentQueries = {
1089
734
  create(data) {
1090
- const id = nanoid();
1091
- const result = getDb().insert(subagentExecutions).values({
1092
- id,
1093
- sessionId: data.sessionId,
1094
- toolCallId: data.toolCallId,
1095
- subagentType: data.subagentType,
1096
- task: data.task,
1097
- model: data.model,
1098
- status: "running",
1099
- steps: [],
1100
- startedAt: /* @__PURE__ */ new Date()
1101
- }).returning().get();
1102
- return result;
735
+ return api("/subagents", { method: "POST", body: data });
1103
736
  },
1104
737
  getById(id) {
1105
- return getDb().select().from(subagentExecutions).where(eq(subagentExecutions.id, id)).get();
738
+ return api(`/subagents/${id}`).catch(() => void 0);
1106
739
  },
1107
740
  getByToolCallId(toolCallId) {
1108
- return getDb().select().from(subagentExecutions).where(eq(subagentExecutions.toolCallId, toolCallId)).get();
741
+ return api(`/subagents/by-tool-call-id/${toolCallId}`).catch(() => void 0);
1109
742
  },
1110
743
  getBySession(sessionId) {
1111
- return getDb().select().from(subagentExecutions).where(eq(subagentExecutions.sessionId, sessionId)).orderBy(desc(subagentExecutions.startedAt)).all();
744
+ return api(`/subagents/session/${sessionId}`);
1112
745
  },
1113
746
  addStep(id, step) {
1114
- const existing = this.getById(id);
1115
- if (!existing) return void 0;
1116
- const currentSteps = existing.steps || [];
1117
- const newSteps = [...currentSteps, step];
1118
- return getDb().update(subagentExecutions).set({ steps: newSteps }).where(eq(subagentExecutions.id, id)).returning().get();
747
+ return api(`/subagents/${id}/add-step`, { method: "POST", body: { step } }).catch(() => void 0);
1119
748
  },
1120
749
  complete(id, result) {
1121
- return getDb().update(subagentExecutions).set({
1122
- status: "completed",
1123
- result,
1124
- completedAt: /* @__PURE__ */ new Date()
1125
- }).where(eq(subagentExecutions.id, id)).returning().get();
750
+ return api(`/subagents/${id}`, { method: "PATCH", body: { status: "completed", result } }).catch(() => void 0);
1126
751
  },
1127
752
  markError(id, error) {
1128
- return getDb().update(subagentExecutions).set({
1129
- status: "error",
1130
- error,
1131
- completedAt: /* @__PURE__ */ new Date()
1132
- }).where(eq(subagentExecutions.id, id)).returning().get();
753
+ return api(`/subagents/${id}`, { method: "PATCH", body: { status: "error", error } }).catch(() => void 0);
1133
754
  },
1134
755
  cancel(id) {
1135
- return getDb().update(subagentExecutions).set({
1136
- status: "cancelled",
1137
- completedAt: /* @__PURE__ */ new Date()
1138
- }).where(eq(subagentExecutions.id, id)).returning().get();
756
+ return api(`/subagents/${id}`, { method: "PATCH", body: { status: "cancelled" } }).catch(() => void 0);
757
+ },
758
+ async deleteBySession(sessionId) {
759
+ const result = await api(`/subagents/session/${sessionId}`, { method: "DELETE" });
760
+ return result.deleted;
761
+ }
762
+ };
763
+ var remoteIndexStatusQueries = {
764
+ upsert(_db, data) {
765
+ return api("/index-status", {
766
+ method: "POST",
767
+ body: {
768
+ ...data,
769
+ lastFullIndex: data.lastFullIndex?.toISOString(),
770
+ lastIncrementalIndex: data.lastIncrementalIndex?.toISOString()
771
+ }
772
+ });
773
+ },
774
+ get(_db, namespace) {
775
+ return api(`/index-status/namespace/${namespace}`).then((r) => r ?? void 0);
776
+ },
777
+ async delete(_db, namespace) {
778
+ const result = await api(`/index-status/namespace/${namespace}`, { method: "DELETE" });
779
+ return result?.success ?? false;
1139
780
  },
1140
- deleteBySession(sessionId) {
1141
- const result = getDb().delete(subagentExecutions).where(eq(subagentExecutions.sessionId, sessionId)).run();
1142
- return result.changes;
781
+ list(_db) {
782
+ return api("/index-status");
1143
783
  }
1144
784
  };
1145
785
 
786
+ // src/db/index.ts
787
+ var initialized = false;
788
+ function initDatabase(config) {
789
+ initRemoteDatabase(config.url, config.authKey);
790
+ initialized = true;
791
+ }
792
+ function getDb() {
793
+ if (!initialized) {
794
+ throw new Error("Database not initialized. Call initDatabase first.");
795
+ }
796
+ return {};
797
+ }
798
+ function closeDatabase() {
799
+ closeRemoteDatabase();
800
+ initialized = false;
801
+ }
802
+ var sessionQueries = remoteSessionQueries;
803
+ var messageQueries = remoteMessageQueries;
804
+ var toolExecutionQueries = remoteToolExecutionQueries;
805
+ var todoQueries = remoteTodoQueries;
806
+ var skillQueries = remoteSkillQueries;
807
+ var activeStreamQueries = remoteActiveStreamQueries;
808
+ var checkpointQueries = remoteCheckpointQueries;
809
+ var fileBackupQueries = remoteFileBackupQueries;
810
+ var subagentQueries = remoteSubagentQueries;
811
+ var indexStatusQueries = remoteIndexStatusQueries;
812
+
1146
813
  // src/config/index.ts
1147
814
  init_types();
1148
815
  init_types();
@@ -1292,6 +959,48 @@ function loadConfig(configPath, workingDirectory) {
1292
959
  const appDataDir = ensureAppDataDirectory();
1293
960
  resolvedDatabasePath = join(appDataDir, "sparkecoder.db");
1294
961
  }
962
+ const resolvedVectorGateway = {
963
+ redisUrl: process.env.VECTOR_REDIS_URL || config.vectorGateway?.redisUrl || null,
964
+ httpUrl: process.env.VECTOR_HTTP_URL || config.vectorGateway?.httpUrl || null,
965
+ embeddingModel: process.env.VECTOR_EMBEDDING_MODEL || config.vectorGateway?.embeddingModel || "gemini-embedding-001",
966
+ namespace: config.vectorGateway?.namespace || null,
967
+ include: config.vectorGateway?.include || [
968
+ "**/*.ts",
969
+ "**/*.tsx",
970
+ "**/*.js",
971
+ "**/*.jsx",
972
+ "**/*.py",
973
+ "**/*.go",
974
+ "**/*.rs",
975
+ "**/*.java",
976
+ "**/*.md",
977
+ "**/*.mdx",
978
+ "**/*.txt"
979
+ ],
980
+ exclude: config.vectorGateway?.exclude || [
981
+ "**/node_modules/**",
982
+ "**/dist/**",
983
+ "**/build/**",
984
+ "**/.git/**",
985
+ "**/.next/**",
986
+ "**/*.min.js",
987
+ "**/*.bundle.js",
988
+ "**/pnpm-lock.yaml",
989
+ "**/package-lock.json",
990
+ "**/yarn.lock",
991
+ "**/.test-workspace/**",
992
+ "**/.semantic-test-workspace/**",
993
+ "**/.semantic-integration-test/**"
994
+ ]
995
+ };
996
+ const DEFAULT_REMOTE_URL = "https://agent-remote-server.sparkecode.com";
997
+ const remoteUrl = process.env.SPARKECODER_REMOTE_URL || config.remoteServer?.url || DEFAULT_REMOTE_URL;
998
+ const remoteAuthKey = process.env.SPARKECODER_AUTH_KEY || config.remoteServer?.authKey || loadStoredAuthKey();
999
+ const resolvedRemoteServer = {
1000
+ url: remoteUrl,
1001
+ authKey: remoteAuthKey,
1002
+ isConfigured: !!remoteUrl && !!remoteAuthKey
1003
+ };
1295
1004
  const resolved = {
1296
1005
  ...config,
1297
1006
  server: {
@@ -1302,7 +1011,9 @@ function loadConfig(configPath, workingDirectory) {
1302
1011
  resolvedWorkingDirectory,
1303
1012
  resolvedSkillsDirectories,
1304
1013
  resolvedDatabasePath,
1305
- discoveredSkills: discovered
1014
+ discoveredSkills: discovered,
1015
+ resolvedVectorGateway,
1016
+ resolvedRemoteServer
1306
1017
  };
1307
1018
  cachedConfig = resolved;
1308
1019
  return resolved;
@@ -1327,6 +1038,55 @@ function requiresApproval(toolName, sessionConfig) {
1327
1038
  }
1328
1039
  return false;
1329
1040
  }
1041
+ var AUTH_KEY_FILE = "auth-key.json";
1042
+ function loadStoredAuthKey() {
1043
+ const keysPath = join(getAppDataDirectory(), AUTH_KEY_FILE);
1044
+ if (!existsSync(keysPath)) {
1045
+ return null;
1046
+ }
1047
+ try {
1048
+ const content = readFileSync(keysPath, "utf-8");
1049
+ const data = JSON.parse(content);
1050
+ return data.authKey || null;
1051
+ } catch {
1052
+ return null;
1053
+ }
1054
+ }
1055
+ function saveAuthKey(authKey3, userId) {
1056
+ const appDir = ensureAppDataDirectory();
1057
+ const keysPath = join(appDir, AUTH_KEY_FILE);
1058
+ const data = {
1059
+ authKey: authKey3,
1060
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1061
+ userId
1062
+ };
1063
+ writeFileSync(keysPath, JSON.stringify(data, null, 2), { mode: 384 });
1064
+ }
1065
+ async function registerWithRemoteServer(serverUrl, name) {
1066
+ const response = await fetch(`${serverUrl}/auth/register`, {
1067
+ method: "POST",
1068
+ headers: { "Content-Type": "application/json" },
1069
+ body: JSON.stringify({ name: name || `CLI ${(/* @__PURE__ */ new Date()).toISOString()}` })
1070
+ });
1071
+ if (!response.ok) {
1072
+ const error = await response.json().catch(() => ({}));
1073
+ throw new Error(error.error || `Failed to register: HTTP ${response.status}`);
1074
+ }
1075
+ const data = await response.json();
1076
+ saveAuthKey(data.authKey, data.userId);
1077
+ return data;
1078
+ }
1079
+ async function ensureRemoteAuthKey(serverUrl) {
1080
+ if (process.env.SPARKECODER_AUTH_KEY) {
1081
+ return process.env.SPARKECODER_AUTH_KEY;
1082
+ }
1083
+ const storedKey = loadStoredAuthKey();
1084
+ if (storedKey) {
1085
+ return storedKey;
1086
+ }
1087
+ const { authKey: authKey3 } = await registerWithRemoteServer(serverUrl);
1088
+ return authKey3;
1089
+ }
1330
1090
  var API_KEYS_FILE = "api-keys.json";
1331
1091
  var PROVIDER_ENV_MAP = {
1332
1092
  anthropic: "ANTHROPIC_API_KEY",
@@ -1440,8 +1200,8 @@ function truncateOutput(output, maxChars = MAX_OUTPUT_CHARS) {
1440
1200
 
1441
1201
  ` + output.slice(-halfMax);
1442
1202
  }
1443
- function calculateContextSize(messages2) {
1444
- return messages2.reduce((total, msg) => {
1203
+ function calculateContextSize(messages) {
1204
+ return messages.reduce((total, msg) => {
1445
1205
  const content = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content);
1446
1206
  return total + content.length;
1447
1207
  }, 0);
@@ -1470,7 +1230,7 @@ import { promisify } from "util";
1470
1230
  import { mkdir, writeFile, readFile } from "fs/promises";
1471
1231
  import { existsSync as existsSync2, mkdirSync as mkdirSync2 } from "fs";
1472
1232
  import { join as join2 } from "path";
1473
- import { nanoid as nanoid2 } from "nanoid";
1233
+ import { nanoid } from "nanoid";
1474
1234
  var execAsync = promisify(exec);
1475
1235
  var SESSION_PREFIX = "spark_";
1476
1236
  var LOG_BASE_DIR = "sessions";
@@ -1490,7 +1250,7 @@ async function isTmuxAvailable() {
1490
1250
  }
1491
1251
  }
1492
1252
  function generateTerminalId() {
1493
- return "t" + nanoid2(9);
1253
+ return "t" + nanoid(9);
1494
1254
  }
1495
1255
  function getSessionName(terminalId) {
1496
1256
  return `${SESSION_PREFIX}${terminalId}`;
@@ -1718,7 +1478,7 @@ async function getMeta(terminalId, workingDirectory, sessionId) {
1718
1478
  }
1719
1479
  async function listSessionTerminals(sessionId, workingDirectory) {
1720
1480
  const terminalsDir = join2(workingDirectory, LOG_BASE_DIR, sessionId, "terminals");
1721
- const terminals3 = [];
1481
+ const terminals2 = [];
1722
1482
  try {
1723
1483
  const { readdir: readdir5 } = await import("fs/promises");
1724
1484
  const entries = await readdir5(terminalsDir, { withFileTypes: true });
@@ -1726,13 +1486,13 @@ async function listSessionTerminals(sessionId, workingDirectory) {
1726
1486
  if (entry.isDirectory()) {
1727
1487
  const meta = await getMeta(entry.name, workingDirectory, sessionId);
1728
1488
  if (meta) {
1729
- terminals3.push(meta);
1489
+ terminals2.push(meta);
1730
1490
  }
1731
1491
  }
1732
1492
  }
1733
1493
  } catch {
1734
1494
  }
1735
- return terminals3;
1495
+ return terminals2;
1736
1496
  }
1737
1497
  async function sendInput(terminalId, input, options = {}) {
1738
1498
  const session = getSessionName(terminalId);
@@ -2147,7 +1907,7 @@ function getCheckpointManager(sessionId, workingDirectory) {
2147
1907
  }
2148
1908
  async function createCheckpoint(sessionId, workingDirectory, messageSequence) {
2149
1909
  const gitHead = await getGitHead(workingDirectory);
2150
- const checkpoint = checkpointQueries.create({
1910
+ const checkpoint = await checkpointQueries.create({
2151
1911
  sessionId,
2152
1912
  messageSequence,
2153
1913
  gitHead
@@ -2164,7 +1924,7 @@ async function backupFile(sessionId, workingDirectory, filePath) {
2164
1924
  }
2165
1925
  const absolutePath = resolve3(workingDirectory, filePath);
2166
1926
  const relativePath = relative2(workingDirectory, absolutePath);
2167
- if (fileBackupQueries.hasBackup(manager.currentCheckpointId, relativePath)) {
1927
+ if (await fileBackupQueries.hasBackup(manager.currentCheckpointId, relativePath)) {
2168
1928
  return null;
2169
1929
  }
2170
1930
  let originalContent = null;
@@ -2177,7 +1937,7 @@ async function backupFile(sessionId, workingDirectory, filePath) {
2177
1937
  console.warn(`[checkpoint] Failed to read file for backup: ${error.message}`);
2178
1938
  }
2179
1939
  }
2180
- const backup = fileBackupQueries.create({
1940
+ const backup = await fileBackupQueries.create({
2181
1941
  checkpointId: manager.currentCheckpointId,
2182
1942
  sessionId,
2183
1943
  filePath: relativePath,
@@ -2187,7 +1947,7 @@ async function backupFile(sessionId, workingDirectory, filePath) {
2187
1947
  return backup;
2188
1948
  }
2189
1949
  async function revertToCheckpoint(sessionId, checkpointId) {
2190
- const session = sessionQueries.getById(sessionId);
1950
+ const session = await sessionQueries.getById(sessionId);
2191
1951
  if (!session) {
2192
1952
  return {
2193
1953
  success: false,
@@ -2198,7 +1958,7 @@ async function revertToCheckpoint(sessionId, checkpointId) {
2198
1958
  error: "Session not found"
2199
1959
  };
2200
1960
  }
2201
- const checkpoint = checkpointQueries.getById(checkpointId);
1961
+ const checkpoint = await checkpointQueries.getById(checkpointId);
2202
1962
  if (!checkpoint || checkpoint.sessionId !== sessionId) {
2203
1963
  return {
2204
1964
  success: false,
@@ -2210,7 +1970,7 @@ async function revertToCheckpoint(sessionId, checkpointId) {
2210
1970
  };
2211
1971
  }
2212
1972
  const workingDirectory = session.workingDirectory;
2213
- const backupsToRevert = fileBackupQueries.getFromSequence(sessionId, checkpoint.messageSequence);
1973
+ const backupsToRevert = await fileBackupQueries.getFromSequence(sessionId, checkpoint.messageSequence);
2214
1974
  const fileToEarliestBackup = /* @__PURE__ */ new Map();
2215
1975
  for (const backup of backupsToRevert) {
2216
1976
  if (!fileToEarliestBackup.has(backup.filePath)) {
@@ -2239,9 +1999,9 @@ async function revertToCheckpoint(sessionId, checkpointId) {
2239
1999
  console.error(`Failed to restore ${filePath}: ${error.message}`);
2240
2000
  }
2241
2001
  }
2242
- const messagesDeleted = messageQueries.deleteFromSequence(sessionId, checkpoint.messageSequence);
2243
- toolExecutionQueries.deleteAfterTime(sessionId, checkpoint.createdAt);
2244
- const checkpointsDeleted = checkpointQueries.deleteAfterSequence(sessionId, checkpoint.messageSequence);
2002
+ const messagesDeleted = await messageQueries.deleteFromSequence(sessionId, checkpoint.messageSequence);
2003
+ await toolExecutionQueries.deleteAfterTime(sessionId, checkpoint.createdAt);
2004
+ const checkpointsDeleted = await checkpointQueries.deleteAfterSequence(sessionId, checkpoint.messageSequence);
2245
2005
  const manager = getCheckpointManager(sessionId, workingDirectory);
2246
2006
  manager.currentCheckpointId = checkpoint.id;
2247
2007
  return {
@@ -2252,16 +2012,16 @@ async function revertToCheckpoint(sessionId, checkpointId) {
2252
2012
  checkpointsDeleted
2253
2013
  };
2254
2014
  }
2255
- function getCheckpoints(sessionId) {
2015
+ async function getCheckpoints(sessionId) {
2256
2016
  return checkpointQueries.getBySession(sessionId);
2257
2017
  }
2258
2018
  async function getSessionDiff(sessionId) {
2259
- const session = sessionQueries.getById(sessionId);
2019
+ const session = await sessionQueries.getById(sessionId);
2260
2020
  if (!session) {
2261
2021
  return { files: [] };
2262
2022
  }
2263
2023
  const workingDirectory = session.workingDirectory;
2264
- const allBackups = fileBackupQueries.getBySession(sessionId);
2024
+ const allBackups = await fileBackupQueries.getBySession(sessionId);
2265
2025
  const fileToOriginalBackup = /* @__PURE__ */ new Map();
2266
2026
  for (const backup of allBackups) {
2267
2027
  if (!fileToOriginalBackup.has(backup.filePath)) {
@@ -3048,7 +2808,7 @@ Best practices:
3048
2808
  error: "No items provided. Include at least one todo item."
3049
2809
  };
3050
2810
  }
3051
- const created = todoQueries.createMany(options.sessionId, items);
2811
+ const created = await todoQueries.createMany(options.sessionId, items);
3052
2812
  return {
3053
2813
  success: true,
3054
2814
  action: "add",
@@ -3057,7 +2817,7 @@ Best practices:
3057
2817
  };
3058
2818
  }
3059
2819
  case "list": {
3060
- const todos = todoQueries.getBySession(options.sessionId);
2820
+ const todos = await todoQueries.getBySession(options.sessionId);
3061
2821
  const stats = {
3062
2822
  total: todos.length,
3063
2823
  pending: todos.filter((t) => t.status === "pending").length,
@@ -3085,7 +2845,7 @@ Best practices:
3085
2845
  error: 'status is required for "mark" action'
3086
2846
  };
3087
2847
  }
3088
- const updated = todoQueries.updateStatus(todoId, status);
2848
+ const updated = await todoQueries.updateStatus(todoId, status);
3089
2849
  if (!updated) {
3090
2850
  return {
3091
2851
  success: false,
@@ -3099,7 +2859,7 @@ Best practices:
3099
2859
  };
3100
2860
  }
3101
2861
  case "clear": {
3102
- const count = todoQueries.clearSession(options.sessionId);
2862
+ const count = await todoQueries.clearSession(options.sessionId);
3103
2863
  return {
3104
2864
  success: true,
3105
2865
  action: "clear",
@@ -3173,7 +2933,7 @@ Once loaded, a skill's content will be available in the conversation context.`,
3173
2933
  error: 'skillName is required for "load" action'
3174
2934
  };
3175
2935
  }
3176
- if (skillQueries.isLoaded(options.sessionId, skillName)) {
2936
+ if (await skillQueries.isLoaded(options.sessionId, skillName)) {
3177
2937
  return {
3178
2938
  success: false,
3179
2939
  error: `Skill "${skillName}" is already loaded in this session`
@@ -3188,7 +2948,7 @@ Once loaded, a skill's content will be available in the conversation context.`,
3188
2948
  availableSkills: allSkills.map((s) => s.name)
3189
2949
  };
3190
2950
  }
3191
- skillQueries.load(options.sessionId, skillName);
2951
+ await skillQueries.load(options.sessionId, skillName);
3192
2952
  return {
3193
2953
  success: true,
3194
2954
  action: "load",
@@ -3416,7 +3176,7 @@ import {
3416
3176
  generateText,
3417
3177
  stepCountIs
3418
3178
  } from "ai";
3419
- import { nanoid as nanoid3 } from "nanoid";
3179
+ import { nanoid as nanoid2 } from "nanoid";
3420
3180
  var Subagent = class {
3421
3181
  /** Model to use (defaults to gemini-2.0-flash) */
3422
3182
  model;
@@ -3429,8 +3189,8 @@ var Subagent = class {
3429
3189
  * Parse the final result from the subagent's output.
3430
3190
  * Override this to structure the result for your subagent type.
3431
3191
  */
3432
- parseResult(text2, steps) {
3433
- return { text: text2, steps };
3192
+ parseResult(text, steps) {
3193
+ return { text, steps };
3434
3194
  }
3435
3195
  /**
3436
3196
  * Run the subagent with streaming progress updates
@@ -3438,7 +3198,7 @@ var Subagent = class {
3438
3198
  async run(options) {
3439
3199
  const { task, sessionId, toolCallId, onProgress, abortSignal } = options;
3440
3200
  const steps = [];
3441
- const execution = subagentQueries.create({
3201
+ const execution = await subagentQueries.create({
3442
3202
  sessionId,
3443
3203
  toolCallId,
3444
3204
  subagentType: this.type,
@@ -3447,12 +3207,12 @@ var Subagent = class {
3447
3207
  });
3448
3208
  const addStep = async (step) => {
3449
3209
  const fullStep = {
3450
- id: nanoid3(8),
3210
+ id: nanoid2(8),
3451
3211
  timestamp: Date.now(),
3452
3212
  ...step
3453
3213
  };
3454
3214
  steps.push(fullStep);
3455
- subagentQueries.addStep(execution.id, fullStep);
3215
+ await subagentQueries.addStep(execution.id, fullStep);
3456
3216
  await onProgress?.({
3457
3217
  type: "step",
3458
3218
  subagentId: execution.id,
@@ -3522,7 +3282,7 @@ var Subagent = class {
3522
3282
  }
3523
3283
  });
3524
3284
  const parsedResult = this.parseResult(result.text, steps);
3525
- subagentQueries.complete(execution.id, parsedResult);
3285
+ await subagentQueries.complete(execution.id, parsedResult);
3526
3286
  await onProgress?.({
3527
3287
  type: "complete",
3528
3288
  subagentId: execution.id,
@@ -3537,7 +3297,7 @@ var Subagent = class {
3537
3297
  };
3538
3298
  } catch (error) {
3539
3299
  const errorMessage = error.message || "Unknown error";
3540
- subagentQueries.markError(execution.id, errorMessage);
3300
+ await subagentQueries.markError(execution.id, errorMessage);
3541
3301
  await onProgress?.({
3542
3302
  type: "error",
3543
3303
  subagentId: execution.id,
@@ -3847,7 +3607,7 @@ Keep your responses concise and focused on actionable information.`;
3847
3607
  })
3848
3608
  };
3849
3609
  }
3850
- parseResult(text2, steps) {
3610
+ parseResult(text, steps) {
3851
3611
  const findings = [];
3852
3612
  let filesSearched = 0;
3853
3613
  let matchCount = 0;
@@ -3891,7 +3651,7 @@ Keep your responses concise and focused on actionable information.`;
3891
3651
  const query = steps.length > 0 ? steps.find((s) => s.type === "text")?.content || "" : "";
3892
3652
  return {
3893
3653
  query,
3894
- summary: text2,
3654
+ summary: text,
3895
3655
  findings: findings.slice(0, 20),
3896
3656
  // Limit findings
3897
3657
  filesSearched,
@@ -4022,9 +3782,362 @@ Context: ${context}` : query;
4022
3782
  });
4023
3783
  }
4024
3784
 
3785
+ // src/tools/semantic-search.ts
3786
+ import { tool as tool9 } from "ai";
3787
+ import { z as z10 } from "zod";
3788
+ import { existsSync as existsSync11, readFileSync as readFileSync4 } from "fs";
3789
+ import { join as join5 } from "path";
3790
+ import { minimatch as minimatch3 } from "minimatch";
3791
+
3792
+ // src/semantic/namespace.ts
3793
+ import { execSync } from "child_process";
3794
+ function getGitRemoteUrl(workingDirectory) {
3795
+ try {
3796
+ const result = execSync("git remote get-url origin", {
3797
+ cwd: workingDirectory,
3798
+ encoding: "utf-8",
3799
+ stdio: ["pipe", "pipe", "pipe"]
3800
+ });
3801
+ return result.trim();
3802
+ } catch {
3803
+ return null;
3804
+ }
3805
+ }
3806
+ function parseGitRemoteUrl(url) {
3807
+ const cleanUrl = url.replace(/\.git$/, "");
3808
+ const sshMatch = cleanUrl.match(/git@[^:]+:([^/]+)\/(.+)$/);
3809
+ if (sshMatch) {
3810
+ return { org: sshMatch[1], repo: sshMatch[2] };
3811
+ }
3812
+ const httpsMatch = cleanUrl.match(/https?:\/\/[^/]+\/([^/]+)\/(.+)$/);
3813
+ if (httpsMatch) {
3814
+ return { org: httpsMatch[1], repo: httpsMatch[2] };
3815
+ }
3816
+ const sshProtoMatch = cleanUrl.match(/ssh:\/\/[^/]+\/([^/]+)\/(.+)$/);
3817
+ if (sshProtoMatch) {
3818
+ return { org: sshProtoMatch[1], repo: sshProtoMatch[2] };
3819
+ }
3820
+ return null;
3821
+ }
3822
+ function sanitizeForNamespace(str) {
3823
+ return str.toLowerCase().replace(/[^a-z0-9]/g, "_").replace(/^_+|_+$/g, "").replace(/_+/g, "_");
3824
+ }
3825
+ async function getRepoNamespace(workingDirectory, configuredNamespace) {
3826
+ if (configuredNamespace) {
3827
+ return configuredNamespace;
3828
+ }
3829
+ const remoteUrl = getGitRemoteUrl(workingDirectory);
3830
+ if (!remoteUrl) {
3831
+ return null;
3832
+ }
3833
+ const parsed = parseGitRemoteUrl(remoteUrl);
3834
+ if (!parsed) {
3835
+ return null;
3836
+ }
3837
+ const org = sanitizeForNamespace(parsed.org);
3838
+ const repo = sanitizeForNamespace(parsed.repo);
3839
+ return `sparkecoder_${org}_${repo}`;
3840
+ }
3841
+
3842
+ // src/semantic/hasher.ts
3843
+ import { createHash } from "crypto";
3844
+
3845
+ // src/semantic/chunker.ts
3846
+ import { extname as extname6, basename as basename2 } from "path";
3847
+
3848
+ // src/semantic/client.ts
3849
+ var remoteServerUrl2 = null;
3850
+ var authKey2 = null;
3851
+ function initVectorClient(serverUrl, key) {
3852
+ remoteServerUrl2 = serverUrl.replace(/\/$/, "");
3853
+ authKey2 = key;
3854
+ }
3855
+ function isVectorClientConfigured() {
3856
+ return !!remoteServerUrl2 && !!authKey2;
3857
+ }
3858
+ async function vectorApi(path, options = {}) {
3859
+ if (!remoteServerUrl2 || !authKey2) {
3860
+ throw new Error("Vector client not initialized - remote server not configured");
3861
+ }
3862
+ const url = `${remoteServerUrl2}/vectors${path}`;
3863
+ const init = {
3864
+ method: options.method || "GET",
3865
+ headers: {
3866
+ "Content-Type": "application/json",
3867
+ "Authorization": `Bearer ${authKey2}`
3868
+ }
3869
+ };
3870
+ if (options.body) {
3871
+ init.body = JSON.stringify(options.body);
3872
+ }
3873
+ const response = await fetch(url, init);
3874
+ if (!response.ok) {
3875
+ const error = await response.json().catch(() => ({ error: "Unknown error" }));
3876
+ throw new Error(error.error || `HTTP ${response.status}`);
3877
+ }
3878
+ return response.json();
3879
+ }
3880
+ var remoteVectorClient = {
3881
+ embeddings: {
3882
+ /**
3883
+ * Create embeddings and store in vector DB
3884
+ */
3885
+ async createAndWait(texts, options) {
3886
+ return vectorApi("/embed", {
3887
+ method: "POST",
3888
+ body: {
3889
+ texts,
3890
+ namespace: options.namespace,
3891
+ embeddingModel: options.embeddingModel
3892
+ }
3893
+ });
3894
+ }
3895
+ },
3896
+ search: {
3897
+ /**
3898
+ * Query vectors using semantic search
3899
+ */
3900
+ async queryAndWait(query, options) {
3901
+ return vectorApi("/search", {
3902
+ method: "POST",
3903
+ body: {
3904
+ query,
3905
+ namespace: options.namespace,
3906
+ topK: options.topK || 10,
3907
+ embeddingModel: options.embeddingModel
3908
+ }
3909
+ });
3910
+ }
3911
+ },
3912
+ /**
3913
+ * Delete a namespace (if supported)
3914
+ */
3915
+ async deleteNamespace(namespace) {
3916
+ await vectorApi(`/namespace/${encodeURIComponent(namespace)}`, {
3917
+ method: "DELETE"
3918
+ });
3919
+ },
3920
+ /**
3921
+ * Close client (no-op for HTTP client)
3922
+ */
3923
+ async close() {
3924
+ }
3925
+ };
3926
+ function getVectorClient() {
3927
+ if (!isVectorClientConfigured()) {
3928
+ try {
3929
+ const config = getConfig();
3930
+ if (config.resolvedRemoteServer.url && config.resolvedRemoteServer.authKey) {
3931
+ initVectorClient(config.resolvedRemoteServer.url, config.resolvedRemoteServer.authKey);
3932
+ } else {
3933
+ return null;
3934
+ }
3935
+ } catch {
3936
+ return null;
3937
+ }
3938
+ }
3939
+ return remoteVectorClient;
3940
+ }
3941
+ async function closeVectorClient() {
3942
+ }
3943
+ function isVectorGatewayConfigured() {
3944
+ try {
3945
+ const config = getConfig();
3946
+ return !!(config.resolvedRemoteServer.url && config.resolvedRemoteServer.authKey);
3947
+ } catch {
3948
+ return false;
3949
+ }
3950
+ }
3951
+ function getEmbeddingModel() {
3952
+ try {
3953
+ const config = getConfig();
3954
+ return config.resolvedVectorGateway.embeddingModel;
3955
+ } catch {
3956
+ return "gemini-embedding-001";
3957
+ }
3958
+ }
3959
+
3960
+ // src/semantic/indexer.ts
3961
+ import { readFileSync as readFileSync3, statSync } from "fs";
3962
+ import { relative as relative7 } from "path";
3963
+ import { minimatch as minimatch2 } from "minimatch";
3964
+ var MAX_FILE_SIZE3 = 1024 * 1024;
3965
+ async function getIndexStatus(workingDirectory) {
3966
+ const config = getConfig();
3967
+ const namespace = await getRepoNamespace(
3968
+ workingDirectory,
3969
+ config.resolvedVectorGateway.namespace
3970
+ );
3971
+ const isConfigured = !!config.resolvedVectorGateway.redisUrl;
3972
+ if (!namespace) {
3973
+ return {
3974
+ namespace: "",
3975
+ totalChunks: 0,
3976
+ lastFullIndex: null,
3977
+ lastIncrementalIndex: null,
3978
+ isConfigured
3979
+ };
3980
+ }
3981
+ try {
3982
+ const db = getDb();
3983
+ const status = await indexStatusQueries.get(db, namespace);
3984
+ if (!status) {
3985
+ return {
3986
+ namespace,
3987
+ totalChunks: 0,
3988
+ lastFullIndex: null,
3989
+ lastIncrementalIndex: null,
3990
+ isConfigured
3991
+ };
3992
+ }
3993
+ return {
3994
+ namespace,
3995
+ totalChunks: status.totalChunks ?? 0,
3996
+ lastFullIndex: status.lastFullIndex ?? null,
3997
+ lastIncrementalIndex: status.lastIncrementalIndex ?? null,
3998
+ isConfigured
3999
+ };
4000
+ } catch {
4001
+ return {
4002
+ namespace,
4003
+ totalChunks: 0,
4004
+ lastFullIndex: null,
4005
+ lastIncrementalIndex: null,
4006
+ isConfigured
4007
+ };
4008
+ }
4009
+ }
4010
+ async function checkIndexExists(workingDirectory) {
4011
+ const status = await getIndexStatus(workingDirectory);
4012
+ return status.totalChunks > 0;
4013
+ }
4014
+
4015
+ // src/tools/semantic-search.ts
4016
+ var semanticSearchInputSchema = z10.object({
4017
+ query: z10.string().describe("Natural language search query describing what you want to find"),
4018
+ topK: z10.number().optional().default(10).describe("Number of results to return (default: 10, max: 50)"),
4019
+ filePattern: z10.string().optional().describe('Filter results by file glob pattern (e.g., "*.ts", "src/**/*.py")'),
4020
+ language: z10.string().optional().describe('Filter by programming language (e.g., "typescript", "python")')
4021
+ });
4022
+ function createSemanticSearchTool(options) {
4023
+ return tool9({
4024
+ description: `Search the codebase using semantic similarity. This tool finds code by understanding its meaning, not just matching text.
4025
+
4026
+ Use this tool when:
4027
+ - You need to understand how something works in the codebase
4028
+ - You're looking for code related to a concept (e.g., "authentication", "database queries")
4029
+ - You want to find implementations of features
4030
+ - The user asks "where is X?" or "how does Y work?"
4031
+
4032
+ This tool requires the repository to be indexed first with 'sparkecoder index'.
4033
+
4034
+ Returns matching code snippets with file paths, line numbers, and relevance scores.`,
4035
+ inputSchema: semanticSearchInputSchema,
4036
+ execute: async ({
4037
+ query,
4038
+ topK,
4039
+ filePattern,
4040
+ language
4041
+ }) => {
4042
+ const startTime = Date.now();
4043
+ try {
4044
+ const config = getConfig();
4045
+ const namespace = await getRepoNamespace(
4046
+ options.workingDirectory,
4047
+ config.resolvedVectorGateway.namespace
4048
+ );
4049
+ if (!namespace) {
4050
+ return {
4051
+ success: false,
4052
+ error: "Repository namespace not found. Ensure this is a git repository with a remote configured."
4053
+ };
4054
+ }
4055
+ const client = getVectorClient();
4056
+ if (!client) {
4057
+ return {
4058
+ success: false,
4059
+ error: 'Vector Gateway not configured. Run "sparkecoder index --status" for setup instructions.'
4060
+ };
4061
+ }
4062
+ try {
4063
+ const limitedTopK = Math.min(Math.max(1, topK), 50);
4064
+ const embeddingModel = getEmbeddingModel();
4065
+ const result = await client.search.queryAndWait(query, {
4066
+ namespace,
4067
+ topK: limitedTopK * 2,
4068
+ includeMetadata: true,
4069
+ embeddingModel
4070
+ });
4071
+ const matches = [];
4072
+ for (const match of result.matches) {
4073
+ const metadata = match.metadata;
4074
+ if (!metadata) continue;
4075
+ const filePath = metadata.filePath;
4076
+ const startLine = metadata.startLine;
4077
+ const endLine = metadata.endLine;
4078
+ const matchLanguage = metadata.language;
4079
+ const symbolName = metadata.symbolName;
4080
+ if (filePattern) {
4081
+ const matchesPattern = minimatch3(filePath, filePattern, { dot: true });
4082
+ if (!matchesPattern) continue;
4083
+ }
4084
+ if (language && matchLanguage !== language.toLowerCase()) {
4085
+ continue;
4086
+ }
4087
+ const fullPath = join5(options.workingDirectory, filePath);
4088
+ if (!existsSync11(fullPath)) {
4089
+ continue;
4090
+ }
4091
+ let snippet = "";
4092
+ try {
4093
+ const content = readFileSync4(fullPath, "utf-8");
4094
+ const lines = content.split("\n");
4095
+ const snippetLines = lines.slice(
4096
+ Math.max(0, startLine - 1),
4097
+ Math.min(lines.length, endLine)
4098
+ );
4099
+ snippet = snippetLines.join("\n");
4100
+ if (snippet.length > 500) {
4101
+ snippet = snippet.slice(0, 500) + "\n... (truncated)";
4102
+ }
4103
+ } catch {
4104
+ }
4105
+ matches.push({
4106
+ filePath,
4107
+ startLine,
4108
+ endLine,
4109
+ score: match.score,
4110
+ snippet,
4111
+ symbolName,
4112
+ language: matchLanguage
4113
+ });
4114
+ if (matches.length >= limitedTopK) {
4115
+ break;
4116
+ }
4117
+ }
4118
+ return {
4119
+ success: true,
4120
+ query,
4121
+ matches,
4122
+ totalMatches: matches.length,
4123
+ duration: Date.now() - startTime
4124
+ };
4125
+ } finally {
4126
+ await closeVectorClient();
4127
+ }
4128
+ } catch (error) {
4129
+ return {
4130
+ success: false,
4131
+ error: `Semantic search failed: ${error instanceof Error ? error.message : String(error)}`
4132
+ };
4133
+ }
4134
+ }
4135
+ });
4136
+ }
4137
+
4025
4138
  // src/tools/index.ts
4026
- function createTools(options) {
4027
- return {
4139
+ async function createTools(options) {
4140
+ const tools = {
4028
4141
  bash: createBashTool({
4029
4142
  workingDirectory: options.workingDirectory,
4030
4143
  sessionId: options.sessionId,
@@ -4056,6 +4169,20 @@ function createTools(options) {
4056
4169
  onProgress: options.onSearchProgress
4057
4170
  })
4058
4171
  };
4172
+ if (options.enableSemanticSearch !== false) {
4173
+ try {
4174
+ if (isVectorGatewayConfigured()) {
4175
+ const hasIndex = await checkIndexExists(options.workingDirectory);
4176
+ if (hasIndex) {
4177
+ tools.semantic_search = createSemanticSearchTool({
4178
+ workingDirectory: options.workingDirectory
4179
+ });
4180
+ }
4181
+ }
4182
+ } catch {
4183
+ }
4184
+ }
4185
+ return tools;
4059
4186
  }
4060
4187
 
4061
4188
  // src/agent/context.ts
@@ -4108,7 +4235,7 @@ async function buildSystemPrompt(options) {
4108
4235
  const skills = await loadAllSkills2(skillsDirectories);
4109
4236
  onDemandSkillsContext = formatSkillsForContext(skills);
4110
4237
  }
4111
- const todos = todoQueries.getBySession(sessionId);
4238
+ const todos = await todoQueries.getBySession(sessionId);
4112
4239
  const todosContext = formatTodosForContext(todos);
4113
4240
  const platform3 = process.platform === "win32" ? "Windows" : process.platform === "darwin" ? "macOS" : "Linux";
4114
4241
  const currentDate = (/* @__PURE__ */ new Date()).toLocaleDateString("en-US", { weekday: "long", year: "numeric", month: "long", day: "numeric" });
@@ -4350,7 +4477,7 @@ var ContextManager = class {
4350
4477
  * Returns ModelMessage[] that can be passed directly to streamText/generateText
4351
4478
  */
4352
4479
  async getMessages() {
4353
- let modelMessages = messageQueries.getModelMessages(this.sessionId);
4480
+ let modelMessages = await messageQueries.getModelMessages(this.sessionId);
4354
4481
  const contextSize = calculateContextSize(modelMessages);
4355
4482
  if (this.autoSummarize && contextSize > this.maxContextChars) {
4356
4483
  modelMessages = await this.summarizeContext(modelMessages);
@@ -4370,13 +4497,13 @@ ${this.summary}`
4370
4497
  /**
4371
4498
  * Summarize older messages to reduce context size
4372
4499
  */
4373
- async summarizeContext(messages2) {
4374
- if (messages2.length <= this.keepRecentMessages) {
4375
- return messages2;
4500
+ async summarizeContext(messages) {
4501
+ if (messages.length <= this.keepRecentMessages) {
4502
+ return messages;
4376
4503
  }
4377
- const splitIndex = messages2.length - this.keepRecentMessages;
4378
- const oldMessages = messages2.slice(0, splitIndex);
4379
- const recentMessages = messages2.slice(splitIndex);
4504
+ const splitIndex = messages.length - this.keepRecentMessages;
4505
+ const oldMessages = messages.slice(0, splitIndex);
4506
+ const recentMessages = messages.slice(splitIndex);
4380
4507
  const historyText = oldMessages.map((msg) => {
4381
4508
  const content = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content);
4382
4509
  return `[${msg.role}]: ${content}`;
@@ -4400,36 +4527,36 @@ ${this.summary}`
4400
4527
  * Add a user message to the context
4401
4528
  * Content can be a string or an array of content parts (for messages with images/files)
4402
4529
  */
4403
- addUserMessage(content) {
4530
+ async addUserMessage(content) {
4404
4531
  const userMessage = {
4405
4532
  role: "user",
4406
4533
  content
4407
4534
  };
4408
- messageQueries.create(this.sessionId, userMessage);
4535
+ await messageQueries.create(this.sessionId, userMessage);
4409
4536
  }
4410
4537
  /**
4411
4538
  * Add response messages from AI SDK directly
4412
4539
  * This is the preferred method - use result.response.messages from streamText/generateText
4413
4540
  */
4414
- addResponseMessages(messages2) {
4415
- messageQueries.addMany(this.sessionId, messages2);
4541
+ async addResponseMessages(messages) {
4542
+ await messageQueries.addMany(this.sessionId, messages);
4416
4543
  }
4417
4544
  /**
4418
4545
  * Get current context statistics
4419
4546
  */
4420
- getStats() {
4421
- const messages2 = messageQueries.getModelMessages(this.sessionId);
4547
+ async getStats() {
4548
+ const messages = await messageQueries.getModelMessages(this.sessionId);
4422
4549
  return {
4423
- messageCount: messages2.length,
4424
- contextChars: calculateContextSize(messages2),
4550
+ messageCount: messages.length,
4551
+ contextChars: calculateContextSize(messages),
4425
4552
  hasSummary: this.summary !== null
4426
4553
  };
4427
4554
  }
4428
4555
  /**
4429
4556
  * Clear all messages in the context
4430
4557
  */
4431
- clear() {
4432
- messageQueries.deleteBySession(this.sessionId);
4558
+ async clear() {
4559
+ await messageQueries.deleteBySession(this.sessionId);
4433
4560
  this.summary = null;
4434
4561
  }
4435
4562
  };
@@ -4449,7 +4576,7 @@ var Agent = class _Agent {
4449
4576
  /**
4450
4577
  * Create tools with optional progress callbacks
4451
4578
  */
4452
- createToolsWithCallbacks(options) {
4579
+ async createToolsWithCallbacks(options) {
4453
4580
  const config = getConfig();
4454
4581
  return createTools({
4455
4582
  sessionId: this.session.id,
@@ -4467,13 +4594,13 @@ var Agent = class _Agent {
4467
4594
  const config = getConfig();
4468
4595
  let session;
4469
4596
  if (options.sessionId) {
4470
- const existing = sessionQueries.getById(options.sessionId);
4597
+ const existing = await sessionQueries.getById(options.sessionId);
4471
4598
  if (!existing) {
4472
4599
  throw new Error(`Session not found: ${options.sessionId}`);
4473
4600
  }
4474
4601
  session = existing;
4475
4602
  } else {
4476
- session = sessionQueries.create({
4603
+ session = await sessionQueries.create({
4477
4604
  name: options.name,
4478
4605
  workingDirectory: options.workingDirectory || config.resolvedWorkingDirectory,
4479
4606
  model: options.model || config.defaultModel,
@@ -4486,7 +4613,7 @@ var Agent = class _Agent {
4486
4613
  keepRecentMessages: config.context?.keepRecentMessages || 10,
4487
4614
  autoSummarize: config.context?.autoSummarize ?? true
4488
4615
  });
4489
- const tools = createTools({
4616
+ const tools = await createTools({
4490
4617
  sessionId: session.id,
4491
4618
  workingDirectory: session.workingDirectory,
4492
4619
  skillsDirectories: config.resolvedSkillsDirectories
@@ -4562,7 +4689,7 @@ ${prompt}` });
4562
4689
  if (!options.skipSaveUserMessage) {
4563
4690
  this.context.addUserMessage(userContent);
4564
4691
  }
4565
- sessionQueries.updateStatus(this.session.id, "active");
4692
+ await sessionQueries.updateStatus(this.session.id, "active");
4566
4693
  const systemPrompt = await buildSystemPrompt({
4567
4694
  workingDirectory: this.session.workingDirectory,
4568
4695
  skillsDirectories: config.resolvedSkillsDirectories,
@@ -4571,14 +4698,14 @@ ${prompt}` });
4571
4698
  // TODO: Pass activeFiles from client for glob matching
4572
4699
  activeFiles: []
4573
4700
  });
4574
- const messages2 = await this.context.getMessages();
4575
- const tools = options.onToolProgress ? this.createToolsWithCallbacks({ onToolProgress: options.onToolProgress }) : this.baseTools;
4701
+ const messages = await this.context.getMessages();
4702
+ const tools = options.onToolProgress ? await this.createToolsWithCallbacks({ onToolProgress: options.onToolProgress }) : this.baseTools;
4576
4703
  const wrappedTools = this.wrapToolsWithApproval(options, tools);
4577
4704
  const useAnthropic = isAnthropicModel(this.session.model);
4578
4705
  const stream = streamText2({
4579
4706
  model: resolveModel(this.session.model),
4580
4707
  system: systemPrompt,
4581
- messages: messages2,
4708
+ messages,
4582
4709
  tools: wrappedTools,
4583
4710
  stopWhen: stepCountIs2(500),
4584
4711
  // Forward abort signal if provided
@@ -4626,14 +4753,14 @@ ${prompt}` });
4626
4753
  discoveredSkills: config.discoveredSkills,
4627
4754
  activeFiles: []
4628
4755
  });
4629
- const messages2 = await this.context.getMessages();
4630
- const tools = options.onToolProgress ? this.createToolsWithCallbacks({ onToolProgress: options.onToolProgress }) : this.baseTools;
4756
+ const messages = await this.context.getMessages();
4757
+ const tools = options.onToolProgress ? await this.createToolsWithCallbacks({ onToolProgress: options.onToolProgress }) : this.baseTools;
4631
4758
  const wrappedTools = this.wrapToolsWithApproval(options, tools);
4632
4759
  const useAnthropic = isAnthropicModel(this.session.model);
4633
4760
  const result = await generateText3({
4634
4761
  model: resolveModel(this.session.model),
4635
4762
  system: systemPrompt,
4636
- messages: messages2,
4763
+ messages,
4637
4764
  tools: wrappedTools,
4638
4765
  stopWhen: stepCountIs2(500),
4639
4766
  // Enable extended thinking/reasoning for models that support it
@@ -4666,11 +4793,11 @@ ${prompt}` });
4666
4793
  wrappedTools[name] = originalTool;
4667
4794
  continue;
4668
4795
  }
4669
- wrappedTools[name] = tool9({
4796
+ wrappedTools[name] = tool10({
4670
4797
  description: originalTool.description || "",
4671
- inputSchema: originalTool.inputSchema || z10.object({}),
4798
+ inputSchema: originalTool.inputSchema || z11.object({}),
4672
4799
  execute: async (input, toolOptions) => {
4673
- const toolCallId = toolOptions.toolCallId || nanoid4();
4800
+ const toolCallId = toolOptions.toolCallId || nanoid3();
4674
4801
  const execution = toolExecutionQueries.create({
4675
4802
  sessionId: this.session.id,
4676
4803
  toolName: name,
@@ -4679,19 +4806,20 @@ ${prompt}` });
4679
4806
  requiresApproval: true,
4680
4807
  status: "pending"
4681
4808
  });
4682
- this.pendingApprovals.set(toolCallId, execution);
4683
- options.onApprovalRequired?.(execution);
4684
- sessionQueries.updateStatus(this.session.id, "waiting");
4809
+ this.pendingApprovals.set(toolCallId, await execution);
4810
+ options.onApprovalRequired?.(await execution);
4811
+ await sessionQueries.updateStatus(this.session.id, "waiting");
4685
4812
  const approved = await new Promise((resolve10) => {
4686
4813
  approvalResolvers.set(toolCallId, { resolve: resolve10, sessionId: this.session.id });
4687
4814
  });
4688
4815
  const resolverData = approvalResolvers.get(toolCallId);
4689
4816
  approvalResolvers.delete(toolCallId);
4690
4817
  this.pendingApprovals.delete(toolCallId);
4818
+ const exec6 = await execution;
4691
4819
  if (!approved) {
4692
4820
  const reason = resolverData?.reason || "User rejected the tool execution";
4693
- toolExecutionQueries.reject(execution.id);
4694
- sessionQueries.updateStatus(this.session.id, "active");
4821
+ await toolExecutionQueries.reject(exec6.id);
4822
+ await sessionQueries.updateStatus(this.session.id, "active");
4695
4823
  return {
4696
4824
  status: "rejected",
4697
4825
  toolCallId,
@@ -4700,14 +4828,14 @@ ${prompt}` });
4700
4828
  message: `Tool "${name}" was rejected by the user. Reason: ${reason}`
4701
4829
  };
4702
4830
  }
4703
- toolExecutionQueries.approve(execution.id);
4704
- sessionQueries.updateStatus(this.session.id, "active");
4831
+ await toolExecutionQueries.approve(exec6.id);
4832
+ await sessionQueries.updateStatus(this.session.id, "active");
4705
4833
  try {
4706
4834
  const result = await originalTool.execute(input, toolOptions);
4707
- toolExecutionQueries.complete(execution.id, result);
4835
+ await toolExecutionQueries.complete(exec6.id, result);
4708
4836
  return result;
4709
4837
  } catch (error) {
4710
- toolExecutionQueries.complete(execution.id, null, error.message);
4838
+ await toolExecutionQueries.complete(exec6.id, null, error.message);
4711
4839
  throw error;
4712
4840
  }
4713
4841
  }
@@ -4730,36 +4858,36 @@ ${prompt}` });
4730
4858
  resolver.resolve(true);
4731
4859
  return { approved: true };
4732
4860
  }
4733
- const pendingFromDb = toolExecutionQueries.getPendingApprovals(this.session.id);
4861
+ const pendingFromDb = await toolExecutionQueries.getPendingApprovals(this.session.id);
4734
4862
  const execution = pendingFromDb.find((e) => e.toolCallId === toolCallId);
4735
4863
  if (!execution) {
4736
4864
  throw new Error(`No pending approval for tool call: ${toolCallId}`);
4737
4865
  }
4738
- toolExecutionQueries.approve(execution.id);
4866
+ await toolExecutionQueries.approve(execution.id);
4739
4867
  return { approved: true };
4740
4868
  }
4741
4869
  /**
4742
4870
  * Reject a pending tool execution
4743
4871
  */
4744
- reject(toolCallId, reason) {
4872
+ async reject(toolCallId, reason) {
4745
4873
  const resolver = approvalResolvers.get(toolCallId);
4746
4874
  if (resolver) {
4747
4875
  resolver.reason = reason;
4748
4876
  resolver.resolve(false);
4749
4877
  return { rejected: true };
4750
4878
  }
4751
- const pendingFromDb = toolExecutionQueries.getPendingApprovals(this.session.id);
4879
+ const pendingFromDb = await toolExecutionQueries.getPendingApprovals(this.session.id);
4752
4880
  const execution = pendingFromDb.find((e) => e.toolCallId === toolCallId);
4753
4881
  if (!execution) {
4754
4882
  throw new Error(`No pending approval for tool call: ${toolCallId}`);
4755
4883
  }
4756
- toolExecutionQueries.reject(execution.id);
4884
+ await toolExecutionQueries.reject(execution.id);
4757
4885
  return { rejected: true };
4758
4886
  }
4759
4887
  /**
4760
4888
  * Get pending approvals
4761
4889
  */
4762
- getPendingApprovals() {
4890
+ async getPendingApprovals() {
4763
4891
  return toolExecutionQueries.getPendingApprovals(this.session.id);
4764
4892
  }
4765
4893
  /**
@@ -4782,8 +4910,8 @@ import { Hono as Hono5 } from "hono";
4782
4910
  import { serve } from "@hono/node-server";
4783
4911
  import { cors } from "hono/cors";
4784
4912
  import { logger } from "hono/logger";
4785
- import { existsSync as existsSync13, mkdirSync as mkdirSync5, writeFileSync as writeFileSync4 } from "fs";
4786
- import { resolve as resolve9, dirname as dirname7, join as join7 } from "path";
4913
+ import { existsSync as existsSync14, mkdirSync as mkdirSync5, writeFileSync as writeFileSync4 } from "fs";
4914
+ import { resolve as resolve9, dirname as dirname7, join as join9 } from "path";
4787
4915
  import { spawn as spawn2 } from "child_process";
4788
4916
  import { createServer as createNetServer } from "net";
4789
4917
  import { fileURLToPath as fileURLToPath3 } from "url";
@@ -4791,35 +4919,35 @@ import { fileURLToPath as fileURLToPath3 } from "url";
4791
4919
  // src/server/routes/sessions.ts
4792
4920
  import { Hono } from "hono";
4793
4921
  import { zValidator } from "@hono/zod-validator";
4794
- import { z as z11 } from "zod";
4795
- import { existsSync as existsSync11, mkdirSync as mkdirSync3, writeFileSync as writeFileSync2, readdirSync, statSync, unlinkSync } from "fs";
4922
+ import { z as z12 } from "zod";
4923
+ import { existsSync as existsSync12, mkdirSync as mkdirSync3, writeFileSync as writeFileSync2, readdirSync, statSync as statSync2, unlinkSync } from "fs";
4796
4924
  import { readdir as readdir4 } from "fs/promises";
4797
- import { join as join4, basename as basename2, extname as extname6, relative as relative7 } from "path";
4798
- import { nanoid as nanoid5 } from "nanoid";
4799
- var sessions2 = new Hono();
4800
- var createSessionSchema = z11.object({
4801
- name: z11.string().optional(),
4802
- workingDirectory: z11.string().optional(),
4803
- model: z11.string().optional(),
4804
- toolApprovals: z11.record(z11.string(), z11.boolean()).optional()
4925
+ import { join as join6, basename as basename3, extname as extname7, relative as relative8 } from "path";
4926
+ import { nanoid as nanoid4 } from "nanoid";
4927
+ var sessions = new Hono();
4928
+ var createSessionSchema = z12.object({
4929
+ name: z12.string().optional(),
4930
+ workingDirectory: z12.string().optional(),
4931
+ model: z12.string().optional(),
4932
+ toolApprovals: z12.record(z12.string(), z12.boolean()).optional()
4805
4933
  });
4806
- var paginationQuerySchema = z11.object({
4807
- limit: z11.string().optional(),
4808
- offset: z11.string().optional()
4934
+ var paginationQuerySchema = z12.object({
4935
+ limit: z12.string().optional(),
4936
+ offset: z12.string().optional()
4809
4937
  });
4810
- var messagesQuerySchema = z11.object({
4811
- limit: z11.string().optional()
4938
+ var messagesQuerySchema = z12.object({
4939
+ limit: z12.string().optional()
4812
4940
  });
4813
- sessions2.get(
4941
+ sessions.get(
4814
4942
  "/",
4815
4943
  zValidator("query", paginationQuerySchema),
4816
4944
  async (c) => {
4817
4945
  const query = c.req.valid("query");
4818
4946
  const limit = parseInt(query.limit || "50");
4819
4947
  const offset = parseInt(query.offset || "0");
4820
- const allSessions = sessionQueries.list(limit, offset);
4821
- const sessionsWithStreamInfo = allSessions.map((s) => {
4822
- const activeStream = activeStreamQueries.getBySessionId(s.id);
4948
+ const allSessions = await sessionQueries.list(limit, offset);
4949
+ const sessionsWithStreamInfo = await Promise.all(allSessions.map(async (s) => {
4950
+ const activeStream = await activeStreamQueries.getBySessionId(s.id);
4823
4951
  return {
4824
4952
  id: s.id,
4825
4953
  name: s.name,
@@ -4831,7 +4959,7 @@ sessions2.get(
4831
4959
  createdAt: s.createdAt.toISOString(),
4832
4960
  updatedAt: s.updatedAt.toISOString()
4833
4961
  };
4834
- });
4962
+ }));
4835
4963
  return c.json({
4836
4964
  sessions: sessionsWithStreamInfo,
4837
4965
  count: allSessions.length,
@@ -4840,7 +4968,7 @@ sessions2.get(
4840
4968
  });
4841
4969
  }
4842
4970
  );
4843
- sessions2.post(
4971
+ sessions.post(
4844
4972
  "/",
4845
4973
  zValidator("json", createSessionSchema),
4846
4974
  async (c) => {
@@ -4863,9 +4991,9 @@ sessions2.post(
4863
4991
  }, 201);
4864
4992
  }
4865
4993
  );
4866
- sessions2.get("/:id", async (c) => {
4994
+ sessions.get("/:id", async (c) => {
4867
4995
  const id = c.req.param("id");
4868
- const session = sessionQueries.getById(id);
4996
+ const session = await sessionQueries.getById(id);
4869
4997
  if (!session) {
4870
4998
  return c.json({ error: "Session not found" }, 404);
4871
4999
  }
@@ -4873,8 +5001,8 @@ sessions2.get("/:id", async (c) => {
4873
5001
  const agent = await Agent.create({ sessionId: id });
4874
5002
  return agent.getContextStats();
4875
5003
  })();
4876
- const todos = todoQueries.getBySession(id);
4877
- const pendingApprovals = toolExecutionQueries.getPendingApprovals(id);
5004
+ const todos = await todoQueries.getBySession(id);
5005
+ const pendingApprovals = await toolExecutionQueries.getPendingApprovals(id);
4878
5006
  return c.json({
4879
5007
  id: session.id,
4880
5008
  name: session.name,
@@ -4899,37 +5027,37 @@ sessions2.get("/:id", async (c) => {
4899
5027
  }))
4900
5028
  });
4901
5029
  });
4902
- sessions2.get(
5030
+ sessions.get(
4903
5031
  "/:id/messages",
4904
5032
  zValidator("query", messagesQuerySchema),
4905
5033
  async (c) => {
4906
5034
  const id = c.req.param("id");
4907
5035
  const query = c.req.valid("query");
4908
5036
  const limit = parseInt(query.limit || "100");
4909
- const session = sessionQueries.getById(id);
5037
+ const session = await sessionQueries.getById(id);
4910
5038
  if (!session) {
4911
5039
  return c.json({ error: "Session not found" }, 404);
4912
5040
  }
4913
- const messages2 = messageQueries.getRecentBySession(id, limit);
5041
+ const messages = await messageQueries.getRecentBySession(id, limit);
4914
5042
  return c.json({
4915
5043
  sessionId: id,
4916
- messages: messages2.map((m) => ({
5044
+ messages: messages.map((m) => ({
4917
5045
  id: m.id,
4918
5046
  ...m.modelMessage,
4919
5047
  // Spread the AI SDK ModelMessage (role, content)
4920
5048
  createdAt: m.createdAt.toISOString()
4921
5049
  })),
4922
- count: messages2.length
5050
+ count: messages.length
4923
5051
  });
4924
5052
  }
4925
5053
  );
4926
- sessions2.get("/:id/tools", async (c) => {
5054
+ sessions.get("/:id/tools", async (c) => {
4927
5055
  const id = c.req.param("id");
4928
- const session = sessionQueries.getById(id);
5056
+ const session = await sessionQueries.getById(id);
4929
5057
  if (!session) {
4930
5058
  return c.json({ error: "Session not found" }, 404);
4931
5059
  }
4932
- const executions = toolExecutionQueries.getBySession(id);
5060
+ const executions = await toolExecutionQueries.getBySession(id);
4933
5061
  return c.json({
4934
5062
  sessionId: id,
4935
5063
  executions: executions.map((e) => ({
@@ -4947,18 +5075,18 @@ sessions2.get("/:id/tools", async (c) => {
4947
5075
  count: executions.length
4948
5076
  });
4949
5077
  });
4950
- var updateSessionSchema = z11.object({
4951
- model: z11.string().optional(),
4952
- name: z11.string().optional(),
4953
- toolApprovals: z11.record(z11.string(), z11.boolean()).optional()
5078
+ var updateSessionSchema = z12.object({
5079
+ model: z12.string().optional(),
5080
+ name: z12.string().optional(),
5081
+ toolApprovals: z12.record(z12.string(), z12.boolean()).optional()
4954
5082
  });
4955
- sessions2.patch(
5083
+ sessions.patch(
4956
5084
  "/:id",
4957
5085
  zValidator("json", updateSessionSchema),
4958
5086
  async (c) => {
4959
5087
  const id = c.req.param("id");
4960
5088
  const body = c.req.valid("json");
4961
- const session = sessionQueries.getById(id);
5089
+ const session = await sessionQueries.getById(id);
4962
5090
  if (!session) {
4963
5091
  return c.json({ error: "Session not found" }, 404);
4964
5092
  }
@@ -4976,7 +5104,7 @@ sessions2.patch(
4976
5104
  }
4977
5105
  };
4978
5106
  }
4979
- const updatedSession = Object.keys(updates).length > 0 ? sessionQueries.update(id, updates) || session : session;
5107
+ const updatedSession = Object.keys(updates).length > 0 ? await sessionQueries.update(id, updates) || session : session;
4980
5108
  return c.json({
4981
5109
  id: updatedSession.id,
4982
5110
  name: updatedSession.name,
@@ -4988,10 +5116,10 @@ sessions2.patch(
4988
5116
  });
4989
5117
  }
4990
5118
  );
4991
- sessions2.delete("/:id", async (c) => {
5119
+ sessions.delete("/:id", async (c) => {
4992
5120
  const id = c.req.param("id");
4993
5121
  try {
4994
- const session = sessionQueries.getById(id);
5122
+ const session = await sessionQueries.getById(id);
4995
5123
  if (session) {
4996
5124
  const terminalIds = await listSessions();
4997
5125
  for (const tid of terminalIds) {
@@ -5004,29 +5132,29 @@ sessions2.delete("/:id", async (c) => {
5004
5132
  } catch (e) {
5005
5133
  }
5006
5134
  clearCheckpointManager(id);
5007
- const deleted = sessionQueries.delete(id);
5135
+ const deleted = await sessionQueries.delete(id);
5008
5136
  if (!deleted) {
5009
5137
  return c.json({ error: "Session not found" }, 404);
5010
5138
  }
5011
5139
  return c.json({ success: true, id });
5012
5140
  });
5013
- sessions2.post("/:id/clear", async (c) => {
5141
+ sessions.post("/:id/clear", async (c) => {
5014
5142
  const id = c.req.param("id");
5015
- const session = sessionQueries.getById(id);
5143
+ const session = await sessionQueries.getById(id);
5016
5144
  if (!session) {
5017
5145
  return c.json({ error: "Session not found" }, 404);
5018
5146
  }
5019
5147
  const agent = await Agent.create({ sessionId: id });
5020
- agent.clearContext();
5148
+ await agent.clearContext();
5021
5149
  return c.json({ success: true, sessionId: id });
5022
5150
  });
5023
- sessions2.get("/:id/todos", async (c) => {
5151
+ sessions.get("/:id/todos", async (c) => {
5024
5152
  const id = c.req.param("id");
5025
- const session = sessionQueries.getById(id);
5153
+ const session = await sessionQueries.getById(id);
5026
5154
  if (!session) {
5027
5155
  return c.json({ error: "Session not found" }, 404);
5028
5156
  }
5029
- const todos = todoQueries.getBySession(id);
5157
+ const todos = await todoQueries.getBySession(id);
5030
5158
  const pending = todos.filter((t) => t.status === "pending");
5031
5159
  const inProgress = todos.filter((t) => t.status === "in_progress");
5032
5160
  const completed = todos.filter((t) => t.status === "completed");
@@ -5055,32 +5183,32 @@ sessions2.get("/:id/todos", async (c) => {
5055
5183
  } : null
5056
5184
  });
5057
5185
  });
5058
- sessions2.get("/:id/checkpoints", async (c) => {
5186
+ sessions.get("/:id/checkpoints", async (c) => {
5059
5187
  const id = c.req.param("id");
5060
- const session = sessionQueries.getById(id);
5188
+ const session = await sessionQueries.getById(id);
5061
5189
  if (!session) {
5062
5190
  return c.json({ error: "Session not found" }, 404);
5063
5191
  }
5064
- const checkpoints2 = getCheckpoints(id);
5192
+ const checkpoints = await getCheckpoints(id);
5065
5193
  return c.json({
5066
5194
  sessionId: id,
5067
- checkpoints: checkpoints2.map((cp) => ({
5195
+ checkpoints: checkpoints.map((cp) => ({
5068
5196
  id: cp.id,
5069
5197
  messageSequence: cp.messageSequence,
5070
5198
  gitHead: cp.gitHead,
5071
5199
  createdAt: cp.createdAt.toISOString()
5072
5200
  })),
5073
- count: checkpoints2.length
5201
+ count: checkpoints.length
5074
5202
  });
5075
5203
  });
5076
- sessions2.post("/:id/revert/:checkpointId", async (c) => {
5204
+ sessions.post("/:id/revert/:checkpointId", async (c) => {
5077
5205
  const sessionId = c.req.param("id");
5078
5206
  const checkpointId = c.req.param("checkpointId");
5079
- const session = sessionQueries.getById(sessionId);
5207
+ const session = await sessionQueries.getById(sessionId);
5080
5208
  if (!session) {
5081
5209
  return c.json({ error: "Session not found" }, 404);
5082
5210
  }
5083
- const activeStream = activeStreamQueries.getBySessionId(sessionId);
5211
+ const activeStream = await activeStreamQueries.getBySessionId(sessionId);
5084
5212
  if (activeStream) {
5085
5213
  return c.json({
5086
5214
  error: "Cannot revert while a stream is active. Stop the stream first.",
@@ -5101,9 +5229,9 @@ sessions2.post("/:id/revert/:checkpointId", async (c) => {
5101
5229
  checkpointsDeleted: result.checkpointsDeleted
5102
5230
  });
5103
5231
  });
5104
- sessions2.get("/:id/diff", async (c) => {
5232
+ sessions.get("/:id/diff", async (c) => {
5105
5233
  const id = c.req.param("id");
5106
- const session = sessionQueries.getById(id);
5234
+ const session = await sessionQueries.getById(id);
5107
5235
  if (!session) {
5108
5236
  return c.json({ error: "Session not found" }, 404);
5109
5237
  }
@@ -5127,10 +5255,10 @@ sessions2.get("/:id/diff", async (c) => {
5127
5255
  }
5128
5256
  });
5129
5257
  });
5130
- sessions2.get("/:id/diff/:filePath", async (c) => {
5258
+ sessions.get("/:id/diff/:filePath", async (c) => {
5131
5259
  const sessionId = c.req.param("id");
5132
5260
  const filePath = decodeURIComponent(c.req.param("filePath"));
5133
- const session = sessionQueries.getById(sessionId);
5261
+ const session = await sessionQueries.getById(sessionId);
5134
5262
  if (!session) {
5135
5263
  return c.json({ error: "Session not found" }, 404);
5136
5264
  }
@@ -5149,29 +5277,29 @@ sessions2.get("/:id/diff/:filePath", async (c) => {
5149
5277
  });
5150
5278
  function getAttachmentsDir(sessionId) {
5151
5279
  const appDataDir = getAppDataDirectory();
5152
- return join4(appDataDir, "attachments", sessionId);
5280
+ return join6(appDataDir, "attachments", sessionId);
5153
5281
  }
5154
5282
  function ensureAttachmentsDir(sessionId) {
5155
5283
  const dir = getAttachmentsDir(sessionId);
5156
- if (!existsSync11(dir)) {
5284
+ if (!existsSync12(dir)) {
5157
5285
  mkdirSync3(dir, { recursive: true });
5158
5286
  }
5159
5287
  return dir;
5160
5288
  }
5161
- sessions2.get("/:id/attachments", async (c) => {
5289
+ sessions.get("/:id/attachments", async (c) => {
5162
5290
  const sessionId = c.req.param("id");
5163
- const session = sessionQueries.getById(sessionId);
5291
+ const session = await sessionQueries.getById(sessionId);
5164
5292
  if (!session) {
5165
5293
  return c.json({ error: "Session not found" }, 404);
5166
5294
  }
5167
5295
  const dir = getAttachmentsDir(sessionId);
5168
- if (!existsSync11(dir)) {
5296
+ if (!existsSync12(dir)) {
5169
5297
  return c.json({ sessionId, attachments: [], count: 0 });
5170
5298
  }
5171
5299
  const files = readdirSync(dir);
5172
5300
  const attachments = files.map((filename) => {
5173
- const filePath = join4(dir, filename);
5174
- const stats = statSync(filePath);
5301
+ const filePath = join6(dir, filename);
5302
+ const stats = statSync2(filePath);
5175
5303
  return {
5176
5304
  id: filename.split("_")[0],
5177
5305
  // Extract the nanoid prefix
@@ -5187,9 +5315,9 @@ sessions2.get("/:id/attachments", async (c) => {
5187
5315
  count: attachments.length
5188
5316
  });
5189
5317
  });
5190
- sessions2.post("/:id/attachments", async (c) => {
5318
+ sessions.post("/:id/attachments", async (c) => {
5191
5319
  const sessionId = c.req.param("id");
5192
- const session = sessionQueries.getById(sessionId);
5320
+ const session = await sessionQueries.getById(sessionId);
5193
5321
  if (!session) {
5194
5322
  return c.json({ error: "Session not found" }, 404);
5195
5323
  }
@@ -5202,10 +5330,10 @@ sessions2.post("/:id/attachments", async (c) => {
5202
5330
  return c.json({ error: "No file provided" }, 400);
5203
5331
  }
5204
5332
  const dir = ensureAttachmentsDir(sessionId);
5205
- const id = nanoid5(10);
5206
- const ext = extname6(file.name) || "";
5207
- const safeFilename = `${id}_${basename2(file.name).replace(/[^a-zA-Z0-9._-]/g, "_")}`;
5208
- const filePath = join4(dir, safeFilename);
5333
+ const id = nanoid4(10);
5334
+ const ext = extname7(file.name) || "";
5335
+ const safeFilename = `${id}_${basename3(file.name).replace(/[^a-zA-Z0-9._-]/g, "_")}`;
5336
+ const filePath = join6(dir, safeFilename);
5209
5337
  const arrayBuffer = await file.arrayBuffer();
5210
5338
  writeFileSync2(filePath, Buffer.from(arrayBuffer));
5211
5339
  return c.json({
@@ -5228,10 +5356,10 @@ sessions2.post("/:id/attachments", async (c) => {
5228
5356
  return c.json({ error: "Missing filename or data" }, 400);
5229
5357
  }
5230
5358
  const dir = ensureAttachmentsDir(sessionId);
5231
- const id = nanoid5(10);
5232
- const ext = extname6(body.filename) || "";
5233
- const safeFilename = `${id}_${basename2(body.filename).replace(/[^a-zA-Z0-9._-]/g, "_")}`;
5234
- const filePath = join4(dir, safeFilename);
5359
+ const id = nanoid4(10);
5360
+ const ext = extname7(body.filename) || "";
5361
+ const safeFilename = `${id}_${basename3(body.filename).replace(/[^a-zA-Z0-9._-]/g, "_")}`;
5362
+ const filePath = join6(dir, safeFilename);
5235
5363
  let base64Data = body.data;
5236
5364
  if (base64Data.includes(",")) {
5237
5365
  base64Data = base64Data.split(",")[1];
@@ -5252,15 +5380,15 @@ sessions2.post("/:id/attachments", async (c) => {
5252
5380
  return c.json({ error: "Failed to upload file" }, 500);
5253
5381
  }
5254
5382
  });
5255
- sessions2.delete("/:id/attachments/:attachmentId", async (c) => {
5383
+ sessions.delete("/:id/attachments/:attachmentId", async (c) => {
5256
5384
  const sessionId = c.req.param("id");
5257
5385
  const attachmentId = c.req.param("attachmentId");
5258
- const session = sessionQueries.getById(sessionId);
5386
+ const session = await sessionQueries.getById(sessionId);
5259
5387
  if (!session) {
5260
5388
  return c.json({ error: "Session not found" }, 404);
5261
5389
  }
5262
5390
  const dir = getAttachmentsDir(sessionId);
5263
- if (!existsSync11(dir)) {
5391
+ if (!existsSync12(dir)) {
5264
5392
  return c.json({ error: "Attachment not found" }, 404);
5265
5393
  }
5266
5394
  const files = readdirSync(dir);
@@ -5268,14 +5396,14 @@ sessions2.delete("/:id/attachments/:attachmentId", async (c) => {
5268
5396
  if (!file) {
5269
5397
  return c.json({ error: "Attachment not found" }, 404);
5270
5398
  }
5271
- const filePath = join4(dir, file);
5399
+ const filePath = join6(dir, file);
5272
5400
  unlinkSync(filePath);
5273
5401
  return c.json({ success: true, id: attachmentId });
5274
5402
  });
5275
- var filesQuerySchema = z11.object({
5276
- query: z11.string().optional(),
5403
+ var filesQuerySchema = z12.object({
5404
+ query: z12.string().optional(),
5277
5405
  // Filter query (e.g., "src/com" to match "src/components")
5278
- limit: z11.string().optional()
5406
+ limit: z12.string().optional()
5279
5407
  // Max results (default 50)
5280
5408
  });
5281
5409
  var IGNORED_DIRECTORIES = /* @__PURE__ */ new Set([
@@ -5351,15 +5479,15 @@ async function listWorkspaceFiles(baseDir, currentDir, query, limit, results = [
5351
5479
  const entries = await readdir4(currentDir, { withFileTypes: true });
5352
5480
  for (const entry of entries) {
5353
5481
  if (results.length >= limit * 2) break;
5354
- const fullPath = join4(currentDir, entry.name);
5355
- const relativePath = relative7(baseDir, fullPath);
5482
+ const fullPath = join6(currentDir, entry.name);
5483
+ const relativePath = relative8(baseDir, fullPath);
5356
5484
  if (entry.isDirectory() && IGNORED_DIRECTORIES.has(entry.name)) {
5357
5485
  continue;
5358
5486
  }
5359
5487
  if (entry.name.startsWith(".")) {
5360
5488
  continue;
5361
5489
  }
5362
- const ext = extname6(entry.name).toLowerCase();
5490
+ const ext = extname7(entry.name).toLowerCase();
5363
5491
  if (IGNORED_EXTENSIONS.has(ext)) {
5364
5492
  continue;
5365
5493
  }
@@ -5387,19 +5515,19 @@ async function listWorkspaceFiles(baseDir, currentDir, query, limit, results = [
5387
5515
  }
5388
5516
  return results;
5389
5517
  }
5390
- sessions2.get(
5518
+ sessions.get(
5391
5519
  "/:id/files",
5392
5520
  zValidator("query", filesQuerySchema),
5393
5521
  async (c) => {
5394
5522
  const sessionId = c.req.param("id");
5395
5523
  const { query = "", limit: limitStr = "50" } = c.req.valid("query");
5396
5524
  const limit = Math.min(parseInt(limitStr) || 50, 100);
5397
- const session = sessionQueries.getById(sessionId);
5525
+ const session = await sessionQueries.getById(sessionId);
5398
5526
  if (!session) {
5399
5527
  return c.json({ error: "Session not found" }, 404);
5400
5528
  }
5401
5529
  const workingDirectory = session.workingDirectory;
5402
- if (!existsSync11(workingDirectory)) {
5530
+ if (!existsSync12(workingDirectory)) {
5403
5531
  return c.json({
5404
5532
  sessionId,
5405
5533
  workingDirectory,
@@ -5452,9 +5580,9 @@ sessions2.get(
5452
5580
  // src/server/routes/agents.ts
5453
5581
  import { Hono as Hono2 } from "hono";
5454
5582
  import { zValidator as zValidator2 } from "@hono/zod-validator";
5455
- import { z as z12 } from "zod";
5456
- import { existsSync as existsSync12, mkdirSync as mkdirSync4, writeFileSync as writeFileSync3 } from "fs";
5457
- import { join as join5 } from "path";
5583
+ import { z as z13 } from "zod";
5584
+ import { existsSync as existsSync13, mkdirSync as mkdirSync4, writeFileSync as writeFileSync3 } from "fs";
5585
+ import { join as join7 } from "path";
5458
5586
 
5459
5587
  // src/server/resumable-stream.ts
5460
5588
  import { createResumableStreamContext } from "resumable-stream/generic";
@@ -5529,7 +5657,7 @@ var streamContext = createResumableStreamContext({
5529
5657
  });
5530
5658
 
5531
5659
  // src/server/routes/agents.ts
5532
- import { nanoid as nanoid6 } from "nanoid";
5660
+ import { nanoid as nanoid5 } from "nanoid";
5533
5661
  var MAX_TOOL_INPUT_LENGTH = 8 * 1024;
5534
5662
  var MAX_TOOL_INPUT_PREVIEW = 2 * 1024;
5535
5663
  var MAX_TOOL_ARGS_CHUNK = 2 * 1024;
@@ -5597,39 +5725,39 @@ async function emitSyntheticToolStreaming(writeSSE, toolCallStarts, toolCallId,
5597
5725
  }
5598
5726
  }
5599
5727
  var agents = new Hono2();
5600
- var attachmentSchema = z12.object({
5601
- type: z12.enum(["image", "file"]),
5602
- data: z12.string(),
5728
+ var attachmentSchema = z13.object({
5729
+ type: z13.enum(["image", "file"]),
5730
+ data: z13.string(),
5603
5731
  // base64 data URL or raw base64
5604
- mediaType: z12.string().optional(),
5605
- filename: z12.string().optional()
5732
+ mediaType: z13.string().optional(),
5733
+ filename: z13.string().optional()
5606
5734
  });
5607
- var runPromptSchema = z12.object({
5608
- prompt: z12.string(),
5735
+ var runPromptSchema = z13.object({
5736
+ prompt: z13.string(),
5609
5737
  // Can be empty if attachments are provided
5610
- attachments: z12.array(attachmentSchema).optional()
5738
+ attachments: z13.array(attachmentSchema).optional()
5611
5739
  }).refine(
5612
5740
  (data) => data.prompt.trim().length > 0 || data.attachments && data.attachments.length > 0,
5613
5741
  { message: "Either prompt or attachments must be provided" }
5614
5742
  );
5615
- var quickStartSchema = z12.object({
5616
- prompt: z12.string().min(1),
5617
- name: z12.string().optional(),
5618
- workingDirectory: z12.string().optional(),
5619
- model: z12.string().optional(),
5620
- toolApprovals: z12.record(z12.string(), z12.boolean()).optional()
5743
+ var quickStartSchema = z13.object({
5744
+ prompt: z13.string().min(1),
5745
+ name: z13.string().optional(),
5746
+ workingDirectory: z13.string().optional(),
5747
+ model: z13.string().optional(),
5748
+ toolApprovals: z13.record(z13.string(), z13.boolean()).optional()
5621
5749
  });
5622
- var rejectSchema = z12.object({
5623
- reason: z12.string().optional()
5750
+ var rejectSchema = z13.object({
5751
+ reason: z13.string().optional()
5624
5752
  }).optional();
5625
5753
  var streamAbortControllers = /* @__PURE__ */ new Map();
5626
5754
  function getAttachmentsDirectory(sessionId) {
5627
5755
  const appDataDir = getAppDataDirectory();
5628
- return join5(appDataDir, "attachments", sessionId);
5756
+ return join7(appDataDir, "attachments", sessionId);
5629
5757
  }
5630
5758
  function saveAttachmentToDisk(sessionId, attachment, index) {
5631
5759
  const attachmentsDir = getAttachmentsDirectory(sessionId);
5632
- if (!existsSync12(attachmentsDir)) {
5760
+ if (!existsSync13(attachmentsDir)) {
5633
5761
  mkdirSync4(attachmentsDir, { recursive: true });
5634
5762
  }
5635
5763
  let filename = attachment.filename;
@@ -5641,7 +5769,7 @@ function saveAttachmentToDisk(sessionId, attachment, index) {
5641
5769
  if (base64Data.includes(",")) {
5642
5770
  base64Data = base64Data.split(",")[1];
5643
5771
  }
5644
- const filePath = join5(attachmentsDir, filename);
5772
+ const filePath = join7(attachmentsDir, filename);
5645
5773
  const buffer = Buffer.from(base64Data, "base64");
5646
5774
  writeFileSync3(filePath, buffer);
5647
5775
  return filePath;
@@ -5903,7 +6031,7 @@ ${prompt}` });
5903
6031
  await writeSSE(JSON.stringify({ type: "finish" }));
5904
6032
  }
5905
6033
  try {
5906
- activeStreamQueries.finish(streamId);
6034
+ await activeStreamQueries.finish(streamId);
5907
6035
  } catch {
5908
6036
  }
5909
6037
  } catch (error) {
@@ -5913,7 +6041,7 @@ ${prompt}` });
5913
6041
  console.error("Agent error:", error);
5914
6042
  await writeSSE(JSON.stringify({ type: "error", errorText: error.message }));
5915
6043
  try {
5916
- activeStreamQueries.markError(streamId);
6044
+ await activeStreamQueries.markError(streamId);
5917
6045
  } catch {
5918
6046
  }
5919
6047
  }
@@ -5932,11 +6060,11 @@ agents.post(
5932
6060
  async (c) => {
5933
6061
  const id = c.req.param("id");
5934
6062
  const { prompt, attachments } = c.req.valid("json");
5935
- const session = sessionQueries.getById(id);
6063
+ const session = await sessionQueries.getById(id);
5936
6064
  if (!session) {
5937
6065
  return c.json({ error: "Session not found" }, 404);
5938
6066
  }
5939
- const nextSequence = messageQueries.getNextSequence(id);
6067
+ const nextSequence = await messageQueries.getNextSequence(id);
5940
6068
  await createCheckpoint(id, session.workingDirectory, nextSequence);
5941
6069
  let userMessageContent;
5942
6070
  const streamAttachments = attachments;
@@ -5993,9 +6121,9 @@ ${prompt}` });
5993
6121
  } else {
5994
6122
  userMessageContent = prompt;
5995
6123
  }
5996
- messageQueries.create(id, { role: "user", content: userMessageContent });
5997
- const streamId = `stream_${id}_${nanoid6(10)}`;
5998
- activeStreamQueries.create(id, streamId);
6124
+ await messageQueries.create(id, { role: "user", content: userMessageContent });
6125
+ const streamId = `stream_${id}_${nanoid5(10)}`;
6126
+ await activeStreamQueries.create(id, streamId);
5999
6127
  const stream = await streamContext.resumableStream(
6000
6128
  streamId,
6001
6129
  createAgentStreamProducer(id, prompt, streamId, streamAttachments)
@@ -6019,13 +6147,13 @@ agents.get("/:id/watch", async (c) => {
6019
6147
  const sessionId = c.req.param("id");
6020
6148
  const resumeAt = c.req.query("resumeAt");
6021
6149
  const explicitStreamId = c.req.query("streamId");
6022
- const session = sessionQueries.getById(sessionId);
6150
+ const session = await sessionQueries.getById(sessionId);
6023
6151
  if (!session) {
6024
6152
  return c.json({ error: "Session not found" }, 404);
6025
6153
  }
6026
6154
  let streamId = explicitStreamId;
6027
6155
  if (!streamId) {
6028
- const activeStream = activeStreamQueries.getBySessionId(sessionId);
6156
+ const activeStream = await activeStreamQueries.getBySessionId(sessionId);
6029
6157
  if (!activeStream) {
6030
6158
  return c.json({ error: "No active stream for this session", hint: "Start a new run with POST /agents/:id/run" }, 404);
6031
6159
  }
@@ -6055,11 +6183,11 @@ agents.get("/:id/watch", async (c) => {
6055
6183
  });
6056
6184
  agents.get("/:id/stream", async (c) => {
6057
6185
  const sessionId = c.req.param("id");
6058
- const session = sessionQueries.getById(sessionId);
6186
+ const session = await sessionQueries.getById(sessionId);
6059
6187
  if (!session) {
6060
6188
  return c.json({ error: "Session not found" }, 404);
6061
6189
  }
6062
- const activeStream = activeStreamQueries.getBySessionId(sessionId);
6190
+ const activeStream = await activeStreamQueries.getBySessionId(sessionId);
6063
6191
  return c.json({
6064
6192
  sessionId,
6065
6193
  hasActiveStream: !!activeStream,
@@ -6077,7 +6205,7 @@ agents.post(
6077
6205
  async (c) => {
6078
6206
  const id = c.req.param("id");
6079
6207
  const { prompt } = c.req.valid("json");
6080
- const session = sessionQueries.getById(id);
6208
+ const session = await sessionQueries.getById(id);
6081
6209
  if (!session) {
6082
6210
  return c.json({ error: "Session not found" }, 404);
6083
6211
  }
@@ -6097,7 +6225,7 @@ agents.post(
6097
6225
  agents.post("/:id/approve/:toolCallId", async (c) => {
6098
6226
  const sessionId = c.req.param("id");
6099
6227
  const toolCallId = c.req.param("toolCallId");
6100
- const session = sessionQueries.getById(sessionId);
6228
+ const session = await sessionQueries.getById(sessionId);
6101
6229
  if (!session) {
6102
6230
  return c.json({ error: "Session not found" }, 404);
6103
6231
  }
@@ -6120,7 +6248,7 @@ agents.post(
6120
6248
  const sessionId = c.req.param("id");
6121
6249
  const toolCallId = c.req.param("toolCallId");
6122
6250
  const body = c.req.valid("json");
6123
- const session = sessionQueries.getById(sessionId);
6251
+ const session = await sessionQueries.getById(sessionId);
6124
6252
  if (!session) {
6125
6253
  return c.json({ error: "Session not found" }, 404);
6126
6254
  }
@@ -6139,11 +6267,11 @@ agents.post(
6139
6267
  );
6140
6268
  agents.get("/:id/approvals", async (c) => {
6141
6269
  const sessionId = c.req.param("id");
6142
- const session = sessionQueries.getById(sessionId);
6270
+ const session = await sessionQueries.getById(sessionId);
6143
6271
  if (!session) {
6144
6272
  return c.json({ error: "Session not found" }, 404);
6145
6273
  }
6146
- const pendingApprovals = toolExecutionQueries.getPendingApprovals(sessionId);
6274
+ const pendingApprovals = await toolExecutionQueries.getPendingApprovals(sessionId);
6147
6275
  return c.json({
6148
6276
  sessionId,
6149
6277
  pendingApprovals: pendingApprovals.map((p) => ({
@@ -6158,11 +6286,11 @@ agents.get("/:id/approvals", async (c) => {
6158
6286
  });
6159
6287
  agents.post("/:id/abort", async (c) => {
6160
6288
  const sessionId = c.req.param("id");
6161
- const session = sessionQueries.getById(sessionId);
6289
+ const session = await sessionQueries.getById(sessionId);
6162
6290
  if (!session) {
6163
6291
  return c.json({ error: "Session not found" }, 404);
6164
6292
  }
6165
- const activeStream = activeStreamQueries.getBySessionId(sessionId);
6293
+ const activeStream = await activeStreamQueries.getBySessionId(sessionId);
6166
6294
  if (!activeStream) {
6167
6295
  return c.json({ error: "No active stream for this session" }, 404);
6168
6296
  }
@@ -6191,9 +6319,9 @@ agents.post(
6191
6319
  sessionConfig: body.toolApprovals ? { toolApprovals: body.toolApprovals } : void 0
6192
6320
  });
6193
6321
  const session = agent.getSession();
6194
- const streamId = `stream_${session.id}_${nanoid6(10)}`;
6322
+ const streamId = `stream_${session.id}_${nanoid5(10)}`;
6195
6323
  await createCheckpoint(session.id, session.workingDirectory, 0);
6196
- activeStreamQueries.create(session.id, streamId);
6324
+ await activeStreamQueries.create(session.id, streamId);
6197
6325
  const createQuickStreamProducer = () => {
6198
6326
  const { readable, writable } = new TransformStream();
6199
6327
  const writer = writable.getWriter();
@@ -6363,14 +6491,14 @@ agents.post(
6363
6491
  } else {
6364
6492
  await writeSSE(JSON.stringify({ type: "finish" }));
6365
6493
  }
6366
- activeStreamQueries.finish(streamId);
6494
+ await activeStreamQueries.finish(streamId);
6367
6495
  } catch (error) {
6368
6496
  if (error.name === "AbortError" || error.message?.includes("aborted")) {
6369
6497
  await writeSSE(JSON.stringify({ type: "abort" }));
6370
6498
  } else {
6371
6499
  console.error("Agent error:", error);
6372
6500
  await writeSSE(JSON.stringify({ type: "error", errorText: error.message }));
6373
- activeStreamQueries.markError(streamId);
6501
+ await activeStreamQueries.markError(streamId);
6374
6502
  }
6375
6503
  } finally {
6376
6504
  cleanupAbortController();
@@ -6404,23 +6532,23 @@ agents.post(
6404
6532
  // src/server/routes/health.ts
6405
6533
  import { Hono as Hono3 } from "hono";
6406
6534
  import { zValidator as zValidator3 } from "@hono/zod-validator";
6407
- import { z as z13 } from "zod";
6408
- import { readFileSync as readFileSync3 } from "fs";
6535
+ import { z as z14 } from "zod";
6536
+ import { readFileSync as readFileSync5 } from "fs";
6409
6537
  import { fileURLToPath as fileURLToPath2 } from "url";
6410
- import { dirname as dirname6, join as join6 } from "path";
6538
+ import { dirname as dirname6, join as join8 } from "path";
6411
6539
  var __filename = fileURLToPath2(import.meta.url);
6412
6540
  var __dirname = dirname6(__filename);
6413
6541
  var possiblePaths = [
6414
- join6(__dirname, "../../package.json"),
6542
+ join8(__dirname, "../../package.json"),
6415
6543
  // From dist/server (bundled)
6416
- join6(__dirname, "../../../package.json")
6544
+ join8(__dirname, "../../../package.json")
6417
6545
  // From dist/server/routes or src/server/routes
6418
6546
  ];
6419
6547
  var currentVersion = "0.0.0";
6420
6548
  var packageName = "sparkecoder";
6421
6549
  for (const packageJsonPath of possiblePaths) {
6422
6550
  try {
6423
- const packageJson = JSON.parse(readFileSync3(packageJsonPath, "utf-8"));
6551
+ const packageJson = JSON.parse(readFileSync5(packageJsonPath, "utf-8"));
6424
6552
  if (packageJson.name === "sparkecoder" || packageJson.name?.startsWith("sparkecoder")) {
6425
6553
  currentVersion = packageJson.version || "0.0.0";
6426
6554
  packageName = packageJson.name || "sparkecoder";
@@ -6510,9 +6638,9 @@ health.get("/api-keys", async (c) => {
6510
6638
  supportedProviders: SUPPORTED_PROVIDERS
6511
6639
  });
6512
6640
  });
6513
- var setApiKeySchema = z13.object({
6514
- provider: z13.string(),
6515
- apiKey: z13.string().min(1)
6641
+ var setApiKeySchema = z14.object({
6642
+ provider: z14.string(),
6643
+ apiKey: z14.string().min(1)
6516
6644
  });
6517
6645
  health.post(
6518
6646
  "/api-keys",
@@ -6551,20 +6679,20 @@ health.delete("/api-keys/:provider", async (c) => {
6551
6679
  // src/server/routes/terminals.ts
6552
6680
  import { Hono as Hono4 } from "hono";
6553
6681
  import { zValidator as zValidator4 } from "@hono/zod-validator";
6554
- import { z as z14 } from "zod";
6555
- var terminals2 = new Hono4();
6556
- var spawnSchema = z14.object({
6557
- command: z14.string(),
6558
- cwd: z14.string().optional(),
6559
- name: z14.string().optional()
6682
+ import { z as z15 } from "zod";
6683
+ var terminals = new Hono4();
6684
+ var spawnSchema = z15.object({
6685
+ command: z15.string(),
6686
+ cwd: z15.string().optional(),
6687
+ name: z15.string().optional()
6560
6688
  });
6561
- terminals2.post(
6689
+ terminals.post(
6562
6690
  "/:sessionId/terminals",
6563
6691
  zValidator4("json", spawnSchema),
6564
6692
  async (c) => {
6565
6693
  const sessionId = c.req.param("sessionId");
6566
6694
  const body = c.req.valid("json");
6567
- const session = sessionQueries.getById(sessionId);
6695
+ const session = await sessionQueries.getById(sessionId);
6568
6696
  if (!session) {
6569
6697
  return c.json({ error: "Session not found" }, 404);
6570
6698
  }
@@ -6588,9 +6716,9 @@ terminals2.post(
6588
6716
  }, 201);
6589
6717
  }
6590
6718
  );
6591
- terminals2.get("/:sessionId/terminals", async (c) => {
6719
+ terminals.get("/:sessionId/terminals", async (c) => {
6592
6720
  const sessionId = c.req.param("sessionId");
6593
- const session = sessionQueries.getById(sessionId);
6721
+ const session = await sessionQueries.getById(sessionId);
6594
6722
  if (!session) {
6595
6723
  return c.json({ error: "Session not found" }, 404);
6596
6724
  }
@@ -6615,10 +6743,10 @@ terminals2.get("/:sessionId/terminals", async (c) => {
6615
6743
  running: terminalList.filter((t) => t.status === "running").length
6616
6744
  });
6617
6745
  });
6618
- terminals2.get("/:sessionId/terminals/:terminalId", async (c) => {
6746
+ terminals.get("/:sessionId/terminals/:terminalId", async (c) => {
6619
6747
  const sessionId = c.req.param("sessionId");
6620
6748
  const terminalId = c.req.param("terminalId");
6621
- const session = sessionQueries.getById(sessionId);
6749
+ const session = await sessionQueries.getById(sessionId);
6622
6750
  if (!session) {
6623
6751
  return c.json({ error: "Session not found" }, 404);
6624
6752
  }
@@ -6637,17 +6765,17 @@ terminals2.get("/:sessionId/terminals/:terminalId", async (c) => {
6637
6765
  // We don't track exit codes in tmux mode
6638
6766
  });
6639
6767
  });
6640
- var logsQuerySchema = z14.object({
6641
- tail: z14.string().optional().transform((v) => v ? parseInt(v, 10) : void 0)
6768
+ var logsQuerySchema = z15.object({
6769
+ tail: z15.string().optional().transform((v) => v ? parseInt(v, 10) : void 0)
6642
6770
  });
6643
- terminals2.get(
6771
+ terminals.get(
6644
6772
  "/:sessionId/terminals/:terminalId/logs",
6645
6773
  zValidator4("query", logsQuerySchema),
6646
6774
  async (c) => {
6647
6775
  const sessionId = c.req.param("sessionId");
6648
6776
  const terminalId = c.req.param("terminalId");
6649
6777
  const query = c.req.valid("query");
6650
- const session = sessionQueries.getById(sessionId);
6778
+ const session = await sessionQueries.getById(sessionId);
6651
6779
  if (!session) {
6652
6780
  return c.json({ error: "Session not found" }, 404);
6653
6781
  }
@@ -6662,10 +6790,10 @@ terminals2.get(
6662
6790
  });
6663
6791
  }
6664
6792
  );
6665
- var killSchema = z14.object({
6666
- signal: z14.enum(["SIGTERM", "SIGKILL"]).optional()
6793
+ var killSchema = z15.object({
6794
+ signal: z15.enum(["SIGTERM", "SIGKILL"]).optional()
6667
6795
  });
6668
- terminals2.post(
6796
+ terminals.post(
6669
6797
  "/:sessionId/terminals/:terminalId/kill",
6670
6798
  zValidator4("json", killSchema.optional()),
6671
6799
  async (c) => {
@@ -6677,10 +6805,10 @@ terminals2.post(
6677
6805
  return c.json({ success: true, message: "Terminal killed" });
6678
6806
  }
6679
6807
  );
6680
- var writeSchema = z14.object({
6681
- input: z14.string()
6808
+ var writeSchema = z15.object({
6809
+ input: z15.string()
6682
6810
  });
6683
- terminals2.post(
6811
+ terminals.post(
6684
6812
  "/:sessionId/terminals/:terminalId/write",
6685
6813
  zValidator4("json", writeSchema),
6686
6814
  async (c) => {
@@ -6697,9 +6825,9 @@ terminals2.post(
6697
6825
  return c.json({ success: true, written: body.input.length });
6698
6826
  }
6699
6827
  );
6700
- terminals2.post("/:sessionId/terminals/kill-all", async (c) => {
6828
+ terminals.post("/:sessionId/terminals/kill-all", async (c) => {
6701
6829
  const sessionId = c.req.param("sessionId");
6702
- const session = sessionQueries.getById(sessionId);
6830
+ const session = await sessionQueries.getById(sessionId);
6703
6831
  if (!session) {
6704
6832
  return c.json({ error: "Session not found" }, 404);
6705
6833
  }
@@ -6714,13 +6842,13 @@ terminals2.post("/:sessionId/terminals/kill-all", async (c) => {
6714
6842
  }
6715
6843
  return c.json({ success: true, killed });
6716
6844
  });
6717
- terminals2.get("/stream/:terminalId", async (c) => {
6845
+ terminals.get("/stream/:terminalId", async (c) => {
6718
6846
  const terminalId = c.req.param("terminalId");
6719
- const sessions3 = sessionQueries.list();
6847
+ const sessions2 = await sessionQueries.list();
6720
6848
  let terminalMeta = null;
6721
6849
  let workingDirectory = process.cwd();
6722
6850
  let foundSessionId;
6723
- for (const session of sessions3) {
6851
+ for (const session of sessions2) {
6724
6852
  terminalMeta = await getMeta(terminalId, session.workingDirectory, session.id);
6725
6853
  if (terminalMeta) {
6726
6854
  workingDirectory = session.workingDirectory;
@@ -6729,7 +6857,7 @@ terminals2.get("/stream/:terminalId", async (c) => {
6729
6857
  }
6730
6858
  }
6731
6859
  if (!terminalMeta) {
6732
- for (const session of sessions3) {
6860
+ for (const session of sessions2) {
6733
6861
  terminalMeta = await getMeta(terminalId, session.workingDirectory);
6734
6862
  if (terminalMeta) {
6735
6863
  workingDirectory = session.workingDirectory;
@@ -6799,10 +6927,10 @@ data: ${JSON.stringify({ status: "stopped" })}
6799
6927
  }
6800
6928
  );
6801
6929
  });
6802
- terminals2.get("/:sessionId/terminals/:terminalId/stream", async (c) => {
6930
+ terminals.get("/:sessionId/terminals/:terminalId/stream", async (c) => {
6803
6931
  const sessionId = c.req.param("sessionId");
6804
6932
  const terminalId = c.req.param("terminalId");
6805
- const session = sessionQueries.getById(sessionId);
6933
+ const session = await sessionQueries.getById(sessionId);
6806
6934
  if (!session) {
6807
6935
  return c.json({ error: "Session not found" }, 404);
6808
6936
  }
@@ -6945,11 +7073,11 @@ function getWebDirectory() {
6945
7073
  try {
6946
7074
  const currentDir = dirname7(fileURLToPath3(import.meta.url));
6947
7075
  const webDir = resolve9(currentDir, "..", "web");
6948
- if (existsSync13(webDir) && existsSync13(join7(webDir, "package.json"))) {
7076
+ if (existsSync14(webDir) && existsSync14(join9(webDir, "package.json"))) {
6949
7077
  return webDir;
6950
7078
  }
6951
7079
  const altWebDir = resolve9(currentDir, "..", "..", "web");
6952
- if (existsSync13(altWebDir) && existsSync13(join7(altWebDir, "package.json"))) {
7080
+ if (existsSync14(altWebDir) && existsSync14(join9(altWebDir, "package.json"))) {
6953
7081
  return altWebDir;
6954
7082
  }
6955
7083
  return null;
@@ -7007,23 +7135,23 @@ async function findWebPort(preferredPort) {
7007
7135
  return { port: preferredPort, alreadyRunning: false };
7008
7136
  }
7009
7137
  function hasProductionBuild(webDir) {
7010
- const buildIdPath = join7(webDir, ".next", "BUILD_ID");
7011
- return existsSync13(buildIdPath);
7138
+ const buildIdPath = join9(webDir, ".next", "BUILD_ID");
7139
+ return existsSync14(buildIdPath);
7012
7140
  }
7013
7141
  function hasSourceFiles(webDir) {
7014
- const appDir = join7(webDir, "src", "app");
7015
- const pagesDir = join7(webDir, "src", "pages");
7016
- const rootAppDir = join7(webDir, "app");
7017
- const rootPagesDir = join7(webDir, "pages");
7018
- return existsSync13(appDir) || existsSync13(pagesDir) || existsSync13(rootAppDir) || existsSync13(rootPagesDir);
7142
+ const appDir = join9(webDir, "src", "app");
7143
+ const pagesDir = join9(webDir, "src", "pages");
7144
+ const rootAppDir = join9(webDir, "app");
7145
+ const rootPagesDir = join9(webDir, "pages");
7146
+ return existsSync14(appDir) || existsSync14(pagesDir) || existsSync14(rootAppDir) || existsSync14(rootPagesDir);
7019
7147
  }
7020
7148
  function getStandaloneServerPath(webDir) {
7021
7149
  const possiblePaths2 = [
7022
- join7(webDir, ".next", "standalone", "server.js"),
7023
- join7(webDir, ".next", "standalone", "web", "server.js")
7150
+ join9(webDir, ".next", "standalone", "server.js"),
7151
+ join9(webDir, ".next", "standalone", "web", "server.js")
7024
7152
  ];
7025
7153
  for (const serverPath of possiblePaths2) {
7026
- if (existsSync13(serverPath)) {
7154
+ if (existsSync14(serverPath)) {
7027
7155
  return serverPath;
7028
7156
  }
7029
7157
  }
@@ -7063,13 +7191,13 @@ async function startWebUI(apiPort, webPort = DEFAULT_WEB_PORT, quiet = false, pu
7063
7191
  if (!quiet) console.log(` \u2713 Web UI already running at http://localhost:${actualPort}`);
7064
7192
  return { process: null, port: actualPort };
7065
7193
  }
7066
- const usePnpm = existsSync13(join7(webDir, "pnpm-lock.yaml"));
7067
- const useNpm = !usePnpm && existsSync13(join7(webDir, "package-lock.json"));
7194
+ const usePnpm = existsSync14(join9(webDir, "pnpm-lock.yaml"));
7195
+ const useNpm = !usePnpm && existsSync14(join9(webDir, "package-lock.json"));
7068
7196
  const pkgManager = usePnpm ? "pnpm" : useNpm ? "npm" : "npx";
7069
7197
  const { NODE_OPTIONS, TSX_TSCONFIG_PATH, ...cleanEnv } = process.env;
7070
7198
  const apiUrl = publicUrl || `http://127.0.0.1:${apiPort}`;
7071
7199
  const runtimeConfig = { apiBaseUrl: apiUrl };
7072
- const runtimeConfigPath = join7(webDir, "runtime-config.json");
7200
+ const runtimeConfigPath = join9(webDir, "runtime-config.json");
7073
7201
  try {
7074
7202
  writeFileSync4(runtimeConfigPath, JSON.stringify(runtimeConfig, null, 2));
7075
7203
  if (!quiet) console.log(` \u{1F4DD} Runtime config written to ${runtimeConfigPath}`);
@@ -7205,10 +7333,10 @@ async function createApp(options = {}) {
7205
7333
  app.use("*", logger());
7206
7334
  }
7207
7335
  app.route("/health", health);
7208
- app.route("/sessions", sessions2);
7336
+ app.route("/sessions", sessions);
7209
7337
  app.route("/agents", agents);
7210
- app.route("/sessions", terminals2);
7211
- app.route("/terminals", terminals2);
7338
+ app.route("/sessions", terminals);
7339
+ app.route("/terminals", terminals);
7212
7340
  app.get("/openapi.json", async (c) => {
7213
7341
  return c.json(generateOpenAPISpec());
7214
7342
  });
@@ -7261,11 +7389,21 @@ async function startServer(options = {}) {
7261
7389
  if (options.workingDirectory) {
7262
7390
  config.resolvedWorkingDirectory = options.workingDirectory;
7263
7391
  }
7264
- if (!existsSync13(config.resolvedWorkingDirectory)) {
7392
+ if (!existsSync14(config.resolvedWorkingDirectory)) {
7265
7393
  mkdirSync5(config.resolvedWorkingDirectory, { recursive: true });
7266
7394
  if (!options.quiet) console.log(`\u{1F4C1} Created agent workspace: ${config.resolvedWorkingDirectory}`);
7267
7395
  }
7268
- initDatabase(config.resolvedDatabasePath);
7396
+ if (!config.resolvedRemoteServer.url) {
7397
+ throw new Error("Remote server not configured. Set REMOTE_SERVER_URL environment variable or remoteServer.url in config");
7398
+ }
7399
+ let authKey3 = config.resolvedRemoteServer.authKey;
7400
+ if (!authKey3) {
7401
+ if (!options.quiet) console.log("\u{1F4E1} Registering with remote server...");
7402
+ authKey3 = await ensureRemoteAuthKey(config.resolvedRemoteServer.url);
7403
+ if (!options.quiet) console.log("\u2713 Registered with remote server");
7404
+ }
7405
+ initDatabase({ url: config.resolvedRemoteServer.url, authKey: authKey3 });
7406
+ if (!options.quiet) console.log(`\u{1F4E1} Using remote database: ${config.resolvedRemoteServer.url}`);
7269
7407
  const port = options.port || config.server.port;
7270
7408
  const host = options.host || config.server.host || "0.0.0.0";
7271
7409
  const publicUrl = options.publicUrl || config.server.publicUrl;
@@ -7298,8 +7436,8 @@ async function startServer(options = {}) {
7298
7436
  }
7299
7437
  function stopServer() {
7300
7438
  stopWebUI();
7301
- listSessions().then(async (sessions3) => {
7302
- for (const id of sessions3) {
7439
+ listSessions().then(async (sessions2) => {
7440
+ for (const id of sessions2) {
7303
7441
  await killTerminal(id);
7304
7442
  }
7305
7443
  }).catch(() => {