@sema-agent/server 1.315.0 → 1.316.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/dist/http/idempotency.d.ts +1 -0
- package/dist/http/idempotency.js +3 -0
- package/dist/http/principal-gate.d.ts +12 -0
- package/dist/http/principal-gate.js +52 -0
- package/dist/http/route-ctx.d.ts +67 -0
- package/dist/http/route-ctx.js +2 -0
- package/dist/http/routes/attachments.d.ts +4 -0
- package/dist/http/routes/attachments.js +106 -0
- package/dist/http/routes/capabilities.d.ts +4 -0
- package/dist/http/routes/capabilities.js +111 -0
- package/dist/http/routes/images.d.ts +20 -0
- package/dist/http/routes/images.js +572 -0
- package/dist/http/routes/memory-policy.d.ts +4 -0
- package/dist/http/routes/memory-policy.js +105 -0
- package/dist/http/routes/observability.d.ts +4 -0
- package/dist/http/routes/observability.js +63 -0
- package/dist/http/routes/session-sync.d.ts +36 -0
- package/dist/http/routes/session-sync.js +513 -0
- package/dist/http/routes/sessions-list.d.ts +4 -0
- package/dist/http/routes/sessions-list.js +117 -0
- package/dist/http/routes/sessions.d.ts +17 -0
- package/dist/http/routes/sessions.js +746 -0
- package/dist/http/send.d.ts +6 -0
- package/dist/http/send.js +19 -0
- package/dist/http/server.d.ts +6 -7
- package/dist/http/server.js +125 -2413
- package/dist/http/sse-log.d.ts +25 -0
- package/dist/http/sse-log.js +59 -0
- package/dist/http/workspace-content.d.ts +3 -0
- package/dist/http/workspace-content.js +20 -0
- package/dist/plugins/approval-store-sql.d.ts +52 -0
- package/dist/plugins/approval-store-sql.js +119 -0
- package/dist/plugins/breaker-state-sql.d.ts +29 -0
- package/dist/plugins/breaker-state-sql.js +104 -0
- package/dist/plugins/file-snapshot-store-sql.d.ts +38 -0
- package/dist/plugins/file-snapshot-store-sql.js +210 -0
- package/dist/plugins/image-index-sql.d.ts +33 -0
- package/dist/plugins/image-index-sql.js +328 -0
- package/dist/plugins/outcome-ledger-sql.d.ts +70 -0
- package/dist/plugins/outcome-ledger-sql.js +158 -0
- package/dist/plugins/pg-approval-store.d.ts +1 -27
- package/dist/plugins/pg-approval-store.js +1 -83
- package/dist/plugins/pg-breaker-state.d.ts +1 -19
- package/dist/plugins/pg-breaker-state.js +1 -88
- package/dist/plugins/pg-file-snapshot-store.d.ts +1 -27
- package/dist/plugins/pg-file-snapshot-store.js +1 -190
- package/dist/plugins/pg-image-index.d.ts +1 -21
- package/dist/plugins/pg-image-index.js +1 -217
- package/dist/plugins/pg-outcome-ledger.d.ts +2 -22
- package/dist/plugins/pg-outcome-ledger.js +1 -104
- package/dist/plugins/pg-resume-anchor-store.d.ts +1 -13
- package/dist/plugins/pg-resume-anchor-store.js +1 -22
- package/dist/plugins/pg-run-store.d.ts +1 -75
- package/dist/plugins/pg-run-store.js +1 -287
- package/dist/plugins/pg-session-policy-store.d.ts +1 -11
- package/dist/plugins/pg-session-policy-store.js +1 -90
- package/dist/plugins/pg-tool-result-store.d.ts +1 -15
- package/dist/plugins/pg-tool-result-store.js +1 -52
- package/dist/plugins/pg-workflow-journal-store.d.ts +1 -20
- package/dist/plugins/pg-workflow-journal-store.js +1 -34
- package/dist/plugins/pg-workflow-run-store.d.ts +1 -55
- package/dist/plugins/pg-workflow-run-store.js +1 -220
- package/dist/plugins/resume-anchor-store-sql.d.ts +23 -0
- package/dist/plugins/resume-anchor-store-sql.js +38 -0
- package/dist/plugins/run-store-sql.d.ts +89 -0
- package/dist/plugins/run-store-sql.js +397 -0
- package/dist/plugins/session-policy-store-sql.d.ts +22 -0
- package/dist/plugins/session-policy-store-sql.js +109 -0
- package/dist/plugins/tidb-approval-store.d.ts +1 -39
- package/dist/plugins/tidb-approval-store.js +1 -71
- package/dist/plugins/tidb-breaker-state.d.ts +1 -17
- package/dist/plugins/tidb-breaker-state.js +1 -74
- package/dist/plugins/tidb-file-snapshot-store.d.ts +1 -27
- package/dist/plugins/tidb-file-snapshot-store.js +1 -192
- package/dist/plugins/tidb-image-index.d.ts +1 -19
- package/dist/plugins/tidb-image-index.js +1 -149
- package/dist/plugins/tidb-outcome-ledger.d.ts +1 -57
- package/dist/plugins/tidb-outcome-ledger.js +1 -97
- package/dist/plugins/tidb-resume-anchor-store.d.ts +1 -13
- package/dist/plugins/tidb-resume-anchor-store.js +1 -22
- package/dist/plugins/tidb-run-store.d.ts +1 -75
- package/dist/plugins/tidb-run-store.js +1 -306
- package/dist/plugins/tidb-session-policy-store.d.ts +1 -11
- package/dist/plugins/tidb-session-policy-store.js +1 -86
- package/dist/plugins/tidb-tool-result-store.d.ts +1 -13
- package/dist/plugins/tidb-tool-result-store.js +1 -38
- package/dist/plugins/tidb-workflow-journal-store.d.ts +1 -20
- package/dist/plugins/tidb-workflow-journal-store.js +1 -34
- package/dist/plugins/tidb-workflow-run-store.d.ts +1 -61
- package/dist/plugins/tidb-workflow-run-store.js +1 -231
- package/dist/plugins/tool-result-store-sql.d.ts +25 -0
- package/dist/plugins/tool-result-store-sql.js +69 -0
- package/dist/plugins/workflow-journal-store-sql.d.ts +30 -0
- package/dist/plugins/workflow-journal-store-sql.js +53 -0
- package/dist/plugins/workflow-run-store-sql.d.ts +85 -0
- package/dist/plugins/workflow-run-store-sql.js +299 -0
- package/package.json +1 -1
|
@@ -9,4 +9,5 @@ export declare class IdempotencyCache<T> {
|
|
|
9
9
|
private live;
|
|
10
10
|
private maybeSweep;
|
|
11
11
|
}
|
|
12
|
+
export declare function scopedIdempotencyKey(raw: string | undefined, source: string | null, principal: string | undefined): string | undefined;
|
|
12
13
|
//# sourceMappingURL=idempotency.d.ts.map
|
package/dist/http/idempotency.js
CHANGED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { IncomingMessage } from "node:http";
|
|
2
|
+
import type { ServiceConfig } from "../config-types.js";
|
|
3
|
+
export declare function headerStr(v: string | string[] | undefined): string | undefined;
|
|
4
|
+
export declare function safeEqual(a: string, b: string): boolean;
|
|
5
|
+
export declare function authorized(req: IncomingMessage, token: string): boolean;
|
|
6
|
+
export declare function systemFor(req: IncomingMessage, config: ServiceConfig): string | null | undefined;
|
|
7
|
+
export declare function bearerPresentedButUnverified(req: IncomingMessage, config: ServiceConfig): boolean;
|
|
8
|
+
export declare function gatedPrincipal(req: IncomingMessage, config: ServiceConfig): string | undefined;
|
|
9
|
+
export declare function explicitOperatorOk(principal: string | undefined, operatorPrincipals: readonly string[]): boolean;
|
|
10
|
+
export declare function isOperator(principal: string | undefined, operatorPrincipals: readonly string[]): boolean;
|
|
11
|
+
export declare function explicitOperator(principal: string | undefined, operatorPrincipals: readonly string[]): boolean;
|
|
12
|
+
//# sourceMappingURL=principal-gate.d.ts.map
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { timingSafeEqual } from "node:crypto";
|
|
2
|
+
import { verifiedPrincipal, ssoVerifiedPrincipal } from "../security.js";
|
|
3
|
+
export function headerStr(v) {
|
|
4
|
+
return Array.isArray(v) ? v[0] : v;
|
|
5
|
+
}
|
|
6
|
+
export function safeEqual(a, b) {
|
|
7
|
+
const ab = Buffer.from(a, "utf8");
|
|
8
|
+
const bb = Buffer.from(b, "utf8");
|
|
9
|
+
return ab.length === bb.length && timingSafeEqual(ab, bb);
|
|
10
|
+
}
|
|
11
|
+
export function authorized(req, token) {
|
|
12
|
+
const h = req.headers.authorization ?? "";
|
|
13
|
+
return safeEqual(h, `Bearer ${token}`) || safeEqual(h, `token ${token}`);
|
|
14
|
+
}
|
|
15
|
+
export function systemFor(req, config) {
|
|
16
|
+
for (const [tok, system] of Object.entries(config.authTokens ?? {})) {
|
|
17
|
+
if (authorized(req, tok))
|
|
18
|
+
return system;
|
|
19
|
+
}
|
|
20
|
+
if (config.authToken && authorized(req, config.authToken))
|
|
21
|
+
return null;
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
export function bearerPresentedButUnverified(req, config) {
|
|
25
|
+
const h = req.headers.authorization;
|
|
26
|
+
if (typeof h !== "string" || !/^(Bearer|token) /i.test(h))
|
|
27
|
+
return false;
|
|
28
|
+
if (ssoVerifiedPrincipal(req))
|
|
29
|
+
return false;
|
|
30
|
+
if (systemFor(req, config) !== undefined)
|
|
31
|
+
return false;
|
|
32
|
+
if (config.traceToken && authorized(req, config.traceToken))
|
|
33
|
+
return false;
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
export function gatedPrincipal(req, config) {
|
|
37
|
+
if (bearerPresentedButUnverified(req, config))
|
|
38
|
+
return undefined;
|
|
39
|
+
return verifiedPrincipal(req, config);
|
|
40
|
+
}
|
|
41
|
+
export function explicitOperatorOk(principal, operatorPrincipals) {
|
|
42
|
+
return operatorPrincipals.length > 0 && principal !== undefined && operatorPrincipals.includes(principal);
|
|
43
|
+
}
|
|
44
|
+
export function isOperator(principal, operatorPrincipals) {
|
|
45
|
+
if (operatorPrincipals.length === 0)
|
|
46
|
+
return true;
|
|
47
|
+
return principal !== undefined && operatorPrincipals.includes(principal);
|
|
48
|
+
}
|
|
49
|
+
export function explicitOperator(principal, operatorPrincipals) {
|
|
50
|
+
return operatorPrincipals.length > 0 && principal !== undefined && operatorPrincipals.includes(principal);
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=principal-gate.js.map
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
2
|
+
import type { TaskStream } from "@sema-agent/core";
|
|
3
|
+
import type { FlatServiceDeps } from "./server.js";
|
|
4
|
+
import type { IdempotencyCache } from "./idempotency.js";
|
|
5
|
+
import type { ImagesLocal } from "./routes/images.js";
|
|
6
|
+
import type { SessionsLocal } from "./routes/sessions.js";
|
|
7
|
+
import type { SessionSyncLocal } from "./routes/session-sync.js";
|
|
8
|
+
export interface RunRegistry {
|
|
9
|
+
idemCache: IdempotencyCache<{
|
|
10
|
+
status: number;
|
|
11
|
+
body: unknown;
|
|
12
|
+
}>;
|
|
13
|
+
inflightRuns: Map<string, AbortController>;
|
|
14
|
+
preemptableRuns: Map<string, AbortController>;
|
|
15
|
+
cancelledViaVerb: Set<string>;
|
|
16
|
+
steerableRuns: Map<string, TaskStream>;
|
|
17
|
+
wakeParkMints: Map<string, Promise<void>>;
|
|
18
|
+
counters: {
|
|
19
|
+
uncountedBillableInflight: number;
|
|
20
|
+
admittedInflight: number;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export interface RouteHelpers {
|
|
24
|
+
readJson(req: IncomingMessage): Promise<unknown>;
|
|
25
|
+
readRawBody(req: IncomingMessage, max: number): Promise<Buffer>;
|
|
26
|
+
rateLimited(req: IncomingMessage, res: ServerResponse): boolean;
|
|
27
|
+
quotaExceeded(req: IncomingMessage, res: ServerResponse): boolean;
|
|
28
|
+
leaseDenied(req: IncomingMessage, res: ServerResponse): Promise<boolean>;
|
|
29
|
+
safeDecode(seg: string): string | null;
|
|
30
|
+
isFleetWide(req: IncomingMessage): boolean;
|
|
31
|
+
runOwnerOk(req: IncomingMessage, res: ServerResponse, owner: string | null): boolean;
|
|
32
|
+
sessionOwnerScope(req: IncomingMessage): {
|
|
33
|
+
fleetWide: boolean;
|
|
34
|
+
gateOwner: string | null;
|
|
35
|
+
};
|
|
36
|
+
sessionOwnerScopeForWrite(req: IncomingMessage): {
|
|
37
|
+
fleetWide: boolean;
|
|
38
|
+
gateOwner: string | null;
|
|
39
|
+
};
|
|
40
|
+
runSessionAcceptOk(req: IncomingMessage, res: ServerResponse, run: {
|
|
41
|
+
sessionId: string | null;
|
|
42
|
+
}, face: string, notFoundError?: string): boolean;
|
|
43
|
+
}
|
|
44
|
+
export interface RouteLocals {
|
|
45
|
+
images: ImagesLocal;
|
|
46
|
+
sessions: SessionsLocal;
|
|
47
|
+
sessionSync: SessionSyncLocal;
|
|
48
|
+
}
|
|
49
|
+
export interface RouteRequestState {
|
|
50
|
+
streamTaskId: string | undefined;
|
|
51
|
+
streamDetached: boolean;
|
|
52
|
+
source: string | null;
|
|
53
|
+
}
|
|
54
|
+
export interface RouteCtxBase {
|
|
55
|
+
deps: FlatServiceDeps;
|
|
56
|
+
registry: RunRegistry;
|
|
57
|
+
helpers: RouteHelpers;
|
|
58
|
+
local: RouteLocals;
|
|
59
|
+
}
|
|
60
|
+
export interface RouteCtx extends RouteCtxBase {
|
|
61
|
+
req: RouteRequestState;
|
|
62
|
+
}
|
|
63
|
+
export type RouteHandler = (req: IncomingMessage, res: ServerResponse, url: string, ctx: RouteCtx) => Promise<boolean>;
|
|
64
|
+
export interface RouteMiss {
|
|
65
|
+
fell: boolean;
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=route-ctx.d.ts.map
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
2
|
+
import type { RouteCtx } from "../route-ctx.js";
|
|
3
|
+
export declare function handleAttachments(req: IncomingMessage, res: ServerResponse, url: string, ctx: RouteCtx): Promise<boolean>;
|
|
4
|
+
//# sourceMappingURL=attachments.d.ts.map
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { uuidv7, sanitizePathComponent } from "@sema-agent/core";
|
|
3
|
+
import { HttpError } from "../../security.js";
|
|
4
|
+
import { sendJson, sendError } from "../send.js";
|
|
5
|
+
import { gatedPrincipal } from "../principal-gate.js";
|
|
6
|
+
export async function handleAttachments(req, res, url, ctx) {
|
|
7
|
+
const miss = { fell: false };
|
|
8
|
+
await handleAttachmentsBody(req, res, url, ctx, miss);
|
|
9
|
+
return !miss.fell;
|
|
10
|
+
}
|
|
11
|
+
async function handleAttachmentsBody(req, res, url, ctx, miss) {
|
|
12
|
+
const { deps } = ctx;
|
|
13
|
+
const { rateLimited, quotaExceeded, safeDecode, readRawBody } = ctx.helpers;
|
|
14
|
+
{
|
|
15
|
+
const am = /^\/v1\/attachments(?:\/([^/?]+))?$/.exec(url);
|
|
16
|
+
if (am) {
|
|
17
|
+
if (rateLimited(req, res) || quotaExceeded(req, res))
|
|
18
|
+
return;
|
|
19
|
+
const store = deps.taskAttachmentStore;
|
|
20
|
+
if (!store) {
|
|
21
|
+
sendJson(res, 501, { error: "attachment store not configured" });
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const principal = gatedPrincipal(req, deps.config);
|
|
25
|
+
if (deps.config.requirePrincipal && principal === undefined) {
|
|
26
|
+
sendJson(res, 401, { error: `missing principal header '${deps.config.principalHeader}'` });
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const owner = principal ?? "default";
|
|
30
|
+
const attId = am[1] !== undefined ? safeDecode(am[1]) : undefined;
|
|
31
|
+
if (attId === undefined && req.method === "POST") {
|
|
32
|
+
const q = new URL(req.url ?? "", "http://x").searchParams;
|
|
33
|
+
const rawName = q.get("name");
|
|
34
|
+
const base = ((rawName ?? "").split(/[/\\]/).filter(Boolean).pop() ?? "").slice(0, 128);
|
|
35
|
+
if (!rawName || base === "" || base === "." || base === ".." || !/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(base)) {
|
|
36
|
+
sendJson(res, 400, { error: "query param `name` is required and must reduce to a safe basename ([A-Za-z0-9._-], not starting with a dot)" });
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const name = sanitizePathComponent(base);
|
|
40
|
+
const mime = String(req.headers["content-type"] ?? "").split(";")[0].trim().toLowerCase();
|
|
41
|
+
if (!/^[a-z0-9!#$&^_.+-]+\/[a-z0-9!#$&^_.+-]+$/.test(mime) || mime.length > 128) {
|
|
42
|
+
sendJson(res, 400, { error: "content-type must be a well-formed mime type (type/subtype)" });
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
const allow = deps.config.attachmentMimeAllowlist;
|
|
46
|
+
if (allow && !allow.some((a) => a === mime || (a.endsWith("/*") && mime.startsWith(a.slice(0, -1))))) {
|
|
47
|
+
sendError(res, 415, "attachment.mime_not_allowed", `mime '${mime}' is not in this deployment's allowlist`, { code: "attachment.mime_not_allowed", allowed: allow });
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const max = deps.config.attachmentMaxBytes ?? 32 * 1024 * 1024;
|
|
51
|
+
let body;
|
|
52
|
+
try {
|
|
53
|
+
body = await readRawBody(req, max);
|
|
54
|
+
}
|
|
55
|
+
catch (err) {
|
|
56
|
+
if (err instanceof HttpError && err.status === 413) {
|
|
57
|
+
sendError(res, 413, "attachment.too_large", `attachment exceeds the per-file limit (${max} bytes)`, { code: "attachment.too_large", maxBytes: max });
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
throw err;
|
|
61
|
+
}
|
|
62
|
+
const rec = {
|
|
63
|
+
id: uuidv7(),
|
|
64
|
+
owner,
|
|
65
|
+
name,
|
|
66
|
+
mime,
|
|
67
|
+
sha256: createHash("sha256").update(body).digest("hex"),
|
|
68
|
+
sizeBytes: body.length,
|
|
69
|
+
createdAtMs: Date.now(),
|
|
70
|
+
};
|
|
71
|
+
await store.put({ ...rec, content: new Uint8Array(body) });
|
|
72
|
+
deps.metrics?.inc("attachment_uploads_total");
|
|
73
|
+
sendJson(res, 201, { id: rec.id, name: rec.name, mime: rec.mime, sha256: rec.sha256, sizeBytes: rec.sizeBytes });
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (attId !== undefined && attId !== null && req.method === "GET") {
|
|
77
|
+
const meta = await store.get(owner, attId);
|
|
78
|
+
const bytes = meta ? await store.getContent(owner, attId) : null;
|
|
79
|
+
if (!meta || !bytes) {
|
|
80
|
+
sendJson(res, 404, { error: "not found" });
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
res.writeHead(200, {
|
|
84
|
+
"content-type": meta.mime,
|
|
85
|
+
"content-length": String(bytes.byteLength),
|
|
86
|
+
"content-disposition": `attachment; filename*=UTF-8''${encodeURIComponent(meta.name)}`,
|
|
87
|
+
});
|
|
88
|
+
res.end(Buffer.from(bytes));
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
if (attId !== undefined && attId !== null && req.method === "DELETE") {
|
|
92
|
+
const ok = await store.delete(owner, attId);
|
|
93
|
+
if (!ok) {
|
|
94
|
+
sendJson(res, 404, { error: "not found" });
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
res.writeHead(204).end();
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
sendJson(res, 404, { error: "not found" });
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
miss.fell = true;
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=attachments.js.map
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
2
|
+
import type { RouteCtx } from "../route-ctx.js";
|
|
3
|
+
export declare function handleCapabilities(req: IncomingMessage, res: ServerResponse, url: string, ctx: RouteCtx): Promise<boolean>;
|
|
4
|
+
//# sourceMappingURL=capabilities.d.ts.map
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { DEFAULT_EFFORT_LEVELS } from "@sema-agent/core";
|
|
2
|
+
import { cwdHonored } from "../../task-cwd.js";
|
|
3
|
+
import { mcpInjectionHonored } from "../../task-mcp.js";
|
|
4
|
+
import { sendJson, sendError } from "../send.js";
|
|
5
|
+
export async function handleCapabilities(req, res, url, ctx) {
|
|
6
|
+
const miss = { fell: false };
|
|
7
|
+
await handleCapabilitiesBody(req, res, url, ctx, miss);
|
|
8
|
+
return !miss.fell;
|
|
9
|
+
}
|
|
10
|
+
async function handleCapabilitiesBody(req, res, url, ctx, miss) {
|
|
11
|
+
const { deps } = ctx;
|
|
12
|
+
if (req.method === "GET" && url.startsWith("/v1/capabilities/scenarios/")) {
|
|
13
|
+
const name = url.slice("/v1/capabilities/scenarios/".length).split("?")[0];
|
|
14
|
+
const detail = deps.scenarioDetails?.[name];
|
|
15
|
+
if (!detail) {
|
|
16
|
+
sendError(res, 404, "scenario_not_found", "scenario not found");
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
sendJson(res, 200, detail);
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
if (req.method === "GET" && url === "/v1/capabilities") {
|
|
23
|
+
sendJson(res, 200, {
|
|
24
|
+
...deps.capabilities,
|
|
25
|
+
asyncRuns: Boolean(deps.runStore),
|
|
26
|
+
artifacts: Boolean(deps.runStore),
|
|
27
|
+
approvals: Boolean(deps.checkpointStore),
|
|
28
|
+
sessions: Boolean(deps.sessionAudit),
|
|
29
|
+
sessionEvents: Boolean(deps.sessionWatch && deps.sessionStorage?.getLeafId && deps.sessionStorage?.ownerOf),
|
|
30
|
+
fleet: deps.fleetBus
|
|
31
|
+
? { stream: true, sessionScope: true, observe: true, resume: "snapshot", bgNotifyFailClosed: true, steerPriority: "advisory" }
|
|
32
|
+
: false,
|
|
33
|
+
workspace: (() => {
|
|
34
|
+
const wfs = deps.fileSnapshotStore;
|
|
35
|
+
return wfs && typeof wfs.listKeys === "function" && typeof wfs.exportManifest === "function" && typeof wfs.getBlob === "function" && Boolean(deps.sessionStorage?.ownerOf)
|
|
36
|
+
? { browse: true, archive: true, maxFileBytes: deps.config.workspaceFileMaxBytes ?? 8 * 1024 * 1024 }
|
|
37
|
+
: false;
|
|
38
|
+
})(),
|
|
39
|
+
leader: Boolean(deps.leaderEndpoint),
|
|
40
|
+
memory: false,
|
|
41
|
+
memoryWrite: false,
|
|
42
|
+
policy: true,
|
|
43
|
+
usage: Boolean(deps.costQuota),
|
|
44
|
+
permissionModeWrite: false,
|
|
45
|
+
modelSelection: true,
|
|
46
|
+
effortSelection: true,
|
|
47
|
+
toolOutput: true,
|
|
48
|
+
messageIdentity: true,
|
|
49
|
+
forwardSubagentEvents: true,
|
|
50
|
+
scratchpadDir: true,
|
|
51
|
+
subagentSteer: Boolean(deps.subagentSteerRegistry) && Boolean(deps.runStore),
|
|
52
|
+
subagentResume: Boolean(deps.subagentSteerRegistry) && Boolean(deps.runStore),
|
|
53
|
+
taskSettings: { permissions: true, permissionMode: true, model: true, outputStyle: true, env: false, hooks: deps.config.requirePrincipal !== true },
|
|
54
|
+
appendSystemPrompt: true,
|
|
55
|
+
compactionModel: true,
|
|
56
|
+
subagentOutput: Boolean(deps.subagentTaskOutput) && Boolean(deps.runStore),
|
|
57
|
+
subagentStream: Boolean(deps.subagentTaskOutput) && Boolean(deps.runStore),
|
|
58
|
+
taskHandles: Boolean(deps.taskHandleOutput) && Boolean(deps.taskHandleStop) && Boolean(deps.runStore),
|
|
59
|
+
taskAgents: deps.config.requirePrincipal !== true,
|
|
60
|
+
retainBackgroundProcesses: deps.config.requirePrincipal !== true,
|
|
61
|
+
interactiveTools: true,
|
|
62
|
+
projectContext: cwdHonored(deps.config),
|
|
63
|
+
mcpInjection: mcpInjectionHonored(deps.config),
|
|
64
|
+
modelUsage: Boolean(deps.runStore && deps.modelUsage),
|
|
65
|
+
sessionInit: true,
|
|
66
|
+
mcp: true,
|
|
67
|
+
mcpElicitation: Boolean(deps.elicitation),
|
|
68
|
+
askUserQuestion: Boolean(deps.question),
|
|
69
|
+
toolApproval: Boolean(deps.toolApproval),
|
|
70
|
+
sideQuery: true,
|
|
71
|
+
sessionList: Boolean(deps.sessionStorage?.listSessions ?? deps.runStore?.listSessions),
|
|
72
|
+
sessionSearch: Boolean(deps.sessionStorage?.listSessions ?? deps.runStore?.listSessions),
|
|
73
|
+
sessionFork: Boolean(deps.sessionStorage?.fork),
|
|
74
|
+
sessionDelete: Boolean(deps.purgeSession),
|
|
75
|
+
resumeAt: Boolean(deps.resumeAnchorStore && deps.sessionStorage?.getLeafId),
|
|
76
|
+
promptSuggestions: true,
|
|
77
|
+
sessionPolicy: Boolean(deps.sessionPolicyStore),
|
|
78
|
+
rewindFiles: Boolean(deps.fileSnapshotStore),
|
|
79
|
+
rewindFilesTo: Boolean(deps.fileSnapshotStore && deps.resumeAnchorStore && deps.sessionStorage?.getLeafId),
|
|
80
|
+
scheduler: Boolean(deps.config.schedulerEnabled && deps.config.requirePrincipal !== true && deps.config.remoteExec?.provider === "host"),
|
|
81
|
+
sessionSync: Boolean(deps.backend && deps.sessionStorage?.exportEntries && deps.fileSnapshotStore),
|
|
82
|
+
sendUserFile: Boolean(deps.sendUserFile && deps.config.sendUserFile?.publicEndpoint),
|
|
83
|
+
s3PublicEndpoint: deps.config.sendUserFile?.publicEndpoint ?? null,
|
|
84
|
+
sendUserFileLedger: Boolean(deps.sendFileLedger),
|
|
85
|
+
workflows: deps.workflowsCapable ?? Boolean(deps.workflowRunStore),
|
|
86
|
+
workflowsList: Boolean(deps.workflowRunStore),
|
|
87
|
+
manualCompact: Boolean(deps.runStore),
|
|
88
|
+
});
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
if (req.method === "GET" && url === "/v1/models") {
|
|
92
|
+
const models = Object.entries(deps.config.models)
|
|
93
|
+
.filter(([name]) => name !== "default")
|
|
94
|
+
.map(([name, m]) => ({
|
|
95
|
+
name,
|
|
96
|
+
id: m.id,
|
|
97
|
+
provider: m.provider,
|
|
98
|
+
reasoning: m.reasoning,
|
|
99
|
+
vision: Array.isArray(m.input) ? m.input.includes("image") : false,
|
|
100
|
+
...(m.contextWindow ? { contextWindow: m.contextWindow } : {}),
|
|
101
|
+
...(m.maxTokens ? { maxOutputTokens: m.maxTokens } : {}),
|
|
102
|
+
...(m.reasoning ? { supportedEffortLevels: [...DEFAULT_EFFORT_LEVELS] } : {}),
|
|
103
|
+
}));
|
|
104
|
+
const nonAlias = Object.entries(deps.config.models).filter(([n]) => n !== "default");
|
|
105
|
+
const defaultName = (nonAlias.find(([, m]) => m === deps.config.model) ?? nonAlias.find(([, m]) => m.id === deps.config.model.id))?.[0];
|
|
106
|
+
sendJson(res, 200, { models, default: deps.config.model.id, defaultModel: { id: deps.config.model.id, ...(defaultName ? { name: defaultName } : {}) } });
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
miss.fell = true;
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=capabilities.js.map
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
2
|
+
import { RateLimiter } from "../../observability/rate-limit.js";
|
|
3
|
+
import type { BakeState, BakeErrorCode } from "../../plugins/store-contracts.js";
|
|
4
|
+
import type { FlatServiceDeps } from "../server.js";
|
|
5
|
+
import type { RouteCtx } from "../route-ctx.js";
|
|
6
|
+
export interface ImagesLocal {
|
|
7
|
+
bakeSubmitLimiter: RateLimiter;
|
|
8
|
+
}
|
|
9
|
+
export declare function createImagesLocal(deps: FlatServiceDeps): ImagesLocal;
|
|
10
|
+
export declare function coarseStatusForState(state: BakeState): "running" | "done" | "failed";
|
|
11
|
+
export declare function errorCodeForExit(exitCode: number | null | undefined, cancelled: boolean): BakeErrorCode;
|
|
12
|
+
export declare const IMAGE_DIGEST_RE: RegExp;
|
|
13
|
+
export declare const IMAGE_PROFILE_RE: RegExp;
|
|
14
|
+
export declare const BAKE_ID_RE: RegExp;
|
|
15
|
+
export declare const BAKE_EVENTS_RE: RegExp;
|
|
16
|
+
export declare const BAKE_CANCEL_RE: RegExp;
|
|
17
|
+
export declare const BAKE_CLAIM_RE: RegExp;
|
|
18
|
+
export declare const BAKE_INGEST_RE: RegExp;
|
|
19
|
+
export declare function handleImages(req: IncomingMessage, res: ServerResponse, url: string, ctx: RouteCtx): Promise<boolean>;
|
|
20
|
+
//# sourceMappingURL=images.d.ts.map
|