sparkecoder 0.1.59 → 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 +662 -203
- package/dist/agent/index.js.map +1 -1
- package/dist/cli.js +906 -129
- 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 +1302 -711
- 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 +692 -101
- package/dist/server/index.js.map +1 -1
- package/dist/skills/default/browser.md +143 -0
- package/dist/skills/default/code-review.md +122 -0
- package/dist/skills/default/debugging.md +105 -0
- package/dist/skills/default/refactoring.md +197 -0
- package/dist/tools/index.d.ts +54 -4
- package/dist/tools/index.js +304 -29
- package/dist/tools/index.js.map +1 -1
- package/package.json +6 -1
- package/src/skills/default/browser.md +143 -0
- package/src/skills/default/code-review.md +122 -0
- package/src/skills/default/debugging.md +105 -0
- package/src/skills/default/refactoring.md +197 -0
- 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/{static/u5qqIWWrYpWW_mZUgKKjg → 8zJH-RqrUQ3scBGbdaCmn}/_buildManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/{static/u5qqIWWrYpWW_mZUgKKjg → 8zJH-RqrUQ3scBGbdaCmn}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/standalone/web/.next/static/{static/u5qqIWWrYpWW_mZUgKKjg → 8zJH-RqrUQ3scBGbdaCmn}/_ssgManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/{u5qqIWWrYpWW_mZUgKKjg → static/8zJH-RqrUQ3scBGbdaCmn}/_buildManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/{u5qqIWWrYpWW_mZUgKKjg → static/8zJH-RqrUQ3scBGbdaCmn}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/standalone/web/.next/static/{u5qqIWWrYpWW_mZUgKKjg → static/8zJH-RqrUQ3scBGbdaCmn}/_ssgManifest.js +0 -0
- /package/web/.next/static/{u5qqIWWrYpWW_mZUgKKjg → 8zJH-RqrUQ3scBGbdaCmn}/_buildManifest.js +0 -0
- /package/web/.next/static/{u5qqIWWrYpWW_mZUgKKjg → 8zJH-RqrUQ3scBGbdaCmn}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/static/{u5qqIWWrYpWW_mZUgKKjg → 8zJH-RqrUQ3scBGbdaCmn}/_ssgManifest.js +0 -0
package/dist/index.js
CHANGED
|
@@ -8,590 +8,251 @@ var __export = (target, all) => {
|
|
|
8
8
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
9
|
};
|
|
10
10
|
|
|
11
|
-
// src/
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|
+
});
|
|
137
|
+
}
|
|
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);
|
|
153
|
+
}
|
|
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
|
|
195
|
+
};
|
|
15
196
|
}
|
|
16
|
-
function
|
|
17
|
-
|
|
18
|
-
|
|
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);
|
|
206
|
+
}
|
|
19
207
|
}
|
|
20
|
-
function
|
|
21
|
-
|
|
22
|
-
if (
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
208
|
+
function ensureAppDataDirectory() {
|
|
209
|
+
const dir = getAppDataDirectory();
|
|
210
|
+
if (!existsSync(dir)) {
|
|
211
|
+
mkdirSync(dir, { recursive: true });
|
|
212
|
+
}
|
|
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
|
+
}
|
|
28
223
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
224
|
+
currentDir = dirname(currentDir);
|
|
225
|
+
}
|
|
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;
|
|
33
231
|
}
|
|
34
232
|
}
|
|
35
|
-
return
|
|
233
|
+
return null;
|
|
36
234
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
235
|
+
function loadConfig(configPath, workingDirectory) {
|
|
236
|
+
const cwd = workingDirectory || process.cwd();
|
|
237
|
+
let rawConfig = {};
|
|
238
|
+
let configDir = cwd;
|
|
239
|
+
if (configPath) {
|
|
240
|
+
if (!existsSync(configPath)) {
|
|
241
|
+
throw new Error(`Config file not found: ${configPath}`);
|
|
242
|
+
}
|
|
243
|
+
const content = readFileSync(configPath, "utf-8");
|
|
244
|
+
rawConfig = JSON.parse(content);
|
|
245
|
+
configDir = dirname(resolve(configPath));
|
|
246
|
+
} else {
|
|
247
|
+
const foundPath = findConfigFile(cwd);
|
|
248
|
+
if (foundPath) {
|
|
249
|
+
const content = readFileSync(foundPath, "utf-8");
|
|
250
|
+
rawConfig = JSON.parse(content);
|
|
251
|
+
configDir = dirname(foundPath);
|
|
47
252
|
}
|
|
48
|
-
};
|
|
49
|
-
if (options.body) {
|
|
50
|
-
init.body = JSON.stringify(options.body);
|
|
51
253
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
const error = await response.json().catch(() => ({ error: "Unknown error" }));
|
|
55
|
-
throw new Error(error.error || `HTTP ${response.status}`);
|
|
56
|
-
}
|
|
57
|
-
const text = await response.text();
|
|
58
|
-
if (!text || text === "null") {
|
|
59
|
-
return null;
|
|
60
|
-
}
|
|
61
|
-
const parsed = JSON.parse(text);
|
|
62
|
-
if (options.skipParseDates) {
|
|
63
|
-
return parsed;
|
|
64
|
-
}
|
|
65
|
-
return parseDates(parsed);
|
|
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 builtInSkillsDir = resolve(dirname(import.meta.url.replace("file://", "")), "../skills/default");
|
|
525
|
-
if (existsSync(builtInSkillsDir)) {
|
|
526
|
-
onDemandDirs.push({ path: builtInSkillsDir, priority: 100 });
|
|
527
|
-
allDirectories.push(builtInSkillsDir);
|
|
528
|
-
}
|
|
529
|
-
return {
|
|
530
|
-
alwaysLoadedDirs,
|
|
531
|
-
onDemandDirs,
|
|
532
|
-
agentsMdPath,
|
|
533
|
-
allDirectories
|
|
534
|
-
};
|
|
535
|
-
}
|
|
536
|
-
function getAppDataDirectory() {
|
|
537
|
-
const appName = "sparkecoder";
|
|
538
|
-
switch (platform()) {
|
|
539
|
-
case "darwin":
|
|
540
|
-
return join(homedir(), "Library", "Application Support", appName);
|
|
541
|
-
case "win32":
|
|
542
|
-
return join(process.env.APPDATA || join(homedir(), "AppData", "Roaming"), appName);
|
|
543
|
-
default:
|
|
544
|
-
return join(process.env.XDG_DATA_HOME || join(homedir(), ".local", "share"), appName);
|
|
545
|
-
}
|
|
546
|
-
}
|
|
547
|
-
function ensureAppDataDirectory() {
|
|
548
|
-
const dir = getAppDataDirectory();
|
|
549
|
-
if (!existsSync(dir)) {
|
|
550
|
-
mkdirSync(dir, { recursive: true });
|
|
551
|
-
}
|
|
552
|
-
return dir;
|
|
553
|
-
}
|
|
554
|
-
function findConfigFile(startDir) {
|
|
555
|
-
let currentDir = startDir;
|
|
556
|
-
while (currentDir !== dirname(currentDir)) {
|
|
557
|
-
for (const fileName of CONFIG_FILE_NAMES) {
|
|
558
|
-
const configPath = resolve(currentDir, fileName);
|
|
559
|
-
if (existsSync(configPath)) {
|
|
560
|
-
return configPath;
|
|
561
|
-
}
|
|
562
|
-
}
|
|
563
|
-
currentDir = dirname(currentDir);
|
|
564
|
-
}
|
|
565
|
-
const appDataDir = getAppDataDirectory();
|
|
566
|
-
for (const fileName of CONFIG_FILE_NAMES) {
|
|
567
|
-
const configPath = join(appDataDir, fileName);
|
|
568
|
-
if (existsSync(configPath)) {
|
|
569
|
-
return configPath;
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
|
-
return null;
|
|
573
|
-
}
|
|
574
|
-
function loadConfig(configPath, workingDirectory) {
|
|
575
|
-
const cwd = workingDirectory || process.cwd();
|
|
576
|
-
let rawConfig = {};
|
|
577
|
-
let configDir = cwd;
|
|
578
|
-
if (configPath) {
|
|
579
|
-
if (!existsSync(configPath)) {
|
|
580
|
-
throw new Error(`Config file not found: ${configPath}`);
|
|
581
|
-
}
|
|
582
|
-
const content = readFileSync(configPath, "utf-8");
|
|
583
|
-
rawConfig = JSON.parse(content);
|
|
584
|
-
configDir = dirname(resolve(configPath));
|
|
585
|
-
} else {
|
|
586
|
-
const foundPath = findConfigFile(cwd);
|
|
587
|
-
if (foundPath) {
|
|
588
|
-
const content = readFileSync(foundPath, "utf-8");
|
|
589
|
-
rawConfig = JSON.parse(content);
|
|
590
|
-
configDir = dirname(foundPath);
|
|
591
|
-
}
|
|
592
|
-
}
|
|
593
|
-
if (process.env.SPARKECODER_MODEL) {
|
|
594
|
-
rawConfig.defaultModel = process.env.SPARKECODER_MODEL;
|
|
254
|
+
if (process.env.SPARKECODER_MODEL) {
|
|
255
|
+
rawConfig.defaultModel = process.env.SPARKECODER_MODEL;
|
|
595
256
|
}
|
|
596
257
|
if (process.env.SPARKECODER_PORT) {
|
|
597
258
|
rawConfig.server = {
|
|
@@ -691,6 +352,9 @@ function getConfig() {
|
|
|
691
352
|
}
|
|
692
353
|
function requiresApproval(toolName, sessionConfig) {
|
|
693
354
|
const config = getConfig();
|
|
355
|
+
if (sessionConfig?.toolApprovals?.["*"] !== void 0) {
|
|
356
|
+
return sessionConfig.toolApprovals["*"];
|
|
357
|
+
}
|
|
694
358
|
if (sessionConfig?.toolApprovals?.[toolName] !== void 0) {
|
|
695
359
|
return sessionConfig.toolApprovals[toolName];
|
|
696
360
|
}
|
|
@@ -814,48 +478,405 @@ function getApiKeyStatus() {
|
|
|
814
478
|
} else {
|
|
815
479
|
source = "env";
|
|
816
480
|
}
|
|
817
|
-
value = envValue;
|
|
818
|
-
} else if (storedValue) {
|
|
819
|
-
source = "storage";
|
|
820
|
-
value = storedValue;
|
|
821
|
-
}
|
|
822
|
-
return {
|
|
823
|
-
provider,
|
|
824
|
-
envVar,
|
|
825
|
-
configured: !!value,
|
|
826
|
-
source,
|
|
827
|
-
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
|
+
}
|
|
828
649
|
};
|
|
829
|
-
|
|
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;
|
|
830
853
|
}
|
|
831
|
-
function
|
|
832
|
-
if (
|
|
833
|
-
|
|
854
|
+
function getDb() {
|
|
855
|
+
if (!initialized) {
|
|
856
|
+
throw new Error("Database not initialized. Call initDatabase first.");
|
|
834
857
|
}
|
|
835
|
-
return
|
|
858
|
+
return {};
|
|
836
859
|
}
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
};
|
|
858
|
-
SUPPORTED_PROVIDERS = Object.keys(PROVIDER_ENV_MAP);
|
|
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"() {
|
|
867
|
+
"use strict";
|
|
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;
|
|
859
880
|
}
|
|
860
881
|
});
|
|
861
882
|
|
|
@@ -874,7 +895,7 @@ __export(skills_exports, {
|
|
|
874
895
|
loadSkillsFromDirectory: () => loadSkillsFromDirectory
|
|
875
896
|
});
|
|
876
897
|
import { readFile as readFile6, readdir } from "fs/promises";
|
|
877
|
-
import { resolve as resolve6, basename, extname as
|
|
898
|
+
import { resolve as resolve6, basename, extname as extname4, relative as relative4 } from "path";
|
|
878
899
|
import { existsSync as existsSync8 } from "fs";
|
|
879
900
|
import { minimatch } from "minimatch";
|
|
880
901
|
function parseSkillFrontmatter(content) {
|
|
@@ -945,7 +966,7 @@ function parseSkillFrontmatter(content) {
|
|
|
945
966
|
}
|
|
946
967
|
}
|
|
947
968
|
function getSkillNameFromPath(filePath) {
|
|
948
|
-
return basename(filePath,
|
|
969
|
+
return basename(filePath, extname4(filePath)).replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
949
970
|
}
|
|
950
971
|
async function loadSkillsFromDirectory(directory, options = {}) {
|
|
951
972
|
const {
|
|
@@ -1250,7 +1271,7 @@ var init_hasher = __esm({
|
|
|
1250
1271
|
});
|
|
1251
1272
|
|
|
1252
1273
|
// src/semantic/chunker.ts
|
|
1253
|
-
import { extname as
|
|
1274
|
+
import { extname as extname6, basename as basename2 } from "path";
|
|
1254
1275
|
var init_chunker = __esm({
|
|
1255
1276
|
"src/semantic/chunker.ts"() {
|
|
1256
1277
|
"use strict";
|
|
@@ -1600,18 +1621,176 @@ var init_semantic_search = __esm({
|
|
|
1600
1621
|
import {
|
|
1601
1622
|
streamText as streamText2,
|
|
1602
1623
|
generateText as generateText3,
|
|
1603
|
-
tool as
|
|
1624
|
+
tool as tool12,
|
|
1604
1625
|
stepCountIs as stepCountIs2
|
|
1605
1626
|
} from "ai";
|
|
1606
1627
|
|
|
1607
1628
|
// src/agent/model.ts
|
|
1608
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();
|
|
1609
1778
|
var ANTHROPIC_PREFIX = "anthropic/";
|
|
1610
1779
|
function isAnthropicModel(modelId) {
|
|
1611
1780
|
const normalized = modelId.trim().toLowerCase();
|
|
1612
1781
|
return normalized.startsWith(ANTHROPIC_PREFIX) || normalized.startsWith("claude-");
|
|
1613
1782
|
}
|
|
1614
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
|
+
}
|
|
1615
1794
|
return gateway(modelId.trim());
|
|
1616
1795
|
}
|
|
1617
1796
|
var SUBAGENT_MODELS = {
|
|
@@ -1623,7 +1802,7 @@ var SUBAGENT_MODELS = {
|
|
|
1623
1802
|
// src/agent/index.ts
|
|
1624
1803
|
init_db();
|
|
1625
1804
|
init_config();
|
|
1626
|
-
import { z as
|
|
1805
|
+
import { z as z13 } from "zod";
|
|
1627
1806
|
import { nanoid as nanoid3 } from "nanoid";
|
|
1628
1807
|
|
|
1629
1808
|
// src/tools/bash.ts
|
|
@@ -2221,26 +2400,41 @@ Terminal output is stored in the global SparkECoder data directory. Use the \`ta
|
|
|
2221
2400
|
import { tool as tool2 } from "ai";
|
|
2222
2401
|
import { z as z3 } from "zod";
|
|
2223
2402
|
import { readFile as readFile2, stat } from "fs/promises";
|
|
2224
|
-
import { resolve as resolve2, relative, isAbsolute } from "path";
|
|
2403
|
+
import { resolve as resolve2, relative, isAbsolute, extname } from "path";
|
|
2225
2404
|
import { existsSync as existsSync3 } from "fs";
|
|
2226
2405
|
var MAX_FILE_SIZE = 5 * 1024 * 1024;
|
|
2406
|
+
var MAX_IMAGE_SIZE = 20 * 1024 * 1024;
|
|
2227
2407
|
var MAX_OUTPUT_CHARS3 = 5e4;
|
|
2408
|
+
var IMAGE_EXTENSIONS = {
|
|
2409
|
+
".png": "image/png",
|
|
2410
|
+
".jpg": "image/jpeg",
|
|
2411
|
+
".jpeg": "image/jpeg",
|
|
2412
|
+
".gif": "image/gif",
|
|
2413
|
+
".webp": "image/webp"
|
|
2414
|
+
};
|
|
2415
|
+
function isImageFile(filePath) {
|
|
2416
|
+
return extname(filePath).toLowerCase() in IMAGE_EXTENSIONS;
|
|
2417
|
+
}
|
|
2418
|
+
function getImageMediaType(filePath) {
|
|
2419
|
+
return IMAGE_EXTENSIONS[extname(filePath).toLowerCase()] || "image/png";
|
|
2420
|
+
}
|
|
2228
2421
|
var readFileInputSchema = z3.object({
|
|
2229
|
-
path: z3.string().describe("The path to the file to read. Can be relative to working directory or absolute."),
|
|
2230
|
-
startLine: z3.number().optional().describe("Optional: Start reading from this line number (1-indexed)"),
|
|
2231
|
-
endLine: z3.number().optional().describe("Optional: Stop reading at this line number (1-indexed, inclusive)")
|
|
2422
|
+
path: z3.string().describe("The path to the file to read. Can be relative to working directory or absolute. Supports text files and images (png, jpg, jpeg, gif, webp)."),
|
|
2423
|
+
startLine: z3.number().optional().describe("Optional: Start reading from this line number (1-indexed). Only for text files."),
|
|
2424
|
+
endLine: z3.number().optional().describe("Optional: Stop reading at this line number (1-indexed, inclusive). Only for text files.")
|
|
2232
2425
|
});
|
|
2233
2426
|
function createReadFileTool(options) {
|
|
2234
2427
|
return tool2({
|
|
2235
2428
|
description: `Read the contents of a file. Provide a path relative to the working directory (${options.workingDirectory}) or an absolute path.
|
|
2236
|
-
|
|
2237
|
-
|
|
2429
|
+
Supports text files (automatically truncated if large) and image files (png, jpg, jpeg, gif, webp).
|
|
2430
|
+
For images, the file contents are returned as visual data you can see and analyze.
|
|
2431
|
+
Use this to understand existing code, check file contents, view screenshots, or gather context.`,
|
|
2238
2432
|
inputSchema: readFileInputSchema,
|
|
2239
|
-
execute: async ({ path, startLine, endLine }) => {
|
|
2433
|
+
execute: async ({ path: filePath, startLine, endLine }) => {
|
|
2240
2434
|
try {
|
|
2241
|
-
const absolutePath = isAbsolute(
|
|
2435
|
+
const absolutePath = isAbsolute(filePath) ? filePath : resolve2(options.workingDirectory, filePath);
|
|
2242
2436
|
const relativePath = relative(options.workingDirectory, absolutePath);
|
|
2243
|
-
if (relativePath.startsWith("..") && !isAbsolute(
|
|
2437
|
+
if (relativePath.startsWith("..") && !isAbsolute(filePath)) {
|
|
2244
2438
|
return {
|
|
2245
2439
|
success: false,
|
|
2246
2440
|
error: "Path escapes the working directory. Use an absolute path if intentional.",
|
|
@@ -2250,22 +2444,43 @@ Use this to understand existing code, check file contents, or gather context.`,
|
|
|
2250
2444
|
if (!existsSync3(absolutePath)) {
|
|
2251
2445
|
return {
|
|
2252
2446
|
success: false,
|
|
2253
|
-
error: `File not found: ${
|
|
2447
|
+
error: `File not found: ${filePath}`,
|
|
2254
2448
|
content: null
|
|
2255
2449
|
};
|
|
2256
2450
|
}
|
|
2257
2451
|
const stats = await stat(absolutePath);
|
|
2258
|
-
if (stats.
|
|
2452
|
+
if (stats.isDirectory()) {
|
|
2259
2453
|
return {
|
|
2260
2454
|
success: false,
|
|
2261
|
-
error:
|
|
2455
|
+
error: 'Path is a directory, not a file. Use bash with "ls" to list directory contents.',
|
|
2262
2456
|
content: null
|
|
2263
2457
|
};
|
|
2264
2458
|
}
|
|
2265
|
-
if (
|
|
2459
|
+
if (isImageFile(absolutePath)) {
|
|
2460
|
+
if (stats.size > MAX_IMAGE_SIZE) {
|
|
2461
|
+
return {
|
|
2462
|
+
success: false,
|
|
2463
|
+
error: `Image is too large (${(stats.size / 1024 / 1024).toFixed(2)}MB). Maximum size is ${MAX_IMAGE_SIZE / 1024 / 1024}MB.`,
|
|
2464
|
+
content: null
|
|
2465
|
+
};
|
|
2466
|
+
}
|
|
2467
|
+
const buffer = await readFile2(absolutePath);
|
|
2468
|
+
const base64 = buffer.toString("base64");
|
|
2469
|
+
const mediaType = getImageMediaType(absolutePath);
|
|
2470
|
+
return {
|
|
2471
|
+
success: true,
|
|
2472
|
+
path: absolutePath,
|
|
2473
|
+
relativePath: relative(options.workingDirectory, absolutePath),
|
|
2474
|
+
content: `[Image: ${relativePath} (${mediaType}, ${(stats.size / 1024).toFixed(1)}KB)]`,
|
|
2475
|
+
mediaType,
|
|
2476
|
+
imageData: base64,
|
|
2477
|
+
sizeBytes: stats.size
|
|
2478
|
+
};
|
|
2479
|
+
}
|
|
2480
|
+
if (stats.size > MAX_FILE_SIZE) {
|
|
2266
2481
|
return {
|
|
2267
2482
|
success: false,
|
|
2268
|
-
error:
|
|
2483
|
+
error: `File is too large (${(stats.size / 1024 / 1024).toFixed(2)}MB). Maximum size is ${MAX_FILE_SIZE / 1024 / 1024}MB.`,
|
|
2269
2484
|
content: null
|
|
2270
2485
|
};
|
|
2271
2486
|
}
|
|
@@ -2281,9 +2496,7 @@ Use this to understand existing code, check file contents, or gather context.`,
|
|
|
2281
2496
|
content: null
|
|
2282
2497
|
};
|
|
2283
2498
|
}
|
|
2284
|
-
content = lines.slice(start, end).join("\n");
|
|
2285
|
-
const lineNumbers = lines.slice(start, end).map((line, idx) => `${(start + idx + 1).toString().padStart(4)}: ${line}`).join("\n");
|
|
2286
|
-
content = lineNumbers;
|
|
2499
|
+
content = lines.slice(start, end).map((line, idx) => `${(start + idx + 1).toString().padStart(4)}: ${line}`).join("\n");
|
|
2287
2500
|
}
|
|
2288
2501
|
const truncatedContent = truncateOutput(content, MAX_OUTPUT_CHARS3);
|
|
2289
2502
|
const wasTruncated = truncatedContent.length < content.length;
|
|
@@ -2310,6 +2523,19 @@ Use this to understand existing code, check file contents, or gather context.`,
|
|
|
2310
2523
|
content: null
|
|
2311
2524
|
};
|
|
2312
2525
|
}
|
|
2526
|
+
},
|
|
2527
|
+
toModelOutput: ({ output }) => {
|
|
2528
|
+
if (output && typeof output === "object" && "imageData" in output && output.imageData) {
|
|
2529
|
+
const result = output;
|
|
2530
|
+
return {
|
|
2531
|
+
type: "content",
|
|
2532
|
+
value: [
|
|
2533
|
+
{ type: "text", text: result.content },
|
|
2534
|
+
{ type: "image-data", data: result.imageData, mediaType: result.mediaType }
|
|
2535
|
+
]
|
|
2536
|
+
};
|
|
2537
|
+
}
|
|
2538
|
+
return typeof output === "string" ? { type: "text", value: output } : { type: "json", value: output };
|
|
2313
2539
|
}
|
|
2314
2540
|
});
|
|
2315
2541
|
}
|
|
@@ -2510,7 +2736,7 @@ function clearCheckpointManager(sessionId) {
|
|
|
2510
2736
|
}
|
|
2511
2737
|
|
|
2512
2738
|
// src/lsp/index.ts
|
|
2513
|
-
import { extname as
|
|
2739
|
+
import { extname as extname3, dirname as dirname4 } from "path";
|
|
2514
2740
|
|
|
2515
2741
|
// src/lsp/servers.ts
|
|
2516
2742
|
import { spawn } from "child_process";
|
|
@@ -2633,9 +2859,9 @@ import {
|
|
|
2633
2859
|
import { pathToFileURL, fileURLToPath } from "url";
|
|
2634
2860
|
import { readFile as readFile4 } from "fs/promises";
|
|
2635
2861
|
import { existsSync as existsSync6 } from "fs";
|
|
2636
|
-
import { extname, normalize } from "path";
|
|
2862
|
+
import { extname as extname2, normalize } from "path";
|
|
2637
2863
|
function getLanguageId(filePath) {
|
|
2638
|
-
const ext =
|
|
2864
|
+
const ext = extname2(filePath).toLowerCase();
|
|
2639
2865
|
const map = {
|
|
2640
2866
|
".ts": "typescript",
|
|
2641
2867
|
".tsx": "typescriptreact",
|
|
@@ -3023,7 +3249,7 @@ var state = {
|
|
|
3023
3249
|
};
|
|
3024
3250
|
async function getClientForFile(filePath) {
|
|
3025
3251
|
const normalized = normalizePath(filePath);
|
|
3026
|
-
const ext =
|
|
3252
|
+
const ext = extname3(normalized);
|
|
3027
3253
|
const serverDef = getServerForExtension(ext);
|
|
3028
3254
|
if (!serverDef) {
|
|
3029
3255
|
return null;
|
|
@@ -3116,7 +3342,7 @@ async function formatDiagnosticsOutput(filePath, options = {}) {
|
|
|
3116
3342
|
return formatDiagnosticsForAgent(filePath, diagnostics, options);
|
|
3117
3343
|
}
|
|
3118
3344
|
function isSupported(filePath) {
|
|
3119
|
-
const ext =
|
|
3345
|
+
const ext = extname3(filePath);
|
|
3120
3346
|
return getServerForExtension(ext) !== null;
|
|
3121
3347
|
}
|
|
3122
3348
|
|
|
@@ -3543,7 +3769,7 @@ Once loaded, a skill's content will be available in the conversation context.`,
|
|
|
3543
3769
|
// src/tools/linter.ts
|
|
3544
3770
|
import { tool as tool6 } from "ai";
|
|
3545
3771
|
import { z as z7 } from "zod";
|
|
3546
|
-
import { resolve as resolve7, relative as relative5, isAbsolute as isAbsolute3, extname as
|
|
3772
|
+
import { resolve as resolve7, relative as relative5, isAbsolute as isAbsolute3, extname as extname5 } from "path";
|
|
3547
3773
|
import { existsSync as existsSync9 } from "fs";
|
|
3548
3774
|
import { readdir as readdir2, stat as stat2 } from "fs/promises";
|
|
3549
3775
|
var linterInputSchema = z7.object({
|
|
@@ -3566,7 +3792,7 @@ async function findSupportedFiles(dir, workingDirectory, maxFiles = 50) {
|
|
|
3566
3792
|
}
|
|
3567
3793
|
await walk(fullPath);
|
|
3568
3794
|
} else if (entry.isFile()) {
|
|
3569
|
-
const ext =
|
|
3795
|
+
const ext = extname5(entry.name);
|
|
3570
3796
|
if (supportedExtensions.includes(ext)) {
|
|
3571
3797
|
files.push(fullPath);
|
|
3572
3798
|
}
|
|
@@ -4903,6 +5129,59 @@ Context: ${context}` : query;
|
|
|
4903
5129
|
|
|
4904
5130
|
// src/tools/index.ts
|
|
4905
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
|
|
4906
5185
|
init_semantic();
|
|
4907
5186
|
init_semantic_search();
|
|
4908
5187
|
async function createTools(options) {
|
|
@@ -4954,6 +5233,10 @@ async function createTools(options) {
|
|
|
4954
5233
|
} catch {
|
|
4955
5234
|
}
|
|
4956
5235
|
}
|
|
5236
|
+
if (options.taskTools) {
|
|
5237
|
+
tools.complete_task = createCompleteTaskTool(options.taskTools);
|
|
5238
|
+
tools.task_failed = createTaskFailedTool(options.taskTools);
|
|
5239
|
+
}
|
|
4957
5240
|
return tools;
|
|
4958
5241
|
}
|
|
4959
5242
|
|
|
@@ -5271,6 +5554,30 @@ function formatTodosForContext(todos) {
|
|
|
5271
5554
|
}
|
|
5272
5555
|
return lines.join("\n");
|
|
5273
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
|
+
}
|
|
5274
5581
|
function createSummaryPrompt(conversationHistory) {
|
|
5275
5582
|
return `Please provide a concise summary of the following conversation history. Focus on:
|
|
5276
5583
|
1. The main task or goal being worked on
|
|
@@ -5532,6 +5839,25 @@ ${this.summary}`
|
|
|
5532
5839
|
}
|
|
5533
5840
|
};
|
|
5534
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
|
+
|
|
5535
5861
|
// src/agent/index.ts
|
|
5536
5862
|
var approvalResolvers = /* @__PURE__ */ new Map();
|
|
5537
5863
|
var Agent = class _Agent {
|
|
@@ -5751,6 +6077,145 @@ ${prompt}` });
|
|
|
5751
6077
|
steps: result.steps
|
|
5752
6078
|
};
|
|
5753
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
|
+
}
|
|
5754
6219
|
/**
|
|
5755
6220
|
* Wrap tools to add approval checking
|
|
5756
6221
|
*/
|
|
@@ -5764,9 +6229,9 @@ ${prompt}` });
|
|
|
5764
6229
|
wrappedTools[name] = originalTool;
|
|
5765
6230
|
continue;
|
|
5766
6231
|
}
|
|
5767
|
-
wrappedTools[name] =
|
|
6232
|
+
wrappedTools[name] = tool12({
|
|
5768
6233
|
description: originalTool.description || "",
|
|
5769
|
-
inputSchema: originalTool.inputSchema ||
|
|
6234
|
+
inputSchema: originalTool.inputSchema || z13.object({}),
|
|
5770
6235
|
execute: async (input, toolOptions) => {
|
|
5771
6236
|
const toolCallId = toolOptions.toolCallId || nanoid3();
|
|
5772
6237
|
const execution = toolExecutionQueries.create({
|
|
@@ -5877,7 +6342,7 @@ ${prompt}` });
|
|
|
5877
6342
|
|
|
5878
6343
|
// src/server/index.ts
|
|
5879
6344
|
import "dotenv/config";
|
|
5880
|
-
import { Hono as
|
|
6345
|
+
import { Hono as Hono6 } from "hono";
|
|
5881
6346
|
import { serve } from "@hono/node-server";
|
|
5882
6347
|
import { cors } from "hono/cors";
|
|
5883
6348
|
import { logger } from "hono/logger";
|
|
@@ -5891,10 +6356,10 @@ import { fileURLToPath as fileURLToPath4 } from "url";
|
|
|
5891
6356
|
init_db();
|
|
5892
6357
|
import { Hono } from "hono";
|
|
5893
6358
|
import { zValidator } from "@hono/zod-validator";
|
|
5894
|
-
import { z as
|
|
6359
|
+
import { z as z14 } from "zod";
|
|
5895
6360
|
import { existsSync as existsSync13, mkdirSync as mkdirSync3, writeFileSync as writeFileSync2, readdirSync, statSync as statSync2, unlinkSync } from "fs";
|
|
5896
6361
|
import { readdir as readdir5 } from "fs/promises";
|
|
5897
|
-
import { join as join5, basename as basename4, extname as
|
|
6362
|
+
import { join as join5, basename as basename4, extname as extname7, relative as relative9 } from "path";
|
|
5898
6363
|
import { nanoid as nanoid4 } from "nanoid";
|
|
5899
6364
|
init_config();
|
|
5900
6365
|
|
|
@@ -5927,18 +6392,18 @@ function cleanupPendingInputs() {
|
|
|
5927
6392
|
}
|
|
5928
6393
|
}
|
|
5929
6394
|
}
|
|
5930
|
-
var createSessionSchema =
|
|
5931
|
-
name:
|
|
5932
|
-
workingDirectory:
|
|
5933
|
-
model:
|
|
5934
|
-
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()
|
|
5935
6400
|
});
|
|
5936
|
-
var paginationQuerySchema =
|
|
5937
|
-
limit:
|
|
5938
|
-
offset:
|
|
6401
|
+
var paginationQuerySchema = z14.object({
|
|
6402
|
+
limit: z14.string().optional(),
|
|
6403
|
+
offset: z14.string().optional()
|
|
5939
6404
|
});
|
|
5940
|
-
var messagesQuerySchema =
|
|
5941
|
-
limit:
|
|
6405
|
+
var messagesQuerySchema = z14.object({
|
|
6406
|
+
limit: z14.string().optional()
|
|
5942
6407
|
});
|
|
5943
6408
|
sessions.get(
|
|
5944
6409
|
"/",
|
|
@@ -6077,10 +6542,10 @@ sessions.get("/:id/tools", async (c) => {
|
|
|
6077
6542
|
count: executions.length
|
|
6078
6543
|
});
|
|
6079
6544
|
});
|
|
6080
|
-
var updateSessionSchema =
|
|
6081
|
-
model:
|
|
6082
|
-
name:
|
|
6083
|
-
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()
|
|
6084
6549
|
});
|
|
6085
6550
|
sessions.patch(
|
|
6086
6551
|
"/:id",
|
|
@@ -6150,8 +6615,8 @@ sessions.post("/:id/clear", async (c) => {
|
|
|
6150
6615
|
await agent.clearContext();
|
|
6151
6616
|
return c.json({ success: true, sessionId: id });
|
|
6152
6617
|
});
|
|
6153
|
-
var pendingInputSchema =
|
|
6154
|
-
text:
|
|
6618
|
+
var pendingInputSchema = z14.object({
|
|
6619
|
+
text: z14.string()
|
|
6155
6620
|
});
|
|
6156
6621
|
sessions.post(
|
|
6157
6622
|
"/:id/pending-input",
|
|
@@ -6182,13 +6647,13 @@ sessions.get("/:id/pending-input", async (c) => {
|
|
|
6182
6647
|
createdAt: pending.createdAt.toISOString()
|
|
6183
6648
|
});
|
|
6184
6649
|
});
|
|
6185
|
-
var devtoolsContextSchema =
|
|
6186
|
-
url:
|
|
6187
|
-
path:
|
|
6188
|
-
pageName:
|
|
6189
|
-
screenWidth:
|
|
6190
|
-
screenHeight:
|
|
6191
|
-
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()
|
|
6192
6657
|
});
|
|
6193
6658
|
sessions.post(
|
|
6194
6659
|
"/:id/devtools-context",
|
|
@@ -6410,7 +6875,7 @@ sessions.post("/:id/attachments", async (c) => {
|
|
|
6410
6875
|
}
|
|
6411
6876
|
const dir = ensureAttachmentsDir(sessionId);
|
|
6412
6877
|
const id = nanoid4(10);
|
|
6413
|
-
const ext =
|
|
6878
|
+
const ext = extname7(file.name) || "";
|
|
6414
6879
|
const safeFilename = `${id}_${basename4(file.name).replace(/[^a-zA-Z0-9._-]/g, "_")}`;
|
|
6415
6880
|
const filePath = join5(dir, safeFilename);
|
|
6416
6881
|
const arrayBuffer = await file.arrayBuffer();
|
|
@@ -6436,7 +6901,7 @@ sessions.post("/:id/attachments", async (c) => {
|
|
|
6436
6901
|
}
|
|
6437
6902
|
const dir = ensureAttachmentsDir(sessionId);
|
|
6438
6903
|
const id = nanoid4(10);
|
|
6439
|
-
const ext =
|
|
6904
|
+
const ext = extname7(body.filename) || "";
|
|
6440
6905
|
const safeFilename = `${id}_${basename4(body.filename).replace(/[^a-zA-Z0-9._-]/g, "_")}`;
|
|
6441
6906
|
const filePath = join5(dir, safeFilename);
|
|
6442
6907
|
let base64Data = body.data;
|
|
@@ -6479,10 +6944,10 @@ sessions.delete("/:id/attachments/:attachmentId", async (c) => {
|
|
|
6479
6944
|
unlinkSync(filePath);
|
|
6480
6945
|
return c.json({ success: true, id: attachmentId });
|
|
6481
6946
|
});
|
|
6482
|
-
var filesQuerySchema =
|
|
6483
|
-
query:
|
|
6947
|
+
var filesQuerySchema = z14.object({
|
|
6948
|
+
query: z14.string().optional(),
|
|
6484
6949
|
// Filter query (e.g., "src/com" to match "src/components")
|
|
6485
|
-
limit:
|
|
6950
|
+
limit: z14.string().optional()
|
|
6486
6951
|
// Max results (default 50)
|
|
6487
6952
|
});
|
|
6488
6953
|
var IGNORED_DIRECTORIES = /* @__PURE__ */ new Set([
|
|
@@ -6566,7 +7031,7 @@ async function listWorkspaceFiles(baseDir, currentDir, query, limit, results = [
|
|
|
6566
7031
|
if (entry.name.startsWith(".")) {
|
|
6567
7032
|
continue;
|
|
6568
7033
|
}
|
|
6569
|
-
const ext =
|
|
7034
|
+
const ext = extname7(entry.name).toLowerCase();
|
|
6570
7035
|
if (IGNORED_EXTENSIONS.has(ext)) {
|
|
6571
7036
|
continue;
|
|
6572
7037
|
}
|
|
@@ -6660,7 +7125,7 @@ sessions.get(
|
|
|
6660
7125
|
init_db();
|
|
6661
7126
|
import { Hono as Hono2 } from "hono";
|
|
6662
7127
|
import { zValidator as zValidator2 } from "@hono/zod-validator";
|
|
6663
|
-
import { z as
|
|
7128
|
+
import { z as z15 } from "zod";
|
|
6664
7129
|
import { existsSync as existsSync14, mkdirSync as mkdirSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
6665
7130
|
import { join as join6 } from "path";
|
|
6666
7131
|
init_config();
|
|
@@ -6825,30 +7290,30 @@ function enrichPromptWithDevtoolsContext(sessionId, prompt) {
|
|
|
6825
7290
|
${prompt}`;
|
|
6826
7291
|
}
|
|
6827
7292
|
var agents = new Hono2();
|
|
6828
|
-
var attachmentSchema =
|
|
6829
|
-
type:
|
|
6830
|
-
data:
|
|
7293
|
+
var attachmentSchema = z15.object({
|
|
7294
|
+
type: z15.enum(["image", "file"]),
|
|
7295
|
+
data: z15.string(),
|
|
6831
7296
|
// base64 data URL or raw base64
|
|
6832
|
-
mediaType:
|
|
6833
|
-
filename:
|
|
7297
|
+
mediaType: z15.string().optional(),
|
|
7298
|
+
filename: z15.string().optional()
|
|
6834
7299
|
});
|
|
6835
|
-
var runPromptSchema =
|
|
6836
|
-
prompt:
|
|
7300
|
+
var runPromptSchema = z15.object({
|
|
7301
|
+
prompt: z15.string(),
|
|
6837
7302
|
// Can be empty if attachments are provided
|
|
6838
|
-
attachments:
|
|
7303
|
+
attachments: z15.array(attachmentSchema).optional()
|
|
6839
7304
|
}).refine(
|
|
6840
7305
|
(data) => data.prompt.trim().length > 0 || data.attachments && data.attachments.length > 0,
|
|
6841
7306
|
{ message: "Either prompt or attachments must be provided" }
|
|
6842
7307
|
);
|
|
6843
|
-
var quickStartSchema =
|
|
6844
|
-
prompt:
|
|
6845
|
-
name:
|
|
6846
|
-
workingDirectory:
|
|
6847
|
-
model:
|
|
6848
|
-
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()
|
|
6849
7314
|
});
|
|
6850
|
-
var rejectSchema =
|
|
6851
|
-
reason:
|
|
7315
|
+
var rejectSchema = z15.object({
|
|
7316
|
+
reason: z15.string().optional()
|
|
6852
7317
|
}).optional();
|
|
6853
7318
|
var streamAbortControllers = /* @__PURE__ */ new Map();
|
|
6854
7319
|
function getAttachmentsDirectory(sessionId) {
|
|
@@ -7278,7 +7743,7 @@ agents.get("/:id/watch", async (c) => {
|
|
|
7278
7743
|
"Cache-Control": "no-cache",
|
|
7279
7744
|
"Connection": "keep-alive",
|
|
7280
7745
|
"x-vercel-ai-ui-message-stream": "v1",
|
|
7281
|
-
"x-stream-id": streamId
|
|
7746
|
+
"x-stream-id": streamId ?? ""
|
|
7282
7747
|
}
|
|
7283
7748
|
});
|
|
7284
7749
|
});
|
|
@@ -7636,7 +8101,7 @@ agents.post(
|
|
|
7636
8101
|
init_config();
|
|
7637
8102
|
import { Hono as Hono3 } from "hono";
|
|
7638
8103
|
import { zValidator as zValidator3 } from "@hono/zod-validator";
|
|
7639
|
-
import { z as
|
|
8104
|
+
import { z as z16 } from "zod";
|
|
7640
8105
|
import { readFileSync as readFileSync5 } from "fs";
|
|
7641
8106
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
7642
8107
|
import { dirname as dirname6, join as join7 } from "path";
|
|
@@ -7746,9 +8211,9 @@ health.get("/api-keys", async (c) => {
|
|
|
7746
8211
|
supportedProviders: SUPPORTED_PROVIDERS
|
|
7747
8212
|
});
|
|
7748
8213
|
});
|
|
7749
|
-
var setApiKeySchema =
|
|
7750
|
-
provider:
|
|
7751
|
-
apiKey:
|
|
8214
|
+
var setApiKeySchema = z16.object({
|
|
8215
|
+
provider: z16.string(),
|
|
8216
|
+
apiKey: z16.string().min(1)
|
|
7752
8217
|
});
|
|
7753
8218
|
health.post(
|
|
7754
8219
|
"/api-keys",
|
|
@@ -7787,13 +8252,13 @@ health.delete("/api-keys/:provider", async (c) => {
|
|
|
7787
8252
|
// src/server/routes/terminals.ts
|
|
7788
8253
|
import { Hono as Hono4 } from "hono";
|
|
7789
8254
|
import { zValidator as zValidator4 } from "@hono/zod-validator";
|
|
7790
|
-
import { z as
|
|
8255
|
+
import { z as z17 } from "zod";
|
|
7791
8256
|
init_db();
|
|
7792
8257
|
var terminals = new Hono4();
|
|
7793
|
-
var spawnSchema =
|
|
7794
|
-
command:
|
|
7795
|
-
cwd:
|
|
7796
|
-
name:
|
|
8258
|
+
var spawnSchema = z17.object({
|
|
8259
|
+
command: z17.string(),
|
|
8260
|
+
cwd: z17.string().optional(),
|
|
8261
|
+
name: z17.string().optional()
|
|
7797
8262
|
});
|
|
7798
8263
|
terminals.post(
|
|
7799
8264
|
"/:sessionId/terminals",
|
|
@@ -7874,8 +8339,8 @@ terminals.get("/:sessionId/terminals/:terminalId", async (c) => {
|
|
|
7874
8339
|
// We don't track exit codes in tmux mode
|
|
7875
8340
|
});
|
|
7876
8341
|
});
|
|
7877
|
-
var logsQuerySchema =
|
|
7878
|
-
tail:
|
|
8342
|
+
var logsQuerySchema = z17.object({
|
|
8343
|
+
tail: z17.string().optional().transform((v) => v ? parseInt(v, 10) : void 0)
|
|
7879
8344
|
});
|
|
7880
8345
|
terminals.get(
|
|
7881
8346
|
"/:sessionId/terminals/:terminalId/logs",
|
|
@@ -7899,8 +8364,8 @@ terminals.get(
|
|
|
7899
8364
|
});
|
|
7900
8365
|
}
|
|
7901
8366
|
);
|
|
7902
|
-
var killSchema =
|
|
7903
|
-
signal:
|
|
8367
|
+
var killSchema = z17.object({
|
|
8368
|
+
signal: z17.enum(["SIGTERM", "SIGKILL"]).optional()
|
|
7904
8369
|
});
|
|
7905
8370
|
terminals.post(
|
|
7906
8371
|
"/:sessionId/terminals/:terminalId/kill",
|
|
@@ -7914,8 +8379,8 @@ terminals.post(
|
|
|
7914
8379
|
return c.json({ success: true, message: "Terminal killed" });
|
|
7915
8380
|
}
|
|
7916
8381
|
);
|
|
7917
|
-
var writeSchema =
|
|
7918
|
-
input:
|
|
8382
|
+
var writeSchema = z17.object({
|
|
8383
|
+
input: z17.string()
|
|
7919
8384
|
});
|
|
7920
8385
|
terminals.post(
|
|
7921
8386
|
"/:sessionId/terminals/:terminalId/write",
|
|
@@ -8096,6 +8561,131 @@ data: ${JSON.stringify({ status: "stopped" })}
|
|
|
8096
8561
|
);
|
|
8097
8562
|
});
|
|
8098
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
|
+
|
|
8099
8689
|
// src/server/index.ts
|
|
8100
8690
|
init_config();
|
|
8101
8691
|
init_db();
|
|
@@ -8432,7 +9022,7 @@ function stopWebUI() {
|
|
|
8432
9022
|
}
|
|
8433
9023
|
}
|
|
8434
9024
|
async function createApp(options = {}) {
|
|
8435
|
-
const app = new
|
|
9025
|
+
const app = new Hono6();
|
|
8436
9026
|
app.use("*", cors({
|
|
8437
9027
|
origin: "*",
|
|
8438
9028
|
// Allow all origins
|
|
@@ -8450,6 +9040,7 @@ async function createApp(options = {}) {
|
|
|
8450
9040
|
app.route("/agents", agents);
|
|
8451
9041
|
app.route("/sessions", terminals);
|
|
8452
9042
|
app.route("/terminals", terminals);
|
|
9043
|
+
app.route("/tasks", tasks_default);
|
|
8453
9044
|
app.get("/openapi.json", async (c) => {
|
|
8454
9045
|
return c.json(generateOpenAPISpec());
|
|
8455
9046
|
});
|