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/agent/index.js
CHANGED
|
@@ -8,6 +8,191 @@ var __export = (target, all) => {
|
|
|
8
8
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
9
|
};
|
|
10
10
|
|
|
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 getAppDataDirectory() {
|
|
145
|
+
const appName = "sparkecoder";
|
|
146
|
+
switch (platform()) {
|
|
147
|
+
case "darwin":
|
|
148
|
+
return join(homedir(), "Library", "Application Support", appName);
|
|
149
|
+
case "win32":
|
|
150
|
+
return join(process.env.APPDATA || join(homedir(), "AppData", "Roaming"), appName);
|
|
151
|
+
default:
|
|
152
|
+
return join(process.env.XDG_DATA_HOME || join(homedir(), ".local", "share"), appName);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
function getConfig() {
|
|
156
|
+
if (!cachedConfig) {
|
|
157
|
+
throw new Error("Config not loaded. Call loadConfig first.");
|
|
158
|
+
}
|
|
159
|
+
return cachedConfig;
|
|
160
|
+
}
|
|
161
|
+
function requiresApproval(toolName, sessionConfig) {
|
|
162
|
+
const config = getConfig();
|
|
163
|
+
if (sessionConfig?.toolApprovals?.["*"] !== void 0) {
|
|
164
|
+
return sessionConfig.toolApprovals["*"];
|
|
165
|
+
}
|
|
166
|
+
if (sessionConfig?.toolApprovals?.[toolName] !== void 0) {
|
|
167
|
+
return sessionConfig.toolApprovals[toolName];
|
|
168
|
+
}
|
|
169
|
+
const globalApprovals = config.toolApprovals;
|
|
170
|
+
if (globalApprovals[toolName] !== void 0) {
|
|
171
|
+
return globalApprovals[toolName];
|
|
172
|
+
}
|
|
173
|
+
if (toolName === "bash") {
|
|
174
|
+
return true;
|
|
175
|
+
}
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
var cachedConfig, PROVIDER_ENV_MAP, SUPPORTED_PROVIDERS;
|
|
179
|
+
var init_config = __esm({
|
|
180
|
+
"src/config/index.ts"() {
|
|
181
|
+
"use strict";
|
|
182
|
+
init_types();
|
|
183
|
+
init_types();
|
|
184
|
+
cachedConfig = null;
|
|
185
|
+
PROVIDER_ENV_MAP = {
|
|
186
|
+
anthropic: "ANTHROPIC_API_KEY",
|
|
187
|
+
openai: "OPENAI_API_KEY",
|
|
188
|
+
google: "GOOGLE_GENERATIVE_AI_API_KEY",
|
|
189
|
+
xai: "XAI_API_KEY",
|
|
190
|
+
"ai-gateway": "AI_GATEWAY_API_KEY"
|
|
191
|
+
};
|
|
192
|
+
SUPPORTED_PROVIDERS = Object.keys(PROVIDER_ENV_MAP);
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
|
|
11
196
|
// src/db/remote.ts
|
|
12
197
|
function parseDates(obj) {
|
|
13
198
|
if (obj === null || obj === void 0) return obj;
|
|
@@ -298,177 +483,6 @@ var init_db = __esm({
|
|
|
298
483
|
}
|
|
299
484
|
});
|
|
300
485
|
|
|
301
|
-
// src/config/types.ts
|
|
302
|
-
import { z } from "zod";
|
|
303
|
-
var ToolApprovalConfigSchema, SkillMetadataSchema, SessionConfigSchema, VectorGatewayConfigSchema, RemoteServerConfigSchema, SparkcoderConfigSchema;
|
|
304
|
-
var init_types = __esm({
|
|
305
|
-
"src/config/types.ts"() {
|
|
306
|
-
"use strict";
|
|
307
|
-
ToolApprovalConfigSchema = z.object({
|
|
308
|
-
bash: z.boolean().optional().default(true),
|
|
309
|
-
write_file: z.boolean().optional().default(false),
|
|
310
|
-
read_file: z.boolean().optional().default(false),
|
|
311
|
-
load_skill: z.boolean().optional().default(false),
|
|
312
|
-
todo: z.boolean().optional().default(false)
|
|
313
|
-
});
|
|
314
|
-
SkillMetadataSchema = z.object({
|
|
315
|
-
name: z.string(),
|
|
316
|
-
description: z.string(),
|
|
317
|
-
// Whether to always inject this skill into context (vs on-demand loading)
|
|
318
|
-
alwaysApply: z.boolean().optional().default(false),
|
|
319
|
-
// Glob patterns - auto-inject when working with matching files
|
|
320
|
-
globs: z.array(z.string()).optional().default([])
|
|
321
|
-
});
|
|
322
|
-
SessionConfigSchema = z.object({
|
|
323
|
-
toolApprovals: z.record(z.string(), z.boolean()).optional(),
|
|
324
|
-
approvalWebhook: z.string().url().optional(),
|
|
325
|
-
skillsDirectory: z.string().optional(),
|
|
326
|
-
maxContextChars: z.number().optional().default(2e5)
|
|
327
|
-
});
|
|
328
|
-
VectorGatewayConfigSchema = z.object({
|
|
329
|
-
// Redis cluster nodes URL for Vector Gateway (or use REDIS_CLUSTER_NODES env var)
|
|
330
|
-
redisUrl: z.string().optional(),
|
|
331
|
-
// HTTP URL for database operations (or use VECTOR_HTTP_URL env var)
|
|
332
|
-
httpUrl: z.string().optional(),
|
|
333
|
-
// Embedding model to use (default: text-embedding-3-small)
|
|
334
|
-
embeddingModel: z.string().default("gemini-embedding-001"),
|
|
335
|
-
// Custom namespace override (auto-generated from git remote if not set)
|
|
336
|
-
namespace: z.string().optional(),
|
|
337
|
-
// File patterns to include in indexing
|
|
338
|
-
include: z.array(z.string()).optional().default([
|
|
339
|
-
"**/*.ts",
|
|
340
|
-
"**/*.tsx",
|
|
341
|
-
"**/*.js",
|
|
342
|
-
"**/*.jsx",
|
|
343
|
-
"**/*.py",
|
|
344
|
-
"**/*.go",
|
|
345
|
-
"**/*.rs",
|
|
346
|
-
"**/*.java",
|
|
347
|
-
"**/*.md",
|
|
348
|
-
"**/*.mdx",
|
|
349
|
-
"**/*.txt"
|
|
350
|
-
]),
|
|
351
|
-
// File patterns to exclude from indexing
|
|
352
|
-
exclude: z.array(z.string()).optional().default([
|
|
353
|
-
"**/node_modules/**",
|
|
354
|
-
"**/dist/**",
|
|
355
|
-
"**/build/**",
|
|
356
|
-
"**/.git/**",
|
|
357
|
-
"**/.next/**",
|
|
358
|
-
"**/*.min.js",
|
|
359
|
-
"**/*.bundle.js",
|
|
360
|
-
"**/pnpm-lock.yaml",
|
|
361
|
-
"**/package-lock.json",
|
|
362
|
-
"**/yarn.lock",
|
|
363
|
-
"**/.test-workspace/**",
|
|
364
|
-
"**/.semantic-test-workspace/**",
|
|
365
|
-
"**/.semantic-integration-test/**"
|
|
366
|
-
])
|
|
367
|
-
}).optional();
|
|
368
|
-
RemoteServerConfigSchema = z.object({
|
|
369
|
-
// URL of the remote server (e.g., https://agent.sparkecode.com)
|
|
370
|
-
url: z.string().url().optional(),
|
|
371
|
-
// Auth key for the remote server (auto-generated on first use if not set)
|
|
372
|
-
// Can also be set via SPARKECODER_AUTH_KEY env var
|
|
373
|
-
authKey: z.string().optional()
|
|
374
|
-
}).optional();
|
|
375
|
-
SparkcoderConfigSchema = z.object({
|
|
376
|
-
// Default model to use (Vercel AI Gateway format)
|
|
377
|
-
defaultModel: z.string().default("anthropic/claude-opus-4-6"),
|
|
378
|
-
// Working directory for file operations
|
|
379
|
-
workingDirectory: z.string().optional(),
|
|
380
|
-
// Tool approval settings
|
|
381
|
-
toolApprovals: ToolApprovalConfigSchema.optional().default({}),
|
|
382
|
-
// Approval webhook URL (called when approval is needed)
|
|
383
|
-
approvalWebhook: z.string().url().optional(),
|
|
384
|
-
// Skills configuration
|
|
385
|
-
skills: z.object({
|
|
386
|
-
// Directory containing skill files
|
|
387
|
-
directory: z.string().optional().default("./skills"),
|
|
388
|
-
// Additional skill directories to include
|
|
389
|
-
additionalDirectories: z.array(z.string()).optional().default([])
|
|
390
|
-
}).optional().default({}),
|
|
391
|
-
// Context management
|
|
392
|
-
context: z.object({
|
|
393
|
-
// Maximum context size before summarization (in characters)
|
|
394
|
-
maxChars: z.number().optional().default(2e5),
|
|
395
|
-
// Enable automatic summarization
|
|
396
|
-
autoSummarize: z.boolean().optional().default(true),
|
|
397
|
-
// Number of recent messages to keep after summarization
|
|
398
|
-
keepRecentMessages: z.number().optional().default(10)
|
|
399
|
-
}).optional().default({}),
|
|
400
|
-
// Server configuration
|
|
401
|
-
server: z.object({
|
|
402
|
-
port: z.number().default(3141),
|
|
403
|
-
host: z.string().default("127.0.0.1"),
|
|
404
|
-
// Public URL for web UI to connect to API (for Docker/remote access)
|
|
405
|
-
// If not set, defaults to http://{host}:{port}
|
|
406
|
-
publicUrl: z.string().url().optional()
|
|
407
|
-
}).default({ port: 3141, host: "127.0.0.1" }),
|
|
408
|
-
// Database path (used for local SQLite - ignored if remoteServer is configured)
|
|
409
|
-
databasePath: z.string().optional().default("./sparkecoder.db"),
|
|
410
|
-
// Remote server configuration (for centralized storage)
|
|
411
|
-
// If configured, uses remote MongoDB instead of local SQLite
|
|
412
|
-
remoteServer: RemoteServerConfigSchema,
|
|
413
|
-
// Vector Gateway configuration for semantic search
|
|
414
|
-
vectorGateway: VectorGatewayConfigSchema
|
|
415
|
-
});
|
|
416
|
-
}
|
|
417
|
-
});
|
|
418
|
-
|
|
419
|
-
// src/config/index.ts
|
|
420
|
-
import { existsSync, readFileSync, mkdirSync, writeFileSync } from "fs";
|
|
421
|
-
import { resolve, dirname, join } from "path";
|
|
422
|
-
import { homedir, platform } from "os";
|
|
423
|
-
function getAppDataDirectory() {
|
|
424
|
-
const appName = "sparkecoder";
|
|
425
|
-
switch (platform()) {
|
|
426
|
-
case "darwin":
|
|
427
|
-
return join(homedir(), "Library", "Application Support", appName);
|
|
428
|
-
case "win32":
|
|
429
|
-
return join(process.env.APPDATA || join(homedir(), "AppData", "Roaming"), appName);
|
|
430
|
-
default:
|
|
431
|
-
return join(process.env.XDG_DATA_HOME || join(homedir(), ".local", "share"), appName);
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
function getConfig() {
|
|
435
|
-
if (!cachedConfig) {
|
|
436
|
-
throw new Error("Config not loaded. Call loadConfig first.");
|
|
437
|
-
}
|
|
438
|
-
return cachedConfig;
|
|
439
|
-
}
|
|
440
|
-
function requiresApproval(toolName, sessionConfig) {
|
|
441
|
-
const config = getConfig();
|
|
442
|
-
if (sessionConfig?.toolApprovals?.[toolName] !== void 0) {
|
|
443
|
-
return sessionConfig.toolApprovals[toolName];
|
|
444
|
-
}
|
|
445
|
-
const globalApprovals = config.toolApprovals;
|
|
446
|
-
if (globalApprovals[toolName] !== void 0) {
|
|
447
|
-
return globalApprovals[toolName];
|
|
448
|
-
}
|
|
449
|
-
if (toolName === "bash") {
|
|
450
|
-
return true;
|
|
451
|
-
}
|
|
452
|
-
return false;
|
|
453
|
-
}
|
|
454
|
-
var cachedConfig, PROVIDER_ENV_MAP, SUPPORTED_PROVIDERS;
|
|
455
|
-
var init_config = __esm({
|
|
456
|
-
"src/config/index.ts"() {
|
|
457
|
-
"use strict";
|
|
458
|
-
init_types();
|
|
459
|
-
init_types();
|
|
460
|
-
cachedConfig = null;
|
|
461
|
-
PROVIDER_ENV_MAP = {
|
|
462
|
-
anthropic: "ANTHROPIC_API_KEY",
|
|
463
|
-
openai: "OPENAI_API_KEY",
|
|
464
|
-
google: "GOOGLE_GENERATIVE_AI_API_KEY",
|
|
465
|
-
xai: "XAI_API_KEY",
|
|
466
|
-
"ai-gateway": "AI_GATEWAY_API_KEY"
|
|
467
|
-
};
|
|
468
|
-
SUPPORTED_PROVIDERS = Object.keys(PROVIDER_ENV_MAP);
|
|
469
|
-
}
|
|
470
|
-
});
|
|
471
|
-
|
|
472
486
|
// src/skills/index.ts
|
|
473
487
|
var skills_exports = {};
|
|
474
488
|
__export(skills_exports, {
|
|
@@ -484,7 +498,7 @@ __export(skills_exports, {
|
|
|
484
498
|
loadSkillsFromDirectory: () => loadSkillsFromDirectory
|
|
485
499
|
});
|
|
486
500
|
import { readFile as readFile6, readdir } from "fs/promises";
|
|
487
|
-
import { resolve as resolve6, basename, extname as
|
|
501
|
+
import { resolve as resolve6, basename, extname as extname4, relative as relative4 } from "path";
|
|
488
502
|
import { existsSync as existsSync8 } from "fs";
|
|
489
503
|
import { minimatch } from "minimatch";
|
|
490
504
|
function parseSkillFrontmatter(content) {
|
|
@@ -555,7 +569,7 @@ function parseSkillFrontmatter(content) {
|
|
|
555
569
|
}
|
|
556
570
|
}
|
|
557
571
|
function getSkillNameFromPath(filePath) {
|
|
558
|
-
return basename(filePath,
|
|
572
|
+
return basename(filePath, extname4(filePath)).replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
559
573
|
}
|
|
560
574
|
async function loadSkillsFromDirectory(directory, options = {}) {
|
|
561
575
|
const {
|
|
@@ -860,7 +874,7 @@ var init_hasher = __esm({
|
|
|
860
874
|
});
|
|
861
875
|
|
|
862
876
|
// src/semantic/chunker.ts
|
|
863
|
-
import { extname as
|
|
877
|
+
import { extname as extname6, basename as basename2 } from "path";
|
|
864
878
|
var init_chunker = __esm({
|
|
865
879
|
"src/semantic/chunker.ts"() {
|
|
866
880
|
"use strict";
|
|
@@ -1210,18 +1224,176 @@ var init_semantic_search = __esm({
|
|
|
1210
1224
|
import {
|
|
1211
1225
|
streamText as streamText2,
|
|
1212
1226
|
generateText as generateText3,
|
|
1213
|
-
tool as
|
|
1227
|
+
tool as tool12,
|
|
1214
1228
|
stepCountIs as stepCountIs2
|
|
1215
1229
|
} from "ai";
|
|
1216
1230
|
|
|
1217
1231
|
// src/agent/model.ts
|
|
1218
1232
|
import { gateway } from "@ai-sdk/gateway";
|
|
1233
|
+
|
|
1234
|
+
// src/agent/remote-model.ts
|
|
1235
|
+
function serializePrompt(prompt) {
|
|
1236
|
+
return prompt.map((msg) => {
|
|
1237
|
+
if (!Array.isArray(msg.content)) return msg;
|
|
1238
|
+
return {
|
|
1239
|
+
...msg,
|
|
1240
|
+
content: msg.content.map((part) => {
|
|
1241
|
+
if (part.type === "file" && part.data instanceof Uint8Array) {
|
|
1242
|
+
return {
|
|
1243
|
+
...part,
|
|
1244
|
+
data: Buffer.from(part.data).toString("base64"),
|
|
1245
|
+
_base64: true
|
|
1246
|
+
};
|
|
1247
|
+
}
|
|
1248
|
+
return part;
|
|
1249
|
+
})
|
|
1250
|
+
};
|
|
1251
|
+
});
|
|
1252
|
+
}
|
|
1253
|
+
function deserializeValue(value) {
|
|
1254
|
+
if (value && typeof value === "object") {
|
|
1255
|
+
if (value.__uint8array && typeof value.data === "string") {
|
|
1256
|
+
return Buffer.from(value.data, "base64");
|
|
1257
|
+
}
|
|
1258
|
+
if (Array.isArray(value)) {
|
|
1259
|
+
return value.map(deserializeValue);
|
|
1260
|
+
}
|
|
1261
|
+
const result = {};
|
|
1262
|
+
for (const [k, v] of Object.entries(value)) {
|
|
1263
|
+
result[k] = deserializeValue(v);
|
|
1264
|
+
}
|
|
1265
|
+
return result;
|
|
1266
|
+
}
|
|
1267
|
+
return value;
|
|
1268
|
+
}
|
|
1269
|
+
function prepareOptions(options) {
|
|
1270
|
+
const { abortSignal, ...rest } = options;
|
|
1271
|
+
return {
|
|
1272
|
+
...rest,
|
|
1273
|
+
prompt: serializePrompt(options.prompt)
|
|
1274
|
+
};
|
|
1275
|
+
}
|
|
1276
|
+
function createRemoteModel(modelId, config) {
|
|
1277
|
+
const baseUrl = config.url.replace(/\/$/, "");
|
|
1278
|
+
const headers = {
|
|
1279
|
+
"Content-Type": "application/json",
|
|
1280
|
+
"Authorization": `Bearer ${config.authKey}`
|
|
1281
|
+
};
|
|
1282
|
+
return {
|
|
1283
|
+
specificationVersion: "v3",
|
|
1284
|
+
provider: "remote-proxy",
|
|
1285
|
+
modelId,
|
|
1286
|
+
supportedUrls: {},
|
|
1287
|
+
async doGenerate(options) {
|
|
1288
|
+
const res = await fetch(`${baseUrl}/inference/generate`, {
|
|
1289
|
+
method: "POST",
|
|
1290
|
+
headers,
|
|
1291
|
+
body: JSON.stringify({
|
|
1292
|
+
modelId,
|
|
1293
|
+
options: prepareOptions(options)
|
|
1294
|
+
}),
|
|
1295
|
+
signal: options.abortSignal
|
|
1296
|
+
});
|
|
1297
|
+
if (!res.ok) {
|
|
1298
|
+
const err = await res.json().catch(() => ({}));
|
|
1299
|
+
throw new Error(
|
|
1300
|
+
`Remote inference failed (${res.status}): ${err.error || res.statusText}`
|
|
1301
|
+
);
|
|
1302
|
+
}
|
|
1303
|
+
const result = await res.json();
|
|
1304
|
+
return deserializeValue(result);
|
|
1305
|
+
},
|
|
1306
|
+
async doStream(options) {
|
|
1307
|
+
const res = await fetch(`${baseUrl}/inference/stream`, {
|
|
1308
|
+
method: "POST",
|
|
1309
|
+
headers,
|
|
1310
|
+
body: JSON.stringify({
|
|
1311
|
+
modelId,
|
|
1312
|
+
options: prepareOptions(options)
|
|
1313
|
+
}),
|
|
1314
|
+
signal: options.abortSignal
|
|
1315
|
+
});
|
|
1316
|
+
if (!res.ok) {
|
|
1317
|
+
const err = await res.json().catch(() => ({}));
|
|
1318
|
+
throw new Error(
|
|
1319
|
+
`Remote inference failed (${res.status}): ${err.error || res.statusText}`
|
|
1320
|
+
);
|
|
1321
|
+
}
|
|
1322
|
+
const reader = res.body.getReader();
|
|
1323
|
+
const decoder = new TextDecoder();
|
|
1324
|
+
let buffer = "";
|
|
1325
|
+
const stream = new ReadableStream({
|
|
1326
|
+
async pull(controller) {
|
|
1327
|
+
while (true) {
|
|
1328
|
+
const { done, value } = await reader.read();
|
|
1329
|
+
if (done) {
|
|
1330
|
+
if (buffer.trim()) {
|
|
1331
|
+
try {
|
|
1332
|
+
const parsed = deserializeValue(JSON.parse(buffer.trim()));
|
|
1333
|
+
if (parsed.type === "error") {
|
|
1334
|
+
controller.error(new Error(parsed.error));
|
|
1335
|
+
} else {
|
|
1336
|
+
controller.enqueue(parsed);
|
|
1337
|
+
}
|
|
1338
|
+
} catch {
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
controller.close();
|
|
1342
|
+
return;
|
|
1343
|
+
}
|
|
1344
|
+
buffer += decoder.decode(value, { stream: true });
|
|
1345
|
+
const lines = buffer.split("\n");
|
|
1346
|
+
buffer = lines.pop() || "";
|
|
1347
|
+
for (const line of lines) {
|
|
1348
|
+
if (!line.trim()) continue;
|
|
1349
|
+
try {
|
|
1350
|
+
const parsed = deserializeValue(JSON.parse(line));
|
|
1351
|
+
if (parsed.type === "error") {
|
|
1352
|
+
controller.error(new Error(parsed.error));
|
|
1353
|
+
return;
|
|
1354
|
+
}
|
|
1355
|
+
controller.enqueue(parsed);
|
|
1356
|
+
} catch {
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
},
|
|
1361
|
+
cancel() {
|
|
1362
|
+
reader.cancel();
|
|
1363
|
+
}
|
|
1364
|
+
});
|
|
1365
|
+
const responseHeaders = {};
|
|
1366
|
+
res.headers.forEach((v, k) => {
|
|
1367
|
+
if (k.startsWith("x-upstream-")) {
|
|
1368
|
+
responseHeaders[k.replace("x-upstream-", "")] = v;
|
|
1369
|
+
}
|
|
1370
|
+
});
|
|
1371
|
+
return {
|
|
1372
|
+
stream,
|
|
1373
|
+
response: Object.keys(responseHeaders).length > 0 ? { headers: responseHeaders } : void 0
|
|
1374
|
+
};
|
|
1375
|
+
}
|
|
1376
|
+
};
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1379
|
+
// src/agent/model.ts
|
|
1380
|
+
init_config();
|
|
1219
1381
|
var ANTHROPIC_PREFIX = "anthropic/";
|
|
1220
1382
|
function isAnthropicModel(modelId) {
|
|
1221
1383
|
const normalized = modelId.trim().toLowerCase();
|
|
1222
1384
|
return normalized.startsWith(ANTHROPIC_PREFIX) || normalized.startsWith("claude-");
|
|
1223
1385
|
}
|
|
1224
1386
|
function resolveModel(modelId) {
|
|
1387
|
+
try {
|
|
1388
|
+
const config = getConfig();
|
|
1389
|
+
if (config.resolvedRemoteServer.isConfigured) {
|
|
1390
|
+
return createRemoteModel(modelId.trim(), {
|
|
1391
|
+
url: config.resolvedRemoteServer.url,
|
|
1392
|
+
authKey: config.resolvedRemoteServer.authKey
|
|
1393
|
+
});
|
|
1394
|
+
}
|
|
1395
|
+
} catch {
|
|
1396
|
+
}
|
|
1225
1397
|
return gateway(modelId.trim());
|
|
1226
1398
|
}
|
|
1227
1399
|
var SUBAGENT_MODELS = {
|
|
@@ -1233,7 +1405,7 @@ var SUBAGENT_MODELS = {
|
|
|
1233
1405
|
// src/agent/index.ts
|
|
1234
1406
|
init_db();
|
|
1235
1407
|
init_config();
|
|
1236
|
-
import { z as
|
|
1408
|
+
import { z as z13 } from "zod";
|
|
1237
1409
|
import { nanoid as nanoid3 } from "nanoid";
|
|
1238
1410
|
|
|
1239
1411
|
// src/tools/bash.ts
|
|
@@ -1766,26 +1938,41 @@ Terminal output is stored in the global SparkECoder data directory. Use the \`ta
|
|
|
1766
1938
|
import { tool as tool2 } from "ai";
|
|
1767
1939
|
import { z as z3 } from "zod";
|
|
1768
1940
|
import { readFile as readFile2, stat } from "fs/promises";
|
|
1769
|
-
import { resolve as resolve2, relative, isAbsolute } from "path";
|
|
1941
|
+
import { resolve as resolve2, relative, isAbsolute, extname } from "path";
|
|
1770
1942
|
import { existsSync as existsSync3 } from "fs";
|
|
1771
1943
|
var MAX_FILE_SIZE = 5 * 1024 * 1024;
|
|
1944
|
+
var MAX_IMAGE_SIZE = 20 * 1024 * 1024;
|
|
1772
1945
|
var MAX_OUTPUT_CHARS3 = 5e4;
|
|
1946
|
+
var IMAGE_EXTENSIONS = {
|
|
1947
|
+
".png": "image/png",
|
|
1948
|
+
".jpg": "image/jpeg",
|
|
1949
|
+
".jpeg": "image/jpeg",
|
|
1950
|
+
".gif": "image/gif",
|
|
1951
|
+
".webp": "image/webp"
|
|
1952
|
+
};
|
|
1953
|
+
function isImageFile(filePath) {
|
|
1954
|
+
return extname(filePath).toLowerCase() in IMAGE_EXTENSIONS;
|
|
1955
|
+
}
|
|
1956
|
+
function getImageMediaType(filePath) {
|
|
1957
|
+
return IMAGE_EXTENSIONS[extname(filePath).toLowerCase()] || "image/png";
|
|
1958
|
+
}
|
|
1773
1959
|
var readFileInputSchema = z3.object({
|
|
1774
|
-
path: z3.string().describe("The path to the file to read. Can be relative to working directory or absolute."),
|
|
1775
|
-
startLine: z3.number().optional().describe("Optional: Start reading from this line number (1-indexed)"),
|
|
1776
|
-
endLine: z3.number().optional().describe("Optional: Stop reading at this line number (1-indexed, inclusive)")
|
|
1960
|
+
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)."),
|
|
1961
|
+
startLine: z3.number().optional().describe("Optional: Start reading from this line number (1-indexed). Only for text files."),
|
|
1962
|
+
endLine: z3.number().optional().describe("Optional: Stop reading at this line number (1-indexed, inclusive). Only for text files.")
|
|
1777
1963
|
});
|
|
1778
1964
|
function createReadFileTool(options) {
|
|
1779
1965
|
return tool2({
|
|
1780
1966
|
description: `Read the contents of a file. Provide a path relative to the working directory (${options.workingDirectory}) or an absolute path.
|
|
1781
|
-
|
|
1782
|
-
|
|
1967
|
+
Supports text files (automatically truncated if large) and image files (png, jpg, jpeg, gif, webp).
|
|
1968
|
+
For images, the file contents are returned as visual data you can see and analyze.
|
|
1969
|
+
Use this to understand existing code, check file contents, view screenshots, or gather context.`,
|
|
1783
1970
|
inputSchema: readFileInputSchema,
|
|
1784
|
-
execute: async ({ path, startLine, endLine }) => {
|
|
1971
|
+
execute: async ({ path: filePath, startLine, endLine }) => {
|
|
1785
1972
|
try {
|
|
1786
|
-
const absolutePath = isAbsolute(
|
|
1973
|
+
const absolutePath = isAbsolute(filePath) ? filePath : resolve2(options.workingDirectory, filePath);
|
|
1787
1974
|
const relativePath = relative(options.workingDirectory, absolutePath);
|
|
1788
|
-
if (relativePath.startsWith("..") && !isAbsolute(
|
|
1975
|
+
if (relativePath.startsWith("..") && !isAbsolute(filePath)) {
|
|
1789
1976
|
return {
|
|
1790
1977
|
success: false,
|
|
1791
1978
|
error: "Path escapes the working directory. Use an absolute path if intentional.",
|
|
@@ -1795,22 +1982,43 @@ Use this to understand existing code, check file contents, or gather context.`,
|
|
|
1795
1982
|
if (!existsSync3(absolutePath)) {
|
|
1796
1983
|
return {
|
|
1797
1984
|
success: false,
|
|
1798
|
-
error: `File not found: ${
|
|
1985
|
+
error: `File not found: ${filePath}`,
|
|
1799
1986
|
content: null
|
|
1800
1987
|
};
|
|
1801
1988
|
}
|
|
1802
1989
|
const stats = await stat(absolutePath);
|
|
1803
|
-
if (stats.
|
|
1990
|
+
if (stats.isDirectory()) {
|
|
1804
1991
|
return {
|
|
1805
1992
|
success: false,
|
|
1806
|
-
error:
|
|
1993
|
+
error: 'Path is a directory, not a file. Use bash with "ls" to list directory contents.',
|
|
1807
1994
|
content: null
|
|
1808
1995
|
};
|
|
1809
1996
|
}
|
|
1810
|
-
if (
|
|
1997
|
+
if (isImageFile(absolutePath)) {
|
|
1998
|
+
if (stats.size > MAX_IMAGE_SIZE) {
|
|
1999
|
+
return {
|
|
2000
|
+
success: false,
|
|
2001
|
+
error: `Image is too large (${(stats.size / 1024 / 1024).toFixed(2)}MB). Maximum size is ${MAX_IMAGE_SIZE / 1024 / 1024}MB.`,
|
|
2002
|
+
content: null
|
|
2003
|
+
};
|
|
2004
|
+
}
|
|
2005
|
+
const buffer = await readFile2(absolutePath);
|
|
2006
|
+
const base64 = buffer.toString("base64");
|
|
2007
|
+
const mediaType = getImageMediaType(absolutePath);
|
|
2008
|
+
return {
|
|
2009
|
+
success: true,
|
|
2010
|
+
path: absolutePath,
|
|
2011
|
+
relativePath: relative(options.workingDirectory, absolutePath),
|
|
2012
|
+
content: `[Image: ${relativePath} (${mediaType}, ${(stats.size / 1024).toFixed(1)}KB)]`,
|
|
2013
|
+
mediaType,
|
|
2014
|
+
imageData: base64,
|
|
2015
|
+
sizeBytes: stats.size
|
|
2016
|
+
};
|
|
2017
|
+
}
|
|
2018
|
+
if (stats.size > MAX_FILE_SIZE) {
|
|
1811
2019
|
return {
|
|
1812
2020
|
success: false,
|
|
1813
|
-
error:
|
|
2021
|
+
error: `File is too large (${(stats.size / 1024 / 1024).toFixed(2)}MB). Maximum size is ${MAX_FILE_SIZE / 1024 / 1024}MB.`,
|
|
1814
2022
|
content: null
|
|
1815
2023
|
};
|
|
1816
2024
|
}
|
|
@@ -1826,9 +2034,7 @@ Use this to understand existing code, check file contents, or gather context.`,
|
|
|
1826
2034
|
content: null
|
|
1827
2035
|
};
|
|
1828
2036
|
}
|
|
1829
|
-
content = lines.slice(start, end).join("\n");
|
|
1830
|
-
const lineNumbers = lines.slice(start, end).map((line, idx) => `${(start + idx + 1).toString().padStart(4)}: ${line}`).join("\n");
|
|
1831
|
-
content = lineNumbers;
|
|
2037
|
+
content = lines.slice(start, end).map((line, idx) => `${(start + idx + 1).toString().padStart(4)}: ${line}`).join("\n");
|
|
1832
2038
|
}
|
|
1833
2039
|
const truncatedContent = truncateOutput(content, MAX_OUTPUT_CHARS3);
|
|
1834
2040
|
const wasTruncated = truncatedContent.length < content.length;
|
|
@@ -1855,6 +2061,19 @@ Use this to understand existing code, check file contents, or gather context.`,
|
|
|
1855
2061
|
content: null
|
|
1856
2062
|
};
|
|
1857
2063
|
}
|
|
2064
|
+
},
|
|
2065
|
+
toModelOutput: ({ output }) => {
|
|
2066
|
+
if (output && typeof output === "object" && "imageData" in output && output.imageData) {
|
|
2067
|
+
const result = output;
|
|
2068
|
+
return {
|
|
2069
|
+
type: "content",
|
|
2070
|
+
value: [
|
|
2071
|
+
{ type: "text", text: result.content },
|
|
2072
|
+
{ type: "image-data", data: result.imageData, mediaType: result.mediaType }
|
|
2073
|
+
]
|
|
2074
|
+
};
|
|
2075
|
+
}
|
|
2076
|
+
return typeof output === "string" ? { type: "text", value: output } : { type: "json", value: output };
|
|
1858
2077
|
}
|
|
1859
2078
|
});
|
|
1860
2079
|
}
|
|
@@ -1919,7 +2138,7 @@ async function backupFile(sessionId, workingDirectory, filePath) {
|
|
|
1919
2138
|
}
|
|
1920
2139
|
|
|
1921
2140
|
// src/lsp/index.ts
|
|
1922
|
-
import { extname as
|
|
2141
|
+
import { extname as extname3, dirname as dirname4 } from "path";
|
|
1923
2142
|
|
|
1924
2143
|
// src/lsp/servers.ts
|
|
1925
2144
|
import { spawn } from "child_process";
|
|
@@ -2042,9 +2261,9 @@ import {
|
|
|
2042
2261
|
import { pathToFileURL, fileURLToPath } from "url";
|
|
2043
2262
|
import { readFile as readFile4 } from "fs/promises";
|
|
2044
2263
|
import { existsSync as existsSync6 } from "fs";
|
|
2045
|
-
import { extname, normalize } from "path";
|
|
2264
|
+
import { extname as extname2, normalize } from "path";
|
|
2046
2265
|
function getLanguageId(filePath) {
|
|
2047
|
-
const ext =
|
|
2266
|
+
const ext = extname2(filePath).toLowerCase();
|
|
2048
2267
|
const map = {
|
|
2049
2268
|
".ts": "typescript",
|
|
2050
2269
|
".tsx": "typescriptreact",
|
|
@@ -2432,7 +2651,7 @@ var state = {
|
|
|
2432
2651
|
};
|
|
2433
2652
|
async function getClientForFile(filePath) {
|
|
2434
2653
|
const normalized = normalizePath(filePath);
|
|
2435
|
-
const ext =
|
|
2654
|
+
const ext = extname3(normalized);
|
|
2436
2655
|
const serverDef = getServerForExtension(ext);
|
|
2437
2656
|
if (!serverDef) {
|
|
2438
2657
|
return null;
|
|
@@ -2525,7 +2744,7 @@ async function formatDiagnosticsOutput(filePath, options = {}) {
|
|
|
2525
2744
|
return formatDiagnosticsForAgent(filePath, diagnostics, options);
|
|
2526
2745
|
}
|
|
2527
2746
|
function isSupported(filePath) {
|
|
2528
|
-
const ext =
|
|
2747
|
+
const ext = extname3(filePath);
|
|
2529
2748
|
return getServerForExtension(ext) !== null;
|
|
2530
2749
|
}
|
|
2531
2750
|
|
|
@@ -2952,7 +3171,7 @@ Once loaded, a skill's content will be available in the conversation context.`,
|
|
|
2952
3171
|
// src/tools/linter.ts
|
|
2953
3172
|
import { tool as tool6 } from "ai";
|
|
2954
3173
|
import { z as z7 } from "zod";
|
|
2955
|
-
import { resolve as resolve7, relative as relative5, isAbsolute as isAbsolute3, extname as
|
|
3174
|
+
import { resolve as resolve7, relative as relative5, isAbsolute as isAbsolute3, extname as extname5 } from "path";
|
|
2956
3175
|
import { existsSync as existsSync9 } from "fs";
|
|
2957
3176
|
import { readdir as readdir2, stat as stat2 } from "fs/promises";
|
|
2958
3177
|
var linterInputSchema = z7.object({
|
|
@@ -2975,7 +3194,7 @@ async function findSupportedFiles(dir, workingDirectory, maxFiles = 50) {
|
|
|
2975
3194
|
}
|
|
2976
3195
|
await walk(fullPath);
|
|
2977
3196
|
} else if (entry.isFile()) {
|
|
2978
|
-
const ext =
|
|
3197
|
+
const ext = extname5(entry.name);
|
|
2979
3198
|
if (supportedExtensions.includes(ext)) {
|
|
2980
3199
|
files.push(fullPath);
|
|
2981
3200
|
}
|
|
@@ -4312,6 +4531,59 @@ Context: ${context}` : query;
|
|
|
4312
4531
|
|
|
4313
4532
|
// src/tools/index.ts
|
|
4314
4533
|
init_semantic_search();
|
|
4534
|
+
|
|
4535
|
+
// src/tools/task.ts
|
|
4536
|
+
import { tool as tool11 } from "ai";
|
|
4537
|
+
import { z as z12 } from "zod";
|
|
4538
|
+
import Ajv from "ajv";
|
|
4539
|
+
var ajv = new Ajv({ allErrors: true });
|
|
4540
|
+
function createCompleteTaskTool(options) {
|
|
4541
|
+
const validate = ajv.compile(options.outputSchema);
|
|
4542
|
+
return tool11({
|
|
4543
|
+
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.",
|
|
4544
|
+
inputSchema: z12.object({
|
|
4545
|
+
result: z12.record(z12.string(), z12.unknown()).describe("The task result as a JSON object matching the output schema")
|
|
4546
|
+
}),
|
|
4547
|
+
execute: async (input) => {
|
|
4548
|
+
const valid = validate(input.result);
|
|
4549
|
+
if (!valid) {
|
|
4550
|
+
const errors = validate.errors?.map((e) => ({
|
|
4551
|
+
path: e.instancePath || "/",
|
|
4552
|
+
message: e.message,
|
|
4553
|
+
params: e.params
|
|
4554
|
+
}));
|
|
4555
|
+
return {
|
|
4556
|
+
status: "validation_error",
|
|
4557
|
+
message: "The result does not match the required output schema. Fix the errors and call complete_task again.",
|
|
4558
|
+
errors,
|
|
4559
|
+
expectedSchema: options.outputSchema
|
|
4560
|
+
};
|
|
4561
|
+
}
|
|
4562
|
+
options.onComplete({ status: "completed", result: input.result });
|
|
4563
|
+
return {
|
|
4564
|
+
status: "completed",
|
|
4565
|
+
message: "Task completed successfully."
|
|
4566
|
+
};
|
|
4567
|
+
}
|
|
4568
|
+
});
|
|
4569
|
+
}
|
|
4570
|
+
function createTaskFailedTool(options) {
|
|
4571
|
+
return tool11({
|
|
4572
|
+
description: "Call this tool if you are unable to complete the task. Provide a clear reason explaining why the task cannot be completed.",
|
|
4573
|
+
inputSchema: z12.object({
|
|
4574
|
+
reason: z12.string().describe("Explanation of why the task cannot be completed")
|
|
4575
|
+
}),
|
|
4576
|
+
execute: async (input) => {
|
|
4577
|
+
options.onComplete({ status: "failed", error: input.reason });
|
|
4578
|
+
return {
|
|
4579
|
+
status: "failed",
|
|
4580
|
+
message: `Task marked as failed: ${input.reason}`
|
|
4581
|
+
};
|
|
4582
|
+
}
|
|
4583
|
+
});
|
|
4584
|
+
}
|
|
4585
|
+
|
|
4586
|
+
// src/tools/index.ts
|
|
4315
4587
|
init_semantic();
|
|
4316
4588
|
init_semantic_search();
|
|
4317
4589
|
async function createTools(options) {
|
|
@@ -4363,6 +4635,10 @@ async function createTools(options) {
|
|
|
4363
4635
|
} catch {
|
|
4364
4636
|
}
|
|
4365
4637
|
}
|
|
4638
|
+
if (options.taskTools) {
|
|
4639
|
+
tools.complete_task = createCompleteTaskTool(options.taskTools);
|
|
4640
|
+
tools.task_failed = createTaskFailedTool(options.taskTools);
|
|
4641
|
+
}
|
|
4366
4642
|
return tools;
|
|
4367
4643
|
}
|
|
4368
4644
|
|
|
@@ -4680,6 +4956,30 @@ function formatTodosForContext(todos) {
|
|
|
4680
4956
|
}
|
|
4681
4957
|
return lines.join("\n");
|
|
4682
4958
|
}
|
|
4959
|
+
function buildTaskPromptAddendum(outputSchema) {
|
|
4960
|
+
return `
|
|
4961
|
+
## Task Mode
|
|
4962
|
+
|
|
4963
|
+
You are running in **task mode**. You have been given a specific task to complete autonomously.
|
|
4964
|
+
|
|
4965
|
+
### Rules
|
|
4966
|
+
1. Work independently \u2014 no human will approve tool calls. All tools run without approval.
|
|
4967
|
+
2. Keep working until the task is fully complete.
|
|
4968
|
+
3. When done, call the \`complete_task\` tool with a JSON result matching the output schema below.
|
|
4969
|
+
4. If you determine the task is impossible or encounter an unrecoverable error, call the \`task_failed\` tool with a clear reason.
|
|
4970
|
+
5. Do NOT stop without calling one of these two tools.
|
|
4971
|
+
|
|
4972
|
+
### Output Schema
|
|
4973
|
+
The \`complete_task\` tool expects a \`result\` object matching this JSON Schema:
|
|
4974
|
+
\`\`\`json
|
|
4975
|
+
${JSON.stringify(outputSchema, null, 2)}
|
|
4976
|
+
\`\`\`
|
|
4977
|
+
|
|
4978
|
+
### Completion Tools
|
|
4979
|
+
- **\`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.
|
|
4980
|
+
- **\`task_failed({ reason: "..." })\`** \u2014 Call only if the task truly cannot be completed.
|
|
4981
|
+
`;
|
|
4982
|
+
}
|
|
4683
4983
|
function createSummaryPrompt(conversationHistory) {
|
|
4684
4984
|
return `Please provide a concise summary of the following conversation history. Focus on:
|
|
4685
4985
|
1. The main task or goal being worked on
|
|
@@ -4941,6 +5241,25 @@ ${this.summary}`
|
|
|
4941
5241
|
}
|
|
4942
5242
|
};
|
|
4943
5243
|
|
|
5244
|
+
// src/utils/webhook.ts
|
|
5245
|
+
async function sendWebhook(url, event) {
|
|
5246
|
+
try {
|
|
5247
|
+
const controller = new AbortController();
|
|
5248
|
+
const timeout = setTimeout(() => controller.abort(), 5e3);
|
|
5249
|
+
await fetch(url, {
|
|
5250
|
+
method: "POST",
|
|
5251
|
+
headers: {
|
|
5252
|
+
"Content-Type": "application/json",
|
|
5253
|
+
"X-SparkECoder-Event": event.type
|
|
5254
|
+
},
|
|
5255
|
+
body: JSON.stringify(event),
|
|
5256
|
+
signal: controller.signal
|
|
5257
|
+
});
|
|
5258
|
+
clearTimeout(timeout);
|
|
5259
|
+
} catch {
|
|
5260
|
+
}
|
|
5261
|
+
}
|
|
5262
|
+
|
|
4944
5263
|
// src/agent/index.ts
|
|
4945
5264
|
var approvalResolvers = /* @__PURE__ */ new Map();
|
|
4946
5265
|
var Agent = class _Agent {
|
|
@@ -5160,6 +5479,145 @@ ${prompt}` });
|
|
|
5160
5479
|
steps: result.steps
|
|
5161
5480
|
};
|
|
5162
5481
|
}
|
|
5482
|
+
/**
|
|
5483
|
+
* Run the agent in task mode — loops autonomously until the agent calls
|
|
5484
|
+
* complete_task or task_failed (or hits maxIterations).
|
|
5485
|
+
* All tools run without approval. Webhook events are fired throughout.
|
|
5486
|
+
*/
|
|
5487
|
+
async runTask(options) {
|
|
5488
|
+
const config = getConfig();
|
|
5489
|
+
const maxIterations = options.taskConfig.maxIterations ?? 50;
|
|
5490
|
+
const webhookUrl = options.taskConfig.webhookUrl;
|
|
5491
|
+
const fireWebhook = (type, data) => {
|
|
5492
|
+
if (!webhookUrl) return;
|
|
5493
|
+
sendWebhook(webhookUrl, {
|
|
5494
|
+
type,
|
|
5495
|
+
taskId: this.session.id,
|
|
5496
|
+
sessionId: this.session.id,
|
|
5497
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5498
|
+
data
|
|
5499
|
+
});
|
|
5500
|
+
};
|
|
5501
|
+
const completion = { signal: null };
|
|
5502
|
+
const onComplete = (signal) => {
|
|
5503
|
+
completion.signal = signal;
|
|
5504
|
+
};
|
|
5505
|
+
const taskTools = await createTools({
|
|
5506
|
+
sessionId: this.session.id,
|
|
5507
|
+
workingDirectory: this.session.workingDirectory,
|
|
5508
|
+
skillsDirectories: config.resolvedSkillsDirectories,
|
|
5509
|
+
onBashProgress: options.onToolProgress ? (progress) => options.onToolProgress({ toolName: "bash", data: progress }) : void 0,
|
|
5510
|
+
onWriteFileProgress: options.onToolProgress ? (progress) => options.onToolProgress({ toolName: "write_file", data: progress }) : void 0,
|
|
5511
|
+
onSearchProgress: options.onToolProgress ? (progress) => options.onToolProgress({ toolName: "explore_agent", data: progress }) : void 0,
|
|
5512
|
+
taskTools: {
|
|
5513
|
+
outputSchema: options.taskConfig.outputSchema,
|
|
5514
|
+
onComplete
|
|
5515
|
+
}
|
|
5516
|
+
});
|
|
5517
|
+
const baseSystemPrompt = await buildSystemPrompt({
|
|
5518
|
+
workingDirectory: this.session.workingDirectory,
|
|
5519
|
+
skillsDirectories: config.resolvedSkillsDirectories,
|
|
5520
|
+
sessionId: this.session.id,
|
|
5521
|
+
discoveredSkills: config.discoveredSkills,
|
|
5522
|
+
activeFiles: []
|
|
5523
|
+
});
|
|
5524
|
+
const taskAddendum = buildTaskPromptAddendum(options.taskConfig.outputSchema);
|
|
5525
|
+
const systemPrompt = `${baseSystemPrompt}
|
|
5526
|
+
|
|
5527
|
+
${taskAddendum}`;
|
|
5528
|
+
fireWebhook("task.started", { prompt: options.prompt });
|
|
5529
|
+
this.context.addUserMessage(options.prompt);
|
|
5530
|
+
let iteration = 0;
|
|
5531
|
+
while (iteration < maxIterations) {
|
|
5532
|
+
iteration++;
|
|
5533
|
+
if (options.abortSignal?.aborted) {
|
|
5534
|
+
const cancelError = "Task was cancelled";
|
|
5535
|
+
fireWebhook("task.failed", { status: "failed", error: cancelError, iterations: iteration });
|
|
5536
|
+
return { status: "failed", error: cancelError, iterations: iteration };
|
|
5537
|
+
}
|
|
5538
|
+
const messages = await this.context.getMessages();
|
|
5539
|
+
const useAnthropic = isAnthropicModel(this.session.model);
|
|
5540
|
+
const result = await generateText3({
|
|
5541
|
+
model: resolveModel(this.session.model),
|
|
5542
|
+
system: systemPrompt,
|
|
5543
|
+
messages,
|
|
5544
|
+
tools: taskTools,
|
|
5545
|
+
stopWhen: stepCountIs2(500),
|
|
5546
|
+
abortSignal: options.abortSignal,
|
|
5547
|
+
providerOptions: useAnthropic ? {
|
|
5548
|
+
anthropic: {
|
|
5549
|
+
thinking: { type: "enabled", budgetTokens: 1e4 }
|
|
5550
|
+
}
|
|
5551
|
+
} : void 0,
|
|
5552
|
+
onStepFinish: (step) => {
|
|
5553
|
+
options.onStepFinish?.(step);
|
|
5554
|
+
fireWebhook("task.step_finished", { iteration, text: step.text });
|
|
5555
|
+
}
|
|
5556
|
+
});
|
|
5557
|
+
const responseMessages = result.response.messages;
|
|
5558
|
+
this.context.addResponseMessages(responseMessages);
|
|
5559
|
+
if (result.text) {
|
|
5560
|
+
options.onText?.(result.text);
|
|
5561
|
+
fireWebhook("task.message", { iteration, text: result.text });
|
|
5562
|
+
}
|
|
5563
|
+
for (const step of result.steps) {
|
|
5564
|
+
if (step.toolCalls) {
|
|
5565
|
+
for (const tc of step.toolCalls) {
|
|
5566
|
+
options.onToolCall?.({ toolCallId: tc.toolCallId, toolName: tc.toolName, input: tc.args });
|
|
5567
|
+
fireWebhook("task.tool_call", { iteration, toolName: tc.toolName, toolCallId: tc.toolCallId, input: tc.args });
|
|
5568
|
+
}
|
|
5569
|
+
}
|
|
5570
|
+
if (step.toolResults) {
|
|
5571
|
+
for (const tr of step.toolResults) {
|
|
5572
|
+
options.onToolResult?.({ toolCallId: tr.toolCallId, toolName: tr.toolName, output: tr.result });
|
|
5573
|
+
fireWebhook("task.tool_result", { iteration, toolName: tr.toolName, toolCallId: tr.toolCallId, output: tr.result });
|
|
5574
|
+
}
|
|
5575
|
+
}
|
|
5576
|
+
}
|
|
5577
|
+
if (completion.signal) {
|
|
5578
|
+
const sig = completion.signal;
|
|
5579
|
+
const finalStatus = sig.status;
|
|
5580
|
+
const eventType = finalStatus === "completed" ? "task.completed" : "task.failed";
|
|
5581
|
+
fireWebhook(eventType, {
|
|
5582
|
+
status: finalStatus,
|
|
5583
|
+
result: sig.result,
|
|
5584
|
+
error: sig.error,
|
|
5585
|
+
iterations: iteration
|
|
5586
|
+
});
|
|
5587
|
+
const updatedTask2 = {
|
|
5588
|
+
...options.taskConfig,
|
|
5589
|
+
status: finalStatus,
|
|
5590
|
+
result: sig.result,
|
|
5591
|
+
error: sig.error,
|
|
5592
|
+
iterations: iteration
|
|
5593
|
+
};
|
|
5594
|
+
await sessionQueries.update(this.session.id, {
|
|
5595
|
+
config: { ...this.session.config, task: updatedTask2 }
|
|
5596
|
+
});
|
|
5597
|
+
return {
|
|
5598
|
+
status: finalStatus,
|
|
5599
|
+
result: sig.result,
|
|
5600
|
+
error: sig.error,
|
|
5601
|
+
iterations: iteration
|
|
5602
|
+
};
|
|
5603
|
+
}
|
|
5604
|
+
this.context.addUserMessage(
|
|
5605
|
+
"Continue working on the task. When done, call `complete_task` with the result. If you cannot complete it, call `task_failed` with a reason."
|
|
5606
|
+
);
|
|
5607
|
+
}
|
|
5608
|
+
const timeoutError = `Task did not complete within ${maxIterations} iterations`;
|
|
5609
|
+
fireWebhook("task.failed", { status: "failed", error: timeoutError, iterations: iteration });
|
|
5610
|
+
const updatedTask = {
|
|
5611
|
+
...options.taskConfig,
|
|
5612
|
+
status: "failed",
|
|
5613
|
+
error: timeoutError,
|
|
5614
|
+
iterations: iteration
|
|
5615
|
+
};
|
|
5616
|
+
await sessionQueries.update(this.session.id, {
|
|
5617
|
+
config: { ...this.session.config, task: updatedTask }
|
|
5618
|
+
});
|
|
5619
|
+
return { status: "failed", error: timeoutError, iterations: iteration };
|
|
5620
|
+
}
|
|
5163
5621
|
/**
|
|
5164
5622
|
* Wrap tools to add approval checking
|
|
5165
5623
|
*/
|
|
@@ -5173,9 +5631,9 @@ ${prompt}` });
|
|
|
5173
5631
|
wrappedTools[name] = originalTool;
|
|
5174
5632
|
continue;
|
|
5175
5633
|
}
|
|
5176
|
-
wrappedTools[name] =
|
|
5634
|
+
wrappedTools[name] = tool12({
|
|
5177
5635
|
description: originalTool.description || "",
|
|
5178
|
-
inputSchema: originalTool.inputSchema ||
|
|
5636
|
+
inputSchema: originalTool.inputSchema || z13.object({}),
|
|
5179
5637
|
execute: async (input, toolOptions) => {
|
|
5180
5638
|
const toolCallId = toolOptions.toolCallId || nanoid3();
|
|
5181
5639
|
const execution = toolExecutionQueries.create({
|
|
@@ -5286,6 +5744,7 @@ ${prompt}` });
|
|
|
5286
5744
|
export {
|
|
5287
5745
|
Agent,
|
|
5288
5746
|
ContextManager,
|
|
5289
|
-
buildSystemPrompt
|
|
5747
|
+
buildSystemPrompt,
|
|
5748
|
+
buildTaskPromptAddendum
|
|
5290
5749
|
};
|
|
5291
5750
|
//# sourceMappingURL=index.js.map
|