sparkecoder 0.1.16 → 0.1.18
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/README.md +1 -1
- package/dist/cli.js +15 -6
- package/dist/cli.js.map +1 -1
- package/dist/index.js +9 -2
- package/dist/index.js.map +1 -1
- package/dist/server/index.js +9 -2
- package/dist/server/index.js.map +1 -1
- package/package.json +1 -1
- 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 +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 +3 -3
- 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/page.js +2 -2
- package/web/.next/standalone/web/.next/server/app/_global-error/page.js.nft.json +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error/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.js +3 -3
- package/web/.next/standalone/web/.next/server/app/_not-found/page.js.nft.json +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 +13 -12
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_full.segment.rsc +13 -12
- 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 +4 -3
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/api/config/route.js +1 -1
- 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 +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/index.html +1 -1
- package/web/.next/standalone/web/.next/server/app/index.rsc +20 -19
- 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 +20 -19
- 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 +4 -3
- package/web/.next/standalone/web/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/chunks/[root-of-the-server]__36edac7c._.js +3 -0
- package/web/.next/standalone/web/.next/server/chunks/[root-of-the-server]__74ebc442._.js +3 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_1b669458._.js → 2374f_1d78db71._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_10a13bff._.js → 2374f_30f9df13._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_42ae2ff5._.js → 2374f_378282b1._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_21324e82._.js → 2374f_5de336d2._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_ba57e34a._.js → 2374f_8825dcc9._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_47e888fe._.js → 2374f_9bf3c7f3._.js} +2 -2
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_63823b79._.js → 2374f_bbc99511._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_9c15fcde._.js → 2374f_d94c2b70._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__0f6b5fa7._.js +3 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/{[root-of-the-server]__e1f2996b._.js → [root-of-the-server]__513c6b45._.js} +2 -2
- package/web/.next/standalone/web/.next/server/chunks/ssr/{[root-of-the-server]__da40fb08._.js → [root-of-the-server]__7f04455b._.js} +2 -2
- package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__be5e2967._.js +3 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/{[root-of-the-server]__10aab2ca._.js → [root-of-the-server]__c3a1e22c._.js} +2 -2
- package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__d2ce4b79._.js +3 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__de58a952._.js +3 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__f18f92f4._.js +10 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/{web_d3c9d897._.js → web_19b6934c._.js} +2 -2
- package/web/.next/standalone/web/.next/server/chunks/ssr/web_96bca05b._.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/25a97bfee12ea1f6.js +1 -0
- package/web/.next/standalone/web/.next/static/chunks/2f9f08f6c6276b0f.js +1 -0
- package/web/.next/{static/chunks/c7e9604ccdb5cb09.js → standalone/web/.next/static/chunks/5ec82ce8f3aabaf0.js} +3 -3
- package/web/.next/standalone/web/.next/static/static/chunks/25a97bfee12ea1f6.js +1 -0
- package/web/.next/standalone/web/.next/static/static/chunks/2f9f08f6c6276b0f.js +1 -0
- package/web/.next/standalone/web/.next/static/{chunks/c7e9604ccdb5cb09.js → static/chunks/5ec82ce8f3aabaf0.js} +3 -3
- package/web/.next/standalone/web/README.md +63 -0
- package/web/.next/standalone/web/components.json +22 -0
- package/web/.next/standalone/web/eslint.config.mjs +18 -0
- package/web/.next/standalone/web/next.config.ts +9 -0
- package/web/.next/standalone/web/package-lock.json +15061 -0
- package/web/.next/standalone/web/postcss.config.mjs +7 -0
- package/web/.next/standalone/web/runtime-config.json +3 -0
- package/web/.next/standalone/web/src/app/(main)/layout.tsx +22 -0
- package/web/.next/standalone/web/src/app/(main)/page.tsx +230 -0
- package/web/.next/standalone/web/src/app/(main)/session/[id]/page.tsx +64 -0
- package/web/.next/standalone/web/src/app/api/config/route.ts +106 -0
- package/web/.next/standalone/web/src/app/api/health/route.ts +63 -0
- package/web/.next/standalone/web/src/app/apple-icon.png +0 -0
- package/web/.next/standalone/web/src/app/favicon.ico +0 -0
- package/web/.next/standalone/web/src/app/globals.css +311 -0
- package/web/.next/standalone/web/src/app/icon.png +0 -0
- package/web/.next/standalone/web/src/app/layout.tsx +100 -0
- package/web/.next/standalone/web/src/app/opengraph-image.png +0 -0
- package/web/.next/standalone/web/src/app/twitter-image.png +0 -0
- package/web/.next/standalone/web/src/components/ai-elements/agent.tsx +141 -0
- package/web/.next/standalone/web/src/components/ai-elements/artifact.tsx +147 -0
- package/web/.next/standalone/web/src/components/ai-elements/attachments.tsx +421 -0
- package/web/.next/standalone/web/src/components/ai-elements/audio-player.tsx +231 -0
- package/web/.next/standalone/web/src/components/ai-elements/bash-tool.tsx +299 -0
- package/web/.next/standalone/web/src/components/ai-elements/canvas.tsx +22 -0
- package/web/.next/standalone/web/src/components/ai-elements/chain-of-thought.tsx +231 -0
- package/web/.next/standalone/web/src/components/ai-elements/checkpoint.tsx +71 -0
- package/web/.next/standalone/web/src/components/ai-elements/code-block.tsx +529 -0
- package/web/.next/standalone/web/src/components/ai-elements/commit.tsx +448 -0
- package/web/.next/standalone/web/src/components/ai-elements/confirmation.tsx +176 -0
- package/web/.next/standalone/web/src/components/ai-elements/connection.tsx +28 -0
- package/web/.next/standalone/web/src/components/ai-elements/context.tsx +408 -0
- package/web/.next/standalone/web/src/components/ai-elements/controls.tsx +18 -0
- package/web/.next/standalone/web/src/components/ai-elements/conversation.tsx +104 -0
- package/web/.next/standalone/web/src/components/ai-elements/edge.tsx +140 -0
- package/web/.next/standalone/web/src/components/ai-elements/environment-variables.tsx +295 -0
- package/web/.next/standalone/web/src/components/ai-elements/file-tree.tsx +258 -0
- package/web/.next/standalone/web/src/components/ai-elements/image.tsx +24 -0
- package/web/.next/standalone/web/src/components/ai-elements/inline-citation.tsx +287 -0
- package/web/.next/standalone/web/src/components/ai-elements/linter-tool.tsx +330 -0
- package/web/.next/standalone/web/src/components/ai-elements/load-skill-tool.tsx +215 -0
- package/web/.next/standalone/web/src/components/ai-elements/loader.tsx +96 -0
- package/web/.next/standalone/web/src/components/ai-elements/message.tsx +421 -0
- package/web/.next/standalone/web/src/components/ai-elements/mic-selector.tsx +370 -0
- package/web/.next/standalone/web/src/components/ai-elements/model-selector.tsx +211 -0
- package/web/.next/standalone/web/src/components/ai-elements/node.tsx +71 -0
- package/web/.next/standalone/web/src/components/ai-elements/open-in-chat.tsx +365 -0
- package/web/.next/standalone/web/src/components/ai-elements/package-info.tsx +233 -0
- package/web/.next/standalone/web/src/components/ai-elements/panel.tsx +15 -0
- package/web/.next/standalone/web/src/components/ai-elements/persona.tsx +270 -0
- package/web/.next/standalone/web/src/components/ai-elements/plan.tsx +142 -0
- package/web/.next/standalone/web/src/components/ai-elements/prompt-input.tsx +1263 -0
- package/web/.next/standalone/web/src/components/ai-elements/queue.tsx +274 -0
- package/web/.next/standalone/web/src/components/ai-elements/read-file-tool.tsx +251 -0
- package/web/.next/standalone/web/src/components/ai-elements/reasoning.tsx +198 -0
- package/web/.next/standalone/web/src/components/ai-elements/sandbox.tsx +131 -0
- package/web/.next/standalone/web/src/components/ai-elements/schema-display.tsx +458 -0
- package/web/.next/standalone/web/src/components/ai-elements/shimmer.tsx +64 -0
- package/web/.next/standalone/web/src/components/ai-elements/snippet.tsx +139 -0
- package/web/.next/standalone/web/src/components/ai-elements/sources.tsx +77 -0
- package/web/.next/standalone/web/src/components/ai-elements/speech-input.tsx +300 -0
- package/web/.next/standalone/web/src/components/ai-elements/stack-trace.tsx +482 -0
- package/web/.next/standalone/web/src/components/ai-elements/suggestion.tsx +60 -0
- package/web/.next/standalone/web/src/components/ai-elements/task.tsx +87 -0
- package/web/.next/standalone/web/src/components/ai-elements/terminal.tsx +261 -0
- package/web/.next/standalone/web/src/components/ai-elements/test-results.tsx +485 -0
- package/web/.next/standalone/web/src/components/ai-elements/todo-panel.tsx +178 -0
- package/web/.next/standalone/web/src/components/ai-elements/todo-tool.tsx +246 -0
- package/web/.next/standalone/web/src/components/ai-elements/tool.tsx +174 -0
- package/web/.next/standalone/web/src/components/ai-elements/toolbar.tsx +16 -0
- package/web/.next/standalone/web/src/components/ai-elements/transcription.tsx +124 -0
- package/web/.next/standalone/web/src/components/ai-elements/voice-selector.tsx +479 -0
- package/web/.next/standalone/web/src/components/ai-elements/web-preview.tsx +263 -0
- package/web/.next/standalone/web/src/components/ai-elements/write-file-tool.tsx +368 -0
- package/web/.next/standalone/web/src/components/api-init.tsx +21 -0
- package/web/.next/standalone/web/src/components/chat-interface.tsx +2074 -0
- package/web/.next/standalone/web/src/components/sessions-sidebar.tsx +875 -0
- package/web/.next/standalone/web/src/components/ui/accordion.tsx +66 -0
- package/web/.next/standalone/web/src/components/ui/alert.tsx +66 -0
- package/web/.next/standalone/web/src/components/ui/avatar.tsx +109 -0
- package/web/.next/standalone/web/src/components/ui/badge.tsx +48 -0
- package/web/.next/standalone/web/src/components/ui/button-group.tsx +83 -0
- package/web/.next/standalone/web/src/components/ui/button.tsx +64 -0
- package/web/.next/standalone/web/src/components/ui/card.tsx +92 -0
- package/web/.next/standalone/web/src/components/ui/carousel.tsx +241 -0
- package/web/.next/standalone/web/src/components/ui/collapsible.tsx +33 -0
- package/web/.next/standalone/web/src/components/ui/command.tsx +184 -0
- package/web/.next/standalone/web/src/components/ui/dialog.tsx +158 -0
- package/web/.next/standalone/web/src/components/ui/dropdown-menu.tsx +257 -0
- package/web/.next/standalone/web/src/components/ui/hover-card.tsx +44 -0
- package/web/.next/standalone/web/src/components/ui/input-group.tsx +170 -0
- package/web/.next/standalone/web/src/components/ui/input.tsx +22 -0
- package/web/.next/standalone/web/src/components/ui/label.tsx +24 -0
- package/web/.next/standalone/web/src/components/ui/popover.tsx +89 -0
- package/web/.next/standalone/web/src/components/ui/progress.tsx +31 -0
- package/web/.next/standalone/web/src/components/ui/scroll-area.tsx +58 -0
- package/web/.next/standalone/web/src/components/ui/select.tsx +190 -0
- package/web/.next/standalone/web/src/components/ui/separator.tsx +28 -0
- package/web/.next/standalone/web/src/components/ui/sheet.tsx +143 -0
- package/web/.next/standalone/web/src/components/ui/sidebar.tsx +726 -0
- package/web/.next/standalone/web/src/components/ui/skeleton.tsx +13 -0
- package/web/.next/standalone/web/src/components/ui/switch.tsx +35 -0
- package/web/.next/standalone/web/src/components/ui/tabs.tsx +91 -0
- package/web/.next/standalone/web/src/components/ui/textarea.tsx +18 -0
- package/web/.next/standalone/web/src/components/ui/tooltip.tsx +61 -0
- package/web/.next/standalone/web/src/hooks/use-mobile.ts +19 -0
- package/web/.next/standalone/web/src/hooks/use-sessions.ts +28 -0
- package/web/.next/standalone/web/src/lib/api.ts +568 -0
- package/web/.next/standalone/web/src/lib/config.ts +178 -0
- package/web/.next/standalone/web/src/lib/utils.ts +6 -0
- package/web/.next/standalone/web/src/test/api.test.ts +125 -0
- package/web/.next/standalone/web/src/test/setup.ts +1 -0
- package/web/.next/standalone/web/tsconfig.json +43 -0
- package/web/.next/standalone/web/vitest.config.ts +17 -0
- package/web/.next/static/chunks/25a97bfee12ea1f6.js +1 -0
- package/web/.next/static/chunks/2f9f08f6c6276b0f.js +1 -0
- package/web/.next/{standalone/web/.next/static/static/chunks/c7e9604ccdb5cb09.js → static/chunks/5ec82ce8f3aabaf0.js} +3 -3
- package/web/.next/standalone/web/.next/server/chunks/[root-of-the-server]__7ba4776a._.js +0 -3
- package/web/.next/standalone/web/.next/server/chunks/[root-of-the-server]__d907af4e._.js +0 -3
- package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__3a7ef2b7._.js +0 -3
- package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__5369f47d._.js +0 -3
- package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__c1f0d54f._.js +0 -3
- package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__fd3d4d25._.js +0 -10
- package/web/.next/standalone/web/.next/static/chunks/85dcb2949e032468.js +0 -1
- package/web/.next/standalone/web/.next/static/static/chunks/85dcb2949e032468.js +0 -1
- package/web/.next/static/chunks/85dcb2949e032468.js +0 -1
- /package/web/.next/standalone/web/.next/static/{3Y3VQCOuXe9_MLEdym2IJ → omR5ZCfnSKddq7WtwIK6Y}/_buildManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/{3Y3VQCOuXe9_MLEdym2IJ → omR5ZCfnSKddq7WtwIK6Y}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/standalone/web/.next/static/{3Y3VQCOuXe9_MLEdym2IJ → omR5ZCfnSKddq7WtwIK6Y}/_ssgManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/static/{3Y3VQCOuXe9_MLEdym2IJ → omR5ZCfnSKddq7WtwIK6Y}/_buildManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/static/{3Y3VQCOuXe9_MLEdym2IJ → omR5ZCfnSKddq7WtwIK6Y}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/standalone/web/.next/static/static/{3Y3VQCOuXe9_MLEdym2IJ → omR5ZCfnSKddq7WtwIK6Y}/_ssgManifest.js +0 -0
- /package/web/.next/static/{3Y3VQCOuXe9_MLEdym2IJ → omR5ZCfnSKddq7WtwIK6Y}/_buildManifest.js +0 -0
- /package/web/.next/static/{3Y3VQCOuXe9_MLEdym2IJ → omR5ZCfnSKddq7WtwIK6Y}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/static/{3Y3VQCOuXe9_MLEdym2IJ → omR5ZCfnSKddq7WtwIK6Y}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,568 @@
|
|
|
1
|
+
import { getApiUrl } from './config';
|
|
2
|
+
|
|
3
|
+
// Get API base URL synchronously from localStorage
|
|
4
|
+
function getApiBase(): string {
|
|
5
|
+
return getApiUrl();
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface SessionConfig {
|
|
9
|
+
toolApprovals?: Record<string, boolean>;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface Session {
|
|
13
|
+
id: string;
|
|
14
|
+
name: string;
|
|
15
|
+
model: string;
|
|
16
|
+
workingDirectory: string;
|
|
17
|
+
status: 'active' | 'waiting' | 'completed' | 'error';
|
|
18
|
+
isStreaming?: boolean;
|
|
19
|
+
config?: SessionConfig;
|
|
20
|
+
createdAt: string;
|
|
21
|
+
updatedAt?: string;
|
|
22
|
+
messageCount?: number;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// AI SDK ModelMessage content types
|
|
26
|
+
export interface TextContentPart {
|
|
27
|
+
type: 'text';
|
|
28
|
+
text: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface ReasoningContentPart {
|
|
32
|
+
type: 'reasoning';
|
|
33
|
+
text: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface ToolCallContentPart {
|
|
37
|
+
type: 'tool-call';
|
|
38
|
+
toolCallId: string;
|
|
39
|
+
toolName: string;
|
|
40
|
+
input?: unknown;
|
|
41
|
+
args?: unknown; // AI SDK uses 'args' in some places
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface ToolResultContentPart {
|
|
45
|
+
type: 'tool-result';
|
|
46
|
+
toolCallId: string;
|
|
47
|
+
toolName: string;
|
|
48
|
+
output?: unknown;
|
|
49
|
+
result?: unknown; // AI SDK uses 'result' in some places
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export type ContentPart = TextContentPart | ReasoningContentPart | ToolCallContentPart | ToolResultContentPart;
|
|
53
|
+
|
|
54
|
+
// AI SDK ModelMessage format (as returned by backend)
|
|
55
|
+
export interface Message {
|
|
56
|
+
id: string;
|
|
57
|
+
role: 'user' | 'assistant' | 'tool' | 'system';
|
|
58
|
+
// Content can be a string OR array of content parts
|
|
59
|
+
content: string | ContentPart[];
|
|
60
|
+
createdAt: string;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface ToolCall {
|
|
64
|
+
toolCallId: string;
|
|
65
|
+
toolName: string;
|
|
66
|
+
input: unknown;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface ToolResult {
|
|
70
|
+
toolCallId: string;
|
|
71
|
+
toolName: string;
|
|
72
|
+
output: unknown;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export interface PendingApproval {
|
|
76
|
+
id: string;
|
|
77
|
+
toolCallId: string;
|
|
78
|
+
toolName: string;
|
|
79
|
+
input: unknown;
|
|
80
|
+
createdAt: string;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export interface TodoItem {
|
|
84
|
+
id: string;
|
|
85
|
+
content: string;
|
|
86
|
+
status: 'pending' | 'in_progress' | 'completed' | 'cancelled';
|
|
87
|
+
order: number;
|
|
88
|
+
createdAt: string;
|
|
89
|
+
updatedAt: string;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export interface TodosResponse {
|
|
93
|
+
todos: TodoItem[];
|
|
94
|
+
stats: {
|
|
95
|
+
total: number;
|
|
96
|
+
pending: number;
|
|
97
|
+
inProgress: number;
|
|
98
|
+
completed: number;
|
|
99
|
+
cancelled: number;
|
|
100
|
+
};
|
|
101
|
+
nextTodo: {
|
|
102
|
+
id: string;
|
|
103
|
+
content: string;
|
|
104
|
+
status: string;
|
|
105
|
+
} | null;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Sessions API
|
|
109
|
+
export async function getSessions(): Promise<Session[]> {
|
|
110
|
+
const res = await fetch(`${getApiBase()}/sessions`);
|
|
111
|
+
const data = await res.json();
|
|
112
|
+
return data.sessions || [];
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export async function getSession(id: string): Promise<Session> {
|
|
116
|
+
const res = await fetch(`${getApiBase()}/sessions/${id}`);
|
|
117
|
+
return res.json();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export async function createSession(params: {
|
|
121
|
+
name?: string;
|
|
122
|
+
model?: string;
|
|
123
|
+
workingDirectory?: string;
|
|
124
|
+
toolApprovals?: Record<string, boolean>;
|
|
125
|
+
}): Promise<Session> {
|
|
126
|
+
const res = await fetch(`${getApiBase()}/sessions`, {
|
|
127
|
+
method: 'POST',
|
|
128
|
+
headers: { 'Content-Type': 'application/json' },
|
|
129
|
+
body: JSON.stringify(params),
|
|
130
|
+
});
|
|
131
|
+
return res.json();
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export async function deleteSession(id: string): Promise<void> {
|
|
135
|
+
await fetch(`${getApiBase()}/sessions/${id}`, { method: 'DELETE' });
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export async function getSessionTodos(sessionId: string): Promise<TodosResponse> {
|
|
139
|
+
const res = await fetch(`${getApiBase()}/sessions/${sessionId}/todos`);
|
|
140
|
+
return res.json();
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export async function updateSession(id: string, updates: { model?: string; name?: string; toolApprovals?: Record<string, boolean> }): Promise<Session> {
|
|
144
|
+
const res = await fetch(`${getApiBase()}/sessions/${id}`, {
|
|
145
|
+
method: 'PATCH',
|
|
146
|
+
headers: { 'Content-Type': 'application/json' },
|
|
147
|
+
body: JSON.stringify(updates),
|
|
148
|
+
});
|
|
149
|
+
return res.json();
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Update a specific tool's approval setting for a session
|
|
154
|
+
* If `requiresApproval` is false, the tool will run without asking
|
|
155
|
+
* Note: Backend merges with existing tool approvals, so we only need to send the changed tool
|
|
156
|
+
*/
|
|
157
|
+
export async function updateToolApproval(
|
|
158
|
+
sessionId: string,
|
|
159
|
+
toolName: string,
|
|
160
|
+
requiresApproval: boolean,
|
|
161
|
+
_currentConfig?: SessionConfig // Kept for backwards compatibility but not used
|
|
162
|
+
): Promise<Session> {
|
|
163
|
+
return updateSession(sessionId, {
|
|
164
|
+
toolApprovals: {
|
|
165
|
+
[toolName]: requiresApproval,
|
|
166
|
+
},
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Agent API
|
|
171
|
+
export async function getSessionMessages(sessionId: string): Promise<Message[]> {
|
|
172
|
+
const res = await fetch(`${getApiBase()}/sessions/${sessionId}/messages`);
|
|
173
|
+
const data = await res.json();
|
|
174
|
+
return data.messages || [];
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export async function getPendingApprovals(sessionId: string): Promise<PendingApproval[]> {
|
|
178
|
+
const res = await fetch(`${getApiBase()}/agents/${sessionId}/approvals`);
|
|
179
|
+
const data = await res.json();
|
|
180
|
+
return data.pendingApprovals || [];
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export async function approveExecution(sessionId: string, executionId: string): Promise<void> {
|
|
184
|
+
await fetch(`${getApiBase()}/agents/${sessionId}/approve/${executionId}`, {
|
|
185
|
+
method: 'POST',
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export async function rejectExecution(
|
|
190
|
+
sessionId: string,
|
|
191
|
+
executionId: string,
|
|
192
|
+
reason?: string
|
|
193
|
+
): Promise<void> {
|
|
194
|
+
await fetch(`${getApiBase()}/agents/${sessionId}/reject/${executionId}`, {
|
|
195
|
+
method: 'POST',
|
|
196
|
+
headers: { 'Content-Type': 'application/json' },
|
|
197
|
+
body: JSON.stringify({ reason }),
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Get active stream info for a session
|
|
202
|
+
export async function getActiveStream(sessionId: string): Promise<{
|
|
203
|
+
hasActiveStream: boolean;
|
|
204
|
+
stream: { streamId: string; status: string; createdAt: string } | null;
|
|
205
|
+
}> {
|
|
206
|
+
const res = await fetch(`${getApiBase()}/agents/${sessionId}/stream`);
|
|
207
|
+
return res.json();
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Abort/stop an active stream for a session
|
|
211
|
+
export async function abortStream(sessionId: string): Promise<{
|
|
212
|
+
success: boolean;
|
|
213
|
+
streamId?: string;
|
|
214
|
+
aborted?: boolean;
|
|
215
|
+
message?: string;
|
|
216
|
+
}> {
|
|
217
|
+
const res = await fetch(`${getApiBase()}/agents/${sessionId}/abort`, {
|
|
218
|
+
method: 'POST',
|
|
219
|
+
});
|
|
220
|
+
return res.json();
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Internal helper to parse SSE stream
|
|
224
|
+
function parseSSEStream(
|
|
225
|
+
response: Response,
|
|
226
|
+
onEvent: (event: SSEEvent) => void,
|
|
227
|
+
onStreamId?: (streamId: string) => void
|
|
228
|
+
): Promise<void> {
|
|
229
|
+
return new Promise(async (resolve, reject) => {
|
|
230
|
+
const reader = response.body?.getReader();
|
|
231
|
+
if (!reader) {
|
|
232
|
+
reject(new Error('No response body'));
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const decoder = new TextDecoder();
|
|
237
|
+
let buffer = '';
|
|
238
|
+
|
|
239
|
+
try {
|
|
240
|
+
while (true) {
|
|
241
|
+
const { done, value } = await reader.read();
|
|
242
|
+
if (done) break;
|
|
243
|
+
|
|
244
|
+
buffer += decoder.decode(value, { stream: true });
|
|
245
|
+
const lines = buffer.split('\n');
|
|
246
|
+
buffer = lines.pop() || '';
|
|
247
|
+
|
|
248
|
+
for (const line of lines) {
|
|
249
|
+
if (line.startsWith('data: ')) {
|
|
250
|
+
const data = line.slice(6);
|
|
251
|
+
if (data === '[DONE]') continue;
|
|
252
|
+
|
|
253
|
+
try {
|
|
254
|
+
const event = JSON.parse(data);
|
|
255
|
+
|
|
256
|
+
// Capture stream ID for reconnection
|
|
257
|
+
if (event.type === 'data-stream-id' && onStreamId) {
|
|
258
|
+
onStreamId(event.streamId);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
onEvent(event as SSEEvent);
|
|
262
|
+
} catch {
|
|
263
|
+
// Skip invalid JSON
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
resolve();
|
|
269
|
+
} catch (err) {
|
|
270
|
+
reject(err);
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Streaming run with resumable stream support
|
|
276
|
+
export function runAgent(
|
|
277
|
+
sessionId: string,
|
|
278
|
+
prompt: string,
|
|
279
|
+
onEvent: (event: SSEEvent) => void,
|
|
280
|
+
options?: {
|
|
281
|
+
onStreamId?: (streamId: string) => void;
|
|
282
|
+
}
|
|
283
|
+
): () => void {
|
|
284
|
+
const controller = new AbortController();
|
|
285
|
+
|
|
286
|
+
fetch(`${getApiBase()}/agents/${sessionId}/run`, {
|
|
287
|
+
method: 'POST',
|
|
288
|
+
headers: { 'Content-Type': 'application/json' },
|
|
289
|
+
body: JSON.stringify({ prompt }),
|
|
290
|
+
signal: controller.signal,
|
|
291
|
+
})
|
|
292
|
+
.then(async (response) => {
|
|
293
|
+
const streamId = response.headers.get('x-stream-id');
|
|
294
|
+
if (streamId && options?.onStreamId) {
|
|
295
|
+
options.onStreamId(streamId);
|
|
296
|
+
}
|
|
297
|
+
await parseSSEStream(response, onEvent, options?.onStreamId);
|
|
298
|
+
})
|
|
299
|
+
.catch((err) => {
|
|
300
|
+
if (err.name !== 'AbortError') {
|
|
301
|
+
console.error('Stream error:', err);
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
return () => controller.abort();
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// Watch/subscribe to an existing stream (for multiple tabs/clients)
|
|
309
|
+
export function watchStream(
|
|
310
|
+
sessionId: string,
|
|
311
|
+
onEvent: (event: SSEEvent) => void,
|
|
312
|
+
options?: {
|
|
313
|
+
streamId?: string;
|
|
314
|
+
resumeAt?: number;
|
|
315
|
+
onStreamId?: (streamId: string) => void;
|
|
316
|
+
}
|
|
317
|
+
): () => void {
|
|
318
|
+
const controller = new AbortController();
|
|
319
|
+
|
|
320
|
+
const params = new URLSearchParams();
|
|
321
|
+
if (options?.streamId) params.set('streamId', options.streamId);
|
|
322
|
+
if (options?.resumeAt !== undefined) params.set('resumeAt', String(options.resumeAt));
|
|
323
|
+
|
|
324
|
+
const url = `${getApiBase()}/agents/${sessionId}/watch${params.toString() ? '?' + params.toString() : ''}`;
|
|
325
|
+
|
|
326
|
+
fetch(url, { signal: controller.signal })
|
|
327
|
+
.then(async (response) => {
|
|
328
|
+
if (!response.ok) {
|
|
329
|
+
const error = await response.json().catch(() => ({ error: 'Unknown error' }));
|
|
330
|
+
console.error('Watch stream error:', error);
|
|
331
|
+
onEvent({ type: 'error', errorText: error.error || 'Failed to watch stream' });
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
const streamId = response.headers.get('x-stream-id');
|
|
336
|
+
if (streamId && options?.onStreamId) {
|
|
337
|
+
options.onStreamId(streamId);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
await parseSSEStream(response, onEvent, options?.onStreamId);
|
|
341
|
+
})
|
|
342
|
+
.catch((err) => {
|
|
343
|
+
if (err.name !== 'AbortError') {
|
|
344
|
+
console.error('Watch stream error:', err);
|
|
345
|
+
}
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
return () => controller.abort();
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
export type SSEEvent =
|
|
352
|
+
| { type: 'start'; messageId: string }
|
|
353
|
+
| { type: 'text-start'; id: string }
|
|
354
|
+
| { type: 'text-delta'; id: string; delta: string }
|
|
355
|
+
| { type: 'text-end'; id: string }
|
|
356
|
+
| { type: 'reasoning-start'; id: string }
|
|
357
|
+
| { type: 'reasoning-delta'; id: string; delta: string }
|
|
358
|
+
| { type: 'reasoning-end'; id: string }
|
|
359
|
+
| { type: 'tool-input-start'; toolCallId: string; toolName: string }
|
|
360
|
+
| { type: 'tool-input-delta'; toolCallId: string; argsTextDelta: string }
|
|
361
|
+
| { type: 'tool-input-available'; toolCallId: string; toolName: string; input: unknown }
|
|
362
|
+
| { type: 'tool-output-available'; toolCallId: string; output: unknown }
|
|
363
|
+
| { type: 'tool-progress'; toolName: string; data: { terminalId: string; status: 'started' | 'running' | 'completed'; command?: string } }
|
|
364
|
+
| { type: 'data-approval-required'; data: PendingApproval }
|
|
365
|
+
| { type: 'data-stream-id'; streamId: string }
|
|
366
|
+
| { type: 'data-session'; data: { id: string; name: string; workingDirectory: string; model: string } }
|
|
367
|
+
| { type: 'data-user-message'; data: { id: string; content: string } }
|
|
368
|
+
| { type: 'finish-step' }
|
|
369
|
+
| { type: 'finish'; finishReason?: string }
|
|
370
|
+
| { type: 'abort' }
|
|
371
|
+
| { type: 'error'; errorText: string };
|
|
372
|
+
|
|
373
|
+
// Terminal stream events
|
|
374
|
+
export interface TerminalStreamEvent {
|
|
375
|
+
type: 'status' | 'stdout' | 'exit';
|
|
376
|
+
data?: string;
|
|
377
|
+
terminalId?: string;
|
|
378
|
+
status?: string;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* Subscribe to a terminal's output stream
|
|
383
|
+
* Returns a cancel function
|
|
384
|
+
*/
|
|
385
|
+
export function streamTerminal(
|
|
386
|
+
terminalId: string,
|
|
387
|
+
onOutput: (data: string) => void,
|
|
388
|
+
onExit?: () => void
|
|
389
|
+
): () => void {
|
|
390
|
+
const controller = new AbortController();
|
|
391
|
+
|
|
392
|
+
const connect = async () => {
|
|
393
|
+
try {
|
|
394
|
+
const response = await fetch(`${getApiBase()}/terminals/stream/${terminalId}`, {
|
|
395
|
+
signal: controller.signal,
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
if (!response.ok || !response.body) {
|
|
399
|
+
console.error('Failed to connect to terminal stream');
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
const reader = response.body.getReader();
|
|
404
|
+
const decoder = new TextDecoder();
|
|
405
|
+
let buffer = '';
|
|
406
|
+
|
|
407
|
+
while (true) {
|
|
408
|
+
const { done, value } = await reader.read();
|
|
409
|
+
if (done) break;
|
|
410
|
+
|
|
411
|
+
buffer += decoder.decode(value, { stream: true });
|
|
412
|
+
const lines = buffer.split('\n\n');
|
|
413
|
+
buffer = lines.pop() || '';
|
|
414
|
+
|
|
415
|
+
for (const chunk of lines) {
|
|
416
|
+
if (!chunk.trim()) continue;
|
|
417
|
+
|
|
418
|
+
// Parse SSE format: "event: type\ndata: {...}"
|
|
419
|
+
const eventMatch = chunk.match(/event:\s*(\w+)/);
|
|
420
|
+
const dataMatch = chunk.match(/data:\s*(.+)/);
|
|
421
|
+
|
|
422
|
+
if (eventMatch && dataMatch) {
|
|
423
|
+
const eventType = eventMatch[1];
|
|
424
|
+
try {
|
|
425
|
+
const data = JSON.parse(dataMatch[1]);
|
|
426
|
+
|
|
427
|
+
if (eventType === 'stdout' && data.data) {
|
|
428
|
+
onOutput(data.data);
|
|
429
|
+
} else if (eventType === 'exit') {
|
|
430
|
+
onExit?.();
|
|
431
|
+
}
|
|
432
|
+
} catch {
|
|
433
|
+
// Skip invalid JSON
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
} catch (error) {
|
|
439
|
+
if (error instanceof Error && error.name !== 'AbortError') {
|
|
440
|
+
console.error('Terminal stream error:', error);
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
};
|
|
444
|
+
|
|
445
|
+
connect();
|
|
446
|
+
|
|
447
|
+
return () => controller.abort();
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// ============================================
|
|
451
|
+
// API Key Management
|
|
452
|
+
// ============================================
|
|
453
|
+
|
|
454
|
+
export interface ApiKeyStatus {
|
|
455
|
+
provider: string;
|
|
456
|
+
envVar: string;
|
|
457
|
+
configured: boolean;
|
|
458
|
+
source: 'env' | 'storage' | 'none';
|
|
459
|
+
maskedKey: string | null;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
export interface ApiKeysResponse {
|
|
463
|
+
providers: ApiKeyStatus[];
|
|
464
|
+
supportedProviders: string[];
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
export async function getApiKeys(): Promise<ApiKeysResponse> {
|
|
468
|
+
const res = await fetch(`${getApiBase()}/health/api-keys`);
|
|
469
|
+
return res.json();
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
export async function setApiKey(provider: string, apiKey: string): Promise<{
|
|
473
|
+
success: boolean;
|
|
474
|
+
provider: string;
|
|
475
|
+
maskedKey: string;
|
|
476
|
+
message: string;
|
|
477
|
+
}> {
|
|
478
|
+
const res = await fetch(`${getApiBase()}/health/api-keys`, {
|
|
479
|
+
method: 'POST',
|
|
480
|
+
headers: { 'Content-Type': 'application/json' },
|
|
481
|
+
body: JSON.stringify({ provider, apiKey }),
|
|
482
|
+
});
|
|
483
|
+
return res.json();
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
export async function removeApiKey(provider: string): Promise<{
|
|
487
|
+
success: boolean;
|
|
488
|
+
provider: string;
|
|
489
|
+
message: string;
|
|
490
|
+
}> {
|
|
491
|
+
const res = await fetch(`${getApiBase()}/health/api-keys/${provider}`, {
|
|
492
|
+
method: 'DELETE',
|
|
493
|
+
});
|
|
494
|
+
return res.json();
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// ============================================
|
|
498
|
+
// Checkpoint / Revert API
|
|
499
|
+
// ============================================
|
|
500
|
+
|
|
501
|
+
export interface Checkpoint {
|
|
502
|
+
id: string;
|
|
503
|
+
messageSequence: number;
|
|
504
|
+
gitHead: string | null;
|
|
505
|
+
createdAt: string;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
export interface CheckpointsResponse {
|
|
509
|
+
sessionId: string;
|
|
510
|
+
checkpoints: Checkpoint[];
|
|
511
|
+
count: number;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
export interface RevertResponse {
|
|
515
|
+
success: boolean;
|
|
516
|
+
sessionId: string;
|
|
517
|
+
checkpointId: string;
|
|
518
|
+
filesRestored: number;
|
|
519
|
+
filesDeleted: number;
|
|
520
|
+
messagesDeleted: number;
|
|
521
|
+
checkpointsDeleted: number;
|
|
522
|
+
error?: string;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
export interface SessionDiffFile {
|
|
526
|
+
path: string;
|
|
527
|
+
status: 'created' | 'modified' | 'deleted';
|
|
528
|
+
hasOriginal: boolean;
|
|
529
|
+
hasCurrent: boolean;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
export interface SessionDiffResponse {
|
|
533
|
+
sessionId: string;
|
|
534
|
+
files: SessionDiffFile[];
|
|
535
|
+
summary: {
|
|
536
|
+
created: number;
|
|
537
|
+
modified: number;
|
|
538
|
+
deleted: number;
|
|
539
|
+
total: number;
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
export async function getSessionCheckpoints(sessionId: string): Promise<CheckpointsResponse> {
|
|
544
|
+
const res = await fetch(`${getApiBase()}/sessions/${sessionId}/checkpoints`);
|
|
545
|
+
if (!res.ok) {
|
|
546
|
+
throw new Error(`Failed to get checkpoints: ${res.statusText}`);
|
|
547
|
+
}
|
|
548
|
+
return res.json();
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
export async function revertToCheckpoint(sessionId: string, checkpointId: string): Promise<RevertResponse> {
|
|
552
|
+
const res = await fetch(`${getApiBase()}/sessions/${sessionId}/revert/${checkpointId}`, {
|
|
553
|
+
method: 'POST',
|
|
554
|
+
});
|
|
555
|
+
if (!res.ok) {
|
|
556
|
+
const error = await res.json();
|
|
557
|
+
throw new Error(error.error || `Failed to revert: ${res.statusText}`);
|
|
558
|
+
}
|
|
559
|
+
return res.json();
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
export async function getSessionDiff(sessionId: string): Promise<SessionDiffResponse> {
|
|
563
|
+
const res = await fetch(`${getApiBase()}/sessions/${sessionId}/diff`);
|
|
564
|
+
if (!res.ok) {
|
|
565
|
+
throw new Error(`Failed to get diff: ${res.statusText}`);
|
|
566
|
+
}
|
|
567
|
+
return res.json();
|
|
568
|
+
}
|