@agentstep/agent-sdk 0.1.0
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/package.json +45 -0
- package/src/auth/middleware.ts +38 -0
- package/src/backends/claude/args.ts +88 -0
- package/src/backends/claude/index.ts +193 -0
- package/src/backends/claude/permission-hook.ts +152 -0
- package/src/backends/claude/tool-bridge.ts +211 -0
- package/src/backends/claude/translator.ts +209 -0
- package/src/backends/claude/wrapper-script.ts +45 -0
- package/src/backends/codex/args.ts +69 -0
- package/src/backends/codex/auth.ts +35 -0
- package/src/backends/codex/index.ts +57 -0
- package/src/backends/codex/setup.ts +37 -0
- package/src/backends/codex/translator.ts +223 -0
- package/src/backends/codex/wrapper-script.ts +26 -0
- package/src/backends/factory/args.ts +45 -0
- package/src/backends/factory/auth.ts +30 -0
- package/src/backends/factory/index.ts +56 -0
- package/src/backends/factory/setup.ts +34 -0
- package/src/backends/factory/translator.ts +139 -0
- package/src/backends/factory/wrapper-script.ts +33 -0
- package/src/backends/gemini/args.ts +44 -0
- package/src/backends/gemini/auth.ts +30 -0
- package/src/backends/gemini/index.ts +53 -0
- package/src/backends/gemini/setup.ts +34 -0
- package/src/backends/gemini/translator.ts +139 -0
- package/src/backends/gemini/wrapper-script.ts +26 -0
- package/src/backends/opencode/args.ts +53 -0
- package/src/backends/opencode/auth.ts +53 -0
- package/src/backends/opencode/index.ts +70 -0
- package/src/backends/opencode/mcp.ts +67 -0
- package/src/backends/opencode/setup.ts +54 -0
- package/src/backends/opencode/translator.ts +168 -0
- package/src/backends/opencode/wrapper-script.ts +46 -0
- package/src/backends/registry.ts +38 -0
- package/src/backends/shared/ndjson.ts +29 -0
- package/src/backends/shared/translator-types.ts +69 -0
- package/src/backends/shared/wrap-prompt.ts +17 -0
- package/src/backends/types.ts +85 -0
- package/src/config/index.ts +95 -0
- package/src/db/agents.ts +185 -0
- package/src/db/api_keys.ts +78 -0
- package/src/db/batch.ts +142 -0
- package/src/db/client.ts +81 -0
- package/src/db/environments.ts +127 -0
- package/src/db/events.ts +208 -0
- package/src/db/memory.ts +143 -0
- package/src/db/migrations.ts +295 -0
- package/src/db/proxy.ts +37 -0
- package/src/db/sessions.ts +295 -0
- package/src/db/vaults.ts +110 -0
- package/src/errors.ts +53 -0
- package/src/handlers/agents.ts +194 -0
- package/src/handlers/batch.ts +41 -0
- package/src/handlers/docs.ts +87 -0
- package/src/handlers/environments.ts +154 -0
- package/src/handlers/events.ts +234 -0
- package/src/handlers/index.ts +12 -0
- package/src/handlers/memory.ts +141 -0
- package/src/handlers/openapi.ts +14 -0
- package/src/handlers/sessions.ts +223 -0
- package/src/handlers/stream.ts +76 -0
- package/src/handlers/threads.ts +26 -0
- package/src/handlers/ui/app.js +984 -0
- package/src/handlers/ui/index.html +112 -0
- package/src/handlers/ui/style.css +164 -0
- package/src/handlers/ui.ts +1281 -0
- package/src/handlers/vaults.ts +99 -0
- package/src/http.ts +35 -0
- package/src/index.ts +104 -0
- package/src/init.ts +227 -0
- package/src/openapi/registry.ts +8 -0
- package/src/openapi/schemas.ts +625 -0
- package/src/openapi/spec.ts +691 -0
- package/src/providers/apple.ts +220 -0
- package/src/providers/daytona.ts +217 -0
- package/src/providers/docker.ts +264 -0
- package/src/providers/e2b.ts +203 -0
- package/src/providers/fly.ts +276 -0
- package/src/providers/modal.ts +222 -0
- package/src/providers/podman.ts +206 -0
- package/src/providers/registry.ts +28 -0
- package/src/providers/shared.ts +11 -0
- package/src/providers/sprites.ts +55 -0
- package/src/providers/types.ts +73 -0
- package/src/providers/vercel.ts +208 -0
- package/src/proxy/forward.ts +111 -0
- package/src/queue/index.ts +111 -0
- package/src/sessions/actor.ts +53 -0
- package/src/sessions/bus.ts +155 -0
- package/src/sessions/driver.ts +818 -0
- package/src/sessions/grader.ts +120 -0
- package/src/sessions/interrupt.ts +14 -0
- package/src/sessions/sweeper.ts +136 -0
- package/src/sessions/threads.ts +126 -0
- package/src/sessions/tools.ts +50 -0
- package/src/shutdown.ts +78 -0
- package/src/sprite/client.ts +294 -0
- package/src/sprite/exec.ts +161 -0
- package/src/sprite/lifecycle.ts +339 -0
- package/src/sprite/pool.ts +65 -0
- package/src/sprite/setup.ts +159 -0
- package/src/state.ts +61 -0
- package/src/types.ts +339 -0
- package/src/util/clock.ts +7 -0
- package/src/util/ids.ts +11 -0
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { routeWrap, jsonOk } from "../http";
|
|
3
|
+
import {
|
|
4
|
+
createSession,
|
|
5
|
+
getSession,
|
|
6
|
+
listSessions,
|
|
7
|
+
updateSessionMutable,
|
|
8
|
+
updateSessionStatus,
|
|
9
|
+
archiveSession,
|
|
10
|
+
} from "../db/sessions";
|
|
11
|
+
import { getAgent } from "../db/agents";
|
|
12
|
+
import { getEnvironment } from "../db/environments";
|
|
13
|
+
import { getActor, dropActor } from "../sessions/actor";
|
|
14
|
+
import { appendEvent, dropEmitter } from "../sessions/bus";
|
|
15
|
+
import { interruptSession } from "../sessions/interrupt";
|
|
16
|
+
import { releaseSession } from "../sprite/lifecycle";
|
|
17
|
+
import { isProxied, markProxied, unmarkProxied } from "../db/proxy";
|
|
18
|
+
import { forwardToAnthropic } from "../proxy/forward";
|
|
19
|
+
import { badRequest, notFound } from "../errors";
|
|
20
|
+
import { nowMs } from "../util/clock";
|
|
21
|
+
import type { SessionStatus } from "../types";
|
|
22
|
+
|
|
23
|
+
const ALLOWED_STATUSES: SessionStatus[] = ["idle", "running", "rescheduling", "terminated"];
|
|
24
|
+
|
|
25
|
+
const AgentRef = z.union([
|
|
26
|
+
z.string(),
|
|
27
|
+
z.object({ id: z.string(), version: z.number().int(), type: z.literal("agent").optional() }),
|
|
28
|
+
]);
|
|
29
|
+
|
|
30
|
+
const ResourceSchema = z.object({
|
|
31
|
+
type: z.enum(["uri", "text"]),
|
|
32
|
+
uri: z.string().optional(),
|
|
33
|
+
content: z.string().optional(),
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
const CreateSchema = z.object({
|
|
37
|
+
agent: AgentRef,
|
|
38
|
+
environment_id: z.string(),
|
|
39
|
+
title: z.string().nullish(),
|
|
40
|
+
metadata: z.record(z.unknown()).optional(),
|
|
41
|
+
max_budget_usd: z.number().positive().optional(),
|
|
42
|
+
resources: z.array(ResourceSchema).optional(),
|
|
43
|
+
vault_ids: z.array(z.string()).optional(),
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
const UpdateSchema = z.object({
|
|
47
|
+
title: z.string().nullish(),
|
|
48
|
+
metadata: z.record(z.unknown()).optional(),
|
|
49
|
+
vault_ids: z.array(z.string()).optional(),
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
export function handleCreateSession(request: Request): Promise<Response> {
|
|
53
|
+
return routeWrap(request, async () => {
|
|
54
|
+
const rawBody = await request.text();
|
|
55
|
+
const body = rawBody ? JSON.parse(rawBody) : null;
|
|
56
|
+
const parsed = CreateSchema.safeParse(body);
|
|
57
|
+
if (!parsed.success) throw badRequest(parsed.error.message);
|
|
58
|
+
|
|
59
|
+
const agentId = typeof parsed.data.agent === "string" ? parsed.data.agent : parsed.data.agent.id;
|
|
60
|
+
|
|
61
|
+
if (isProxied(agentId)) {
|
|
62
|
+
const proxyRes = await forwardToAnthropic(request, "/v1/sessions", { body: rawBody });
|
|
63
|
+
if (proxyRes.ok) {
|
|
64
|
+
try {
|
|
65
|
+
const data = (await proxyRes.clone().json()) as { id: string };
|
|
66
|
+
markProxied(data.id, "session");
|
|
67
|
+
} catch { /* best-effort */ }
|
|
68
|
+
}
|
|
69
|
+
return proxyRes;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const agentVersion =
|
|
73
|
+
typeof parsed.data.agent === "string" ? undefined : parsed.data.agent.version;
|
|
74
|
+
|
|
75
|
+
const agent = getAgent(agentId, agentVersion);
|
|
76
|
+
if (!agent) throw notFound(`agent not found: ${agentId}`);
|
|
77
|
+
|
|
78
|
+
const env = getEnvironment(parsed.data.environment_id);
|
|
79
|
+
if (!env) throw notFound(`environment not found: ${parsed.data.environment_id}`);
|
|
80
|
+
if (env.state !== "ready") {
|
|
81
|
+
throw badRequest(
|
|
82
|
+
`environment is not ready (state=${env.state}${env.state_message ? `: ${env.state_message}` : ""})`,
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const session = createSession({
|
|
87
|
+
agent_id: agent.id,
|
|
88
|
+
agent_version: agent.version,
|
|
89
|
+
environment_id: env.id,
|
|
90
|
+
title: parsed.data.title ?? null,
|
|
91
|
+
metadata: parsed.data.metadata ?? {},
|
|
92
|
+
max_budget_usd: parsed.data.max_budget_usd ?? null,
|
|
93
|
+
resources: parsed.data.resources?.length ? parsed.data.resources : null,
|
|
94
|
+
vault_ids: parsed.data.vault_ids?.length ? parsed.data.vault_ids : null,
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
getActor(session.id);
|
|
98
|
+
return jsonOk(session, 201);
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function handleListSessions(request: Request): Promise<Response> {
|
|
103
|
+
return routeWrap(request, async ({ request: req }) => {
|
|
104
|
+
const url = new URL(req.url);
|
|
105
|
+
const limit = url.searchParams.get("limit");
|
|
106
|
+
const order = url.searchParams.get("order") as "asc" | "desc" | null;
|
|
107
|
+
const includeArchived = url.searchParams.get("include_archived") === "true";
|
|
108
|
+
const cursor = url.searchParams.get("page") ?? undefined;
|
|
109
|
+
const agentId = url.searchParams.get("agent_id") ?? undefined;
|
|
110
|
+
const agentVersion = url.searchParams.get("agent_version");
|
|
111
|
+
const environmentId = url.searchParams.get("environment_id") ?? undefined;
|
|
112
|
+
|
|
113
|
+
const statusRaw = url.searchParams.get("status");
|
|
114
|
+
let status: SessionStatus | undefined;
|
|
115
|
+
if (statusRaw != null) {
|
|
116
|
+
if (!ALLOWED_STATUSES.includes(statusRaw as SessionStatus)) {
|
|
117
|
+
throw badRequest(
|
|
118
|
+
`invalid status value: ${statusRaw} (allowed: ${ALLOWED_STATUSES.join(",")})`,
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
status = statusRaw as SessionStatus;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const data = listSessions({
|
|
125
|
+
agent_id: agentId,
|
|
126
|
+
agent_version: agentVersion ? Number(agentVersion) : undefined,
|
|
127
|
+
environmentId,
|
|
128
|
+
status,
|
|
129
|
+
limit: limit ? Number(limit) : undefined,
|
|
130
|
+
order: order ?? undefined,
|
|
131
|
+
includeArchived,
|
|
132
|
+
cursor,
|
|
133
|
+
createdGt: parseMs(url.searchParams.get("created_at[gt]")),
|
|
134
|
+
createdGte: parseMs(url.searchParams.get("created_at[gte]")),
|
|
135
|
+
createdLt: parseMs(url.searchParams.get("created_at[lt]")),
|
|
136
|
+
createdLte: parseMs(url.searchParams.get("created_at[lte]")),
|
|
137
|
+
});
|
|
138
|
+
return jsonOk({
|
|
139
|
+
data,
|
|
140
|
+
next_page: data.length > 0 ? data[data.length - 1].id : null,
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export function handleGetSession(request: Request, id: string): Promise<Response> {
|
|
146
|
+
return routeWrap(request, async () => {
|
|
147
|
+
if (isProxied(id)) return forwardToAnthropic(request, `/v1/sessions/${id}`);
|
|
148
|
+
const session = getSession(id);
|
|
149
|
+
if (!session) throw notFound(`session ${id} not found`);
|
|
150
|
+
return jsonOk(session);
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export function handleUpdateSession(request: Request, id: string): Promise<Response> {
|
|
155
|
+
return routeWrap(request, async () => {
|
|
156
|
+
if (isProxied(id)) return forwardToAnthropic(request, `/v1/sessions/${id}`);
|
|
157
|
+
const body = await request.json().catch(() => null);
|
|
158
|
+
const parsed = UpdateSchema.safeParse(body);
|
|
159
|
+
if (!parsed.success) throw badRequest(parsed.error.message);
|
|
160
|
+
|
|
161
|
+
const updated = updateSessionMutable(id, {
|
|
162
|
+
title: parsed.data.title,
|
|
163
|
+
metadata: parsed.data.metadata,
|
|
164
|
+
});
|
|
165
|
+
if (!updated) throw notFound(`session ${id} not found`);
|
|
166
|
+
return jsonOk(updated);
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export function handleDeleteSession(request: Request, id: string): Promise<Response> {
|
|
171
|
+
return routeWrap(request, async () => {
|
|
172
|
+
if (isProxied(id)) {
|
|
173
|
+
const res = await forwardToAnthropic(request, `/v1/sessions/${id}`);
|
|
174
|
+
if (res.ok) unmarkProxied(id);
|
|
175
|
+
return res;
|
|
176
|
+
}
|
|
177
|
+
const session = getSession(id);
|
|
178
|
+
if (!session) throw notFound(`session ${id} not found`);
|
|
179
|
+
|
|
180
|
+
const actor = getActor(id);
|
|
181
|
+
await actor.enqueue(async () => {
|
|
182
|
+
interruptSession(id);
|
|
183
|
+
await releaseSession(id);
|
|
184
|
+
appendEvent(id, {
|
|
185
|
+
type: "session.status_terminated",
|
|
186
|
+
payload: { reason: "deleted" },
|
|
187
|
+
origin: "server",
|
|
188
|
+
processedAt: nowMs(),
|
|
189
|
+
});
|
|
190
|
+
updateSessionStatus(id, "terminated", "deleted");
|
|
191
|
+
});
|
|
192
|
+
dropActor(id);
|
|
193
|
+
dropEmitter(id);
|
|
194
|
+
return jsonOk({ id, type: "session_deleted" });
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export function handleArchiveSession(request: Request, id: string): Promise<Response> {
|
|
199
|
+
return routeWrap(request, async () => {
|
|
200
|
+
if (isProxied(id)) {
|
|
201
|
+
const res = await forwardToAnthropic(request, `/v1/sessions/${id}/archive`);
|
|
202
|
+
if (res.ok) unmarkProxied(id);
|
|
203
|
+
return res;
|
|
204
|
+
}
|
|
205
|
+
const session = getSession(id);
|
|
206
|
+
if (!session) throw notFound(`session ${id} not found`);
|
|
207
|
+
|
|
208
|
+
const actor = getActor(id);
|
|
209
|
+
await actor.enqueue(async () => {
|
|
210
|
+
await releaseSession(id);
|
|
211
|
+
archiveSession(id);
|
|
212
|
+
});
|
|
213
|
+
return jsonOk(getSession(id));
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function parseMs(v: string | null): number | undefined {
|
|
218
|
+
if (!v) return undefined;
|
|
219
|
+
const n = Number(v);
|
|
220
|
+
if (Number.isFinite(n)) return n;
|
|
221
|
+
const t = Date.parse(v);
|
|
222
|
+
return Number.isFinite(t) ? t : undefined;
|
|
223
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { ensureInitialized } from "../init";
|
|
2
|
+
import { authenticate } from "../auth/middleware";
|
|
3
|
+
import { subscribe } from "../sessions/bus";
|
|
4
|
+
import { getSession } from "../db/sessions";
|
|
5
|
+
import { isProxied } from "../db/proxy";
|
|
6
|
+
import { forwardToAnthropic } from "../proxy/forward";
|
|
7
|
+
import { toResponse, notFound } from "../errors";
|
|
8
|
+
import type { ManagedEvent } from "../types";
|
|
9
|
+
|
|
10
|
+
export async function handleSessionStream(request: Request, sessionId: string): Promise<Response> {
|
|
11
|
+
try {
|
|
12
|
+
await ensureInitialized();
|
|
13
|
+
await authenticate(request);
|
|
14
|
+
|
|
15
|
+
if (isProxied(sessionId)) {
|
|
16
|
+
const res = await forwardToAnthropic(request, `/v1/sessions/${sessionId}/stream`);
|
|
17
|
+
const headers = new Headers(res.headers);
|
|
18
|
+
headers.set("X-Accel-Buffering", "no");
|
|
19
|
+
return new Response(res.body, { status: res.status, headers });
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const session = getSession(sessionId);
|
|
23
|
+
if (!session) throw notFound(`session ${sessionId} not found`);
|
|
24
|
+
|
|
25
|
+
const url = new URL(request.url);
|
|
26
|
+
const lastEventId = request.headers.get("last-event-id");
|
|
27
|
+
const afterSeq = lastEventId
|
|
28
|
+
? Number(lastEventId)
|
|
29
|
+
: Number(url.searchParams.get("after_seq") ?? "0");
|
|
30
|
+
|
|
31
|
+
const encoder = new TextEncoder();
|
|
32
|
+
const { readable, writable } = new TransformStream<Uint8Array, Uint8Array>();
|
|
33
|
+
const writer = writable.getWriter();
|
|
34
|
+
|
|
35
|
+
const write = (payload: string) => {
|
|
36
|
+
writer.write(encoder.encode(payload)).catch(() => {});
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const writeEvent = (evt: ManagedEvent) => {
|
|
40
|
+
const lines = [
|
|
41
|
+
`id: ${evt.seq}`,
|
|
42
|
+
`event: ${evt.type}`,
|
|
43
|
+
`data: ${JSON.stringify(evt)}`,
|
|
44
|
+
"",
|
|
45
|
+
"",
|
|
46
|
+
].join("\n");
|
|
47
|
+
write(lines);
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const keepalive = setInterval(() => {
|
|
51
|
+
write(`data: {"type":"ping"}\n\n`);
|
|
52
|
+
}, 15_000);
|
|
53
|
+
|
|
54
|
+
const sub = subscribe(sessionId, Number.isFinite(afterSeq) ? afterSeq : 0, writeEvent);
|
|
55
|
+
|
|
56
|
+
const abort = () => {
|
|
57
|
+
clearInterval(keepalive);
|
|
58
|
+
sub.unsubscribe();
|
|
59
|
+
try {
|
|
60
|
+
writer.close();
|
|
61
|
+
} catch { /* ignore */ }
|
|
62
|
+
};
|
|
63
|
+
request.signal.addEventListener("abort", abort);
|
|
64
|
+
|
|
65
|
+
return new Response(readable, {
|
|
66
|
+
headers: {
|
|
67
|
+
"Content-Type": "text/event-stream",
|
|
68
|
+
"Cache-Control": "no-cache",
|
|
69
|
+
Connection: "keep-alive",
|
|
70
|
+
"X-Accel-Buffering": "no",
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
} catch (err) {
|
|
74
|
+
return toResponse(err);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { routeWrap, jsonOk } from "../http";
|
|
2
|
+
import { getSession, listSessions } from "../db/sessions";
|
|
3
|
+
import { notFound } from "../errors";
|
|
4
|
+
|
|
5
|
+
export function handleListThreads(request: Request, sessionId: string): Promise<Response> {
|
|
6
|
+
return routeWrap(request, async () => {
|
|
7
|
+
const session = getSession(sessionId);
|
|
8
|
+
if (!session) throw notFound(`session ${sessionId} not found`);
|
|
9
|
+
|
|
10
|
+
const url = new URL(request.url);
|
|
11
|
+
const limit = url.searchParams.get("limit");
|
|
12
|
+
const order = url.searchParams.get("order") as "asc" | "desc" | null;
|
|
13
|
+
|
|
14
|
+
const data = listSessions({
|
|
15
|
+
parent_session_id: sessionId,
|
|
16
|
+
limit: limit ? Number(limit) : undefined,
|
|
17
|
+
order: order ?? undefined,
|
|
18
|
+
includeArchived: true, // show all threads including completed ones
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
return jsonOk({
|
|
22
|
+
data,
|
|
23
|
+
next_page: data.length > 0 ? data[data.length - 1].id : null,
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
}
|