@desplega.ai/agent-swarm 2.6.0 → 2.7.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 -0
- package/dist/{acp-adapter-dgfakfpw.js → acp-adapter-ma1js5n7.js} +6 -6
- package/dist/{actions-fbqma8aa.js → actions-1vhmtvg9.js} +18 -18
- package/dist/{amp-adapter-en0jy6hs.js → amp-adapter-sxqqvmdk.js} +3 -3
- package/dist/{app-k8a5cgpp.js → app-mwx1d5a9.js} +3 -3
- package/dist/{assistant-a16433gr.js → assistant-yx1p6g82.js} +20 -20
- package/dist/{boot-reembed-f4nn561n.js → boot-reembed-5h37a2c2.js} +1 -1
- package/dist/{boot-reembed-6c1sjfnr.js → boot-reembed-7h78vmvv.js} +2 -1
- package/dist/{boot-scrub-sweep-d4w9gajf.js → boot-scrub-sweep-8mawd4gw.js} +3 -3
- package/dist/{boot-seeds-9bmkspyw.js → boot-seeds-ca84dhnn.js} +4 -4
- package/dist/{claude-adapter-nys1kjxa.js → claude-adapter-wqt4hfkq.js} +7 -7
- package/dist/{claude-managed-adapter-55207973.js → claude-managed-adapter-6fjqtr2z.js} +2 -2
- package/dist/{claude-managed-setup-3cm35gey.js → claude-managed-setup-rqv98sea.js} +2 -2
- package/dist/{claude-sdk-session-nw5yd8cp.js → claude-sdk-session-f29f3b2x.js} +7 -7
- package/dist/{cli-sxgtf5fw.js → cli-07q03xm2.js} +1 -1
- package/dist/{cli-k21gx5ht.js → cli-1hhbzkbb.js} +6 -6
- package/dist/{cli-v49fkghp.js → cli-1m9x8dr5.js} +1 -1
- package/dist/{cli-24j1jeeb.js → cli-2pvzmas1.js} +3 -3
- package/dist/{cli-w2656ka1.js → cli-2y7v133v.js} +7 -7
- package/dist/{cli-g8ka5j4j.js → cli-48gxjmcz.js} +94 -36
- package/dist/{cli-2sjhpsns.js → cli-54sj64fc.js} +1 -1
- package/dist/{cli-aj8djyqt.js → cli-60sty27p.js} +2 -2
- package/dist/{cli-4hxrf0p4.js → cli-6a9qfa8b.js} +308 -47
- package/dist/{cli-n7asmce6.js → cli-6mgdba42.js} +3 -3
- package/dist/{cli-gf95q3bm.js → cli-6srv653c.js} +3 -3
- package/dist/{cli-9abdgn5t.js → cli-86ehn4s5.js} +3 -3
- package/dist/{cli-bqztfyve.js → cli-8mbc9za9.js} +1 -1
- package/dist/{cli-a2rr0fmg.js → cli-8wmj18cz.js} +1 -1
- package/dist/{cli-c2habvsf.js → cli-9gsm2rzp.js} +8 -8
- package/dist/{cli-4rf0f5vg.js → cli-9pshx6rd.js} +4 -2
- package/dist/{cli-abkm71vr.js → cli-a6ds2epe.js} +1 -1
- package/dist/{cli-esakngh8.js → cli-a89zdqz9.js} +1 -1
- package/dist/{cli-3ndmpxxv.js → cli-ay40dnj3.js} +2 -2
- package/dist/{cli-kf4jree9.js → cli-aypd09nf.js} +1 -1
- package/dist/{cli-6y51ryva.js → cli-b3yzyc57.js} +1 -1
- package/dist/{cli-ag2yrnt5.js → cli-c668vvcf.js} +2 -2
- package/dist/{cli-5sckpqcx.js → cli-d20qvpr2.js} +1 -1
- package/dist/{cli-a34pr3w1.js → cli-e1595bgx.js} +2 -2
- package/dist/{cli-m70bajzr.js → cli-f12mr20z.js} +1 -1
- package/dist/{cli-dqygeeh5.js → cli-f1e0ptjj.js} +10 -1
- package/dist/{cli-mvjz5an3.js → cli-f4dbh7a5.js} +7 -7
- package/dist/{cli-az25fn5f.js → cli-gsfk58nw.js} +2 -2
- package/dist/{cli-1tem6ns9.js → cli-gt4h8cxw.js} +3 -2
- package/dist/{cli-cz37jqss.js → cli-h5jeg46p.js} +2 -2
- package/dist/{cli-emhjcj2y.js → cli-he9gbdx0.js} +60 -10
- package/dist/{cli-3fwqy71j.js → cli-j0ppywdr.js} +38 -31
- package/dist/{cli-5rj83bam.js → cli-j3y0x2n4.js} +34 -33
- package/dist/{cli-3gfvmzht.js → cli-j9bpmykh.js} +73 -25
- package/dist/{cli-m5a9m0ak.js → cli-kv7sbc2c.js} +3 -3
- package/dist/{cli-g20jjwt4.js → cli-m30k9777.js} +12 -12
- package/dist/{cli-683y0b3a.js → cli-m5e802r7.js} +2 -2
- package/dist/{cli-nn6vx2mq.js → cli-ptfq202x.js} +5 -3
- package/dist/{cli-d2dpf95n.js → cli-qj442fws.js} +1 -1
- package/dist/{cli-03gg9pd1.js → cli-rd5m70gj.js} +4 -4
- package/dist/{cli-gnsb1zxa.js → cli-s09j9vp5.js} +1 -1
- package/dist/{cli-zqne1qcj.js → cli-s0mgx214.js} +2 -2
- package/dist/{cli-sqma6vw1.js → cli-sgqm2582.js} +5 -5
- package/dist/{cli-nq677csy.js → cli-sjctjr48.js} +1 -1
- package/dist/{cli-3aqd4jaa.js → cli-twctnkng.js} +3 -3
- package/dist/{cli-j6rktc91.js → cli-v6shg7na.js} +1 -1
- package/dist/{cli-7enjwya9.js → cli-vz78s5wm.js} +1 -1
- package/dist/{cli-art7c1zh.js → cli-wqr7cea4.js} +4 -4
- package/dist/{cli-hfeb68z4.js → cli-x37r7qwf.js} +11 -3
- package/dist/{cli-ffvsj2qw.js → cli-yd2nreqw.js} +2 -2
- package/dist/{cli-ca6banwz.js → cli-ymjpkxgn.js} +6 -6
- package/dist/{cli-mtnj4fwa.js → cli-yxbsmx56.js} +5 -5
- package/dist/cli.js +18 -16
- package/dist/{codex-adapter-d95q4qsn.js → codex-adapter-zk59x2zh.js} +5 -5
- package/dist/{codex-hook-brpnycn5.js → codex-hook-xyjhvxwd.js} +1 -1
- package/dist/{codex-session-runner-gdwd8h0c.js → codex-session-runner-kh6s4j3n.js} +5 -5
- package/dist/{commands-jqmgk54b.js → commands-68et1qv2.js} +2 -2
- package/dist/{controller-z3jq589f.js → controller-fyrbnzg4.js} +2 -2
- package/dist/{controllers-connect-r2za39ef.js → controllers-connect-nep9h2xv.js} +19 -19
- package/dist/{cursor-adapter-4mr4jgkw.js → cursor-adapter-yss3pg7t.js} +1 -1
- package/dist/{db-w36wwb5g.js → db-28gjyvxj.js} +2 -2
- package/dist/{db-ppv2tze7.js → db-8ftwvzba.js} +3 -3
- package/dist/{dispatcher-pv5h77vf.js → dispatcher-hayexmkr.js} +17 -17
- package/dist/{dsh-adapter-d4pdpb3r.js → dsh-adapter-46wawff9.js} +1 -1
- package/dist/{easter-egg-3d-4sp4dvmc.js → easter-egg-3d-8d1e0h4m.js} +1 -1
- package/dist/{execute-jev839y1.js → execute-cjrfxfnm.js} +1 -1
- package/dist/{external-channel-y6nkzhnn.js → external-channel-xbmxcg09.js} +2 -2
- package/dist/{grok-adapter-t08b9ntm.js → grok-adapter-ag4wks2a.js} +7 -7
- package/dist/handlers-tzmevqb5.js +41 -0
- package/dist/{heartbeat-vn31xwdk.js → heartbeat-psfvcgwp.js} +20 -20
- package/dist/{hook-6d0zbvvt.js → hook-8pz04697.js} +5 -5
- package/dist/{hook-cpjs8q51.js → hook-cyq4mryn.js} +6 -6
- package/dist/{http-9c6medda.js → http-haj1nd6k.js} +109 -114
- package/dist/{index-qhjf2htm.js → index-jwt2bj6f.js} +261 -96
- package/dist/{index-g6k9tctj.js → index-q52f47pe.js} +1 -1
- package/dist/{index-pkma1yf8.js → index-syvhnvtw.js} +1 -1
- package/dist/{index-yqgbfgaj.js → index-y1ajx04a.js} +20 -20
- package/dist/{index-k0yt1tvq.js → index-z5h22pes.js} +17 -17
- package/dist/{index-38t35h0n.js → index-zyq8ct52.js} +17 -17
- package/dist/{keepalive-em0117bp.js → keepalive-xdvsamvj.js} +3 -3
- package/dist/{lead-23e7z59t.js → lead-hqg57kdp.js} +35 -35
- package/dist/{maintenance-kbme3qy9.js → maintenance-4y2xzh3a.js} +1 -1
- package/dist/{oauth-refresh-sweep-xysj4yse.js → oauth-refresh-sweep-jd4r7zbs.js} +8 -3
- package/dist/{onboard-p6q0vz97.js → onboard-18447q9q.js} +5 -5
- package/dist/{opencode-adapter-esv3k9yn.js → opencode-adapter-26b2frdf.js} +5 -5
- package/dist/{otel-impl-54qhqh1z.js → otel-impl-f9tgr8yw.js} +1 -1
- package/dist/{pi-mono-adapter-mymbwhj0.js → pi-mono-adapter-qn6qbga1.js} +33 -12
- package/dist/{pricing-refresh-yb75wv33.js → pricing-refresh-dw2tgb53.js} +3 -3
- package/dist/{rbac-roles-1vp2mw4p.js → rbac-roles-aygm2760.js} +2 -2
- package/dist/{rbac-roles-dexhc9x8.js → rbac-roles-s4nwj2m3.js} +1 -1
- package/dist/{render-v2-tjay5hhf.js → render-v2-yfvdfr0x.js} +3 -3
- package/dist/{resume-8ayf37g5.js → resume-1bmvjbf3.js} +7 -7
- package/dist/{runtime-hqe5cqcd.js → runtime-f7tpzedy.js} +1 -1
- package/dist/{session-access-fdhp987s.js → session-access-r5garej6.js} +4 -4
- package/dist/{session-source-backfill-qgtc86z1.js → session-source-backfill-424wzv0r.js} +2 -2
- package/dist/{setup-4rrmqcq3.js → setup-8tygvxkq.js} +1 -1
- package/dist/{virtual-modules-52km00d6.js → virtual-modules-xsw323b3.js} +2 -2
- package/dist/{work-objects-p28fgd70.js → work-objects-ggnghpgf.js} +20 -20
- package/dist/{worker-e3w7vye1.js → worker-sqjd1nqf.js} +35 -35
- package/openapi.json +69 -1
- package/package.json +5 -3
- package/src/be/automation-preflight.ts +2 -0
- package/src/be/db/api-keys.ts +9 -0
- package/src/be/db/workflow-runs.ts +36 -5
- package/src/be/db.ts +80 -22
- package/src/be/memory/task-learning.ts +155 -0
- package/src/be/migrations/245_script_executor.sql +3 -0
- package/src/be/oauth-refresh-sweep.ts +2 -1
- package/src/be/scripts/boot-reembed.ts +2 -0
- package/src/be/scripts/db.ts +14 -4
- package/src/be/scripts/embeddings.ts +2 -0
- package/src/be/scripts/run-saved.ts +1 -0
- package/src/be/scripts/typecheck.ts +9 -3
- package/src/be/seed-skills/index.ts +7 -0
- package/src/commands/onboard/compose-generator.ts +5 -3
- package/src/commands/provider-credentials.ts +2 -1
- package/src/heartbeat/heartbeat.ts +14 -1
- package/src/heartbeat/script-runner-probe.ts +47 -0
- package/src/http/api-keys.ts +24 -9
- package/src/http/idempotency-key.ts +1 -1
- package/src/http/index.ts +3 -3
- package/src/http/pages-public.ts +38 -5
- package/src/http/scripts.ts +27 -0
- package/src/http/workflows.ts +10 -0
- package/src/http/x.ts +5 -0
- package/src/prompts/base-prompt.ts +21 -1
- package/src/prompts/session-templates.ts +17 -3
- package/src/providers/amp-adapter.ts +1 -1
- package/src/providers/dsh-adapter.ts +1 -1
- package/src/providers/pi-mono-adapter.ts +41 -11
- package/src/scheduler/scheduler.ts +2 -1
- package/src/scripts-runtime/eval-harness.ts +8 -1
- package/src/scripts-runtime/executors/native.ts +22 -16
- package/src/scripts-runtime/executors/quickjs-prelude.ts +103 -1
- package/src/scripts-runtime/executors/quickjs-runner.ts +2 -1
- package/src/scripts-runtime/executors/quickjs.ts +7 -0
- package/src/scripts-runtime/executors/registry.ts +32 -0
- package/src/scripts-runtime/executors/types.ts +7 -0
- package/src/scripts-runtime/host-validated-args.ts +133 -0
- package/src/scripts-runtime/loader.ts +30 -3
- package/src/scripts-runtime/swarm-sdk.ts +9 -2
- package/src/scripts-runtime/types/stdlib.d.ts +9 -1
- package/src/scripts-runtime/types/swarm-sdk.d.ts +9 -1
- package/src/tests/api-key-status-by-suffix.test.ts +242 -0
- package/src/tests/base-prompt.test.ts +70 -2
- package/src/tests/community-linear-delivery-loop.test.ts +2099 -0
- package/src/tests/credential-check.test.ts +4 -2
- package/src/tests/docs-index.test.ts +135 -0
- package/src/tests/dsh-adapter.test.ts +1 -1
- package/src/tests/fetch-resolved-env.test.ts +16 -2
- package/src/tests/heartbeat-checklist.test.ts +89 -0
- package/src/tests/heartbeat-script-runner-probe.test.ts +83 -0
- package/src/tests/oauth-refresh-sweep.test.ts +34 -1
- package/src/tests/onboard-compose.test.ts +20 -0
- package/src/tests/opencode-adapter.test.ts +2 -1
- package/src/tests/pages-frame-ancestors.test.ts +162 -0
- package/src/tests/pi-codemode.test.ts +2 -1
- package/src/tests/pi-mono-adapter.test.ts +54 -0
- package/src/tests/pi-native-mcp.test.ts +13 -1
- package/src/tests/pi-tool-exposure.test.ts +8 -8
- package/src/tests/probe-tool-schema-tokens.test.ts +144 -0
- package/src/tests/prompt-template-session.test.ts +4 -3
- package/src/tests/request-composition-derive.test.ts +329 -0
- package/src/tests/script-connections.test.ts +2 -0
- package/src/tests/script-executor-conformance.test.ts +36 -1
- package/src/tests/script-executor-quickjs-pool.test.ts +50 -0
- package/src/tests/script-executor-quickjs.test.ts +140 -0
- package/src/tests/script-host-validated-args-native.test.ts +196 -0
- package/src/tests/script-per-script-executor.test.ts +415 -0
- package/src/tests/scripts-external-api.test.ts +45 -0
- package/src/tests/sdk-allowlist.test.ts +3 -2
- package/src/tests/self-improvement.test.ts +4 -4
- package/src/tests/store-progress-learning.test.ts +288 -0
- package/src/tests/ui-logs-parser.test.ts +180 -0
- package/src/tests/workflow-engine-v2.test.ts +116 -1
- package/src/tests/workflow-mcp-trigger-schema.test.ts +209 -0
- package/src/tests/workflow-run-finished-event.test.ts +263 -0
- package/src/tests/workflow-swarm-script.test.ts +34 -0
- package/src/tools/script-common.ts +1 -0
- package/src/tools/script-run.ts +7 -0
- package/src/tools/script-upsert.ts +7 -0
- package/src/tools/store-progress.ts +67 -1
- package/src/tools/workflows/trigger-workflow.ts +104 -10
- package/src/types.ts +12 -1
- package/src/utils/credentials.ts +5 -2
- package/src/workflows/engine.ts +18 -16
- package/src/workflows/executors/swarm-script.ts +5 -0
- package/src/workflows/foreach-driver.ts +5 -11
- package/src/workflows/node-timeout.ts +30 -0
- package/templates/community/linear-delivery-loop/PLAYBOOK.md +323 -0
- package/templates/community/linear-delivery-loop/README.md +38 -0
- package/templates/community/linear-delivery-loop/config.example.json +31 -0
- package/templates/community/linear-delivery-loop/lead-prompt.md +34 -0
- package/templates/community/linear-delivery-loop/schedule.json +10 -0
- package/templates/community/linear-delivery-loop/scripts/linear-delivery-setup.ts +148 -0
- package/templates/community/linear-delivery-loop/scripts/linear-delivery-tick.ts +1781 -0
- package/templates/skills/swarm-docs/config.json +14 -0
- package/templates/skills/swarm-docs/content.md +144 -0
- package/dist/handlers-eqsnmv95.js +0 -41
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
import { afterEach, beforeAll, describe, expect, test } from "bun:test";
|
|
2
|
+
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import { Readable } from "node:stream";
|
|
5
|
+
import { createAgent, getScriptRun, initDb, listScriptRuns } from "../be/db";
|
|
6
|
+
import { getScriptById, insertScript, upsertScriptByName } from "../be/scripts/db";
|
|
7
|
+
import { setScriptEmbeddingProviderForTests } from "../be/scripts/embeddings";
|
|
8
|
+
import { runSavedScriptAsAgent } from "../be/scripts/run-saved";
|
|
9
|
+
import { typecheckScript } from "../be/scripts/typecheck";
|
|
10
|
+
import { handleScripts } from "../http/scripts";
|
|
11
|
+
import {
|
|
12
|
+
isExecutorAllowedByDeployment,
|
|
13
|
+
resolveScriptExecutor,
|
|
14
|
+
} from "../scripts-runtime/executors/registry";
|
|
15
|
+
import { runScript } from "../scripts-runtime/loader";
|
|
16
|
+
import { type ScheduledTask, ScriptExecutorNameSchema, ScriptRecordSchema } from "../types";
|
|
17
|
+
import { SwarmScriptExecutor } from "../workflows/executors/swarm-script";
|
|
18
|
+
|
|
19
|
+
process.env.TMPDIR ??= os.tmpdir().replaceAll("\\", "/");
|
|
20
|
+
process.env.API_KEY ??= "test-per-script-executor-api-key";
|
|
21
|
+
process.env.MCP_BASE_URL ??= "http://127.0.0.1:19999";
|
|
22
|
+
|
|
23
|
+
const RUNTIME_PROBE_SOURCE =
|
|
24
|
+
'export default async () => ({ hasProcess: typeof (globalThis as { process?: unknown }).process !== "undefined" });';
|
|
25
|
+
|
|
26
|
+
const savedScriptExecutorEnv = process.env.SCRIPT_EXECUTOR;
|
|
27
|
+
|
|
28
|
+
afterEach(() => {
|
|
29
|
+
if (savedScriptExecutorEnv === undefined) {
|
|
30
|
+
delete process.env.SCRIPT_EXECUTOR;
|
|
31
|
+
} else {
|
|
32
|
+
process.env.SCRIPT_EXECUTOR = savedScriptExecutorEnv;
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
describe("per-script executor schema (#2013)", () => {
|
|
37
|
+
test("ScriptExecutorNameSchema accepts native and quickjs and rejects invalid names", () => {
|
|
38
|
+
expect(ScriptExecutorNameSchema.safeParse("native").success).toBe(true);
|
|
39
|
+
expect(ScriptExecutorNameSchema.safeParse("quickjs").success).toBe(true);
|
|
40
|
+
expect(ScriptExecutorNameSchema.safeParse("e2b").success).toBe(false);
|
|
41
|
+
expect(ScriptExecutorNameSchema.safeParse("").success).toBe(false);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test("ScriptRecordSchema defaults executor to null and accepts explicit executor", () => {
|
|
45
|
+
const base = {
|
|
46
|
+
id: "00000000-0000-0000-0000-000000000001",
|
|
47
|
+
name: "demo",
|
|
48
|
+
scope: "agent" as const,
|
|
49
|
+
scopeId: "00000000-0000-0000-0000-000000000002",
|
|
50
|
+
source: RUNTIME_PROBE_SOURCE,
|
|
51
|
+
description: "",
|
|
52
|
+
intent: "",
|
|
53
|
+
signatureJson: "{}",
|
|
54
|
+
argsJsonSchema: null,
|
|
55
|
+
contentHash: "abc",
|
|
56
|
+
version: 1,
|
|
57
|
+
isScratch: false,
|
|
58
|
+
typeChecked: true,
|
|
59
|
+
fsMode: "none" as const,
|
|
60
|
+
createdByAgentId: null,
|
|
61
|
+
createdAt: "2026-10-26T00:00:00.000Z",
|
|
62
|
+
updatedAt: "2026-10-26T00:00:00.000Z",
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
expect(ScriptRecordSchema.parse(base).executor).toBeNull();
|
|
66
|
+
expect(ScriptRecordSchema.parse({ ...base, executor: "quickjs" }).executor).toBe("quickjs");
|
|
67
|
+
expect(ScriptRecordSchema.parse({ ...base, executor: "native" }).executor).toBe("native");
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
describe("runScript per-script executor precedence (#2013)", () => {
|
|
72
|
+
test("explicit executor: 'quickjs' overrides SCRIPT_EXECUTOR=native", async () => {
|
|
73
|
+
process.env.SCRIPT_EXECUTOR = "native";
|
|
74
|
+
const out = await runScript({
|
|
75
|
+
source: RUNTIME_PROBE_SOURCE,
|
|
76
|
+
agentId: "00000000-0000-0000-0000-000000000001",
|
|
77
|
+
executor: "quickjs",
|
|
78
|
+
egressSecrets: [],
|
|
79
|
+
});
|
|
80
|
+
expect(out.exitCode).toBe(0);
|
|
81
|
+
expect(out.error).toBeUndefined();
|
|
82
|
+
expect(out.result).toEqual({ hasProcess: false });
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test("explicit executor: 'native' is denied when deployment SCRIPT_EXECUTOR=quickjs", async () => {
|
|
86
|
+
process.env.SCRIPT_EXECUTOR = "quickjs";
|
|
87
|
+
expect(isExecutorAllowedByDeployment("native")).toBe(false);
|
|
88
|
+
expect(isExecutorAllowedByDeployment("quickjs")).toBe(true);
|
|
89
|
+
expect(isExecutorAllowedByDeployment(null)).toBe(true);
|
|
90
|
+
expect(() => resolveScriptExecutor("native")).toThrow(
|
|
91
|
+
'Executor "native" is not permitted when deployment SCRIPT_EXECUTOR="quickjs"',
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
let canaryHits = 0;
|
|
95
|
+
const server = Bun.serve({
|
|
96
|
+
hostname: "127.0.0.1",
|
|
97
|
+
port: 0,
|
|
98
|
+
fetch() {
|
|
99
|
+
canaryHits += 1;
|
|
100
|
+
return new Response("reached");
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
try {
|
|
104
|
+
const out = await runScript({
|
|
105
|
+
source: `export default async () => { await fetch("http://127.0.0.1:${server.port}/canary"); return { ok: true }; };`,
|
|
106
|
+
agentId: "00000000-0000-0000-0000-000000000001",
|
|
107
|
+
executor: "native",
|
|
108
|
+
egressSecrets: [],
|
|
109
|
+
});
|
|
110
|
+
expect(out.exitCode).toBe(1);
|
|
111
|
+
expect(out.error).toBe("executor_error");
|
|
112
|
+
expect(out.stderr).toContain(
|
|
113
|
+
'Executor "native" is not permitted when deployment SCRIPT_EXECUTOR="quickjs"',
|
|
114
|
+
);
|
|
115
|
+
expect(canaryHits).toBe(0);
|
|
116
|
+
} finally {
|
|
117
|
+
server.stop(true);
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
test("null or omitted executor falls back to SCRIPT_EXECUTOR", async () => {
|
|
122
|
+
process.env.SCRIPT_EXECUTOR = "quickjs";
|
|
123
|
+
const quickjsDefault = await runScript({
|
|
124
|
+
source: RUNTIME_PROBE_SOURCE,
|
|
125
|
+
agentId: "00000000-0000-0000-0000-000000000001",
|
|
126
|
+
executor: null,
|
|
127
|
+
egressSecrets: [],
|
|
128
|
+
});
|
|
129
|
+
expect(quickjsDefault.exitCode).toBe(0);
|
|
130
|
+
expect(quickjsDefault.result).toEqual({ hasProcess: false });
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
test("null or omitted executor falls back to native when SCRIPT_EXECUTOR is unset", async () => {
|
|
134
|
+
delete process.env.SCRIPT_EXECUTOR;
|
|
135
|
+
const nativeDefault = await runScript({
|
|
136
|
+
source: RUNTIME_PROBE_SOURCE,
|
|
137
|
+
agentId: "00000000-0000-0000-0000-000000000001",
|
|
138
|
+
executor: null,
|
|
139
|
+
egressSecrets: [],
|
|
140
|
+
});
|
|
141
|
+
expect(nativeDefault.exitCode).toBe(0);
|
|
142
|
+
expect(nativeDefault.result).toEqual({ hasProcess: true });
|
|
143
|
+
}, 15_000);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
describe("scripts DB & runSavedScriptAsAgent executor persistence (#2013)", () => {
|
|
147
|
+
let agentId: string;
|
|
148
|
+
|
|
149
|
+
beforeAll(async () => {
|
|
150
|
+
initDb(":memory:");
|
|
151
|
+
setScriptEmbeddingProviderForTests({
|
|
152
|
+
name: "test/noop",
|
|
153
|
+
dimensions: 1,
|
|
154
|
+
async embed() {
|
|
155
|
+
return null;
|
|
156
|
+
},
|
|
157
|
+
async embedBatch(texts) {
|
|
158
|
+
return texts.map(() => null);
|
|
159
|
+
},
|
|
160
|
+
});
|
|
161
|
+
const agent = await createAgent({ name: "exec-select-worker", isLead: false, status: "idle" });
|
|
162
|
+
agentId = agent.id;
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
test("insertScript and upsertScriptByName persist executor and runSavedScriptAsAgent uses it", async () => {
|
|
166
|
+
const inserted = await insertScript({
|
|
167
|
+
name: `exec-probe-${crypto.randomUUID().slice(0, 8)}`,
|
|
168
|
+
scope: "agent",
|
|
169
|
+
scopeId: agentId,
|
|
170
|
+
source: RUNTIME_PROBE_SOURCE,
|
|
171
|
+
description: "probe",
|
|
172
|
+
intent: "test",
|
|
173
|
+
signatureJson: "{}",
|
|
174
|
+
executor: "quickjs",
|
|
175
|
+
agentId,
|
|
176
|
+
typeChecked: true,
|
|
177
|
+
});
|
|
178
|
+
expect(inserted.executor).toBe("quickjs");
|
|
179
|
+
expect((await getScriptById(inserted.id))?.executor).toBe("quickjs");
|
|
180
|
+
|
|
181
|
+
process.env.SCRIPT_EXECUTOR = "native";
|
|
182
|
+
const quickjsOut = await runSavedScriptAsAgent(
|
|
183
|
+
{ script: inserted, input: null, agentId },
|
|
184
|
+
{
|
|
185
|
+
buildCredentials: async () => ({
|
|
186
|
+
egressSecrets: [],
|
|
187
|
+
failedBindings: [],
|
|
188
|
+
userConfig: {},
|
|
189
|
+
}),
|
|
190
|
+
},
|
|
191
|
+
);
|
|
192
|
+
expect(quickjsOut.exitCode).toBe(0);
|
|
193
|
+
expect(quickjsOut.result).toEqual({ hasProcess: false });
|
|
194
|
+
|
|
195
|
+
// Metadata-only upsert changing executor from "quickjs" to "native"
|
|
196
|
+
const updated = await upsertScriptByName({
|
|
197
|
+
name: inserted.name,
|
|
198
|
+
scope: "agent",
|
|
199
|
+
scopeId: agentId,
|
|
200
|
+
source: RUNTIME_PROBE_SOURCE,
|
|
201
|
+
description: "probe",
|
|
202
|
+
intent: "test",
|
|
203
|
+
signatureJson: "{}",
|
|
204
|
+
executor: "native",
|
|
205
|
+
agentId,
|
|
206
|
+
typeChecked: true,
|
|
207
|
+
});
|
|
208
|
+
expect(updated.contentDeduped).toBe(true);
|
|
209
|
+
expect(updated.script.executor).toBe("native");
|
|
210
|
+
|
|
211
|
+
process.env.SCRIPT_EXECUTOR = "native";
|
|
212
|
+
const nativeOut = await runSavedScriptAsAgent(
|
|
213
|
+
{ script: updated.script, input: null, agentId },
|
|
214
|
+
{
|
|
215
|
+
buildCredentials: async () => ({
|
|
216
|
+
egressSecrets: [],
|
|
217
|
+
failedBindings: [],
|
|
218
|
+
userConfig: {},
|
|
219
|
+
}),
|
|
220
|
+
},
|
|
221
|
+
);
|
|
222
|
+
expect(nativeOut.exitCode).toBe(0);
|
|
223
|
+
expect(nativeOut.result).toEqual({ hasProcess: true });
|
|
224
|
+
|
|
225
|
+
// Under operator ceiling SCRIPT_EXECUTOR=quickjs, saved native script is denied
|
|
226
|
+
process.env.SCRIPT_EXECUTOR = "quickjs";
|
|
227
|
+
const deniedOut = await runSavedScriptAsAgent(
|
|
228
|
+
{ script: updated.script, input: null, agentId },
|
|
229
|
+
{
|
|
230
|
+
buildCredentials: async () => ({
|
|
231
|
+
egressSecrets: [],
|
|
232
|
+
failedBindings: [],
|
|
233
|
+
userConfig: {},
|
|
234
|
+
}),
|
|
235
|
+
},
|
|
236
|
+
);
|
|
237
|
+
expect(deniedOut.exitCode).toBe(1);
|
|
238
|
+
expect(deniedOut.error).toBe("executor_error");
|
|
239
|
+
expect(deniedOut.stderr).toContain(
|
|
240
|
+
'Executor "native" is not permitted when deployment SCRIPT_EXECUTOR="quickjs"',
|
|
241
|
+
);
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
test("HTTP /api/scripts/upsert, /api/scripts/run, and GET /api/scripts enforce ceiling and expose executor", async () => {
|
|
245
|
+
async function invokeScriptsHttp(opts: {
|
|
246
|
+
method: string;
|
|
247
|
+
pathSegments: string[];
|
|
248
|
+
queryParams?: URLSearchParams;
|
|
249
|
+
body?: unknown;
|
|
250
|
+
agentId?: string;
|
|
251
|
+
}): Promise<{ status: number; body: unknown }> {
|
|
252
|
+
const payload = opts.body !== undefined ? JSON.stringify(opts.body) : "";
|
|
253
|
+
const req = Object.assign(Readable.from(payload ? [Buffer.from(payload)] : []), {
|
|
254
|
+
method: opts.method,
|
|
255
|
+
url: `/${opts.pathSegments.join("/")}`,
|
|
256
|
+
headers: {
|
|
257
|
+
"content-type": "application/json",
|
|
258
|
+
...(opts.agentId ? { "x-agent-id": opts.agentId } : {}),
|
|
259
|
+
},
|
|
260
|
+
}) as unknown as IncomingMessage;
|
|
261
|
+
|
|
262
|
+
let status = 200;
|
|
263
|
+
const chunks: Buffer[] = [];
|
|
264
|
+
const res = {
|
|
265
|
+
writeHead(code: number) {
|
|
266
|
+
status = code;
|
|
267
|
+
return this;
|
|
268
|
+
},
|
|
269
|
+
setHeader() {
|
|
270
|
+
return this;
|
|
271
|
+
},
|
|
272
|
+
end(chunk?: string | Buffer) {
|
|
273
|
+
if (chunk) chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
274
|
+
return this;
|
|
275
|
+
},
|
|
276
|
+
} as unknown as ServerResponse;
|
|
277
|
+
|
|
278
|
+
await handleScripts(
|
|
279
|
+
req,
|
|
280
|
+
res,
|
|
281
|
+
opts.pathSegments,
|
|
282
|
+
opts.queryParams ?? new URLSearchParams(),
|
|
283
|
+
opts.agentId,
|
|
284
|
+
);
|
|
285
|
+
const text = Buffer.concat(chunks).toString("utf8");
|
|
286
|
+
return { status, body: text ? JSON.parse(text) : null };
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
process.env.SCRIPT_EXECUTOR = "quickjs";
|
|
290
|
+
|
|
291
|
+
const upsertDenied = await invokeScriptsHttp({
|
|
292
|
+
method: "POST",
|
|
293
|
+
pathSegments: ["api", "scripts", "upsert"],
|
|
294
|
+
agentId,
|
|
295
|
+
body: {
|
|
296
|
+
name: "escape-attempt",
|
|
297
|
+
scope: "agent",
|
|
298
|
+
source: RUNTIME_PROBE_SOURCE,
|
|
299
|
+
executor: "native",
|
|
300
|
+
},
|
|
301
|
+
});
|
|
302
|
+
expect(upsertDenied.status).toBe(400);
|
|
303
|
+
expect((upsertDenied.body as { error?: string }).error).toContain(
|
|
304
|
+
'Executor "native" is not permitted when deployment SCRIPT_EXECUTOR="quickjs"',
|
|
305
|
+
);
|
|
306
|
+
|
|
307
|
+
const runDenied = await invokeScriptsHttp({
|
|
308
|
+
method: "POST",
|
|
309
|
+
pathSegments: ["api", "scripts", "run"],
|
|
310
|
+
agentId,
|
|
311
|
+
body: {
|
|
312
|
+
source: RUNTIME_PROBE_SOURCE,
|
|
313
|
+
executor: "native",
|
|
314
|
+
},
|
|
315
|
+
});
|
|
316
|
+
expect(runDenied.status).toBe(400);
|
|
317
|
+
expect((runDenied.body as { error?: string }).error).toContain(
|
|
318
|
+
'Executor "native" is not permitted when deployment SCRIPT_EXECUTOR="quickjs"',
|
|
319
|
+
);
|
|
320
|
+
|
|
321
|
+
const listRes = await invokeScriptsHttp({
|
|
322
|
+
method: "GET",
|
|
323
|
+
pathSegments: ["api", "scripts"],
|
|
324
|
+
queryParams: new URLSearchParams("scope=agent"),
|
|
325
|
+
});
|
|
326
|
+
expect(listRes.status).toBe(200);
|
|
327
|
+
const listed = (listRes.body as { scripts: Array<{ executor: string | null }> }).scripts;
|
|
328
|
+
expect(listed.length).toBeGreaterThan(0);
|
|
329
|
+
expect(listed.some((s) => s.executor === "native" || s.executor === "quickjs")).toBe(true);
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
test("workflow SwarmScriptExecutor and scheduler dispatchScheduleTarget forward script.executor", async () => {
|
|
333
|
+
process.env.SCRIPT_EXECUTOR = "native";
|
|
334
|
+
const globalQuickjs = await insertScript({
|
|
335
|
+
name: `global-qjs-${crypto.randomUUID().slice(0, 8)}`,
|
|
336
|
+
scope: "global",
|
|
337
|
+
scopeId: null,
|
|
338
|
+
source: RUNTIME_PROBE_SOURCE,
|
|
339
|
+
description: "global quickjs script",
|
|
340
|
+
intent: "test",
|
|
341
|
+
signatureJson: "{}",
|
|
342
|
+
executor: "quickjs",
|
|
343
|
+
agentId,
|
|
344
|
+
typeChecked: true,
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
const wfExecutor = new SwarmScriptExecutor({
|
|
348
|
+
db: {
|
|
349
|
+
getWorkflow: async () => null,
|
|
350
|
+
} as never,
|
|
351
|
+
eventBus: { emit: () => {} } as never,
|
|
352
|
+
});
|
|
353
|
+
const wfResult = await wfExecutor.run({
|
|
354
|
+
config: { scriptName: globalQuickjs.name, scope: "global", fsMode: "none" },
|
|
355
|
+
context: { agentId },
|
|
356
|
+
meta: {
|
|
357
|
+
runId: crypto.randomUUID(),
|
|
358
|
+
stepId: crypto.randomUUID(),
|
|
359
|
+
nodeId: "n1",
|
|
360
|
+
workflowId: crypto.randomUUID(),
|
|
361
|
+
},
|
|
362
|
+
});
|
|
363
|
+
expect(wfResult.status).toBe("success");
|
|
364
|
+
if (wfResult.status === "success") {
|
|
365
|
+
expect(wfResult.output.result).toEqual({ hasProcess: false });
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
const { executeScheduleScript } = await import("../scheduler/scheduler");
|
|
369
|
+
const schedule: ScheduledTask = {
|
|
370
|
+
id: crypto.randomUUID(),
|
|
371
|
+
name: `sched-${globalQuickjs.name}`,
|
|
372
|
+
taskTemplate: "",
|
|
373
|
+
targetType: "script",
|
|
374
|
+
scriptName: globalQuickjs.name,
|
|
375
|
+
scriptArgs: {},
|
|
376
|
+
tags: [],
|
|
377
|
+
priority: 50,
|
|
378
|
+
enabled: true,
|
|
379
|
+
scheduleType: "recurring",
|
|
380
|
+
consecutiveErrors: 0,
|
|
381
|
+
createdByAgentId: agentId,
|
|
382
|
+
createdAt: new Date().toISOString(),
|
|
383
|
+
lastUpdatedAt: new Date().toISOString(),
|
|
384
|
+
};
|
|
385
|
+
await executeScheduleScript(schedule);
|
|
386
|
+
const runs = await listScriptRuns({ limit: 10 });
|
|
387
|
+
const matchedRun = runs.find((r) => r.scriptName === globalQuickjs.name);
|
|
388
|
+
expect(matchedRun?.status).toBe("completed");
|
|
389
|
+
const fullRun = matchedRun ? await getScriptRun(matchedRun.id) : null;
|
|
390
|
+
expect(fullRun?.output).toEqual({ hasProcess: false });
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
test("typecheckScript accepts executor on ctx.swarm.script_upsert and ctx.swarm.script_run", async () => {
|
|
394
|
+
const valid = await typecheckScript(
|
|
395
|
+
`import type { ScriptContext } from "swarm-sdk";
|
|
396
|
+
export default async (_args: unknown, ctx: ScriptContext) => {
|
|
397
|
+
await ctx.swarm.script_upsert({ name: "demo", source: "export default async () => 1;", executor: "quickjs" });
|
|
398
|
+
await ctx.swarm.script_run({ name: "demo", executor: "quickjs" });
|
|
399
|
+
return { ok: true };
|
|
400
|
+
};`,
|
|
401
|
+
{ agentId },
|
|
402
|
+
);
|
|
403
|
+
expect(valid.ok).toBe(true);
|
|
404
|
+
|
|
405
|
+
const invalid = await typecheckScript(
|
|
406
|
+
`import type { ScriptContext } from "swarm-sdk";
|
|
407
|
+
export default async (_args: unknown, ctx: ScriptContext) => {
|
|
408
|
+
await ctx.swarm.script_run({ name: "demo", executor: "e2b" });
|
|
409
|
+
return { ok: true };
|
|
410
|
+
};`,
|
|
411
|
+
{ agentId },
|
|
412
|
+
);
|
|
413
|
+
expect(invalid.ok).toBe(false);
|
|
414
|
+
});
|
|
415
|
+
});
|
|
@@ -421,6 +421,51 @@ describe("public execution route", () => {
|
|
|
421
421
|
expect(body.error.type).toBe("args_validation");
|
|
422
422
|
});
|
|
423
423
|
|
|
424
|
+
test("POST {} against z.object with inherited Object.prototype keys (constructor, toString) retains Zod rejection under quickjs", async () => {
|
|
425
|
+
const prevExecutor = process.env.SCRIPT_EXECUTOR;
|
|
426
|
+
process.env.SCRIPT_EXECUTOR = "quickjs";
|
|
427
|
+
try {
|
|
428
|
+
for (const propName of ["constructor", "toString"] as const) {
|
|
429
|
+
const script = await insertScript({
|
|
430
|
+
name: `proto-${propName}-${crypto.randomUUID().slice(0, 8)}`,
|
|
431
|
+
scope: "agent",
|
|
432
|
+
scopeId: workerId,
|
|
433
|
+
source: `import { z } from "zod";\nexport const argsSchema = z.object({ ${propName}: z.string().nullable().optional() });\nexport default async function (args: z.infer<typeof argsSchema>) { return { ok: true, val: args.${propName} ?? null }; }`,
|
|
434
|
+
description: "Prototype key test",
|
|
435
|
+
intent: "test fixture",
|
|
436
|
+
signatureJson: "{}",
|
|
437
|
+
argsJsonSchema: JSON.stringify({
|
|
438
|
+
type: "object",
|
|
439
|
+
properties: { [propName]: { type: ["string", "null"] } },
|
|
440
|
+
}),
|
|
441
|
+
agentId: workerId,
|
|
442
|
+
typeChecked: true,
|
|
443
|
+
});
|
|
444
|
+
const endpoint = await createScriptApi({
|
|
445
|
+
scriptId: script.id,
|
|
446
|
+
agentId: workerId,
|
|
447
|
+
authMode: "none",
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
const res = await dispatch(`/api/x/script/${endpoint.id}`, {
|
|
451
|
+
method: "POST",
|
|
452
|
+
body: JSON.stringify({}),
|
|
453
|
+
auth: null,
|
|
454
|
+
});
|
|
455
|
+
expect(res.status).toBe(200);
|
|
456
|
+
const body = (await res.json()) as {
|
|
457
|
+
ok: boolean;
|
|
458
|
+
error: { type: string; message: string } | null;
|
|
459
|
+
};
|
|
460
|
+
expect(body.ok).toBe(false);
|
|
461
|
+
expect(body.error?.type).toBe("runtime_error");
|
|
462
|
+
}
|
|
463
|
+
} finally {
|
|
464
|
+
if (prevExecutor === undefined) delete process.env.SCRIPT_EXECUTOR;
|
|
465
|
+
else process.env.SCRIPT_EXECUTOR = prevExecutor;
|
|
466
|
+
}
|
|
467
|
+
});
|
|
468
|
+
|
|
424
469
|
test("disabled endpoint → 404", async () => {
|
|
425
470
|
const script = await insertDoubler();
|
|
426
471
|
const endpoint = await createScriptApi({
|
|
@@ -291,8 +291,9 @@ describe("script SDK allowlist", () => {
|
|
|
291
291
|
|
|
292
292
|
test("bundled swarm-sdk.d.ts uses triggerData (not input) for workflow_trigger", async () => {
|
|
293
293
|
const types = await Bun.file("src/scripts-runtime/types/swarm-sdk.d.ts").text();
|
|
294
|
-
expect(types).
|
|
295
|
-
expect(types).
|
|
294
|
+
expect(types).toMatch(/workflow_trigger\(args: \{\s*id: string;\s*triggerData\?/);
|
|
295
|
+
expect(types).toMatch(/workflow_trigger\(args: \{[^}]*\bwait\?: boolean;/);
|
|
296
|
+
expect(types).not.toMatch(/workflow_trigger\(args: \{\s*id: string;\s*input\?/);
|
|
296
297
|
});
|
|
297
298
|
});
|
|
298
299
|
|
|
@@ -503,17 +503,17 @@ describe("Self-Improvement Mechanisms", () => {
|
|
|
503
503
|
const MEMORY_SKILL_POINTER =
|
|
504
504
|
"You MUST use the `memory` skill before you store, edit, or delete a memory.";
|
|
505
505
|
|
|
506
|
-
test("a worker prompt
|
|
506
|
+
test("a worker prompt points learnings at store-progress and names the memory skill", async () => {
|
|
507
507
|
const prompt = await getBasePrompt({ role: "worker", agentId: workerId });
|
|
508
508
|
|
|
509
|
-
expect(prompt).toContain("`
|
|
509
|
+
expect(prompt).toContain("as `learning` on a `store-progress` call");
|
|
510
510
|
expect(prompt).toContain(MEMORY_SKILL_POINTER);
|
|
511
511
|
});
|
|
512
512
|
|
|
513
|
-
test("a lead prompt
|
|
513
|
+
test("a lead prompt points learnings at store-progress and names the memory skill", async () => {
|
|
514
514
|
const prompt = await getBasePrompt({ role: "lead", agentId: leadId });
|
|
515
515
|
|
|
516
|
-
expect(prompt).toContain("`
|
|
516
|
+
expect(prompt).toContain("as `learning` on a `store-progress` call");
|
|
517
517
|
expect(prompt).toContain(MEMORY_SKILL_POINTER);
|
|
518
518
|
});
|
|
519
519
|
|