sparkecoder 0.1.60 → 0.1.61
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 +4 -3
- package/dist/agent/index.js +588 -176
- package/dist/agent/index.js.map +1 -1
- package/dist/cli.js +818 -95
- package/dist/cli.js.map +1 -1
- package/dist/db/index.d.ts +3 -2
- package/dist/db/index.js.map +1 -1
- package/dist/{index-Csad1Nx4.d.ts → index-DuGtMaAJ.d.ts} +85 -4
- package/dist/index.d.ts +6 -5
- package/dist/index.js +1202 -665
- package/dist/index.js.map +1 -1
- package/dist/schema-C7Mm4Ykn.d.ts +1410 -0
- package/dist/{search-BETuS1vh.d.ts → search-C_IFImt1.d.ts} +3 -3
- package/dist/server/index.js +605 -68
- package/dist/server/index.js.map +1 -1
- package/dist/tools/index.d.ts +38 -3
- package/dist/tools/index.js +230 -2
- package/dist/tools/index.js.map +1 -1
- package/package.json +5 -3
- 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/(main)/page.js.nft.json +1 -1
- package/web/.next/standalone/web/.next/server/app/(main)/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/app/(main)/session/[id]/page.js.nft.json +1 -1
- package/web/.next/standalone/web/.next/server/app/(main)/session/[id]/page_client-reference-manifest.js +1 -1
- 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/page_client-reference-manifest.js +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 +2 -2
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
- 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 +2 -2
- 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 +2 -2
- package/web/.next/standalone/web/.next/server/app/api/config/route.js.nft.json +1 -1
- package/web/.next/standalone/web/.next/server/app/api/health/route.js.nft.json +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/installation/page_client-reference-manifest.js +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 +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_full.segment.rsc +2 -2
- 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 +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_tree.segment.rsc +2 -2
- 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/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/skills/page_client-reference-manifest.js +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 +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_full.segment.rsc +2 -2
- 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 +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_tree.segment.rsc +2 -2
- 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/page_client-reference-manifest.js +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 +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_full.segment.rsc +2 -2
- 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 +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_tree.segment.rsc +2 -2
- 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 +2 -2
- package/web/.next/standalone/web/.next/server/app/docs.segments/_full.segment.rsc +2 -2
- 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 +2 -2
- package/web/.next/standalone/web/.next/server/app/docs.segments/_tree.segment.rsc +2 -2
- 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/embed/[id]/page.js.nft.json +1 -1
- package/web/.next/standalone/web/.next/server/app/embed/[id]/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/app/index.html +1 -1
- package/web/.next/standalone/web/.next/server/app/index.rsc +4 -4
- package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p/__PAGE__.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/index.segments/_full.segment.rsc +4 -4
- 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 +2 -2
- package/web/.next/standalone/web/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_19289e11._.js → 2374f_03d22c16._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_b4b86c1f._.js → 2374f_0d0b1fb9._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_4858a1ea._.js → 2374f_3f7d6d28._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_51385fed._.js → 2374f_6166d14d._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_40e35a02._.js → 2374f_67b9525f._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_fb95e3c9._.js → 2374f_6b436efc._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_2f0d9f6f._.js → 2374f_784d851c._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_35475cbe._.js → 2374f_976b2ded._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_7db22cde._.js → 2374f_98eba491._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_90b8e4fb._.js → 2374f_99b7b533._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_4666c827._.js → 2374f_a1a36483._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_076f03ec._.js → 2374f_b98835eb._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_b17fce11._.js → 2374f_e19eaecc._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_d8b9ce38._.js → 2374f_e3aec189._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{[root-of-the-server]__7775f784._.js → [root-of-the-server]__4f176a16._.js} +2 -2
- package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__d59f831d._.js +15 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/{web_645f4b90._.js → web_693f514e._.js} +2 -2
- package/web/.next/standalone/web/.next/server/chunks/ssr/web_a42a2651._.js +7 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/web_dc6ce793._.js +7 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/web_src_components_sessions-sidebar_tsx_92510070._.js +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/web/.next/standalone/web/.next/static/chunks/0358a0e7a40cfb93.css +1 -0
- package/web/.next/standalone/web/.next/static/chunks/1ecde4c0d426a635.js +1 -0
- package/web/.next/standalone/web/.next/static/chunks/20e0fa99c7a1c1fc.js +13 -0
- package/web/.next/standalone/web/.next/static/chunks/36688a049d72e8ab.js +5 -0
- package/web/.next/standalone/web/.next/static/chunks/95436454a7559b0d.js +7 -0
- package/web/.next/standalone/web/.next/static/chunks/a751ca474cc46212.js +5 -0
- package/web/.next/standalone/web/.next/static/static/chunks/0358a0e7a40cfb93.css +1 -0
- package/web/.next/standalone/web/.next/static/static/chunks/1ecde4c0d426a635.js +1 -0
- package/web/.next/standalone/web/.next/static/static/chunks/20e0fa99c7a1c1fc.js +13 -0
- package/web/.next/standalone/web/.next/static/static/chunks/36688a049d72e8ab.js +5 -0
- package/web/.next/standalone/web/.next/static/static/chunks/95436454a7559b0d.js +7 -0
- package/web/.next/standalone/web/.next/static/static/chunks/a751ca474cc46212.js +5 -0
- package/web/.next/standalone/web/src/components/ai-elements/complete-task-tool.tsx +126 -0
- package/web/.next/standalone/web/src/components/chat-interface.tsx +68 -3
- package/web/.next/standalone/web/src/components/sessions-sidebar.tsx +18 -1
- package/web/.next/standalone/web/src/lib/api.ts +12 -0
- package/web/.next/static/chunks/0358a0e7a40cfb93.css +1 -0
- package/web/.next/static/chunks/1ecde4c0d426a635.js +1 -0
- package/web/.next/static/chunks/20e0fa99c7a1c1fc.js +13 -0
- package/web/.next/static/chunks/36688a049d72e8ab.js +5 -0
- package/web/.next/static/chunks/95436454a7559b0d.js +7 -0
- package/web/.next/static/chunks/a751ca474cc46212.js +5 -0
- package/dist/schema-NcQknWCg.d.ts +0 -295
- package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__bd396152._.js +0 -15
- package/web/.next/standalone/web/.next/server/chunks/ssr/web_9c9f0e3b._.js +0 -7
- package/web/.next/standalone/web/.next/server/chunks/ssr/web_d08270f7._.js +0 -7
- package/web/.next/standalone/web/.next/static/chunks/2868b007ce5163fc.css +0 -1
- package/web/.next/standalone/web/.next/static/chunks/3f295b6960943c38.js +0 -1
- package/web/.next/standalone/web/.next/static/chunks/631b023d37a08635.js +0 -13
- package/web/.next/standalone/web/.next/static/chunks/a2b4737b190d1b54.js +0 -5
- package/web/.next/standalone/web/.next/static/chunks/e97212fcc8221479.js +0 -5
- package/web/.next/standalone/web/.next/static/chunks/f6e47c8a9766ce91.js +0 -7
- package/web/.next/standalone/web/.next/static/static/chunks/2868b007ce5163fc.css +0 -1
- package/web/.next/standalone/web/.next/static/static/chunks/3f295b6960943c38.js +0 -1
- package/web/.next/standalone/web/.next/static/static/chunks/631b023d37a08635.js +0 -13
- package/web/.next/standalone/web/.next/static/static/chunks/a2b4737b190d1b54.js +0 -5
- package/web/.next/standalone/web/.next/static/static/chunks/e97212fcc8221479.js +0 -5
- package/web/.next/standalone/web/.next/static/static/chunks/f6e47c8a9766ce91.js +0 -7
- package/web/.next/static/chunks/2868b007ce5163fc.css +0 -1
- package/web/.next/static/chunks/3f295b6960943c38.js +0 -1
- package/web/.next/static/chunks/631b023d37a08635.js +0 -13
- package/web/.next/static/chunks/a2b4737b190d1b54.js +0 -5
- package/web/.next/static/chunks/e97212fcc8221479.js +0 -5
- package/web/.next/static/chunks/f6e47c8a9766ce91.js +0 -7
- /package/web/.next/standalone/web/.next/static/{R5xiWSOp_Nqqe_js-LROo → 8zJH-RqrUQ3scBGbdaCmn}/_buildManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/{R5xiWSOp_Nqqe_js-LROo → 8zJH-RqrUQ3scBGbdaCmn}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/standalone/web/.next/static/{R5xiWSOp_Nqqe_js-LROo → 8zJH-RqrUQ3scBGbdaCmn}/_ssgManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/static/{R5xiWSOp_Nqqe_js-LROo → 8zJH-RqrUQ3scBGbdaCmn}/_buildManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/static/{R5xiWSOp_Nqqe_js-LROo → 8zJH-RqrUQ3scBGbdaCmn}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/standalone/web/.next/static/static/{R5xiWSOp_Nqqe_js-LROo → 8zJH-RqrUQ3scBGbdaCmn}/_ssgManifest.js +0 -0
- /package/web/.next/static/{R5xiWSOp_Nqqe_js-LROo → 8zJH-RqrUQ3scBGbdaCmn}/_buildManifest.js +0 -0
- /package/web/.next/static/{R5xiWSOp_Nqqe_js-LROo → 8zJH-RqrUQ3scBGbdaCmn}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/static/{R5xiWSOp_Nqqe_js-LROo → 8zJH-RqrUQ3scBGbdaCmn}/_ssgManifest.js +0 -0
package/dist/index.js
CHANGED
|
@@ -8,575 +8,229 @@ var __export = (target, all) => {
|
|
|
8
8
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
9
|
};
|
|
10
10
|
|
|
11
|
-
// src/
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
|
|
11
|
+
// src/config/types.ts
|
|
12
|
+
import { z } from "zod";
|
|
13
|
+
var ToolApprovalConfigSchema, SkillMetadataSchema, TaskConfigSchema, SessionConfigSchema, VectorGatewayConfigSchema, RemoteServerConfigSchema, SparkcoderConfigSchema;
|
|
14
|
+
var init_types = __esm({
|
|
15
|
+
"src/config/types.ts"() {
|
|
16
|
+
"use strict";
|
|
17
|
+
ToolApprovalConfigSchema = z.object({
|
|
18
|
+
bash: z.boolean().optional().default(true),
|
|
19
|
+
write_file: z.boolean().optional().default(false),
|
|
20
|
+
read_file: z.boolean().optional().default(false),
|
|
21
|
+
load_skill: z.boolean().optional().default(false),
|
|
22
|
+
todo: z.boolean().optional().default(false)
|
|
23
|
+
});
|
|
24
|
+
SkillMetadataSchema = z.object({
|
|
25
|
+
name: z.string(),
|
|
26
|
+
description: z.string(),
|
|
27
|
+
// Whether to always inject this skill into context (vs on-demand loading)
|
|
28
|
+
alwaysApply: z.boolean().optional().default(false),
|
|
29
|
+
// Glob patterns - auto-inject when working with matching files
|
|
30
|
+
globs: z.array(z.string()).optional().default([])
|
|
31
|
+
});
|
|
32
|
+
TaskConfigSchema = z.object({
|
|
33
|
+
enabled: z.boolean(),
|
|
34
|
+
outputSchema: z.record(z.string(), z.unknown()),
|
|
35
|
+
webhookUrl: z.string().url().optional(),
|
|
36
|
+
maxIterations: z.number().optional(),
|
|
37
|
+
status: z.enum(["running", "completed", "failed"]),
|
|
38
|
+
result: z.unknown().optional(),
|
|
39
|
+
error: z.string().optional(),
|
|
40
|
+
iterations: z.number().optional()
|
|
41
|
+
});
|
|
42
|
+
SessionConfigSchema = z.object({
|
|
43
|
+
toolApprovals: z.record(z.string(), z.boolean()).optional(),
|
|
44
|
+
approvalWebhook: z.string().url().optional(),
|
|
45
|
+
skillsDirectory: z.string().optional(),
|
|
46
|
+
maxContextChars: z.number().optional().default(2e5),
|
|
47
|
+
task: TaskConfigSchema.optional()
|
|
48
|
+
});
|
|
49
|
+
VectorGatewayConfigSchema = z.object({
|
|
50
|
+
// Redis cluster nodes URL for Vector Gateway (or use REDIS_CLUSTER_NODES env var)
|
|
51
|
+
redisUrl: z.string().optional(),
|
|
52
|
+
// HTTP URL for database operations (or use VECTOR_HTTP_URL env var)
|
|
53
|
+
httpUrl: z.string().optional(),
|
|
54
|
+
// Embedding model to use (default: text-embedding-3-small)
|
|
55
|
+
embeddingModel: z.string().default("gemini-embedding-001"),
|
|
56
|
+
// Custom namespace override (auto-generated from git remote if not set)
|
|
57
|
+
namespace: z.string().optional(),
|
|
58
|
+
// File patterns to include in indexing
|
|
59
|
+
include: z.array(z.string()).optional().default([
|
|
60
|
+
"**/*.ts",
|
|
61
|
+
"**/*.tsx",
|
|
62
|
+
"**/*.js",
|
|
63
|
+
"**/*.jsx",
|
|
64
|
+
"**/*.py",
|
|
65
|
+
"**/*.go",
|
|
66
|
+
"**/*.rs",
|
|
67
|
+
"**/*.java",
|
|
68
|
+
"**/*.md",
|
|
69
|
+
"**/*.mdx",
|
|
70
|
+
"**/*.txt"
|
|
71
|
+
]),
|
|
72
|
+
// File patterns to exclude from indexing
|
|
73
|
+
exclude: z.array(z.string()).optional().default([
|
|
74
|
+
"**/node_modules/**",
|
|
75
|
+
"**/dist/**",
|
|
76
|
+
"**/build/**",
|
|
77
|
+
"**/.git/**",
|
|
78
|
+
"**/.next/**",
|
|
79
|
+
"**/*.min.js",
|
|
80
|
+
"**/*.bundle.js",
|
|
81
|
+
"**/pnpm-lock.yaml",
|
|
82
|
+
"**/package-lock.json",
|
|
83
|
+
"**/yarn.lock",
|
|
84
|
+
"**/.test-workspace/**",
|
|
85
|
+
"**/.semantic-test-workspace/**",
|
|
86
|
+
"**/.semantic-integration-test/**"
|
|
87
|
+
])
|
|
88
|
+
}).optional();
|
|
89
|
+
RemoteServerConfigSchema = z.object({
|
|
90
|
+
// URL of the remote server (e.g., https://agent.sparkecode.com)
|
|
91
|
+
url: z.string().url().optional(),
|
|
92
|
+
// Auth key for the remote server (auto-generated on first use if not set)
|
|
93
|
+
// Can also be set via SPARKECODER_AUTH_KEY env var
|
|
94
|
+
authKey: z.string().optional()
|
|
95
|
+
}).optional();
|
|
96
|
+
SparkcoderConfigSchema = z.object({
|
|
97
|
+
// Default model to use (Vercel AI Gateway format)
|
|
98
|
+
defaultModel: z.string().default("anthropic/claude-opus-4-6"),
|
|
99
|
+
// Working directory for file operations
|
|
100
|
+
workingDirectory: z.string().optional(),
|
|
101
|
+
// Tool approval settings
|
|
102
|
+
toolApprovals: ToolApprovalConfigSchema.optional().default({}),
|
|
103
|
+
// Approval webhook URL (called when approval is needed)
|
|
104
|
+
approvalWebhook: z.string().url().optional(),
|
|
105
|
+
// Skills configuration
|
|
106
|
+
skills: z.object({
|
|
107
|
+
// Directory containing skill files
|
|
108
|
+
directory: z.string().optional().default("./skills"),
|
|
109
|
+
// Additional skill directories to include
|
|
110
|
+
additionalDirectories: z.array(z.string()).optional().default([])
|
|
111
|
+
}).optional().default({}),
|
|
112
|
+
// Context management
|
|
113
|
+
context: z.object({
|
|
114
|
+
// Maximum context size before summarization (in characters)
|
|
115
|
+
maxChars: z.number().optional().default(2e5),
|
|
116
|
+
// Enable automatic summarization
|
|
117
|
+
autoSummarize: z.boolean().optional().default(true),
|
|
118
|
+
// Number of recent messages to keep after summarization
|
|
119
|
+
keepRecentMessages: z.number().optional().default(10)
|
|
120
|
+
}).optional().default({}),
|
|
121
|
+
// Server configuration
|
|
122
|
+
server: z.object({
|
|
123
|
+
port: z.number().default(3141),
|
|
124
|
+
host: z.string().default("127.0.0.1"),
|
|
125
|
+
// Public URL for web UI to connect to API (for Docker/remote access)
|
|
126
|
+
// If not set, defaults to http://{host}:{port}
|
|
127
|
+
publicUrl: z.string().url().optional()
|
|
128
|
+
}).default({ port: 3141, host: "127.0.0.1" }),
|
|
129
|
+
// Database path (used for local SQLite - ignored if remoteServer is configured)
|
|
130
|
+
databasePath: z.string().optional().default("./sparkecoder.db"),
|
|
131
|
+
// Remote server configuration (for centralized storage)
|
|
132
|
+
// If configured, uses remote MongoDB instead of local SQLite
|
|
133
|
+
remoteServer: RemoteServerConfigSchema,
|
|
134
|
+
// Vector Gateway configuration for semantic search
|
|
135
|
+
vectorGateway: VectorGatewayConfigSchema
|
|
136
|
+
});
|
|
34
137
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
// src/config/index.ts
|
|
141
|
+
import { existsSync, readFileSync, mkdirSync, writeFileSync } from "fs";
|
|
142
|
+
import { resolve, dirname, join } from "path";
|
|
143
|
+
import { homedir, platform } from "os";
|
|
144
|
+
function discoverSkillDirectories(workingDir) {
|
|
145
|
+
const alwaysLoadedDirs = [];
|
|
146
|
+
const onDemandDirs = [];
|
|
147
|
+
const allDirectories = [];
|
|
148
|
+
let agentsMdPath = null;
|
|
149
|
+
const sparkRulesDir = join(workingDir, ".sparkecoder", "rules");
|
|
150
|
+
if (existsSync(sparkRulesDir)) {
|
|
151
|
+
alwaysLoadedDirs.push({ path: sparkRulesDir, priority: 1 });
|
|
152
|
+
allDirectories.push(sparkRulesDir);
|
|
40
153
|
}
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
154
|
+
const sparkSkillsDir = join(workingDir, ".sparkecoder", "skills");
|
|
155
|
+
if (existsSync(sparkSkillsDir)) {
|
|
156
|
+
onDemandDirs.push({ path: sparkSkillsDir, priority: 2 });
|
|
157
|
+
allDirectories.push(sparkSkillsDir);
|
|
158
|
+
}
|
|
159
|
+
const cursorRulesDir = join(workingDir, ".cursor", "rules");
|
|
160
|
+
if (existsSync(cursorRulesDir)) {
|
|
161
|
+
onDemandDirs.push({ path: cursorRulesDir, priority: 3 });
|
|
162
|
+
allDirectories.push(cursorRulesDir);
|
|
163
|
+
}
|
|
164
|
+
const claudeSkillsDir = join(workingDir, ".claude", "skills");
|
|
165
|
+
if (existsSync(claudeSkillsDir)) {
|
|
166
|
+
onDemandDirs.push({ path: claudeSkillsDir, priority: 4 });
|
|
167
|
+
allDirectories.push(claudeSkillsDir);
|
|
168
|
+
}
|
|
169
|
+
const legacySkillsDir = join(workingDir, "skills");
|
|
170
|
+
if (existsSync(legacySkillsDir)) {
|
|
171
|
+
onDemandDirs.push({ path: legacySkillsDir, priority: 5 });
|
|
172
|
+
allDirectories.push(legacySkillsDir);
|
|
173
|
+
}
|
|
174
|
+
const agentsMd = join(workingDir, "AGENTS.md");
|
|
175
|
+
if (existsSync(agentsMd)) {
|
|
176
|
+
agentsMdPath = agentsMd;
|
|
177
|
+
}
|
|
178
|
+
const baseDir = dirname(import.meta.url.replace("file://", ""));
|
|
179
|
+
const builtInCandidates = [
|
|
180
|
+
resolve(baseDir, "../skills/default"),
|
|
181
|
+
// dev: src/config → src/skills/default
|
|
182
|
+
resolve(baseDir, "./skills/default")
|
|
183
|
+
// prod: dist/ → dist/skills/default
|
|
184
|
+
];
|
|
185
|
+
const builtInSkillsDir = builtInCandidates.find((p) => existsSync(p));
|
|
186
|
+
if (builtInSkillsDir) {
|
|
187
|
+
onDemandDirs.push({ path: builtInSkillsDir, priority: 100 });
|
|
188
|
+
allDirectories.push(builtInSkillsDir);
|
|
189
|
+
}
|
|
190
|
+
return {
|
|
191
|
+
alwaysLoadedDirs,
|
|
192
|
+
onDemandDirs,
|
|
193
|
+
agentsMdPath,
|
|
194
|
+
allDirectories
|
|
48
195
|
};
|
|
49
|
-
|
|
50
|
-
|
|
196
|
+
}
|
|
197
|
+
function getAppDataDirectory() {
|
|
198
|
+
const appName = "sparkecoder";
|
|
199
|
+
switch (platform()) {
|
|
200
|
+
case "darwin":
|
|
201
|
+
return join(homedir(), "Library", "Application Support", appName);
|
|
202
|
+
case "win32":
|
|
203
|
+
return join(process.env.APPDATA || join(homedir(), "AppData", "Roaming"), appName);
|
|
204
|
+
default:
|
|
205
|
+
return join(process.env.XDG_DATA_HOME || join(homedir(), ".local", "share"), appName);
|
|
51
206
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
207
|
+
}
|
|
208
|
+
function ensureAppDataDirectory() {
|
|
209
|
+
const dir = getAppDataDirectory();
|
|
210
|
+
if (!existsSync(dir)) {
|
|
211
|
+
mkdirSync(dir, { recursive: true });
|
|
56
212
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
213
|
+
return dir;
|
|
214
|
+
}
|
|
215
|
+
function findConfigFile(startDir) {
|
|
216
|
+
let currentDir = startDir;
|
|
217
|
+
while (currentDir !== dirname(currentDir)) {
|
|
218
|
+
for (const fileName of CONFIG_FILE_NAMES) {
|
|
219
|
+
const configPath = resolve(currentDir, fileName);
|
|
220
|
+
if (existsSync(configPath)) {
|
|
221
|
+
return configPath;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
currentDir = dirname(currentDir);
|
|
60
225
|
}
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
226
|
+
const appDataDir = getAppDataDirectory();
|
|
227
|
+
for (const fileName of CONFIG_FILE_NAMES) {
|
|
228
|
+
const configPath = join(appDataDir, fileName);
|
|
229
|
+
if (existsSync(configPath)) {
|
|
230
|
+
return configPath;
|
|
231
|
+
}
|
|
64
232
|
}
|
|
65
|
-
return
|
|
66
|
-
}
|
|
67
|
-
var remoteServerUrl, authKey, DATE_FIELDS, MODEL_MESSAGE_FIELDS, remoteSessionQueries, remoteMessageQueries, remoteToolExecutionQueries, remoteTodoQueries, remoteSkillQueries, remoteActiveStreamQueries, remoteCheckpointQueries, remoteFileBackupQueries, remoteSubagentQueries, remoteIndexStatusQueries;
|
|
68
|
-
var init_remote = __esm({
|
|
69
|
-
"src/db/remote.ts"() {
|
|
70
|
-
"use strict";
|
|
71
|
-
remoteServerUrl = null;
|
|
72
|
-
authKey = null;
|
|
73
|
-
DATE_FIELDS = ["createdAt", "updatedAt", "startedAt", "completedAt", "stoppedAt", "finishedAt", "loadedAt", "indexedAt", "lastFullIndex", "lastIncrementalIndex"];
|
|
74
|
-
MODEL_MESSAGE_FIELDS = ["modelMessage", "modelMessages"];
|
|
75
|
-
remoteSessionQueries = {
|
|
76
|
-
create(data) {
|
|
77
|
-
return api("/sessions", { method: "POST", body: data });
|
|
78
|
-
},
|
|
79
|
-
getById(id) {
|
|
80
|
-
return api(`/sessions/${id}`).catch(() => void 0);
|
|
81
|
-
},
|
|
82
|
-
list(limit = 50, offset = 0) {
|
|
83
|
-
return api(`/sessions?limit=${limit}&offset=${offset}`);
|
|
84
|
-
},
|
|
85
|
-
updateStatus(id, status) {
|
|
86
|
-
return api(`/sessions/${id}`, { method: "PATCH", body: { status } });
|
|
87
|
-
},
|
|
88
|
-
updateModel(id, model) {
|
|
89
|
-
return api(`/sessions/${id}`, { method: "PATCH", body: { model } });
|
|
90
|
-
},
|
|
91
|
-
update(id, updates) {
|
|
92
|
-
return api(`/sessions/${id}`, { method: "PATCH", body: updates });
|
|
93
|
-
},
|
|
94
|
-
delete(id) {
|
|
95
|
-
return api(`/sessions/${id}`, { method: "DELETE" }).then((r) => r?.success ?? false);
|
|
96
|
-
}
|
|
97
|
-
};
|
|
98
|
-
remoteMessageQueries = {
|
|
99
|
-
async getNextSequence(sessionId) {
|
|
100
|
-
const result = await api(`/messages/session/${sessionId}/next-sequence`);
|
|
101
|
-
return result.nextSequence;
|
|
102
|
-
},
|
|
103
|
-
create(sessionId, modelMessage) {
|
|
104
|
-
return api("/messages", { method: "POST", body: { sessionId, modelMessage } });
|
|
105
|
-
},
|
|
106
|
-
addMany(sessionId, modelMessages) {
|
|
107
|
-
return api("/messages/batch", { method: "POST", body: { sessionId, modelMessages } });
|
|
108
|
-
},
|
|
109
|
-
getBySession(sessionId) {
|
|
110
|
-
return api(`/messages/session/${sessionId}`);
|
|
111
|
-
},
|
|
112
|
-
getModelMessages(sessionId) {
|
|
113
|
-
return api(`/messages/session/${sessionId}/model-messages`, { skipParseDates: true });
|
|
114
|
-
},
|
|
115
|
-
async getRecentBySession(sessionId, limit = 50) {
|
|
116
|
-
const messages = await api(`/messages/session/${sessionId}`);
|
|
117
|
-
return messages.slice(-limit);
|
|
118
|
-
},
|
|
119
|
-
async countBySession(sessionId) {
|
|
120
|
-
const result = await api(`/messages/session/${sessionId}/count`);
|
|
121
|
-
return result.count;
|
|
122
|
-
},
|
|
123
|
-
async deleteBySession(sessionId) {
|
|
124
|
-
const result = await api(`/messages/session/${sessionId}`, { method: "DELETE" });
|
|
125
|
-
return result.deleted;
|
|
126
|
-
},
|
|
127
|
-
async deleteFromSequence(sessionId, fromSequence) {
|
|
128
|
-
const result = await api(
|
|
129
|
-
`/messages/session/${sessionId}/from-sequence/${fromSequence}`,
|
|
130
|
-
{ method: "DELETE" }
|
|
131
|
-
);
|
|
132
|
-
return result.deleted;
|
|
133
|
-
}
|
|
134
|
-
};
|
|
135
|
-
remoteToolExecutionQueries = {
|
|
136
|
-
create(data) {
|
|
137
|
-
return api("/tool-executions", { method: "POST", body: data });
|
|
138
|
-
},
|
|
139
|
-
getById(id) {
|
|
140
|
-
return api(`/tool-executions/${id}`).catch(() => void 0);
|
|
141
|
-
},
|
|
142
|
-
getByToolCallId(toolCallId) {
|
|
143
|
-
return api(`/tool-executions/by-tool-call-id/${toolCallId}`).catch(() => void 0);
|
|
144
|
-
},
|
|
145
|
-
getPendingApprovals(sessionId) {
|
|
146
|
-
return api(`/tool-executions/session/${sessionId}/pending`);
|
|
147
|
-
},
|
|
148
|
-
approve(id) {
|
|
149
|
-
return api(`/tool-executions/${id}`, { method: "PATCH", body: { status: "approved" } });
|
|
150
|
-
},
|
|
151
|
-
reject(id) {
|
|
152
|
-
return api(`/tool-executions/${id}`, { method: "PATCH", body: { status: "rejected" } });
|
|
153
|
-
},
|
|
154
|
-
complete(id, output, error) {
|
|
155
|
-
return api(`/tool-executions/${id}`, {
|
|
156
|
-
method: "PATCH",
|
|
157
|
-
body: { status: error ? "error" : "completed", output, error }
|
|
158
|
-
});
|
|
159
|
-
},
|
|
160
|
-
getBySession(sessionId) {
|
|
161
|
-
return api(`/tool-executions/session/${sessionId}`);
|
|
162
|
-
},
|
|
163
|
-
async deleteAfterTime(sessionId, afterTime) {
|
|
164
|
-
const timestamp = afterTime instanceof Date ? afterTime.getTime() : new Date(afterTime).getTime();
|
|
165
|
-
const result = await api(
|
|
166
|
-
`/tool-executions/session/${sessionId}/after/${timestamp}`,
|
|
167
|
-
{ method: "DELETE" }
|
|
168
|
-
);
|
|
169
|
-
return result.deleted;
|
|
170
|
-
}
|
|
171
|
-
};
|
|
172
|
-
remoteTodoQueries = {
|
|
173
|
-
create(data) {
|
|
174
|
-
return api("/todos", { method: "POST", body: data });
|
|
175
|
-
},
|
|
176
|
-
createMany(sessionId, items) {
|
|
177
|
-
return api("/todos/batch", { method: "POST", body: { sessionId, items } });
|
|
178
|
-
},
|
|
179
|
-
getBySession(sessionId) {
|
|
180
|
-
return api(`/todos/session/${sessionId}`);
|
|
181
|
-
},
|
|
182
|
-
updateStatus(id, status) {
|
|
183
|
-
return api(`/todos/${id}`, { method: "PATCH", body: { status } });
|
|
184
|
-
},
|
|
185
|
-
async delete(id) {
|
|
186
|
-
const result = await api(`/todos/${id}`, { method: "DELETE" });
|
|
187
|
-
return result?.success ?? false;
|
|
188
|
-
},
|
|
189
|
-
async clearSession(sessionId) {
|
|
190
|
-
const result = await api(`/todos/session/${sessionId}`, { method: "DELETE" });
|
|
191
|
-
return result.deleted;
|
|
192
|
-
}
|
|
193
|
-
};
|
|
194
|
-
remoteSkillQueries = {
|
|
195
|
-
load(sessionId, skillName) {
|
|
196
|
-
return api("/skills", { method: "POST", body: { sessionId, skillName } });
|
|
197
|
-
},
|
|
198
|
-
getBySession(sessionId) {
|
|
199
|
-
return api(`/skills/session/${sessionId}`);
|
|
200
|
-
},
|
|
201
|
-
async isLoaded(sessionId, skillName) {
|
|
202
|
-
const result = await api(`/skills/session/${sessionId}/is-loaded/${skillName}`);
|
|
203
|
-
return result.isLoaded;
|
|
204
|
-
}
|
|
205
|
-
};
|
|
206
|
-
remoteActiveStreamQueries = {
|
|
207
|
-
create(sessionId, streamId) {
|
|
208
|
-
return api("/streams", { method: "POST", body: { sessionId, streamId } });
|
|
209
|
-
},
|
|
210
|
-
getBySessionId(sessionId) {
|
|
211
|
-
return api(`/streams/session/${sessionId}`).then((r) => r ?? void 0);
|
|
212
|
-
},
|
|
213
|
-
getByStreamId(streamId) {
|
|
214
|
-
return api(`/streams/by-stream-id/${streamId}`).catch(() => void 0);
|
|
215
|
-
},
|
|
216
|
-
finish(streamId) {
|
|
217
|
-
return api(`/streams/by-stream-id/${streamId}`, { method: "PATCH", body: { status: "finished" } });
|
|
218
|
-
},
|
|
219
|
-
markError(streamId) {
|
|
220
|
-
return api(`/streams/by-stream-id/${streamId}`, { method: "PATCH", body: { status: "error" } });
|
|
221
|
-
},
|
|
222
|
-
async deleteBySession(sessionId) {
|
|
223
|
-
const result = await api(`/streams/session/${sessionId}`, { method: "DELETE" });
|
|
224
|
-
return result.deleted;
|
|
225
|
-
}
|
|
226
|
-
};
|
|
227
|
-
remoteCheckpointQueries = {
|
|
228
|
-
create(data) {
|
|
229
|
-
return api("/checkpoints", { method: "POST", body: data });
|
|
230
|
-
},
|
|
231
|
-
getById(id) {
|
|
232
|
-
return api(`/checkpoints/${id}`).catch(() => void 0);
|
|
233
|
-
},
|
|
234
|
-
getBySession(sessionId) {
|
|
235
|
-
return api(`/checkpoints/session/${sessionId}`);
|
|
236
|
-
},
|
|
237
|
-
getByMessageSequence(sessionId, messageSequence) {
|
|
238
|
-
return api(`/checkpoints/session/${sessionId}/by-sequence/${messageSequence}`).then((r) => r ?? void 0);
|
|
239
|
-
},
|
|
240
|
-
getLatest(sessionId) {
|
|
241
|
-
return api(`/checkpoints/session/${sessionId}/latest`).then((r) => r ?? void 0);
|
|
242
|
-
},
|
|
243
|
-
async deleteAfterSequence(sessionId, messageSequence) {
|
|
244
|
-
const result = await api(
|
|
245
|
-
`/checkpoints/session/${sessionId}/after-sequence/${messageSequence}`,
|
|
246
|
-
{ method: "DELETE" }
|
|
247
|
-
);
|
|
248
|
-
return result.deleted;
|
|
249
|
-
},
|
|
250
|
-
async deleteBySession(sessionId) {
|
|
251
|
-
const result = await api(`/checkpoints/session/${sessionId}`, { method: "DELETE" });
|
|
252
|
-
return result.deleted;
|
|
253
|
-
}
|
|
254
|
-
};
|
|
255
|
-
remoteFileBackupQueries = {
|
|
256
|
-
create(data) {
|
|
257
|
-
return api("/file-backups", { method: "POST", body: data });
|
|
258
|
-
},
|
|
259
|
-
getByCheckpoint(checkpointId) {
|
|
260
|
-
return api(`/file-backups/checkpoint/${checkpointId}`);
|
|
261
|
-
},
|
|
262
|
-
getBySession(sessionId) {
|
|
263
|
-
return api(`/file-backups/session/${sessionId}`);
|
|
264
|
-
},
|
|
265
|
-
getFromSequence(sessionId, messageSequence) {
|
|
266
|
-
return api(`/file-backups/session/${sessionId}/from-sequence/${messageSequence}`);
|
|
267
|
-
},
|
|
268
|
-
async hasBackup(checkpointId, filePath) {
|
|
269
|
-
const result = await api(
|
|
270
|
-
`/file-backups/checkpoint/${checkpointId}/has-backup/${encodeURIComponent(filePath)}`
|
|
271
|
-
);
|
|
272
|
-
return result.hasBackup;
|
|
273
|
-
},
|
|
274
|
-
async deleteBySession(sessionId) {
|
|
275
|
-
const result = await api(`/file-backups/session/${sessionId}`, { method: "DELETE" });
|
|
276
|
-
return result.deleted;
|
|
277
|
-
}
|
|
278
|
-
};
|
|
279
|
-
remoteSubagentQueries = {
|
|
280
|
-
create(data) {
|
|
281
|
-
return api("/subagents", { method: "POST", body: data });
|
|
282
|
-
},
|
|
283
|
-
getById(id) {
|
|
284
|
-
return api(`/subagents/${id}`).catch(() => void 0);
|
|
285
|
-
},
|
|
286
|
-
getByToolCallId(toolCallId) {
|
|
287
|
-
return api(`/subagents/by-tool-call-id/${toolCallId}`).catch(() => void 0);
|
|
288
|
-
},
|
|
289
|
-
getBySession(sessionId) {
|
|
290
|
-
return api(`/subagents/session/${sessionId}`);
|
|
291
|
-
},
|
|
292
|
-
addStep(id, step) {
|
|
293
|
-
return api(`/subagents/${id}/add-step`, { method: "POST", body: { step } }).catch(() => void 0);
|
|
294
|
-
},
|
|
295
|
-
complete(id, result) {
|
|
296
|
-
return api(`/subagents/${id}`, { method: "PATCH", body: { status: "completed", result } }).catch(() => void 0);
|
|
297
|
-
},
|
|
298
|
-
markError(id, error) {
|
|
299
|
-
return api(`/subagents/${id}`, { method: "PATCH", body: { status: "error", error } }).catch(() => void 0);
|
|
300
|
-
},
|
|
301
|
-
cancel(id) {
|
|
302
|
-
return api(`/subagents/${id}`, { method: "PATCH", body: { status: "cancelled" } }).catch(() => void 0);
|
|
303
|
-
},
|
|
304
|
-
async deleteBySession(sessionId) {
|
|
305
|
-
const result = await api(`/subagents/session/${sessionId}`, { method: "DELETE" });
|
|
306
|
-
return result.deleted;
|
|
307
|
-
}
|
|
308
|
-
};
|
|
309
|
-
remoteIndexStatusQueries = {
|
|
310
|
-
upsert(_db, data) {
|
|
311
|
-
return api("/index-status", {
|
|
312
|
-
method: "POST",
|
|
313
|
-
body: {
|
|
314
|
-
...data,
|
|
315
|
-
lastFullIndex: data.lastFullIndex?.toISOString(),
|
|
316
|
-
lastIncrementalIndex: data.lastIncrementalIndex?.toISOString()
|
|
317
|
-
}
|
|
318
|
-
});
|
|
319
|
-
},
|
|
320
|
-
get(_db, namespace) {
|
|
321
|
-
return api(`/index-status/namespace/${namespace}`).then((r) => r ?? void 0);
|
|
322
|
-
},
|
|
323
|
-
async delete(_db, namespace) {
|
|
324
|
-
const result = await api(`/index-status/namespace/${namespace}`, { method: "DELETE" });
|
|
325
|
-
return result?.success ?? false;
|
|
326
|
-
},
|
|
327
|
-
list(_db) {
|
|
328
|
-
return api("/index-status");
|
|
329
|
-
}
|
|
330
|
-
};
|
|
331
|
-
}
|
|
332
|
-
});
|
|
333
|
-
|
|
334
|
-
// src/db/index.ts
|
|
335
|
-
function initDatabase(config) {
|
|
336
|
-
initRemoteDatabase(config.url, config.authKey);
|
|
337
|
-
initialized = true;
|
|
338
|
-
}
|
|
339
|
-
function getDb() {
|
|
340
|
-
if (!initialized) {
|
|
341
|
-
throw new Error("Database not initialized. Call initDatabase first.");
|
|
342
|
-
}
|
|
343
|
-
return {};
|
|
344
|
-
}
|
|
345
|
-
function closeDatabase() {
|
|
346
|
-
closeRemoteDatabase();
|
|
347
|
-
initialized = false;
|
|
348
|
-
}
|
|
349
|
-
var initialized, sessionQueries, messageQueries, toolExecutionQueries, todoQueries, skillQueries, activeStreamQueries, checkpointQueries, fileBackupQueries, subagentQueries, indexStatusQueries;
|
|
350
|
-
var init_db = __esm({
|
|
351
|
-
"src/db/index.ts"() {
|
|
352
|
-
"use strict";
|
|
353
|
-
init_remote();
|
|
354
|
-
initialized = false;
|
|
355
|
-
sessionQueries = remoteSessionQueries;
|
|
356
|
-
messageQueries = remoteMessageQueries;
|
|
357
|
-
toolExecutionQueries = remoteToolExecutionQueries;
|
|
358
|
-
todoQueries = remoteTodoQueries;
|
|
359
|
-
skillQueries = remoteSkillQueries;
|
|
360
|
-
activeStreamQueries = remoteActiveStreamQueries;
|
|
361
|
-
checkpointQueries = remoteCheckpointQueries;
|
|
362
|
-
fileBackupQueries = remoteFileBackupQueries;
|
|
363
|
-
subagentQueries = remoteSubagentQueries;
|
|
364
|
-
indexStatusQueries = remoteIndexStatusQueries;
|
|
365
|
-
}
|
|
366
|
-
});
|
|
367
|
-
|
|
368
|
-
// src/config/types.ts
|
|
369
|
-
import { z } from "zod";
|
|
370
|
-
var ToolApprovalConfigSchema, SkillMetadataSchema, SessionConfigSchema, VectorGatewayConfigSchema, RemoteServerConfigSchema, SparkcoderConfigSchema;
|
|
371
|
-
var init_types = __esm({
|
|
372
|
-
"src/config/types.ts"() {
|
|
373
|
-
"use strict";
|
|
374
|
-
ToolApprovalConfigSchema = z.object({
|
|
375
|
-
bash: z.boolean().optional().default(true),
|
|
376
|
-
write_file: z.boolean().optional().default(false),
|
|
377
|
-
read_file: z.boolean().optional().default(false),
|
|
378
|
-
load_skill: z.boolean().optional().default(false),
|
|
379
|
-
todo: z.boolean().optional().default(false)
|
|
380
|
-
});
|
|
381
|
-
SkillMetadataSchema = z.object({
|
|
382
|
-
name: z.string(),
|
|
383
|
-
description: z.string(),
|
|
384
|
-
// Whether to always inject this skill into context (vs on-demand loading)
|
|
385
|
-
alwaysApply: z.boolean().optional().default(false),
|
|
386
|
-
// Glob patterns - auto-inject when working with matching files
|
|
387
|
-
globs: z.array(z.string()).optional().default([])
|
|
388
|
-
});
|
|
389
|
-
SessionConfigSchema = z.object({
|
|
390
|
-
toolApprovals: z.record(z.string(), z.boolean()).optional(),
|
|
391
|
-
approvalWebhook: z.string().url().optional(),
|
|
392
|
-
skillsDirectory: z.string().optional(),
|
|
393
|
-
maxContextChars: z.number().optional().default(2e5)
|
|
394
|
-
});
|
|
395
|
-
VectorGatewayConfigSchema = z.object({
|
|
396
|
-
// Redis cluster nodes URL for Vector Gateway (or use REDIS_CLUSTER_NODES env var)
|
|
397
|
-
redisUrl: z.string().optional(),
|
|
398
|
-
// HTTP URL for database operations (or use VECTOR_HTTP_URL env var)
|
|
399
|
-
httpUrl: z.string().optional(),
|
|
400
|
-
// Embedding model to use (default: text-embedding-3-small)
|
|
401
|
-
embeddingModel: z.string().default("gemini-embedding-001"),
|
|
402
|
-
// Custom namespace override (auto-generated from git remote if not set)
|
|
403
|
-
namespace: z.string().optional(),
|
|
404
|
-
// File patterns to include in indexing
|
|
405
|
-
include: z.array(z.string()).optional().default([
|
|
406
|
-
"**/*.ts",
|
|
407
|
-
"**/*.tsx",
|
|
408
|
-
"**/*.js",
|
|
409
|
-
"**/*.jsx",
|
|
410
|
-
"**/*.py",
|
|
411
|
-
"**/*.go",
|
|
412
|
-
"**/*.rs",
|
|
413
|
-
"**/*.java",
|
|
414
|
-
"**/*.md",
|
|
415
|
-
"**/*.mdx",
|
|
416
|
-
"**/*.txt"
|
|
417
|
-
]),
|
|
418
|
-
// File patterns to exclude from indexing
|
|
419
|
-
exclude: z.array(z.string()).optional().default([
|
|
420
|
-
"**/node_modules/**",
|
|
421
|
-
"**/dist/**",
|
|
422
|
-
"**/build/**",
|
|
423
|
-
"**/.git/**",
|
|
424
|
-
"**/.next/**",
|
|
425
|
-
"**/*.min.js",
|
|
426
|
-
"**/*.bundle.js",
|
|
427
|
-
"**/pnpm-lock.yaml",
|
|
428
|
-
"**/package-lock.json",
|
|
429
|
-
"**/yarn.lock",
|
|
430
|
-
"**/.test-workspace/**",
|
|
431
|
-
"**/.semantic-test-workspace/**",
|
|
432
|
-
"**/.semantic-integration-test/**"
|
|
433
|
-
])
|
|
434
|
-
}).optional();
|
|
435
|
-
RemoteServerConfigSchema = z.object({
|
|
436
|
-
// URL of the remote server (e.g., https://agent.sparkecode.com)
|
|
437
|
-
url: z.string().url().optional(),
|
|
438
|
-
// Auth key for the remote server (auto-generated on first use if not set)
|
|
439
|
-
// Can also be set via SPARKECODER_AUTH_KEY env var
|
|
440
|
-
authKey: z.string().optional()
|
|
441
|
-
}).optional();
|
|
442
|
-
SparkcoderConfigSchema = z.object({
|
|
443
|
-
// Default model to use (Vercel AI Gateway format)
|
|
444
|
-
defaultModel: z.string().default("anthropic/claude-opus-4-6"),
|
|
445
|
-
// Working directory for file operations
|
|
446
|
-
workingDirectory: z.string().optional(),
|
|
447
|
-
// Tool approval settings
|
|
448
|
-
toolApprovals: ToolApprovalConfigSchema.optional().default({}),
|
|
449
|
-
// Approval webhook URL (called when approval is needed)
|
|
450
|
-
approvalWebhook: z.string().url().optional(),
|
|
451
|
-
// Skills configuration
|
|
452
|
-
skills: z.object({
|
|
453
|
-
// Directory containing skill files
|
|
454
|
-
directory: z.string().optional().default("./skills"),
|
|
455
|
-
// Additional skill directories to include
|
|
456
|
-
additionalDirectories: z.array(z.string()).optional().default([])
|
|
457
|
-
}).optional().default({}),
|
|
458
|
-
// Context management
|
|
459
|
-
context: z.object({
|
|
460
|
-
// Maximum context size before summarization (in characters)
|
|
461
|
-
maxChars: z.number().optional().default(2e5),
|
|
462
|
-
// Enable automatic summarization
|
|
463
|
-
autoSummarize: z.boolean().optional().default(true),
|
|
464
|
-
// Number of recent messages to keep after summarization
|
|
465
|
-
keepRecentMessages: z.number().optional().default(10)
|
|
466
|
-
}).optional().default({}),
|
|
467
|
-
// Server configuration
|
|
468
|
-
server: z.object({
|
|
469
|
-
port: z.number().default(3141),
|
|
470
|
-
host: z.string().default("127.0.0.1"),
|
|
471
|
-
// Public URL for web UI to connect to API (for Docker/remote access)
|
|
472
|
-
// If not set, defaults to http://{host}:{port}
|
|
473
|
-
publicUrl: z.string().url().optional()
|
|
474
|
-
}).default({ port: 3141, host: "127.0.0.1" }),
|
|
475
|
-
// Database path (used for local SQLite - ignored if remoteServer is configured)
|
|
476
|
-
databasePath: z.string().optional().default("./sparkecoder.db"),
|
|
477
|
-
// Remote server configuration (for centralized storage)
|
|
478
|
-
// If configured, uses remote MongoDB instead of local SQLite
|
|
479
|
-
remoteServer: RemoteServerConfigSchema,
|
|
480
|
-
// Vector Gateway configuration for semantic search
|
|
481
|
-
vectorGateway: VectorGatewayConfigSchema
|
|
482
|
-
});
|
|
483
|
-
}
|
|
484
|
-
});
|
|
485
|
-
|
|
486
|
-
// src/config/index.ts
|
|
487
|
-
import { existsSync, readFileSync, mkdirSync, writeFileSync } from "fs";
|
|
488
|
-
import { resolve, dirname, join } from "path";
|
|
489
|
-
import { homedir, platform } from "os";
|
|
490
|
-
function discoverSkillDirectories(workingDir) {
|
|
491
|
-
const alwaysLoadedDirs = [];
|
|
492
|
-
const onDemandDirs = [];
|
|
493
|
-
const allDirectories = [];
|
|
494
|
-
let agentsMdPath = null;
|
|
495
|
-
const sparkRulesDir = join(workingDir, ".sparkecoder", "rules");
|
|
496
|
-
if (existsSync(sparkRulesDir)) {
|
|
497
|
-
alwaysLoadedDirs.push({ path: sparkRulesDir, priority: 1 });
|
|
498
|
-
allDirectories.push(sparkRulesDir);
|
|
499
|
-
}
|
|
500
|
-
const sparkSkillsDir = join(workingDir, ".sparkecoder", "skills");
|
|
501
|
-
if (existsSync(sparkSkillsDir)) {
|
|
502
|
-
onDemandDirs.push({ path: sparkSkillsDir, priority: 2 });
|
|
503
|
-
allDirectories.push(sparkSkillsDir);
|
|
504
|
-
}
|
|
505
|
-
const cursorRulesDir = join(workingDir, ".cursor", "rules");
|
|
506
|
-
if (existsSync(cursorRulesDir)) {
|
|
507
|
-
onDemandDirs.push({ path: cursorRulesDir, priority: 3 });
|
|
508
|
-
allDirectories.push(cursorRulesDir);
|
|
509
|
-
}
|
|
510
|
-
const claudeSkillsDir = join(workingDir, ".claude", "skills");
|
|
511
|
-
if (existsSync(claudeSkillsDir)) {
|
|
512
|
-
onDemandDirs.push({ path: claudeSkillsDir, priority: 4 });
|
|
513
|
-
allDirectories.push(claudeSkillsDir);
|
|
514
|
-
}
|
|
515
|
-
const legacySkillsDir = join(workingDir, "skills");
|
|
516
|
-
if (existsSync(legacySkillsDir)) {
|
|
517
|
-
onDemandDirs.push({ path: legacySkillsDir, priority: 5 });
|
|
518
|
-
allDirectories.push(legacySkillsDir);
|
|
519
|
-
}
|
|
520
|
-
const agentsMd = join(workingDir, "AGENTS.md");
|
|
521
|
-
if (existsSync(agentsMd)) {
|
|
522
|
-
agentsMdPath = agentsMd;
|
|
523
|
-
}
|
|
524
|
-
const baseDir = dirname(import.meta.url.replace("file://", ""));
|
|
525
|
-
const builtInCandidates = [
|
|
526
|
-
resolve(baseDir, "../skills/default"),
|
|
527
|
-
// dev: src/config → src/skills/default
|
|
528
|
-
resolve(baseDir, "./skills/default")
|
|
529
|
-
// prod: dist/ → dist/skills/default
|
|
530
|
-
];
|
|
531
|
-
const builtInSkillsDir = builtInCandidates.find((p) => existsSync(p));
|
|
532
|
-
if (builtInSkillsDir) {
|
|
533
|
-
onDemandDirs.push({ path: builtInSkillsDir, priority: 100 });
|
|
534
|
-
allDirectories.push(builtInSkillsDir);
|
|
535
|
-
}
|
|
536
|
-
return {
|
|
537
|
-
alwaysLoadedDirs,
|
|
538
|
-
onDemandDirs,
|
|
539
|
-
agentsMdPath,
|
|
540
|
-
allDirectories
|
|
541
|
-
};
|
|
542
|
-
}
|
|
543
|
-
function getAppDataDirectory() {
|
|
544
|
-
const appName = "sparkecoder";
|
|
545
|
-
switch (platform()) {
|
|
546
|
-
case "darwin":
|
|
547
|
-
return join(homedir(), "Library", "Application Support", appName);
|
|
548
|
-
case "win32":
|
|
549
|
-
return join(process.env.APPDATA || join(homedir(), "AppData", "Roaming"), appName);
|
|
550
|
-
default:
|
|
551
|
-
return join(process.env.XDG_DATA_HOME || join(homedir(), ".local", "share"), appName);
|
|
552
|
-
}
|
|
553
|
-
}
|
|
554
|
-
function ensureAppDataDirectory() {
|
|
555
|
-
const dir = getAppDataDirectory();
|
|
556
|
-
if (!existsSync(dir)) {
|
|
557
|
-
mkdirSync(dir, { recursive: true });
|
|
558
|
-
}
|
|
559
|
-
return dir;
|
|
560
|
-
}
|
|
561
|
-
function findConfigFile(startDir) {
|
|
562
|
-
let currentDir = startDir;
|
|
563
|
-
while (currentDir !== dirname(currentDir)) {
|
|
564
|
-
for (const fileName of CONFIG_FILE_NAMES) {
|
|
565
|
-
const configPath = resolve(currentDir, fileName);
|
|
566
|
-
if (existsSync(configPath)) {
|
|
567
|
-
return configPath;
|
|
568
|
-
}
|
|
569
|
-
}
|
|
570
|
-
currentDir = dirname(currentDir);
|
|
571
|
-
}
|
|
572
|
-
const appDataDir = getAppDataDirectory();
|
|
573
|
-
for (const fileName of CONFIG_FILE_NAMES) {
|
|
574
|
-
const configPath = join(appDataDir, fileName);
|
|
575
|
-
if (existsSync(configPath)) {
|
|
576
|
-
return configPath;
|
|
577
|
-
}
|
|
578
|
-
}
|
|
579
|
-
return null;
|
|
233
|
+
return null;
|
|
580
234
|
}
|
|
581
235
|
function loadConfig(configPath, workingDirectory) {
|
|
582
236
|
const cwd = workingDirectory || process.cwd();
|
|
@@ -698,6 +352,9 @@ function getConfig() {
|
|
|
698
352
|
}
|
|
699
353
|
function requiresApproval(toolName, sessionConfig) {
|
|
700
354
|
const config = getConfig();
|
|
355
|
+
if (sessionConfig?.toolApprovals?.["*"] !== void 0) {
|
|
356
|
+
return sessionConfig.toolApprovals["*"];
|
|
357
|
+
}
|
|
701
358
|
if (sessionConfig?.toolApprovals?.[toolName] !== void 0) {
|
|
702
359
|
return sessionConfig.toolApprovals[toolName];
|
|
703
360
|
}
|
|
@@ -821,48 +478,405 @@ function getApiKeyStatus() {
|
|
|
821
478
|
} else {
|
|
822
479
|
source = "env";
|
|
823
480
|
}
|
|
824
|
-
value = envValue;
|
|
825
|
-
} else if (storedValue) {
|
|
826
|
-
source = "storage";
|
|
827
|
-
value = storedValue;
|
|
828
|
-
}
|
|
829
|
-
return {
|
|
830
|
-
provider,
|
|
831
|
-
envVar,
|
|
832
|
-
configured: !!value,
|
|
833
|
-
source,
|
|
834
|
-
maskedKey: value ? maskApiKey(value) : null
|
|
481
|
+
value = envValue;
|
|
482
|
+
} else if (storedValue) {
|
|
483
|
+
source = "storage";
|
|
484
|
+
value = storedValue;
|
|
485
|
+
}
|
|
486
|
+
return {
|
|
487
|
+
provider,
|
|
488
|
+
envVar,
|
|
489
|
+
configured: !!value,
|
|
490
|
+
source,
|
|
491
|
+
maskedKey: value ? maskApiKey(value) : null
|
|
492
|
+
};
|
|
493
|
+
});
|
|
494
|
+
}
|
|
495
|
+
function maskApiKey(key) {
|
|
496
|
+
if (key.length <= 12) {
|
|
497
|
+
return "****" + key.slice(-4);
|
|
498
|
+
}
|
|
499
|
+
return key.slice(0, 4) + "..." + key.slice(-4);
|
|
500
|
+
}
|
|
501
|
+
var CONFIG_FILE_NAMES, cachedConfig, AUTH_KEY_FILE, API_KEYS_FILE, PROVIDER_ENV_MAP, SUPPORTED_PROVIDERS;
|
|
502
|
+
var init_config = __esm({
|
|
503
|
+
"src/config/index.ts"() {
|
|
504
|
+
"use strict";
|
|
505
|
+
init_types();
|
|
506
|
+
init_types();
|
|
507
|
+
CONFIG_FILE_NAMES = [
|
|
508
|
+
"sparkecoder.config.json",
|
|
509
|
+
"sparkecoder.json",
|
|
510
|
+
".sparkecoder.json"
|
|
511
|
+
];
|
|
512
|
+
cachedConfig = null;
|
|
513
|
+
AUTH_KEY_FILE = "auth-key.json";
|
|
514
|
+
API_KEYS_FILE = "api-keys.json";
|
|
515
|
+
PROVIDER_ENV_MAP = {
|
|
516
|
+
anthropic: "ANTHROPIC_API_KEY",
|
|
517
|
+
openai: "OPENAI_API_KEY",
|
|
518
|
+
google: "GOOGLE_GENERATIVE_AI_API_KEY",
|
|
519
|
+
xai: "XAI_API_KEY",
|
|
520
|
+
"ai-gateway": "AI_GATEWAY_API_KEY"
|
|
521
|
+
};
|
|
522
|
+
SUPPORTED_PROVIDERS = Object.keys(PROVIDER_ENV_MAP);
|
|
523
|
+
}
|
|
524
|
+
});
|
|
525
|
+
|
|
526
|
+
// src/db/remote.ts
|
|
527
|
+
function initRemoteDatabase(serverUrl, key) {
|
|
528
|
+
remoteServerUrl = serverUrl.replace(/\/$/, "");
|
|
529
|
+
authKey = key;
|
|
530
|
+
}
|
|
531
|
+
function closeRemoteDatabase() {
|
|
532
|
+
remoteServerUrl = null;
|
|
533
|
+
authKey = null;
|
|
534
|
+
}
|
|
535
|
+
function parseDates(obj) {
|
|
536
|
+
if (obj === null || obj === void 0) return obj;
|
|
537
|
+
if (Array.isArray(obj)) return obj.map(parseDates);
|
|
538
|
+
if (typeof obj !== "object" || obj instanceof Date) return obj;
|
|
539
|
+
const result = { ...obj };
|
|
540
|
+
for (const key of Object.keys(result)) {
|
|
541
|
+
if (MODEL_MESSAGE_FIELDS.includes(key)) {
|
|
542
|
+
continue;
|
|
543
|
+
}
|
|
544
|
+
if (DATE_FIELDS.includes(key) && typeof result[key] === "string") {
|
|
545
|
+
result[key] = new Date(result[key]);
|
|
546
|
+
} else if (typeof result[key] === "object") {
|
|
547
|
+
result[key] = parseDates(result[key]);
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
return result;
|
|
551
|
+
}
|
|
552
|
+
async function api(path, options = {}) {
|
|
553
|
+
if (!remoteServerUrl || !authKey) {
|
|
554
|
+
throw new Error("Remote database not initialized");
|
|
555
|
+
}
|
|
556
|
+
const url = `${remoteServerUrl}/db${path}`;
|
|
557
|
+
const init = {
|
|
558
|
+
method: options.method || "GET",
|
|
559
|
+
headers: {
|
|
560
|
+
"Content-Type": "application/json",
|
|
561
|
+
"Authorization": `Bearer ${authKey}`
|
|
562
|
+
}
|
|
563
|
+
};
|
|
564
|
+
if (options.body) {
|
|
565
|
+
init.body = JSON.stringify(options.body);
|
|
566
|
+
}
|
|
567
|
+
const response = await fetch(url, init);
|
|
568
|
+
if (!response.ok) {
|
|
569
|
+
const error = await response.json().catch(() => ({ error: "Unknown error" }));
|
|
570
|
+
throw new Error(error.error || `HTTP ${response.status}`);
|
|
571
|
+
}
|
|
572
|
+
const text = await response.text();
|
|
573
|
+
if (!text || text === "null") {
|
|
574
|
+
return null;
|
|
575
|
+
}
|
|
576
|
+
const parsed = JSON.parse(text);
|
|
577
|
+
if (options.skipParseDates) {
|
|
578
|
+
return parsed;
|
|
579
|
+
}
|
|
580
|
+
return parseDates(parsed);
|
|
581
|
+
}
|
|
582
|
+
var remoteServerUrl, authKey, DATE_FIELDS, MODEL_MESSAGE_FIELDS, remoteSessionQueries, remoteMessageQueries, remoteToolExecutionQueries, remoteTodoQueries, remoteSkillQueries, remoteActiveStreamQueries, remoteCheckpointQueries, remoteFileBackupQueries, remoteSubagentQueries, remoteIndexStatusQueries;
|
|
583
|
+
var init_remote = __esm({
|
|
584
|
+
"src/db/remote.ts"() {
|
|
585
|
+
"use strict";
|
|
586
|
+
remoteServerUrl = null;
|
|
587
|
+
authKey = null;
|
|
588
|
+
DATE_FIELDS = ["createdAt", "updatedAt", "startedAt", "completedAt", "stoppedAt", "finishedAt", "loadedAt", "indexedAt", "lastFullIndex", "lastIncrementalIndex"];
|
|
589
|
+
MODEL_MESSAGE_FIELDS = ["modelMessage", "modelMessages"];
|
|
590
|
+
remoteSessionQueries = {
|
|
591
|
+
create(data) {
|
|
592
|
+
return api("/sessions", { method: "POST", body: data });
|
|
593
|
+
},
|
|
594
|
+
getById(id) {
|
|
595
|
+
return api(`/sessions/${id}`).catch(() => void 0);
|
|
596
|
+
},
|
|
597
|
+
list(limit = 50, offset = 0) {
|
|
598
|
+
return api(`/sessions?limit=${limit}&offset=${offset}`);
|
|
599
|
+
},
|
|
600
|
+
updateStatus(id, status) {
|
|
601
|
+
return api(`/sessions/${id}`, { method: "PATCH", body: { status } });
|
|
602
|
+
},
|
|
603
|
+
updateModel(id, model) {
|
|
604
|
+
return api(`/sessions/${id}`, { method: "PATCH", body: { model } });
|
|
605
|
+
},
|
|
606
|
+
update(id, updates) {
|
|
607
|
+
return api(`/sessions/${id}`, { method: "PATCH", body: updates });
|
|
608
|
+
},
|
|
609
|
+
delete(id) {
|
|
610
|
+
return api(`/sessions/${id}`, { method: "DELETE" }).then((r) => r?.success ?? false);
|
|
611
|
+
}
|
|
612
|
+
};
|
|
613
|
+
remoteMessageQueries = {
|
|
614
|
+
async getNextSequence(sessionId) {
|
|
615
|
+
const result = await api(`/messages/session/${sessionId}/next-sequence`);
|
|
616
|
+
return result.nextSequence;
|
|
617
|
+
},
|
|
618
|
+
create(sessionId, modelMessage) {
|
|
619
|
+
return api("/messages", { method: "POST", body: { sessionId, modelMessage } });
|
|
620
|
+
},
|
|
621
|
+
addMany(sessionId, modelMessages) {
|
|
622
|
+
return api("/messages/batch", { method: "POST", body: { sessionId, modelMessages } });
|
|
623
|
+
},
|
|
624
|
+
getBySession(sessionId) {
|
|
625
|
+
return api(`/messages/session/${sessionId}`);
|
|
626
|
+
},
|
|
627
|
+
getModelMessages(sessionId) {
|
|
628
|
+
return api(`/messages/session/${sessionId}/model-messages`, { skipParseDates: true });
|
|
629
|
+
},
|
|
630
|
+
async getRecentBySession(sessionId, limit = 50) {
|
|
631
|
+
const messages = await api(`/messages/session/${sessionId}`);
|
|
632
|
+
return messages.slice(-limit);
|
|
633
|
+
},
|
|
634
|
+
async countBySession(sessionId) {
|
|
635
|
+
const result = await api(`/messages/session/${sessionId}/count`);
|
|
636
|
+
return result.count;
|
|
637
|
+
},
|
|
638
|
+
async deleteBySession(sessionId) {
|
|
639
|
+
const result = await api(`/messages/session/${sessionId}`, { method: "DELETE" });
|
|
640
|
+
return result.deleted;
|
|
641
|
+
},
|
|
642
|
+
async deleteFromSequence(sessionId, fromSequence) {
|
|
643
|
+
const result = await api(
|
|
644
|
+
`/messages/session/${sessionId}/from-sequence/${fromSequence}`,
|
|
645
|
+
{ method: "DELETE" }
|
|
646
|
+
);
|
|
647
|
+
return result.deleted;
|
|
648
|
+
}
|
|
835
649
|
};
|
|
836
|
-
|
|
650
|
+
remoteToolExecutionQueries = {
|
|
651
|
+
create(data) {
|
|
652
|
+
return api("/tool-executions", { method: "POST", body: data });
|
|
653
|
+
},
|
|
654
|
+
getById(id) {
|
|
655
|
+
return api(`/tool-executions/${id}`).catch(() => void 0);
|
|
656
|
+
},
|
|
657
|
+
getByToolCallId(toolCallId) {
|
|
658
|
+
return api(`/tool-executions/by-tool-call-id/${toolCallId}`).catch(() => void 0);
|
|
659
|
+
},
|
|
660
|
+
getPendingApprovals(sessionId) {
|
|
661
|
+
return api(`/tool-executions/session/${sessionId}/pending`);
|
|
662
|
+
},
|
|
663
|
+
approve(id) {
|
|
664
|
+
return api(`/tool-executions/${id}`, { method: "PATCH", body: { status: "approved" } });
|
|
665
|
+
},
|
|
666
|
+
reject(id) {
|
|
667
|
+
return api(`/tool-executions/${id}`, { method: "PATCH", body: { status: "rejected" } });
|
|
668
|
+
},
|
|
669
|
+
complete(id, output, error) {
|
|
670
|
+
return api(`/tool-executions/${id}`, {
|
|
671
|
+
method: "PATCH",
|
|
672
|
+
body: { status: error ? "error" : "completed", output, error }
|
|
673
|
+
});
|
|
674
|
+
},
|
|
675
|
+
getBySession(sessionId) {
|
|
676
|
+
return api(`/tool-executions/session/${sessionId}`);
|
|
677
|
+
},
|
|
678
|
+
async deleteAfterTime(sessionId, afterTime) {
|
|
679
|
+
const timestamp = afterTime instanceof Date ? afterTime.getTime() : new Date(afterTime).getTime();
|
|
680
|
+
const result = await api(
|
|
681
|
+
`/tool-executions/session/${sessionId}/after/${timestamp}`,
|
|
682
|
+
{ method: "DELETE" }
|
|
683
|
+
);
|
|
684
|
+
return result.deleted;
|
|
685
|
+
}
|
|
686
|
+
};
|
|
687
|
+
remoteTodoQueries = {
|
|
688
|
+
create(data) {
|
|
689
|
+
return api("/todos", { method: "POST", body: data });
|
|
690
|
+
},
|
|
691
|
+
createMany(sessionId, items) {
|
|
692
|
+
return api("/todos/batch", { method: "POST", body: { sessionId, items } });
|
|
693
|
+
},
|
|
694
|
+
getBySession(sessionId) {
|
|
695
|
+
return api(`/todos/session/${sessionId}`);
|
|
696
|
+
},
|
|
697
|
+
updateStatus(id, status) {
|
|
698
|
+
return api(`/todos/${id}`, { method: "PATCH", body: { status } });
|
|
699
|
+
},
|
|
700
|
+
async delete(id) {
|
|
701
|
+
const result = await api(`/todos/${id}`, { method: "DELETE" });
|
|
702
|
+
return result?.success ?? false;
|
|
703
|
+
},
|
|
704
|
+
async clearSession(sessionId) {
|
|
705
|
+
const result = await api(`/todos/session/${sessionId}`, { method: "DELETE" });
|
|
706
|
+
return result.deleted;
|
|
707
|
+
}
|
|
708
|
+
};
|
|
709
|
+
remoteSkillQueries = {
|
|
710
|
+
load(sessionId, skillName) {
|
|
711
|
+
return api("/skills", { method: "POST", body: { sessionId, skillName } });
|
|
712
|
+
},
|
|
713
|
+
getBySession(sessionId) {
|
|
714
|
+
return api(`/skills/session/${sessionId}`);
|
|
715
|
+
},
|
|
716
|
+
async isLoaded(sessionId, skillName) {
|
|
717
|
+
const result = await api(`/skills/session/${sessionId}/is-loaded/${skillName}`);
|
|
718
|
+
return result.isLoaded;
|
|
719
|
+
}
|
|
720
|
+
};
|
|
721
|
+
remoteActiveStreamQueries = {
|
|
722
|
+
create(sessionId, streamId) {
|
|
723
|
+
return api("/streams", { method: "POST", body: { sessionId, streamId } });
|
|
724
|
+
},
|
|
725
|
+
getBySessionId(sessionId) {
|
|
726
|
+
return api(`/streams/session/${sessionId}`).then((r) => r ?? void 0);
|
|
727
|
+
},
|
|
728
|
+
getByStreamId(streamId) {
|
|
729
|
+
return api(`/streams/by-stream-id/${streamId}`).catch(() => void 0);
|
|
730
|
+
},
|
|
731
|
+
finish(streamId) {
|
|
732
|
+
return api(`/streams/by-stream-id/${streamId}`, { method: "PATCH", body: { status: "finished" } });
|
|
733
|
+
},
|
|
734
|
+
markError(streamId) {
|
|
735
|
+
return api(`/streams/by-stream-id/${streamId}`, { method: "PATCH", body: { status: "error" } });
|
|
736
|
+
},
|
|
737
|
+
async deleteBySession(sessionId) {
|
|
738
|
+
const result = await api(`/streams/session/${sessionId}`, { method: "DELETE" });
|
|
739
|
+
return result.deleted;
|
|
740
|
+
}
|
|
741
|
+
};
|
|
742
|
+
remoteCheckpointQueries = {
|
|
743
|
+
create(data) {
|
|
744
|
+
return api("/checkpoints", { method: "POST", body: data });
|
|
745
|
+
},
|
|
746
|
+
getById(id) {
|
|
747
|
+
return api(`/checkpoints/${id}`).catch(() => void 0);
|
|
748
|
+
},
|
|
749
|
+
getBySession(sessionId) {
|
|
750
|
+
return api(`/checkpoints/session/${sessionId}`);
|
|
751
|
+
},
|
|
752
|
+
getByMessageSequence(sessionId, messageSequence) {
|
|
753
|
+
return api(`/checkpoints/session/${sessionId}/by-sequence/${messageSequence}`).then((r) => r ?? void 0);
|
|
754
|
+
},
|
|
755
|
+
getLatest(sessionId) {
|
|
756
|
+
return api(`/checkpoints/session/${sessionId}/latest`).then((r) => r ?? void 0);
|
|
757
|
+
},
|
|
758
|
+
async deleteAfterSequence(sessionId, messageSequence) {
|
|
759
|
+
const result = await api(
|
|
760
|
+
`/checkpoints/session/${sessionId}/after-sequence/${messageSequence}`,
|
|
761
|
+
{ method: "DELETE" }
|
|
762
|
+
);
|
|
763
|
+
return result.deleted;
|
|
764
|
+
},
|
|
765
|
+
async deleteBySession(sessionId) {
|
|
766
|
+
const result = await api(`/checkpoints/session/${sessionId}`, { method: "DELETE" });
|
|
767
|
+
return result.deleted;
|
|
768
|
+
}
|
|
769
|
+
};
|
|
770
|
+
remoteFileBackupQueries = {
|
|
771
|
+
create(data) {
|
|
772
|
+
return api("/file-backups", { method: "POST", body: data });
|
|
773
|
+
},
|
|
774
|
+
getByCheckpoint(checkpointId) {
|
|
775
|
+
return api(`/file-backups/checkpoint/${checkpointId}`);
|
|
776
|
+
},
|
|
777
|
+
getBySession(sessionId) {
|
|
778
|
+
return api(`/file-backups/session/${sessionId}`);
|
|
779
|
+
},
|
|
780
|
+
getFromSequence(sessionId, messageSequence) {
|
|
781
|
+
return api(`/file-backups/session/${sessionId}/from-sequence/${messageSequence}`);
|
|
782
|
+
},
|
|
783
|
+
async hasBackup(checkpointId, filePath) {
|
|
784
|
+
const result = await api(
|
|
785
|
+
`/file-backups/checkpoint/${checkpointId}/has-backup/${encodeURIComponent(filePath)}`
|
|
786
|
+
);
|
|
787
|
+
return result.hasBackup;
|
|
788
|
+
},
|
|
789
|
+
async deleteBySession(sessionId) {
|
|
790
|
+
const result = await api(`/file-backups/session/${sessionId}`, { method: "DELETE" });
|
|
791
|
+
return result.deleted;
|
|
792
|
+
}
|
|
793
|
+
};
|
|
794
|
+
remoteSubagentQueries = {
|
|
795
|
+
create(data) {
|
|
796
|
+
return api("/subagents", { method: "POST", body: data });
|
|
797
|
+
},
|
|
798
|
+
getById(id) {
|
|
799
|
+
return api(`/subagents/${id}`).catch(() => void 0);
|
|
800
|
+
},
|
|
801
|
+
getByToolCallId(toolCallId) {
|
|
802
|
+
return api(`/subagents/by-tool-call-id/${toolCallId}`).catch(() => void 0);
|
|
803
|
+
},
|
|
804
|
+
getBySession(sessionId) {
|
|
805
|
+
return api(`/subagents/session/${sessionId}`);
|
|
806
|
+
},
|
|
807
|
+
addStep(id, step) {
|
|
808
|
+
return api(`/subagents/${id}/add-step`, { method: "POST", body: { step } }).catch(() => void 0);
|
|
809
|
+
},
|
|
810
|
+
complete(id, result) {
|
|
811
|
+
return api(`/subagents/${id}`, { method: "PATCH", body: { status: "completed", result } }).catch(() => void 0);
|
|
812
|
+
},
|
|
813
|
+
markError(id, error) {
|
|
814
|
+
return api(`/subagents/${id}`, { method: "PATCH", body: { status: "error", error } }).catch(() => void 0);
|
|
815
|
+
},
|
|
816
|
+
cancel(id) {
|
|
817
|
+
return api(`/subagents/${id}`, { method: "PATCH", body: { status: "cancelled" } }).catch(() => void 0);
|
|
818
|
+
},
|
|
819
|
+
async deleteBySession(sessionId) {
|
|
820
|
+
const result = await api(`/subagents/session/${sessionId}`, { method: "DELETE" });
|
|
821
|
+
return result.deleted;
|
|
822
|
+
}
|
|
823
|
+
};
|
|
824
|
+
remoteIndexStatusQueries = {
|
|
825
|
+
upsert(_db, data) {
|
|
826
|
+
return api("/index-status", {
|
|
827
|
+
method: "POST",
|
|
828
|
+
body: {
|
|
829
|
+
...data,
|
|
830
|
+
lastFullIndex: data.lastFullIndex?.toISOString(),
|
|
831
|
+
lastIncrementalIndex: data.lastIncrementalIndex?.toISOString()
|
|
832
|
+
}
|
|
833
|
+
});
|
|
834
|
+
},
|
|
835
|
+
get(_db, namespace) {
|
|
836
|
+
return api(`/index-status/namespace/${namespace}`).then((r) => r ?? void 0);
|
|
837
|
+
},
|
|
838
|
+
async delete(_db, namespace) {
|
|
839
|
+
const result = await api(`/index-status/namespace/${namespace}`, { method: "DELETE" });
|
|
840
|
+
return result?.success ?? false;
|
|
841
|
+
},
|
|
842
|
+
list(_db) {
|
|
843
|
+
return api("/index-status");
|
|
844
|
+
}
|
|
845
|
+
};
|
|
846
|
+
}
|
|
847
|
+
});
|
|
848
|
+
|
|
849
|
+
// src/db/index.ts
|
|
850
|
+
function initDatabase(config) {
|
|
851
|
+
initRemoteDatabase(config.url, config.authKey);
|
|
852
|
+
initialized = true;
|
|
837
853
|
}
|
|
838
|
-
function
|
|
839
|
-
if (
|
|
840
|
-
|
|
854
|
+
function getDb() {
|
|
855
|
+
if (!initialized) {
|
|
856
|
+
throw new Error("Database not initialized. Call initDatabase first.");
|
|
841
857
|
}
|
|
842
|
-
return
|
|
858
|
+
return {};
|
|
843
859
|
}
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
860
|
+
function closeDatabase() {
|
|
861
|
+
closeRemoteDatabase();
|
|
862
|
+
initialized = false;
|
|
863
|
+
}
|
|
864
|
+
var initialized, sessionQueries, messageQueries, toolExecutionQueries, todoQueries, skillQueries, activeStreamQueries, checkpointQueries, fileBackupQueries, subagentQueries, indexStatusQueries;
|
|
865
|
+
var init_db = __esm({
|
|
866
|
+
"src/db/index.ts"() {
|
|
847
867
|
"use strict";
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
openai: "OPENAI_API_KEY",
|
|
861
|
-
google: "GOOGLE_GENERATIVE_AI_API_KEY",
|
|
862
|
-
xai: "XAI_API_KEY",
|
|
863
|
-
"ai-gateway": "AI_GATEWAY_API_KEY"
|
|
864
|
-
};
|
|
865
|
-
SUPPORTED_PROVIDERS = Object.keys(PROVIDER_ENV_MAP);
|
|
868
|
+
init_remote();
|
|
869
|
+
initialized = false;
|
|
870
|
+
sessionQueries = remoteSessionQueries;
|
|
871
|
+
messageQueries = remoteMessageQueries;
|
|
872
|
+
toolExecutionQueries = remoteToolExecutionQueries;
|
|
873
|
+
todoQueries = remoteTodoQueries;
|
|
874
|
+
skillQueries = remoteSkillQueries;
|
|
875
|
+
activeStreamQueries = remoteActiveStreamQueries;
|
|
876
|
+
checkpointQueries = remoteCheckpointQueries;
|
|
877
|
+
fileBackupQueries = remoteFileBackupQueries;
|
|
878
|
+
subagentQueries = remoteSubagentQueries;
|
|
879
|
+
indexStatusQueries = remoteIndexStatusQueries;
|
|
866
880
|
}
|
|
867
881
|
});
|
|
868
882
|
|
|
@@ -1607,18 +1621,176 @@ var init_semantic_search = __esm({
|
|
|
1607
1621
|
import {
|
|
1608
1622
|
streamText as streamText2,
|
|
1609
1623
|
generateText as generateText3,
|
|
1610
|
-
tool as
|
|
1624
|
+
tool as tool12,
|
|
1611
1625
|
stepCountIs as stepCountIs2
|
|
1612
1626
|
} from "ai";
|
|
1613
1627
|
|
|
1614
1628
|
// src/agent/model.ts
|
|
1615
1629
|
import { gateway } from "@ai-sdk/gateway";
|
|
1630
|
+
|
|
1631
|
+
// src/agent/remote-model.ts
|
|
1632
|
+
function serializePrompt(prompt) {
|
|
1633
|
+
return prompt.map((msg) => {
|
|
1634
|
+
if (!Array.isArray(msg.content)) return msg;
|
|
1635
|
+
return {
|
|
1636
|
+
...msg,
|
|
1637
|
+
content: msg.content.map((part) => {
|
|
1638
|
+
if (part.type === "file" && part.data instanceof Uint8Array) {
|
|
1639
|
+
return {
|
|
1640
|
+
...part,
|
|
1641
|
+
data: Buffer.from(part.data).toString("base64"),
|
|
1642
|
+
_base64: true
|
|
1643
|
+
};
|
|
1644
|
+
}
|
|
1645
|
+
return part;
|
|
1646
|
+
})
|
|
1647
|
+
};
|
|
1648
|
+
});
|
|
1649
|
+
}
|
|
1650
|
+
function deserializeValue(value) {
|
|
1651
|
+
if (value && typeof value === "object") {
|
|
1652
|
+
if (value.__uint8array && typeof value.data === "string") {
|
|
1653
|
+
return Buffer.from(value.data, "base64");
|
|
1654
|
+
}
|
|
1655
|
+
if (Array.isArray(value)) {
|
|
1656
|
+
return value.map(deserializeValue);
|
|
1657
|
+
}
|
|
1658
|
+
const result = {};
|
|
1659
|
+
for (const [k, v] of Object.entries(value)) {
|
|
1660
|
+
result[k] = deserializeValue(v);
|
|
1661
|
+
}
|
|
1662
|
+
return result;
|
|
1663
|
+
}
|
|
1664
|
+
return value;
|
|
1665
|
+
}
|
|
1666
|
+
function prepareOptions(options) {
|
|
1667
|
+
const { abortSignal, ...rest } = options;
|
|
1668
|
+
return {
|
|
1669
|
+
...rest,
|
|
1670
|
+
prompt: serializePrompt(options.prompt)
|
|
1671
|
+
};
|
|
1672
|
+
}
|
|
1673
|
+
function createRemoteModel(modelId, config) {
|
|
1674
|
+
const baseUrl = config.url.replace(/\/$/, "");
|
|
1675
|
+
const headers = {
|
|
1676
|
+
"Content-Type": "application/json",
|
|
1677
|
+
"Authorization": `Bearer ${config.authKey}`
|
|
1678
|
+
};
|
|
1679
|
+
return {
|
|
1680
|
+
specificationVersion: "v3",
|
|
1681
|
+
provider: "remote-proxy",
|
|
1682
|
+
modelId,
|
|
1683
|
+
supportedUrls: {},
|
|
1684
|
+
async doGenerate(options) {
|
|
1685
|
+
const res = await fetch(`${baseUrl}/inference/generate`, {
|
|
1686
|
+
method: "POST",
|
|
1687
|
+
headers,
|
|
1688
|
+
body: JSON.stringify({
|
|
1689
|
+
modelId,
|
|
1690
|
+
options: prepareOptions(options)
|
|
1691
|
+
}),
|
|
1692
|
+
signal: options.abortSignal
|
|
1693
|
+
});
|
|
1694
|
+
if (!res.ok) {
|
|
1695
|
+
const err = await res.json().catch(() => ({}));
|
|
1696
|
+
throw new Error(
|
|
1697
|
+
`Remote inference failed (${res.status}): ${err.error || res.statusText}`
|
|
1698
|
+
);
|
|
1699
|
+
}
|
|
1700
|
+
const result = await res.json();
|
|
1701
|
+
return deserializeValue(result);
|
|
1702
|
+
},
|
|
1703
|
+
async doStream(options) {
|
|
1704
|
+
const res = await fetch(`${baseUrl}/inference/stream`, {
|
|
1705
|
+
method: "POST",
|
|
1706
|
+
headers,
|
|
1707
|
+
body: JSON.stringify({
|
|
1708
|
+
modelId,
|
|
1709
|
+
options: prepareOptions(options)
|
|
1710
|
+
}),
|
|
1711
|
+
signal: options.abortSignal
|
|
1712
|
+
});
|
|
1713
|
+
if (!res.ok) {
|
|
1714
|
+
const err = await res.json().catch(() => ({}));
|
|
1715
|
+
throw new Error(
|
|
1716
|
+
`Remote inference failed (${res.status}): ${err.error || res.statusText}`
|
|
1717
|
+
);
|
|
1718
|
+
}
|
|
1719
|
+
const reader = res.body.getReader();
|
|
1720
|
+
const decoder = new TextDecoder();
|
|
1721
|
+
let buffer = "";
|
|
1722
|
+
const stream = new ReadableStream({
|
|
1723
|
+
async pull(controller) {
|
|
1724
|
+
while (true) {
|
|
1725
|
+
const { done, value } = await reader.read();
|
|
1726
|
+
if (done) {
|
|
1727
|
+
if (buffer.trim()) {
|
|
1728
|
+
try {
|
|
1729
|
+
const parsed = deserializeValue(JSON.parse(buffer.trim()));
|
|
1730
|
+
if (parsed.type === "error") {
|
|
1731
|
+
controller.error(new Error(parsed.error));
|
|
1732
|
+
} else {
|
|
1733
|
+
controller.enqueue(parsed);
|
|
1734
|
+
}
|
|
1735
|
+
} catch {
|
|
1736
|
+
}
|
|
1737
|
+
}
|
|
1738
|
+
controller.close();
|
|
1739
|
+
return;
|
|
1740
|
+
}
|
|
1741
|
+
buffer += decoder.decode(value, { stream: true });
|
|
1742
|
+
const lines = buffer.split("\n");
|
|
1743
|
+
buffer = lines.pop() || "";
|
|
1744
|
+
for (const line of lines) {
|
|
1745
|
+
if (!line.trim()) continue;
|
|
1746
|
+
try {
|
|
1747
|
+
const parsed = deserializeValue(JSON.parse(line));
|
|
1748
|
+
if (parsed.type === "error") {
|
|
1749
|
+
controller.error(new Error(parsed.error));
|
|
1750
|
+
return;
|
|
1751
|
+
}
|
|
1752
|
+
controller.enqueue(parsed);
|
|
1753
|
+
} catch {
|
|
1754
|
+
}
|
|
1755
|
+
}
|
|
1756
|
+
}
|
|
1757
|
+
},
|
|
1758
|
+
cancel() {
|
|
1759
|
+
reader.cancel();
|
|
1760
|
+
}
|
|
1761
|
+
});
|
|
1762
|
+
const responseHeaders = {};
|
|
1763
|
+
res.headers.forEach((v, k) => {
|
|
1764
|
+
if (k.startsWith("x-upstream-")) {
|
|
1765
|
+
responseHeaders[k.replace("x-upstream-", "")] = v;
|
|
1766
|
+
}
|
|
1767
|
+
});
|
|
1768
|
+
return {
|
|
1769
|
+
stream,
|
|
1770
|
+
response: Object.keys(responseHeaders).length > 0 ? { headers: responseHeaders } : void 0
|
|
1771
|
+
};
|
|
1772
|
+
}
|
|
1773
|
+
};
|
|
1774
|
+
}
|
|
1775
|
+
|
|
1776
|
+
// src/agent/model.ts
|
|
1777
|
+
init_config();
|
|
1616
1778
|
var ANTHROPIC_PREFIX = "anthropic/";
|
|
1617
1779
|
function isAnthropicModel(modelId) {
|
|
1618
1780
|
const normalized = modelId.trim().toLowerCase();
|
|
1619
1781
|
return normalized.startsWith(ANTHROPIC_PREFIX) || normalized.startsWith("claude-");
|
|
1620
1782
|
}
|
|
1621
1783
|
function resolveModel(modelId) {
|
|
1784
|
+
try {
|
|
1785
|
+
const config = getConfig();
|
|
1786
|
+
if (config.resolvedRemoteServer.isConfigured) {
|
|
1787
|
+
return createRemoteModel(modelId.trim(), {
|
|
1788
|
+
url: config.resolvedRemoteServer.url,
|
|
1789
|
+
authKey: config.resolvedRemoteServer.authKey
|
|
1790
|
+
});
|
|
1791
|
+
}
|
|
1792
|
+
} catch {
|
|
1793
|
+
}
|
|
1622
1794
|
return gateway(modelId.trim());
|
|
1623
1795
|
}
|
|
1624
1796
|
var SUBAGENT_MODELS = {
|
|
@@ -1630,7 +1802,7 @@ var SUBAGENT_MODELS = {
|
|
|
1630
1802
|
// src/agent/index.ts
|
|
1631
1803
|
init_db();
|
|
1632
1804
|
init_config();
|
|
1633
|
-
import { z as
|
|
1805
|
+
import { z as z13 } from "zod";
|
|
1634
1806
|
import { nanoid as nanoid3 } from "nanoid";
|
|
1635
1807
|
|
|
1636
1808
|
// src/tools/bash.ts
|
|
@@ -4957,6 +5129,59 @@ Context: ${context}` : query;
|
|
|
4957
5129
|
|
|
4958
5130
|
// src/tools/index.ts
|
|
4959
5131
|
init_semantic_search();
|
|
5132
|
+
|
|
5133
|
+
// src/tools/task.ts
|
|
5134
|
+
import { tool as tool11 } from "ai";
|
|
5135
|
+
import { z as z12 } from "zod";
|
|
5136
|
+
import Ajv from "ajv";
|
|
5137
|
+
var ajv = new Ajv({ allErrors: true });
|
|
5138
|
+
function createCompleteTaskTool(options) {
|
|
5139
|
+
const validate = ajv.compile(options.outputSchema);
|
|
5140
|
+
return tool11({
|
|
5141
|
+
description: "Call this tool when you have completed the task. Pass the result as a JSON object matching the required output schema. If the result does not match the schema, you will receive validation errors and should fix and retry.",
|
|
5142
|
+
inputSchema: z12.object({
|
|
5143
|
+
result: z12.record(z12.string(), z12.unknown()).describe("The task result as a JSON object matching the output schema")
|
|
5144
|
+
}),
|
|
5145
|
+
execute: async (input) => {
|
|
5146
|
+
const valid = validate(input.result);
|
|
5147
|
+
if (!valid) {
|
|
5148
|
+
const errors = validate.errors?.map((e) => ({
|
|
5149
|
+
path: e.instancePath || "/",
|
|
5150
|
+
message: e.message,
|
|
5151
|
+
params: e.params
|
|
5152
|
+
}));
|
|
5153
|
+
return {
|
|
5154
|
+
status: "validation_error",
|
|
5155
|
+
message: "The result does not match the required output schema. Fix the errors and call complete_task again.",
|
|
5156
|
+
errors,
|
|
5157
|
+
expectedSchema: options.outputSchema
|
|
5158
|
+
};
|
|
5159
|
+
}
|
|
5160
|
+
options.onComplete({ status: "completed", result: input.result });
|
|
5161
|
+
return {
|
|
5162
|
+
status: "completed",
|
|
5163
|
+
message: "Task completed successfully."
|
|
5164
|
+
};
|
|
5165
|
+
}
|
|
5166
|
+
});
|
|
5167
|
+
}
|
|
5168
|
+
function createTaskFailedTool(options) {
|
|
5169
|
+
return tool11({
|
|
5170
|
+
description: "Call this tool if you are unable to complete the task. Provide a clear reason explaining why the task cannot be completed.",
|
|
5171
|
+
inputSchema: z12.object({
|
|
5172
|
+
reason: z12.string().describe("Explanation of why the task cannot be completed")
|
|
5173
|
+
}),
|
|
5174
|
+
execute: async (input) => {
|
|
5175
|
+
options.onComplete({ status: "failed", error: input.reason });
|
|
5176
|
+
return {
|
|
5177
|
+
status: "failed",
|
|
5178
|
+
message: `Task marked as failed: ${input.reason}`
|
|
5179
|
+
};
|
|
5180
|
+
}
|
|
5181
|
+
});
|
|
5182
|
+
}
|
|
5183
|
+
|
|
5184
|
+
// src/tools/index.ts
|
|
4960
5185
|
init_semantic();
|
|
4961
5186
|
init_semantic_search();
|
|
4962
5187
|
async function createTools(options) {
|
|
@@ -5008,6 +5233,10 @@ async function createTools(options) {
|
|
|
5008
5233
|
} catch {
|
|
5009
5234
|
}
|
|
5010
5235
|
}
|
|
5236
|
+
if (options.taskTools) {
|
|
5237
|
+
tools.complete_task = createCompleteTaskTool(options.taskTools);
|
|
5238
|
+
tools.task_failed = createTaskFailedTool(options.taskTools);
|
|
5239
|
+
}
|
|
5011
5240
|
return tools;
|
|
5012
5241
|
}
|
|
5013
5242
|
|
|
@@ -5325,6 +5554,30 @@ function formatTodosForContext(todos) {
|
|
|
5325
5554
|
}
|
|
5326
5555
|
return lines.join("\n");
|
|
5327
5556
|
}
|
|
5557
|
+
function buildTaskPromptAddendum(outputSchema) {
|
|
5558
|
+
return `
|
|
5559
|
+
## Task Mode
|
|
5560
|
+
|
|
5561
|
+
You are running in **task mode**. You have been given a specific task to complete autonomously.
|
|
5562
|
+
|
|
5563
|
+
### Rules
|
|
5564
|
+
1. Work independently \u2014 no human will approve tool calls. All tools run without approval.
|
|
5565
|
+
2. Keep working until the task is fully complete.
|
|
5566
|
+
3. When done, call the \`complete_task\` tool with a JSON result matching the output schema below.
|
|
5567
|
+
4. If you determine the task is impossible or encounter an unrecoverable error, call the \`task_failed\` tool with a clear reason.
|
|
5568
|
+
5. Do NOT stop without calling one of these two tools.
|
|
5569
|
+
|
|
5570
|
+
### Output Schema
|
|
5571
|
+
The \`complete_task\` tool expects a \`result\` object matching this JSON Schema:
|
|
5572
|
+
\`\`\`json
|
|
5573
|
+
${JSON.stringify(outputSchema, null, 2)}
|
|
5574
|
+
\`\`\`
|
|
5575
|
+
|
|
5576
|
+
### Completion Tools
|
|
5577
|
+
- **\`complete_task({ result: ... })\`** \u2014 Call when the task is done. The result is validated against the schema above. If validation fails you will get errors back \u2014 fix and retry.
|
|
5578
|
+
- **\`task_failed({ reason: "..." })\`** \u2014 Call only if the task truly cannot be completed.
|
|
5579
|
+
`;
|
|
5580
|
+
}
|
|
5328
5581
|
function createSummaryPrompt(conversationHistory) {
|
|
5329
5582
|
return `Please provide a concise summary of the following conversation history. Focus on:
|
|
5330
5583
|
1. The main task or goal being worked on
|
|
@@ -5586,6 +5839,25 @@ ${this.summary}`
|
|
|
5586
5839
|
}
|
|
5587
5840
|
};
|
|
5588
5841
|
|
|
5842
|
+
// src/utils/webhook.ts
|
|
5843
|
+
async function sendWebhook(url, event) {
|
|
5844
|
+
try {
|
|
5845
|
+
const controller = new AbortController();
|
|
5846
|
+
const timeout = setTimeout(() => controller.abort(), 5e3);
|
|
5847
|
+
await fetch(url, {
|
|
5848
|
+
method: "POST",
|
|
5849
|
+
headers: {
|
|
5850
|
+
"Content-Type": "application/json",
|
|
5851
|
+
"X-SparkECoder-Event": event.type
|
|
5852
|
+
},
|
|
5853
|
+
body: JSON.stringify(event),
|
|
5854
|
+
signal: controller.signal
|
|
5855
|
+
});
|
|
5856
|
+
clearTimeout(timeout);
|
|
5857
|
+
} catch {
|
|
5858
|
+
}
|
|
5859
|
+
}
|
|
5860
|
+
|
|
5589
5861
|
// src/agent/index.ts
|
|
5590
5862
|
var approvalResolvers = /* @__PURE__ */ new Map();
|
|
5591
5863
|
var Agent = class _Agent {
|
|
@@ -5805,6 +6077,145 @@ ${prompt}` });
|
|
|
5805
6077
|
steps: result.steps
|
|
5806
6078
|
};
|
|
5807
6079
|
}
|
|
6080
|
+
/**
|
|
6081
|
+
* Run the agent in task mode — loops autonomously until the agent calls
|
|
6082
|
+
* complete_task or task_failed (or hits maxIterations).
|
|
6083
|
+
* All tools run without approval. Webhook events are fired throughout.
|
|
6084
|
+
*/
|
|
6085
|
+
async runTask(options) {
|
|
6086
|
+
const config = getConfig();
|
|
6087
|
+
const maxIterations = options.taskConfig.maxIterations ?? 50;
|
|
6088
|
+
const webhookUrl = options.taskConfig.webhookUrl;
|
|
6089
|
+
const fireWebhook = (type, data) => {
|
|
6090
|
+
if (!webhookUrl) return;
|
|
6091
|
+
sendWebhook(webhookUrl, {
|
|
6092
|
+
type,
|
|
6093
|
+
taskId: this.session.id,
|
|
6094
|
+
sessionId: this.session.id,
|
|
6095
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6096
|
+
data
|
|
6097
|
+
});
|
|
6098
|
+
};
|
|
6099
|
+
const completion = { signal: null };
|
|
6100
|
+
const onComplete = (signal) => {
|
|
6101
|
+
completion.signal = signal;
|
|
6102
|
+
};
|
|
6103
|
+
const taskTools = await createTools({
|
|
6104
|
+
sessionId: this.session.id,
|
|
6105
|
+
workingDirectory: this.session.workingDirectory,
|
|
6106
|
+
skillsDirectories: config.resolvedSkillsDirectories,
|
|
6107
|
+
onBashProgress: options.onToolProgress ? (progress) => options.onToolProgress({ toolName: "bash", data: progress }) : void 0,
|
|
6108
|
+
onWriteFileProgress: options.onToolProgress ? (progress) => options.onToolProgress({ toolName: "write_file", data: progress }) : void 0,
|
|
6109
|
+
onSearchProgress: options.onToolProgress ? (progress) => options.onToolProgress({ toolName: "explore_agent", data: progress }) : void 0,
|
|
6110
|
+
taskTools: {
|
|
6111
|
+
outputSchema: options.taskConfig.outputSchema,
|
|
6112
|
+
onComplete
|
|
6113
|
+
}
|
|
6114
|
+
});
|
|
6115
|
+
const baseSystemPrompt = await buildSystemPrompt({
|
|
6116
|
+
workingDirectory: this.session.workingDirectory,
|
|
6117
|
+
skillsDirectories: config.resolvedSkillsDirectories,
|
|
6118
|
+
sessionId: this.session.id,
|
|
6119
|
+
discoveredSkills: config.discoveredSkills,
|
|
6120
|
+
activeFiles: []
|
|
6121
|
+
});
|
|
6122
|
+
const taskAddendum = buildTaskPromptAddendum(options.taskConfig.outputSchema);
|
|
6123
|
+
const systemPrompt = `${baseSystemPrompt}
|
|
6124
|
+
|
|
6125
|
+
${taskAddendum}`;
|
|
6126
|
+
fireWebhook("task.started", { prompt: options.prompt });
|
|
6127
|
+
this.context.addUserMessage(options.prompt);
|
|
6128
|
+
let iteration = 0;
|
|
6129
|
+
while (iteration < maxIterations) {
|
|
6130
|
+
iteration++;
|
|
6131
|
+
if (options.abortSignal?.aborted) {
|
|
6132
|
+
const cancelError = "Task was cancelled";
|
|
6133
|
+
fireWebhook("task.failed", { status: "failed", error: cancelError, iterations: iteration });
|
|
6134
|
+
return { status: "failed", error: cancelError, iterations: iteration };
|
|
6135
|
+
}
|
|
6136
|
+
const messages = await this.context.getMessages();
|
|
6137
|
+
const useAnthropic = isAnthropicModel(this.session.model);
|
|
6138
|
+
const result = await generateText3({
|
|
6139
|
+
model: resolveModel(this.session.model),
|
|
6140
|
+
system: systemPrompt,
|
|
6141
|
+
messages,
|
|
6142
|
+
tools: taskTools,
|
|
6143
|
+
stopWhen: stepCountIs2(500),
|
|
6144
|
+
abortSignal: options.abortSignal,
|
|
6145
|
+
providerOptions: useAnthropic ? {
|
|
6146
|
+
anthropic: {
|
|
6147
|
+
thinking: { type: "enabled", budgetTokens: 1e4 }
|
|
6148
|
+
}
|
|
6149
|
+
} : void 0,
|
|
6150
|
+
onStepFinish: (step) => {
|
|
6151
|
+
options.onStepFinish?.(step);
|
|
6152
|
+
fireWebhook("task.step_finished", { iteration, text: step.text });
|
|
6153
|
+
}
|
|
6154
|
+
});
|
|
6155
|
+
const responseMessages = result.response.messages;
|
|
6156
|
+
this.context.addResponseMessages(responseMessages);
|
|
6157
|
+
if (result.text) {
|
|
6158
|
+
options.onText?.(result.text);
|
|
6159
|
+
fireWebhook("task.message", { iteration, text: result.text });
|
|
6160
|
+
}
|
|
6161
|
+
for (const step of result.steps) {
|
|
6162
|
+
if (step.toolCalls) {
|
|
6163
|
+
for (const tc of step.toolCalls) {
|
|
6164
|
+
options.onToolCall?.({ toolCallId: tc.toolCallId, toolName: tc.toolName, input: tc.args });
|
|
6165
|
+
fireWebhook("task.tool_call", { iteration, toolName: tc.toolName, toolCallId: tc.toolCallId, input: tc.args });
|
|
6166
|
+
}
|
|
6167
|
+
}
|
|
6168
|
+
if (step.toolResults) {
|
|
6169
|
+
for (const tr of step.toolResults) {
|
|
6170
|
+
options.onToolResult?.({ toolCallId: tr.toolCallId, toolName: tr.toolName, output: tr.result });
|
|
6171
|
+
fireWebhook("task.tool_result", { iteration, toolName: tr.toolName, toolCallId: tr.toolCallId, output: tr.result });
|
|
6172
|
+
}
|
|
6173
|
+
}
|
|
6174
|
+
}
|
|
6175
|
+
if (completion.signal) {
|
|
6176
|
+
const sig = completion.signal;
|
|
6177
|
+
const finalStatus = sig.status;
|
|
6178
|
+
const eventType = finalStatus === "completed" ? "task.completed" : "task.failed";
|
|
6179
|
+
fireWebhook(eventType, {
|
|
6180
|
+
status: finalStatus,
|
|
6181
|
+
result: sig.result,
|
|
6182
|
+
error: sig.error,
|
|
6183
|
+
iterations: iteration
|
|
6184
|
+
});
|
|
6185
|
+
const updatedTask2 = {
|
|
6186
|
+
...options.taskConfig,
|
|
6187
|
+
status: finalStatus,
|
|
6188
|
+
result: sig.result,
|
|
6189
|
+
error: sig.error,
|
|
6190
|
+
iterations: iteration
|
|
6191
|
+
};
|
|
6192
|
+
await sessionQueries.update(this.session.id, {
|
|
6193
|
+
config: { ...this.session.config, task: updatedTask2 }
|
|
6194
|
+
});
|
|
6195
|
+
return {
|
|
6196
|
+
status: finalStatus,
|
|
6197
|
+
result: sig.result,
|
|
6198
|
+
error: sig.error,
|
|
6199
|
+
iterations: iteration
|
|
6200
|
+
};
|
|
6201
|
+
}
|
|
6202
|
+
this.context.addUserMessage(
|
|
6203
|
+
"Continue working on the task. When done, call `complete_task` with the result. If you cannot complete it, call `task_failed` with a reason."
|
|
6204
|
+
);
|
|
6205
|
+
}
|
|
6206
|
+
const timeoutError = `Task did not complete within ${maxIterations} iterations`;
|
|
6207
|
+
fireWebhook("task.failed", { status: "failed", error: timeoutError, iterations: iteration });
|
|
6208
|
+
const updatedTask = {
|
|
6209
|
+
...options.taskConfig,
|
|
6210
|
+
status: "failed",
|
|
6211
|
+
error: timeoutError,
|
|
6212
|
+
iterations: iteration
|
|
6213
|
+
};
|
|
6214
|
+
await sessionQueries.update(this.session.id, {
|
|
6215
|
+
config: { ...this.session.config, task: updatedTask }
|
|
6216
|
+
});
|
|
6217
|
+
return { status: "failed", error: timeoutError, iterations: iteration };
|
|
6218
|
+
}
|
|
5808
6219
|
/**
|
|
5809
6220
|
* Wrap tools to add approval checking
|
|
5810
6221
|
*/
|
|
@@ -5818,9 +6229,9 @@ ${prompt}` });
|
|
|
5818
6229
|
wrappedTools[name] = originalTool;
|
|
5819
6230
|
continue;
|
|
5820
6231
|
}
|
|
5821
|
-
wrappedTools[name] =
|
|
6232
|
+
wrappedTools[name] = tool12({
|
|
5822
6233
|
description: originalTool.description || "",
|
|
5823
|
-
inputSchema: originalTool.inputSchema ||
|
|
6234
|
+
inputSchema: originalTool.inputSchema || z13.object({}),
|
|
5824
6235
|
execute: async (input, toolOptions) => {
|
|
5825
6236
|
const toolCallId = toolOptions.toolCallId || nanoid3();
|
|
5826
6237
|
const execution = toolExecutionQueries.create({
|
|
@@ -5931,7 +6342,7 @@ ${prompt}` });
|
|
|
5931
6342
|
|
|
5932
6343
|
// src/server/index.ts
|
|
5933
6344
|
import "dotenv/config";
|
|
5934
|
-
import { Hono as
|
|
6345
|
+
import { Hono as Hono6 } from "hono";
|
|
5935
6346
|
import { serve } from "@hono/node-server";
|
|
5936
6347
|
import { cors } from "hono/cors";
|
|
5937
6348
|
import { logger } from "hono/logger";
|
|
@@ -5945,7 +6356,7 @@ import { fileURLToPath as fileURLToPath4 } from "url";
|
|
|
5945
6356
|
init_db();
|
|
5946
6357
|
import { Hono } from "hono";
|
|
5947
6358
|
import { zValidator } from "@hono/zod-validator";
|
|
5948
|
-
import { z as
|
|
6359
|
+
import { z as z14 } from "zod";
|
|
5949
6360
|
import { existsSync as existsSync13, mkdirSync as mkdirSync3, writeFileSync as writeFileSync2, readdirSync, statSync as statSync2, unlinkSync } from "fs";
|
|
5950
6361
|
import { readdir as readdir5 } from "fs/promises";
|
|
5951
6362
|
import { join as join5, basename as basename4, extname as extname7, relative as relative9 } from "path";
|
|
@@ -5981,18 +6392,18 @@ function cleanupPendingInputs() {
|
|
|
5981
6392
|
}
|
|
5982
6393
|
}
|
|
5983
6394
|
}
|
|
5984
|
-
var createSessionSchema =
|
|
5985
|
-
name:
|
|
5986
|
-
workingDirectory:
|
|
5987
|
-
model:
|
|
5988
|
-
toolApprovals:
|
|
6395
|
+
var createSessionSchema = z14.object({
|
|
6396
|
+
name: z14.string().optional(),
|
|
6397
|
+
workingDirectory: z14.string().optional(),
|
|
6398
|
+
model: z14.string().optional(),
|
|
6399
|
+
toolApprovals: z14.record(z14.string(), z14.boolean()).optional()
|
|
5989
6400
|
});
|
|
5990
|
-
var paginationQuerySchema =
|
|
5991
|
-
limit:
|
|
5992
|
-
offset:
|
|
6401
|
+
var paginationQuerySchema = z14.object({
|
|
6402
|
+
limit: z14.string().optional(),
|
|
6403
|
+
offset: z14.string().optional()
|
|
5993
6404
|
});
|
|
5994
|
-
var messagesQuerySchema =
|
|
5995
|
-
limit:
|
|
6405
|
+
var messagesQuerySchema = z14.object({
|
|
6406
|
+
limit: z14.string().optional()
|
|
5996
6407
|
});
|
|
5997
6408
|
sessions.get(
|
|
5998
6409
|
"/",
|
|
@@ -6131,10 +6542,10 @@ sessions.get("/:id/tools", async (c) => {
|
|
|
6131
6542
|
count: executions.length
|
|
6132
6543
|
});
|
|
6133
6544
|
});
|
|
6134
|
-
var updateSessionSchema =
|
|
6135
|
-
model:
|
|
6136
|
-
name:
|
|
6137
|
-
toolApprovals:
|
|
6545
|
+
var updateSessionSchema = z14.object({
|
|
6546
|
+
model: z14.string().optional(),
|
|
6547
|
+
name: z14.string().optional(),
|
|
6548
|
+
toolApprovals: z14.record(z14.string(), z14.boolean()).optional()
|
|
6138
6549
|
});
|
|
6139
6550
|
sessions.patch(
|
|
6140
6551
|
"/:id",
|
|
@@ -6204,8 +6615,8 @@ sessions.post("/:id/clear", async (c) => {
|
|
|
6204
6615
|
await agent.clearContext();
|
|
6205
6616
|
return c.json({ success: true, sessionId: id });
|
|
6206
6617
|
});
|
|
6207
|
-
var pendingInputSchema =
|
|
6208
|
-
text:
|
|
6618
|
+
var pendingInputSchema = z14.object({
|
|
6619
|
+
text: z14.string()
|
|
6209
6620
|
});
|
|
6210
6621
|
sessions.post(
|
|
6211
6622
|
"/:id/pending-input",
|
|
@@ -6236,13 +6647,13 @@ sessions.get("/:id/pending-input", async (c) => {
|
|
|
6236
6647
|
createdAt: pending.createdAt.toISOString()
|
|
6237
6648
|
});
|
|
6238
6649
|
});
|
|
6239
|
-
var devtoolsContextSchema =
|
|
6240
|
-
url:
|
|
6241
|
-
path:
|
|
6242
|
-
pageName:
|
|
6243
|
-
screenWidth:
|
|
6244
|
-
screenHeight:
|
|
6245
|
-
devicePixelRatio:
|
|
6650
|
+
var devtoolsContextSchema = z14.object({
|
|
6651
|
+
url: z14.string(),
|
|
6652
|
+
path: z14.string(),
|
|
6653
|
+
pageName: z14.string().optional(),
|
|
6654
|
+
screenWidth: z14.number().optional(),
|
|
6655
|
+
screenHeight: z14.number().optional(),
|
|
6656
|
+
devicePixelRatio: z14.number().optional()
|
|
6246
6657
|
});
|
|
6247
6658
|
sessions.post(
|
|
6248
6659
|
"/:id/devtools-context",
|
|
@@ -6533,10 +6944,10 @@ sessions.delete("/:id/attachments/:attachmentId", async (c) => {
|
|
|
6533
6944
|
unlinkSync(filePath);
|
|
6534
6945
|
return c.json({ success: true, id: attachmentId });
|
|
6535
6946
|
});
|
|
6536
|
-
var filesQuerySchema =
|
|
6537
|
-
query:
|
|
6947
|
+
var filesQuerySchema = z14.object({
|
|
6948
|
+
query: z14.string().optional(),
|
|
6538
6949
|
// Filter query (e.g., "src/com" to match "src/components")
|
|
6539
|
-
limit:
|
|
6950
|
+
limit: z14.string().optional()
|
|
6540
6951
|
// Max results (default 50)
|
|
6541
6952
|
});
|
|
6542
6953
|
var IGNORED_DIRECTORIES = /* @__PURE__ */ new Set([
|
|
@@ -6714,7 +7125,7 @@ sessions.get(
|
|
|
6714
7125
|
init_db();
|
|
6715
7126
|
import { Hono as Hono2 } from "hono";
|
|
6716
7127
|
import { zValidator as zValidator2 } from "@hono/zod-validator";
|
|
6717
|
-
import { z as
|
|
7128
|
+
import { z as z15 } from "zod";
|
|
6718
7129
|
import { existsSync as existsSync14, mkdirSync as mkdirSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
6719
7130
|
import { join as join6 } from "path";
|
|
6720
7131
|
init_config();
|
|
@@ -6879,30 +7290,30 @@ function enrichPromptWithDevtoolsContext(sessionId, prompt) {
|
|
|
6879
7290
|
${prompt}`;
|
|
6880
7291
|
}
|
|
6881
7292
|
var agents = new Hono2();
|
|
6882
|
-
var attachmentSchema =
|
|
6883
|
-
type:
|
|
6884
|
-
data:
|
|
7293
|
+
var attachmentSchema = z15.object({
|
|
7294
|
+
type: z15.enum(["image", "file"]),
|
|
7295
|
+
data: z15.string(),
|
|
6885
7296
|
// base64 data URL or raw base64
|
|
6886
|
-
mediaType:
|
|
6887
|
-
filename:
|
|
7297
|
+
mediaType: z15.string().optional(),
|
|
7298
|
+
filename: z15.string().optional()
|
|
6888
7299
|
});
|
|
6889
|
-
var runPromptSchema =
|
|
6890
|
-
prompt:
|
|
7300
|
+
var runPromptSchema = z15.object({
|
|
7301
|
+
prompt: z15.string(),
|
|
6891
7302
|
// Can be empty if attachments are provided
|
|
6892
|
-
attachments:
|
|
7303
|
+
attachments: z15.array(attachmentSchema).optional()
|
|
6893
7304
|
}).refine(
|
|
6894
7305
|
(data) => data.prompt.trim().length > 0 || data.attachments && data.attachments.length > 0,
|
|
6895
7306
|
{ message: "Either prompt or attachments must be provided" }
|
|
6896
7307
|
);
|
|
6897
|
-
var quickStartSchema =
|
|
6898
|
-
prompt:
|
|
6899
|
-
name:
|
|
6900
|
-
workingDirectory:
|
|
6901
|
-
model:
|
|
6902
|
-
toolApprovals:
|
|
7308
|
+
var quickStartSchema = z15.object({
|
|
7309
|
+
prompt: z15.string().min(1),
|
|
7310
|
+
name: z15.string().optional(),
|
|
7311
|
+
workingDirectory: z15.string().optional(),
|
|
7312
|
+
model: z15.string().optional(),
|
|
7313
|
+
toolApprovals: z15.record(z15.string(), z15.boolean()).optional()
|
|
6903
7314
|
});
|
|
6904
|
-
var rejectSchema =
|
|
6905
|
-
reason:
|
|
7315
|
+
var rejectSchema = z15.object({
|
|
7316
|
+
reason: z15.string().optional()
|
|
6906
7317
|
}).optional();
|
|
6907
7318
|
var streamAbortControllers = /* @__PURE__ */ new Map();
|
|
6908
7319
|
function getAttachmentsDirectory(sessionId) {
|
|
@@ -7332,7 +7743,7 @@ agents.get("/:id/watch", async (c) => {
|
|
|
7332
7743
|
"Cache-Control": "no-cache",
|
|
7333
7744
|
"Connection": "keep-alive",
|
|
7334
7745
|
"x-vercel-ai-ui-message-stream": "v1",
|
|
7335
|
-
"x-stream-id": streamId
|
|
7746
|
+
"x-stream-id": streamId ?? ""
|
|
7336
7747
|
}
|
|
7337
7748
|
});
|
|
7338
7749
|
});
|
|
@@ -7690,7 +8101,7 @@ agents.post(
|
|
|
7690
8101
|
init_config();
|
|
7691
8102
|
import { Hono as Hono3 } from "hono";
|
|
7692
8103
|
import { zValidator as zValidator3 } from "@hono/zod-validator";
|
|
7693
|
-
import { z as
|
|
8104
|
+
import { z as z16 } from "zod";
|
|
7694
8105
|
import { readFileSync as readFileSync5 } from "fs";
|
|
7695
8106
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
7696
8107
|
import { dirname as dirname6, join as join7 } from "path";
|
|
@@ -7800,9 +8211,9 @@ health.get("/api-keys", async (c) => {
|
|
|
7800
8211
|
supportedProviders: SUPPORTED_PROVIDERS
|
|
7801
8212
|
});
|
|
7802
8213
|
});
|
|
7803
|
-
var setApiKeySchema =
|
|
7804
|
-
provider:
|
|
7805
|
-
apiKey:
|
|
8214
|
+
var setApiKeySchema = z16.object({
|
|
8215
|
+
provider: z16.string(),
|
|
8216
|
+
apiKey: z16.string().min(1)
|
|
7806
8217
|
});
|
|
7807
8218
|
health.post(
|
|
7808
8219
|
"/api-keys",
|
|
@@ -7841,13 +8252,13 @@ health.delete("/api-keys/:provider", async (c) => {
|
|
|
7841
8252
|
// src/server/routes/terminals.ts
|
|
7842
8253
|
import { Hono as Hono4 } from "hono";
|
|
7843
8254
|
import { zValidator as zValidator4 } from "@hono/zod-validator";
|
|
7844
|
-
import { z as
|
|
8255
|
+
import { z as z17 } from "zod";
|
|
7845
8256
|
init_db();
|
|
7846
8257
|
var terminals = new Hono4();
|
|
7847
|
-
var spawnSchema =
|
|
7848
|
-
command:
|
|
7849
|
-
cwd:
|
|
7850
|
-
name:
|
|
8258
|
+
var spawnSchema = z17.object({
|
|
8259
|
+
command: z17.string(),
|
|
8260
|
+
cwd: z17.string().optional(),
|
|
8261
|
+
name: z17.string().optional()
|
|
7851
8262
|
});
|
|
7852
8263
|
terminals.post(
|
|
7853
8264
|
"/:sessionId/terminals",
|
|
@@ -7928,8 +8339,8 @@ terminals.get("/:sessionId/terminals/:terminalId", async (c) => {
|
|
|
7928
8339
|
// We don't track exit codes in tmux mode
|
|
7929
8340
|
});
|
|
7930
8341
|
});
|
|
7931
|
-
var logsQuerySchema =
|
|
7932
|
-
tail:
|
|
8342
|
+
var logsQuerySchema = z17.object({
|
|
8343
|
+
tail: z17.string().optional().transform((v) => v ? parseInt(v, 10) : void 0)
|
|
7933
8344
|
});
|
|
7934
8345
|
terminals.get(
|
|
7935
8346
|
"/:sessionId/terminals/:terminalId/logs",
|
|
@@ -7953,8 +8364,8 @@ terminals.get(
|
|
|
7953
8364
|
});
|
|
7954
8365
|
}
|
|
7955
8366
|
);
|
|
7956
|
-
var killSchema =
|
|
7957
|
-
signal:
|
|
8367
|
+
var killSchema = z17.object({
|
|
8368
|
+
signal: z17.enum(["SIGTERM", "SIGKILL"]).optional()
|
|
7958
8369
|
});
|
|
7959
8370
|
terminals.post(
|
|
7960
8371
|
"/:sessionId/terminals/:terminalId/kill",
|
|
@@ -7968,8 +8379,8 @@ terminals.post(
|
|
|
7968
8379
|
return c.json({ success: true, message: "Terminal killed" });
|
|
7969
8380
|
}
|
|
7970
8381
|
);
|
|
7971
|
-
var writeSchema =
|
|
7972
|
-
input:
|
|
8382
|
+
var writeSchema = z17.object({
|
|
8383
|
+
input: z17.string()
|
|
7973
8384
|
});
|
|
7974
8385
|
terminals.post(
|
|
7975
8386
|
"/:sessionId/terminals/:terminalId/write",
|
|
@@ -8150,6 +8561,131 @@ data: ${JSON.stringify({ status: "stopped" })}
|
|
|
8150
8561
|
);
|
|
8151
8562
|
});
|
|
8152
8563
|
|
|
8564
|
+
// src/server/routes/tasks.ts
|
|
8565
|
+
init_db();
|
|
8566
|
+
import { Hono as Hono5 } from "hono";
|
|
8567
|
+
import { zValidator as zValidator5 } from "@hono/zod-validator";
|
|
8568
|
+
import { z as z18 } from "zod";
|
|
8569
|
+
init_config();
|
|
8570
|
+
var tasks = new Hono5();
|
|
8571
|
+
var taskAbortControllers = /* @__PURE__ */ new Map();
|
|
8572
|
+
var createTaskSchema = z18.object({
|
|
8573
|
+
prompt: z18.string().min(1),
|
|
8574
|
+
outputSchema: z18.record(z18.string(), z18.unknown()),
|
|
8575
|
+
webhookUrl: z18.string().url().optional(),
|
|
8576
|
+
model: z18.string().optional(),
|
|
8577
|
+
workingDirectory: z18.string().optional(),
|
|
8578
|
+
name: z18.string().optional(),
|
|
8579
|
+
maxIterations: z18.number().int().min(1).max(500).optional()
|
|
8580
|
+
});
|
|
8581
|
+
tasks.post(
|
|
8582
|
+
"/",
|
|
8583
|
+
zValidator5("json", createTaskSchema),
|
|
8584
|
+
async (c) => {
|
|
8585
|
+
const body = c.req.valid("json");
|
|
8586
|
+
const config = getConfig();
|
|
8587
|
+
const taskConfig = {
|
|
8588
|
+
enabled: true,
|
|
8589
|
+
outputSchema: body.outputSchema,
|
|
8590
|
+
webhookUrl: body.webhookUrl,
|
|
8591
|
+
maxIterations: body.maxIterations ?? 50,
|
|
8592
|
+
status: "running"
|
|
8593
|
+
};
|
|
8594
|
+
const agent = await Agent.create({
|
|
8595
|
+
name: body.name || "Task",
|
|
8596
|
+
workingDirectory: body.workingDirectory || config.resolvedWorkingDirectory,
|
|
8597
|
+
model: body.model || config.defaultModel,
|
|
8598
|
+
sessionConfig: {
|
|
8599
|
+
toolApprovals: { bash: false, write_file: false, read_file: false },
|
|
8600
|
+
task: taskConfig
|
|
8601
|
+
}
|
|
8602
|
+
});
|
|
8603
|
+
const taskId = agent.sessionId;
|
|
8604
|
+
const abortController = new AbortController();
|
|
8605
|
+
taskAbortControllers.set(taskId, abortController);
|
|
8606
|
+
(async () => {
|
|
8607
|
+
try {
|
|
8608
|
+
await agent.runTask({
|
|
8609
|
+
prompt: body.prompt,
|
|
8610
|
+
taskConfig,
|
|
8611
|
+
abortSignal: abortController.signal
|
|
8612
|
+
});
|
|
8613
|
+
} catch (err) {
|
|
8614
|
+
if (err.name === "AbortError" || abortController.signal.aborted) {
|
|
8615
|
+
console.log(`[TASK] Task ${taskId} was cancelled`);
|
|
8616
|
+
} else {
|
|
8617
|
+
console.error(`[TASK] Error in task ${taskId}:`, err.message);
|
|
8618
|
+
const failedTask = {
|
|
8619
|
+
...taskConfig,
|
|
8620
|
+
status: "failed",
|
|
8621
|
+
error: err.message || "Unknown error"
|
|
8622
|
+
};
|
|
8623
|
+
await sessionQueries.update(taskId, {
|
|
8624
|
+
config: {
|
|
8625
|
+
toolApprovals: { bash: false, write_file: false, read_file: false },
|
|
8626
|
+
task: failedTask
|
|
8627
|
+
}
|
|
8628
|
+
});
|
|
8629
|
+
}
|
|
8630
|
+
} finally {
|
|
8631
|
+
taskAbortControllers.delete(taskId);
|
|
8632
|
+
}
|
|
8633
|
+
})();
|
|
8634
|
+
return c.json({ taskId, status: "running" }, 201);
|
|
8635
|
+
}
|
|
8636
|
+
);
|
|
8637
|
+
tasks.get("/:id", async (c) => {
|
|
8638
|
+
const id = c.req.param("id");
|
|
8639
|
+
const session = await sessionQueries.getById(id);
|
|
8640
|
+
if (!session) {
|
|
8641
|
+
return c.json({ error: "Task not found" }, 404);
|
|
8642
|
+
}
|
|
8643
|
+
const task = session.config?.task;
|
|
8644
|
+
if (!task?.enabled) {
|
|
8645
|
+
return c.json({ error: "Session is not a task" }, 400);
|
|
8646
|
+
}
|
|
8647
|
+
return c.json({
|
|
8648
|
+
taskId: id,
|
|
8649
|
+
status: task.status,
|
|
8650
|
+
result: task.result,
|
|
8651
|
+
error: task.error,
|
|
8652
|
+
iterations: task.iterations,
|
|
8653
|
+
model: session.model,
|
|
8654
|
+
name: session.name,
|
|
8655
|
+
createdAt: session.createdAt.toISOString(),
|
|
8656
|
+
updatedAt: session.updatedAt.toISOString()
|
|
8657
|
+
});
|
|
8658
|
+
});
|
|
8659
|
+
tasks.post("/:id/cancel", async (c) => {
|
|
8660
|
+
const id = c.req.param("id");
|
|
8661
|
+
const session = await sessionQueries.getById(id);
|
|
8662
|
+
if (!session) {
|
|
8663
|
+
return c.json({ error: "Task not found" }, 404);
|
|
8664
|
+
}
|
|
8665
|
+
const task = session.config?.task;
|
|
8666
|
+
if (!task?.enabled) {
|
|
8667
|
+
return c.json({ error: "Session is not a task" }, 400);
|
|
8668
|
+
}
|
|
8669
|
+
if (task.status !== "running") {
|
|
8670
|
+
return c.json({ error: `Task is already ${task.status}` }, 400);
|
|
8671
|
+
}
|
|
8672
|
+
const abortController = taskAbortControllers.get(id);
|
|
8673
|
+
if (abortController) {
|
|
8674
|
+
abortController.abort();
|
|
8675
|
+
taskAbortControllers.delete(id);
|
|
8676
|
+
}
|
|
8677
|
+
const cancelledTask = {
|
|
8678
|
+
...task,
|
|
8679
|
+
status: "failed",
|
|
8680
|
+
error: "Task cancelled by user"
|
|
8681
|
+
};
|
|
8682
|
+
await sessionQueries.update(id, {
|
|
8683
|
+
config: { ...session.config, task: cancelledTask }
|
|
8684
|
+
});
|
|
8685
|
+
return c.json({ taskId: id, status: "failed", error: "Task cancelled by user" });
|
|
8686
|
+
});
|
|
8687
|
+
var tasks_default = tasks;
|
|
8688
|
+
|
|
8153
8689
|
// src/server/index.ts
|
|
8154
8690
|
init_config();
|
|
8155
8691
|
init_db();
|
|
@@ -8486,7 +9022,7 @@ function stopWebUI() {
|
|
|
8486
9022
|
}
|
|
8487
9023
|
}
|
|
8488
9024
|
async function createApp(options = {}) {
|
|
8489
|
-
const app = new
|
|
9025
|
+
const app = new Hono6();
|
|
8490
9026
|
app.use("*", cors({
|
|
8491
9027
|
origin: "*",
|
|
8492
9028
|
// Allow all origins
|
|
@@ -8504,6 +9040,7 @@ async function createApp(options = {}) {
|
|
|
8504
9040
|
app.route("/agents", agents);
|
|
8505
9041
|
app.route("/sessions", terminals);
|
|
8506
9042
|
app.route("/terminals", terminals);
|
|
9043
|
+
app.route("/tasks", tasks_default);
|
|
8507
9044
|
app.get("/openapi.json", async (c) => {
|
|
8508
9045
|
return c.json(generateOpenAPISpec());
|
|
8509
9046
|
});
|