@desplega.ai/agent-swarm 1.129.0 → 1.131.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/README.md +2 -2
- package/dist/{actions-kfv2dawe.js → actions-w53mbns9.js} +11 -11
- package/dist/{anthropic-messages-3xgme8my.js → anthropic-messages-rjmd2e7n.js} +5 -5
- package/dist/{app-q8vjk6e8.js → app-m9gwqsqv.js} +8 -8
- package/dist/{artifact-hg4t1nx7.js → artifact-jysj3wrg.js} +2 -2
- package/dist/{assistant-g037r345.js → assistant-bp65m6f3.js} +13 -13
- package/dist/{azure-openai-responses-xa4sh9f6.js → azure-openai-responses-c0yjmbfd.js} +8 -5
- package/dist/{boot-reembed-18pzsrxa.js → boot-reembed-62p0at2k.js} +6 -6
- package/dist/{boot-reembed-wpks2cj8.js → boot-reembed-xmfp6wdh.js} +7 -7
- package/dist/{boot-scrub-logs-n7szd57r.js → boot-scrub-logs-rr8q2dms.js} +5 -5
- package/dist/{claude-adapter-qz4r0ajt.js → claude-adapter-2y47mrkd.js} +9 -9
- package/dist/{cli-mhjnbgb6.js → cli-0rcyq6d9.js} +450 -186
- package/dist/{cli-ttkmehy0.js → cli-2q72qz2s.js} +274 -25
- package/dist/{cli-gztgc3hz.js → cli-2wadx7p2.js} +2 -2
- package/dist/cli-3fj60etf.js +153 -0
- package/dist/{cli-7ecgm5v0.js → cli-3kf51j15.js} +1 -1
- package/dist/{cli-vgexpfsr.js → cli-57khth04.js} +6 -3
- package/dist/{cli-2mbe7vk0.js → cli-5dsbcjjw.js} +1 -1
- package/dist/{cli-etr62zy6.js → cli-5kx36evn.js} +2 -2
- package/dist/{cli-v2tr6amf.js → cli-5n6e5yfn.js} +30 -8
- package/dist/{cli-hx02s9zq.js → cli-5v4rw6yd.js} +2 -2
- package/dist/{cli-v37yr6mf.js → cli-5ze9rv97.js} +4 -4
- package/dist/{cli-ch7t5v7n.js → cli-6map995v.js} +9 -4
- package/dist/{cli-h0457a67.js → cli-70xqj0z3.js} +3 -3
- package/dist/{cli-kk4f9r17.js → cli-72q8m8gk.js} +8 -6
- package/dist/{cli-7padk69a.js → cli-78xwvkhj.js} +2 -2
- package/dist/{cli-dxtxacy7.js → cli-9dr5qzc0.js} +45 -8
- package/dist/{cli-17zxdgsq.js → cli-a40rnf71.js} +6 -6
- package/dist/{cli-t53vnnxz.js → cli-a4jzdpa3.js} +2514 -1055
- package/dist/{cli-w10fkrf1.js → cli-akdvwc9v.js} +354 -115
- package/dist/{cli-1qkk5er0.js → cli-bmze986d.js} +1 -1
- package/dist/{cli-ga12dc1d.js → cli-bqw0mt1n.js} +3 -3
- package/dist/{cli-0sf92efp.js → cli-cdeg08wx.js} +1 -1
- package/dist/{cli-a8ynhkyj.js → cli-cvgjwnh1.js} +17 -15
- package/dist/{cli-9fxg3v5m.js → cli-d2tfkcga.js} +4 -4
- package/dist/{cli-nhcd7x43.js → cli-d3x3x1c9.js} +2 -2
- package/dist/{cli-6nh2qxvh.js → cli-darcwax3.js} +1 -1
- package/dist/{cli-yjbgmysx.js → cli-e7ckcg4v.js} +12 -12
- package/dist/{cli-4n2gcjpd.js → cli-efxfnq6q.js} +4 -3
- package/dist/{cli-z9xqqhfk.js → cli-er3a4bb0.js} +1 -1
- package/dist/{cli-xg2eezyv.js → cli-f1am9szv.js} +1 -1
- package/dist/{cli-xy4cwsm9.js → cli-fywms69x.js} +1 -1
- package/dist/{cli-33jj76y8.js → cli-g6b8x1h8.js} +1 -1
- package/dist/{cli-epsvdckx.js → cli-gm9cbhbq.js} +20 -10
- package/dist/{cli-rkndnn90.js → cli-k9qkf1hw.js} +6 -4
- package/dist/{cli-zejvt395.js → cli-kh3ba2zf.js} +1 -1
- package/dist/{cli-gb25p1h2.js → cli-nm5gtp1x.js} +2 -2
- package/dist/{cli-akxnpx17.js → cli-pm6xbh84.js} +1829 -1629
- package/dist/{cli-2k0ztbfp.js → cli-psh3c4va.js} +1 -1
- package/dist/{cli-39vgrsep.js → cli-qv61hp37.js} +2 -2
- package/dist/{cli-hv2fndhm.js → cli-r6kqqbj0.js} +6 -4
- package/dist/{cli-1y1cm570.js → cli-rzbpmxdr.js} +1 -1
- package/dist/{cli-mmrk00vp.js → cli-t1xze5qh.js} +1 -1
- package/dist/{cli-g252a9ky.js → cli-t55yw3a1.js} +2 -2
- package/dist/{cli-7bz8dzqa.js → cli-t76vw5db.js} +1184 -1522
- package/dist/{cli-4n4mdv21.js → cli-xpxde7rm.js} +7 -3
- package/dist/{cli-fpaa5k2q.js → cli-yw8vghx2.js} +54 -18
- package/dist/cli-zsspvvm2.js +490 -0
- package/dist/cli.js +27 -25
- package/dist/{codex-adapter-7v6t7ff6.js → codex-adapter-d24q75wk.js} +7 -7
- package/dist/{codex-hook-amndawyb.js → codex-hook-hq54t9ft.js} +2 -2
- package/dist/{codex-session-runner-e7j4khe2.js → codex-session-runner-g50vcsex.js} +7 -7
- package/dist/{commands-ex0k5tbf.js → commands-c3nrc5hg.js} +5 -5
- package/dist/{db-df7jatfc.js → db-nt7fg3tw.js} +5 -5
- package/dist/{google-generative-ai-y42hnw9p.js → google-generative-ai-hysrd8qx.js} +7 -5
- package/dist/{google-vertex-4sg66gb6.js → google-vertex-p5arrga0.js} +7 -5
- package/dist/{handlers-1j2cfqt2.js → handlers-fkr6swat.js} +13 -13
- package/dist/{hook-nx9a0mj3.js → hook-77wpvnjc.js} +7 -7
- package/dist/{hook-w6yaf6sy.js → hook-g019yehy.js} +8 -8
- package/dist/{http-4tz5h8wg.js → http-jewj0syk.js} +2993 -716
- package/dist/{index-0bpkzb09.js → index-070v4eyb.js} +3 -3
- package/dist/{index-0s67qv7h.js → index-6gm5he8h.js} +11 -11
- package/dist/{index-35gtv6gj.js → index-7n2xpbd4.js} +310 -40
- package/dist/{index-ker3gccb.js → index-9znhar3x.js} +4 -4
- package/dist/{index-5n2c8yay.js → index-hase6zbe.js} +2 -2
- package/dist/{index-kcf3tm9h.js → index-hfvggd0z.js} +12 -12
- package/dist/{index-7c0jqna0.js → index-hp1kdbjj.js} +13 -13
- package/dist/{keepalive-3j4a9168.js → keepalive-j0fjvqfd.js} +7 -7
- package/dist/{lead-6kpevymb.js → lead-1n8x30xe.js} +33 -33
- package/dist/{maintenance-m1k82ean.js → maintenance-gvev11sa.js} +7 -7
- package/dist/{mistral-conversations-bjkdfb6z.js → mistral-conversations-81fwav0j.js} +6 -6
- package/dist/{oauth-refresh-sweep-881k8e4q.js → oauth-refresh-sweep-s9ecsvh0.js} +6 -6
- package/dist/{onboard-c0ykffjn.js → onboard-pvm5b38w.js} +2 -2
- package/dist/{openai-codex-responses-xetseqvt.js → openai-codex-responses-5g27pd6f.js} +43 -23
- package/dist/{openai-completions-5r2g5axa.js → openai-completions-3vkmfz2a.js} +55 -11
- package/dist/{openai-responses-ce67hd23.js → openai-responses-dg4t7j7a.js} +8 -5
- package/dist/{openrouter-images-pzfp22jb.js → openrouter-images-g3gm8931.js} +1 -1
- package/dist/{otel-impl-sqagjerv.js → otel-impl-p6jvgd9c.js} +6 -4
- package/dist/{otel-3mp81ppd.js → otel-nqa12wf3.js} +2 -2
- package/dist/{pi-mono-adapter-k584k16g.js → pi-mono-adapter-t091s27g.js} +17117 -7517
- package/dist/{pricing-refresh-y82whbf9.js → pricing-refresh-k49rcyed.js} +7 -7
- package/dist/{rbac-roles-nvkqmzyd.js → rbac-roles-30pspvy7.js} +7 -7
- package/dist/{rbac-roles-4ht8gegc.js → rbac-roles-cr80g96t.js} +6 -6
- package/dist/{render-v2-wvqdgvvp.js → render-v2-xm457ng1.js} +8 -8
- package/dist/{seed-pricing-fy3rxhde.js → seed-pricing-yhpv35fr.js} +6 -6
- package/dist/{setup-ak5cnc6f.js → setup-nytnmwwr.js} +2 -2
- package/dist/{worker-d8769q8a.js → worker-msytr2cw.js} +33 -33
- package/openapi.json +34039 -11036
- package/package.json +8 -6
- package/src/apps/definition.ts +368 -26
- package/src/apps/row-store.ts +66 -10
- package/src/apps/schema-migrate.ts +95 -1
- package/src/apps/store.ts +13 -4
- package/src/apps/sync-run-as.ts +18 -0
- package/src/apps/sync.ts +934 -0
- package/src/apps/version.ts +16 -2
- package/src/assets/key.ts +8 -1
- package/src/be/asset-key-audit.ts +3 -1
- package/src/be/db-queries/mcp-oauth.ts +297 -24
- package/src/be/db.ts +42 -1
- package/src/be/migrations/129_asset_keys_apps_scripts.sql +161 -0
- package/src/be/scripts/db.ts +5 -2
- package/src/be/scripts/typecheck.ts +1 -0
- package/src/be/seed-scripts/catalog/app-sync-run.ts +32 -0
- package/src/be/seed-scripts/catalog/github-issues-pull.ts +102 -0
- package/src/be/seed-scripts/index.ts +18 -0
- package/src/be/seed-skills/index.ts +7 -0
- package/src/http/active-sessions.ts +26 -18
- package/src/http/agents.ts +94 -27
- package/src/http/api-keys.ts +91 -17
- package/src/http/approval-requests.ts +87 -10
- package/src/http/apps.ts +361 -53
- package/src/http/assets.ts +165 -31
- package/src/http/budgets.ts +7 -5
- package/src/http/codex-oauth-keep-warm.ts +21 -3
- package/src/http/config.ts +46 -16
- package/src/http/context.ts +33 -6
- package/src/http/ecosystem.ts +20 -6
- package/src/http/events.ts +24 -9
- package/src/http/favorites.ts +20 -6
- package/src/http/fs.ts +60 -17
- package/src/http/heartbeat.ts +18 -4
- package/src/http/inbox-state.ts +12 -6
- package/src/http/integrations.ts +25 -5
- package/src/http/kv.ts +36 -15
- package/src/http/mcp-bridge.ts +5 -1
- package/src/http/mcp-oauth.ts +332 -17
- package/src/http/mcp-servers.ts +56 -18
- package/src/http/memory.ts +282 -29
- package/src/http/metrics.ts +80 -17
- package/src/http/oauth-callback.ts +10 -4
- package/src/http/oauth-generic.ts +5 -1
- package/src/http/oauth-locks.ts +6 -3
- package/src/http/openapi.ts +57 -9
- package/src/http/page-proxy.ts +25 -5
- package/src/http/pages-public.ts +37 -20
- package/src/http/pages.ts +93 -28
- package/src/http/poll.ts +89 -5
- package/src/http/pricing.ts +9 -6
- package/src/http/prompt-templates.ts +85 -21
- package/src/http/route-def.ts +67 -4
- package/src/http/schedules.ts +63 -14
- package/src/http/script-connection-proxy.ts +30 -4
- package/src/http/script-connections.ts +393 -43
- package/src/http/script-runs.ts +73 -19
- package/src/http/scripts.ts +257 -39
- package/src/http/session-data.ts +87 -14
- package/src/http/sessions.ts +80 -5
- package/src/http/skills.ts +115 -35
- package/src/http/stats.ts +94 -13
- package/src/http/task-templates.ts +6 -4
- package/src/http/tasks.ts +230 -61
- package/src/http/trackers/jira.ts +90 -30
- package/src/http/trackers/linear.ts +46 -12
- package/src/http/users.ts +162 -25
- package/src/http/utils.ts +12 -2
- package/src/http/webhooks.ts +30 -16
- package/src/http/workflow-events.ts +18 -5
- package/src/http/workflows.ts +128 -39
- package/src/http/x.ts +37 -6
- package/src/oauth/ensure-mcp-token.ts +16 -1
- package/src/oauth/mcp-wrapper.ts +281 -26
- package/src/providers/pi-mono-adapter.ts +1 -1
- package/src/scripts-runtime/credential-broker/README.md +1 -1
- package/src/scripts-runtime/credential-broker/fetch-patch.ts +12 -2
- package/src/scripts-runtime/sdk-allowlist.ts +1 -0
- package/src/scripts-runtime/stdlib/fetch.ts +6 -0
- package/src/scripts-runtime/types/stdlib.d.ts +1 -0
- package/src/scripts-runtime/types/swarm-sdk.d.ts +1 -0
- package/src/server.ts +2 -0
- package/src/tests/apps-rbac.test.ts +110 -0
- package/src/tests/apps-spike2.test.ts +4 -1
- package/src/tests/apps-sync-engine.test.ts +2540 -0
- package/src/tests/apps-sync.test.ts +1627 -0
- package/src/tests/asset-key-api.test.ts +48 -2
- package/src/tests/asset-key-invariants.test.ts +92 -0
- package/src/tests/asset-key-migration.test.ts +174 -8
- package/src/tests/asset-key-normalization.test.ts +2 -0
- package/src/tests/credential-broker.test.ts +27 -2
- package/src/tests/mcp-oauth-dcr-reuse.test.ts +743 -0
- package/src/tests/mcp-oauth-ensure-token.test.ts +45 -0
- package/src/tests/mcp-oauth-manual-client.test.ts +135 -2
- package/src/tests/mcp-oauth-queries.test.ts +153 -0
- package/src/tests/mcp-oauth-wrapper.test.ts +432 -0
- package/src/tests/memory-http-recall-gating.test.ts +2 -0
- package/src/tests/openapi-response-contract.test.ts +138 -0
- package/src/tests/rbac-admission-e2e.test.ts +68 -0
- package/src/tests/script-connections.test.ts +3 -0
- package/src/tests/scripts-runtime-fetch.test.ts +121 -0
- package/src/tests/scripts-runtime-secret-egress.test.ts +4 -2
- package/src/tests/seed-scripts.test.ts +2 -2
- package/src/tests/swarm-tool-result-gate.test.ts +32 -0
- package/src/tests/task-working-dir.test.ts +8 -4
- package/src/tests/whoami-route.test.ts +127 -0
- package/src/tests/workflow-engine-v2.test.ts +80 -7
- package/src/tests/workflow-swarm-script.test.ts +104 -0
- package/src/tools/app-get.ts +10 -2
- package/src/tools/app-rollback.ts +1 -0
- package/src/tools/app-sync.ts +112 -0
- package/src/tools/script-common.ts +20 -0
- package/src/tools/tool-config.ts +2 -1
- package/src/tools/utils.ts +11 -0
- package/src/tools/workflows/create-workflow.ts +4 -1
- package/src/types.ts +1394 -1206
- package/src/utils/template.ts +8 -1
- package/src/utils/zod-openapi.ts +11 -0
- package/src/workflows/engine.ts +27 -4
- package/src/workflows/executors/swarm-script.ts +28 -0
- package/src/workflows/retry-poller.ts +19 -2
- package/templates/skills/apps/content.md +94 -5
- package/templates/skills/asset-namespaces/content.md +2 -2
- package/templates/skills/scheduled-task-resilience/SKILL.md +102 -0
- package/templates/skills/scheduled-task-resilience/config.json +14 -0
- package/templates/skills/scheduled-task-resilience/content.md +97 -0
- package/dist/cli-7wmybrt5.js +0 -115
- package/dist/cli-d91pvmq1.js +0 -14
package/src/http/route-def.ts
CHANGED
|
@@ -10,12 +10,26 @@ type HttpMethod = "get" | "post" | "put" | "delete" | "patch";
|
|
|
10
10
|
interface RouteResponseDef {
|
|
11
11
|
description: string;
|
|
12
12
|
schema?: z.ZodType;
|
|
13
|
+
/**
|
|
14
|
+
* Reason this response intentionally carries no JSON schema — SSE stream,
|
|
15
|
+
* binary download, HTML, redirect, proxied upstream body, intentionally
|
|
16
|
+
* dynamic shape, etc. Mutually exclusive with `schema`. Every 2xx response
|
|
17
|
+
* (except bodiless 204/205/304) must declare one of the two — enforced by
|
|
18
|
+
* `bun run check:openapi-response-coverage` (CI).
|
|
19
|
+
*/
|
|
20
|
+
unstructured?: string;
|
|
13
21
|
}
|
|
14
22
|
|
|
23
|
+
type ResponsesDef = Record<number, RouteResponseDef>;
|
|
24
|
+
|
|
25
|
+
/** Extracts the payload type respond() accepts for one response def. */
|
|
26
|
+
type ResponseData<R> = R extends { schema: infer S extends z.ZodType } ? z.input<S> : never;
|
|
27
|
+
|
|
15
28
|
export interface RouteDef<
|
|
16
29
|
TParams extends z.ZodType = z.ZodType,
|
|
17
30
|
TQuery extends z.ZodType = z.ZodType,
|
|
18
31
|
TBody extends z.ZodType = z.ZodType,
|
|
32
|
+
TResponses extends ResponsesDef = ResponsesDef,
|
|
19
33
|
> {
|
|
20
34
|
method: HttpMethod;
|
|
21
35
|
path: string; // OpenAPI-style: "/api/tasks/{id}"
|
|
@@ -28,7 +42,7 @@ export interface RouteDef<
|
|
|
28
42
|
params?: TParams;
|
|
29
43
|
query?: TQuery;
|
|
30
44
|
body?: TBody;
|
|
31
|
-
responses:
|
|
45
|
+
responses: TResponses;
|
|
32
46
|
auth?: {
|
|
33
47
|
apiKey?: boolean; // default true
|
|
34
48
|
agentId?: boolean; // requires X-Agent-ID
|
|
@@ -49,7 +63,7 @@ interface ParsedRequest<TParams, TQuery, TBody> {
|
|
|
49
63
|
body: TBody;
|
|
50
64
|
}
|
|
51
65
|
|
|
52
|
-
interface RouteHandle<TParams, TQuery, TBody> {
|
|
66
|
+
interface RouteHandle<TParams, TQuery, TBody, TResponses extends ResponsesDef = ResponsesDef> {
|
|
53
67
|
/** Check if this route matches the request */
|
|
54
68
|
match(method: string | undefined, pathSegments: string[]): boolean;
|
|
55
69
|
|
|
@@ -61,6 +75,24 @@ interface RouteHandle<TParams, TQuery, TBody> {
|
|
|
61
75
|
queryParams: URLSearchParams,
|
|
62
76
|
): Promise<ParsedRequest<TParams, TQuery, TBody> | null>;
|
|
63
77
|
|
|
78
|
+
/**
|
|
79
|
+
* Typed JSON egress: send `data` as the response body for `code`, with `data`
|
|
80
|
+
* compile-time checked against the `schema` declared for that code — the
|
|
81
|
+
* declared OpenAPI shape and the wire shape cannot drift silently. Only
|
|
82
|
+
* callable for codes that declare a `schema`; keep `jsonError` for error
|
|
83
|
+
* paths and raw `res` for bodiless/unstructured responses.
|
|
84
|
+
*
|
|
85
|
+
* Outside production the payload is additionally runtime-validated against
|
|
86
|
+
* the schema (throws → 500 + test failure), catching mismatches the type
|
|
87
|
+
* system can't see through `any`-typed DB rows. Always sends the raw `data`
|
|
88
|
+
* object, never the parsed output — validation must not change the wire.
|
|
89
|
+
*/
|
|
90
|
+
respond<C extends keyof TResponses & number>(
|
|
91
|
+
res: ServerResponse,
|
|
92
|
+
code: C,
|
|
93
|
+
data: ResponseData<TResponses[C]>,
|
|
94
|
+
): void;
|
|
95
|
+
|
|
64
96
|
/** The raw definition (for OpenAPI generation) */
|
|
65
97
|
def: RouteDef;
|
|
66
98
|
}
|
|
@@ -154,18 +186,49 @@ export function describeRequestRoute(
|
|
|
154
186
|
|
|
155
187
|
// ─── Factory ─────────────────────────────────────────────────────────────────
|
|
156
188
|
|
|
189
|
+
/**
|
|
190
|
+
* Runtime response validation gate for `respond()`. NODE_ENV is deliberately
|
|
191
|
+
* unset on prod deploys, so this is opt-in outside `bun test` (which sets
|
|
192
|
+
* NODE_ENV=test): response schemas must never introduce a production failure
|
|
193
|
+
* mode — they fail loudly in tests and (opt-in) local dev instead.
|
|
194
|
+
*/
|
|
195
|
+
const VALIDATE_RESPONSES =
|
|
196
|
+
process.env.NODE_ENV === "test" ||
|
|
197
|
+
process.env.NODE_ENV === "development" ||
|
|
198
|
+
process.env.VALIDATE_HTTP_RESPONSES === "true";
|
|
199
|
+
|
|
157
200
|
export function route<
|
|
158
201
|
TParams extends z.ZodType = z.ZodUndefined,
|
|
159
202
|
TQuery extends z.ZodType = z.ZodUndefined,
|
|
160
203
|
TBody extends z.ZodType = z.ZodUndefined,
|
|
204
|
+
TResponses extends ResponsesDef = ResponsesDef,
|
|
161
205
|
>(
|
|
162
|
-
def: RouteDef<TParams, TQuery, TBody>,
|
|
163
|
-
): RouteHandle<z.infer<TParams>, z.infer<TQuery>, z.infer<TBody
|
|
206
|
+
def: RouteDef<TParams, TQuery, TBody, TResponses>,
|
|
207
|
+
): RouteHandle<z.infer<TParams>, z.infer<TQuery>, z.infer<TBody>, TResponses> {
|
|
164
208
|
routeRegistry.push(def as RouteDef);
|
|
165
209
|
|
|
166
210
|
return {
|
|
167
211
|
def: def as RouteDef,
|
|
168
212
|
|
|
213
|
+
respond(res, code, data) {
|
|
214
|
+
if (VALIDATE_RESPONSES) {
|
|
215
|
+
const schema = (def.responses as ResponsesDef)[code]?.schema;
|
|
216
|
+
if (schema) {
|
|
217
|
+
const result = schema.safeParse(data);
|
|
218
|
+
if (!result.success) {
|
|
219
|
+
throw new Error(
|
|
220
|
+
`Response schema violation: ${def.method.toUpperCase()} ${def.path} ${code} — ` +
|
|
221
|
+
result.error.issues
|
|
222
|
+
.map((i) => `${i.path.join(".") || "<root>"}: ${i.message}`)
|
|
223
|
+
.join("; "),
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
res.writeHead(code, { "Content-Type": "application/json" });
|
|
229
|
+
res.end(JSON.stringify(data));
|
|
230
|
+
},
|
|
231
|
+
|
|
169
232
|
match(method, pathSegments) {
|
|
170
233
|
return matchRoute(
|
|
171
234
|
method,
|
package/src/http/schedules.ts
CHANGED
|
@@ -18,14 +18,16 @@ import { mergeScheduleTiming, validateRecurringTiming } from "../be/schedules/va
|
|
|
18
18
|
import { getScript } from "../be/scripts/db";
|
|
19
19
|
import { calculateNextRun, dispatchScheduleTarget } from "../scheduler/scheduler";
|
|
20
20
|
import {
|
|
21
|
+
AgentTaskSchema,
|
|
21
22
|
AssetKeySchema,
|
|
22
23
|
ModelTierSchema,
|
|
24
|
+
ScheduledTaskSchema,
|
|
23
25
|
ScheduledTaskTargetTypeSchema,
|
|
24
26
|
splitLegacyModelAlias,
|
|
25
27
|
} from "../types";
|
|
26
28
|
import { resolveHttpFavoriteOwner } from "./favorite-owner";
|
|
27
29
|
import { route } from "./route-def";
|
|
28
|
-
import {
|
|
30
|
+
import { jsonError } from "./utils";
|
|
29
31
|
|
|
30
32
|
// ─── Route Definitions ───────────────────────────────────────────────────────
|
|
31
33
|
|
|
@@ -51,6 +53,34 @@ const scheduleUpdateBodySchema = z.object({
|
|
|
51
53
|
scriptArgs: z.record(z.string(), z.unknown()).nullable().optional(),
|
|
52
54
|
});
|
|
53
55
|
|
|
56
|
+
// `ScheduledTaskSchema` carries `.refine()` checks, so Zod v4 forbids the
|
|
57
|
+
// plain `.extend()` / `.omit()` chain-builders here (they throw at call
|
|
58
|
+
// time: "Object schemas containing refinements cannot be extended/omitted.
|
|
59
|
+
// Use `.safeExtend()` instead."). Use `.safeExtend()` for additive shapes,
|
|
60
|
+
// and build the taskTemplate-dropping summary shape from `.shape` directly.
|
|
61
|
+
|
|
62
|
+
/** Full schedule row decorated with the caller's favorite flag (full-mode `listSchedules`). */
|
|
63
|
+
const scheduleWithFavoriteSchema = ScheduledTaskSchema.safeExtend({ favorite: z.boolean() });
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* `getSchedule` response: `favorite` is optional because the handler falls
|
|
67
|
+
* back to the undecorated row (`decorated ?? schedule`) if `withFavoriteFlags`
|
|
68
|
+
* ever returned an empty array — structurally unreachable for a 1-row input,
|
|
69
|
+
* but the schema stays honest about what the code could send.
|
|
70
|
+
*/
|
|
71
|
+
const scheduleWithOptionalFavoriteSchema = ScheduledTaskSchema.safeExtend({
|
|
72
|
+
favorite: z.boolean().optional(),
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
/** Slim list row — `taskTemplate` swapped for a bounded preview (see `ScheduledTaskSummary`). */
|
|
76
|
+
const { taskTemplate: _omittedTaskTemplate, ...scheduleShapeWithoutTemplate } =
|
|
77
|
+
ScheduledTaskSchema.shape;
|
|
78
|
+
const scheduleSummaryWithFavoriteSchema = z.object({
|
|
79
|
+
...scheduleShapeWithoutTemplate,
|
|
80
|
+
taskTemplatePreview: z.string(),
|
|
81
|
+
favorite: z.boolean(),
|
|
82
|
+
});
|
|
83
|
+
|
|
54
84
|
const createSchedule = route({
|
|
55
85
|
method: "post",
|
|
56
86
|
path: "/api/schedules",
|
|
@@ -81,7 +111,7 @@ const createSchedule = route({
|
|
|
81
111
|
runAt: z.string().optional(),
|
|
82
112
|
}),
|
|
83
113
|
responses: {
|
|
84
|
-
201: { description: "Schedule created" },
|
|
114
|
+
201: { description: "Schedule created", schema: ScheduledTaskSchema },
|
|
85
115
|
400: { description: "Validation error" },
|
|
86
116
|
409: { description: "Duplicate name" },
|
|
87
117
|
},
|
|
@@ -95,7 +125,14 @@ const runScheduleNow = route({
|
|
|
95
125
|
tags: ["Schedules"],
|
|
96
126
|
params: z.object({ id: z.string() }),
|
|
97
127
|
responses: {
|
|
98
|
-
200: {
|
|
128
|
+
200: {
|
|
129
|
+
description: "Schedule run triggered",
|
|
130
|
+
schema: z.object({
|
|
131
|
+
schedule: ScheduledTaskSchema.nullable(),
|
|
132
|
+
workflowRunIds: z.array(z.string()).optional(),
|
|
133
|
+
task: AgentTaskSchema.optional(),
|
|
134
|
+
}),
|
|
135
|
+
},
|
|
99
136
|
400: { description: "Schedule is disabled" },
|
|
100
137
|
404: { description: "Schedule not found" },
|
|
101
138
|
},
|
|
@@ -131,7 +168,16 @@ const listSchedules = route({
|
|
|
131
168
|
keyPrefix: AssetKeySchema.optional(),
|
|
132
169
|
}),
|
|
133
170
|
responses: {
|
|
134
|
-
200: {
|
|
171
|
+
200: {
|
|
172
|
+
description: "List of schedules",
|
|
173
|
+
schema: z.object({
|
|
174
|
+
schedules: z.union([
|
|
175
|
+
z.array(scheduleSummaryWithFavoriteSchema),
|
|
176
|
+
z.array(scheduleWithFavoriteSchema),
|
|
177
|
+
]),
|
|
178
|
+
count: z.number().int(),
|
|
179
|
+
}),
|
|
180
|
+
},
|
|
135
181
|
},
|
|
136
182
|
});
|
|
137
183
|
|
|
@@ -143,7 +189,7 @@ const getSchedule = route({
|
|
|
143
189
|
tags: ["Schedules"],
|
|
144
190
|
params: z.object({ id: z.string() }),
|
|
145
191
|
responses: {
|
|
146
|
-
200: { description: "Schedule details" },
|
|
192
|
+
200: { description: "Schedule details", schema: scheduleWithOptionalFavoriteSchema },
|
|
147
193
|
404: { description: "Schedule not found" },
|
|
148
194
|
},
|
|
149
195
|
});
|
|
@@ -157,7 +203,7 @@ const updateSchedule = route({
|
|
|
157
203
|
params: z.object({ id: z.string() }),
|
|
158
204
|
body: scheduleUpdateBodySchema,
|
|
159
205
|
responses: {
|
|
160
|
-
200: { description: "Schedule updated" },
|
|
206
|
+
200: { description: "Schedule updated", schema: ScheduledTaskSchema.nullable() },
|
|
161
207
|
400: { description: "Validation error" },
|
|
162
208
|
404: { description: "Schedule not found" },
|
|
163
209
|
409: { description: "Duplicate name" },
|
|
@@ -175,7 +221,7 @@ const patchSchedule = route({
|
|
|
175
221
|
params: z.object({ id: z.string() }),
|
|
176
222
|
body: scheduleUpdateBodySchema,
|
|
177
223
|
responses: {
|
|
178
|
-
200: { description: "Schedule patched" },
|
|
224
|
+
200: { description: "Schedule patched", schema: ScheduledTaskSchema.nullable() },
|
|
179
225
|
400: { description: "Validation error" },
|
|
180
226
|
404: { description: "Schedule not found" },
|
|
181
227
|
409: { description: "Duplicate name" },
|
|
@@ -194,7 +240,7 @@ const deleteSchedule = route({
|
|
|
194
240
|
tags: ["Schedules"],
|
|
195
241
|
params: z.object({ id: z.string() }),
|
|
196
242
|
responses: {
|
|
197
|
-
200: { description: "Schedule deleted" },
|
|
243
|
+
200: { description: "Schedule deleted", schema: z.object({ success: z.literal(true) }) },
|
|
198
244
|
404: { description: "Schedule not found" },
|
|
199
245
|
},
|
|
200
246
|
});
|
|
@@ -234,7 +280,10 @@ export async function handleSchedules(
|
|
|
234
280
|
favoriteScope,
|
|
235
281
|
itemType: "schedule",
|
|
236
282
|
});
|
|
237
|
-
|
|
283
|
+
listSchedules.respond(res, 200, {
|
|
284
|
+
schedules: decoratedSchedules,
|
|
285
|
+
count: decoratedSchedules.length,
|
|
286
|
+
});
|
|
238
287
|
return true;
|
|
239
288
|
}
|
|
240
289
|
|
|
@@ -379,7 +428,7 @@ export async function handleSchedules(
|
|
|
379
428
|
createdBy: resolveHttpAuditUserId(req, myAgentId) ?? undefined,
|
|
380
429
|
});
|
|
381
430
|
|
|
382
|
-
|
|
431
|
+
createSchedule.respond(res, 201, schedule);
|
|
383
432
|
} catch (_error) {
|
|
384
433
|
jsonError(res, "Failed to create schedule", 500);
|
|
385
434
|
}
|
|
@@ -420,7 +469,7 @@ export async function handleSchedules(
|
|
|
420
469
|
}
|
|
421
470
|
|
|
422
471
|
const updatedSchedule = getScheduledTaskById(parsed.params.id);
|
|
423
|
-
|
|
472
|
+
runScheduleNow.respond(res, 200, { schedule: updatedSchedule, workflowRunIds, task });
|
|
424
473
|
} catch (error) {
|
|
425
474
|
jsonError(res, error instanceof Error ? error.message : "Failed to run schedule", 500);
|
|
426
475
|
}
|
|
@@ -439,7 +488,7 @@ export async function handleSchedules(
|
|
|
439
488
|
|
|
440
489
|
const favoriteScope = resolveHttpFavoriteOwner(req, myAgentId)?.scope;
|
|
441
490
|
const [decorated] = withFavoriteFlags([schedule], { favoriteScope, itemType: "schedule" });
|
|
442
|
-
|
|
491
|
+
getSchedule.respond(res, 200, decorated ?? schedule);
|
|
443
492
|
return true;
|
|
444
493
|
}
|
|
445
494
|
|
|
@@ -601,7 +650,7 @@ export async function handleSchedules(
|
|
|
601
650
|
const updatedBy = resolveHttpAuditUserId(req, myAgentId);
|
|
602
651
|
if (updatedBy !== null) body.updatedBy = updatedBy;
|
|
603
652
|
const schedule = updateScheduledTask(parsed.params.id, body);
|
|
604
|
-
|
|
653
|
+
routeHandle.respond(res, 200, schedule);
|
|
605
654
|
return true;
|
|
606
655
|
}
|
|
607
656
|
|
|
@@ -615,7 +664,7 @@ export async function handleSchedules(
|
|
|
615
664
|
return true;
|
|
616
665
|
}
|
|
617
666
|
|
|
618
|
-
|
|
667
|
+
deleteSchedule.respond(res, 200, { success: true });
|
|
619
668
|
return true;
|
|
620
669
|
}
|
|
621
670
|
|
|
@@ -5,7 +5,30 @@ import { callMcpServerTool } from "@/be/mcp-proxy";
|
|
|
5
5
|
import { getScriptConnectionById } from "@/be/script-connections";
|
|
6
6
|
import { can } from "@/rbac";
|
|
7
7
|
import { route } from "./route-def";
|
|
8
|
-
import {
|
|
8
|
+
import { jsonError } from "./utils";
|
|
9
|
+
|
|
10
|
+
// Mirrors `McpToolCallResult` from `src/mcp-client/http-client.ts`.
|
|
11
|
+
const McpToolCallResultSchema = z.object({
|
|
12
|
+
content: z.array(z.object({ type: z.string(), text: z.string().optional() })),
|
|
13
|
+
isError: z.boolean().optional(),
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
// Mirrors `McpJsonRpcError` from `src/mcp-client/http-client.ts`.
|
|
17
|
+
const McpJsonRpcErrorSchema = z.object({
|
|
18
|
+
code: z.number().optional(),
|
|
19
|
+
message: z.string().optional(),
|
|
20
|
+
data: z.unknown().optional(),
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
// Mirrors `McpToolCallEnvelope` from `src/mcp-client/http-client.ts` — the
|
|
24
|
+
// success/failure envelope returned by `callMcpServerTool`, plus the
|
|
25
|
+
// `{ ok: false, error: string }` shape the catch block below constructs
|
|
26
|
+
// directly (a subtype of the `ok: false` branch, since `error` accepts a
|
|
27
|
+
// plain string there too).
|
|
28
|
+
const McpToolCallEnvelopeSchema = z.union([
|
|
29
|
+
z.object({ ok: z.literal(true), result: McpToolCallResultSchema }),
|
|
30
|
+
z.object({ ok: z.literal(false), error: z.union([McpJsonRpcErrorSchema, z.string()]) }),
|
|
31
|
+
]);
|
|
9
32
|
|
|
10
33
|
const mcpCallRoute = route({
|
|
11
34
|
method: "post",
|
|
@@ -21,7 +44,7 @@ const mcpCallRoute = route({
|
|
|
21
44
|
arguments: z.record(z.string(), z.unknown()).optional(),
|
|
22
45
|
}),
|
|
23
46
|
responses: {
|
|
24
|
-
200: { description: "MCP call result" },
|
|
47
|
+
200: { description: "MCP call result", schema: McpToolCallEnvelopeSchema },
|
|
25
48
|
400: { description: "Invalid MCP connection or request" },
|
|
26
49
|
403: { description: "Not allowed to invoke this MCP connection" },
|
|
27
50
|
404: { description: "Script connection or agent not found" },
|
|
@@ -108,9 +131,12 @@ export async function handleScriptConnectionProxy(
|
|
|
108
131
|
parsed.body.arguments ?? {},
|
|
109
132
|
{ agentId, timeoutMs: 30_000 },
|
|
110
133
|
);
|
|
111
|
-
|
|
134
|
+
mcpCallRoute.respond(res, 200, result);
|
|
112
135
|
} catch (err) {
|
|
113
|
-
|
|
136
|
+
mcpCallRoute.respond(res, 200, {
|
|
137
|
+
ok: false,
|
|
138
|
+
error: err instanceof Error ? err.message : String(err),
|
|
139
|
+
});
|
|
114
140
|
}
|
|
115
141
|
return true;
|
|
116
142
|
}
|