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