@desplega.ai/agent-swarm 2.0.0 → 2.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/dist/{acp-adapter-sj2g0m84.js → acp-adapter-kr4abrsk.js} +10 -10
- package/dist/{actions-fegk44gr.js → actions-eh4g6w2r.js} +27 -27
- package/dist/{amp-adapter-ad8ft89x.js → amp-adapter-7h7wdt8q.js} +5 -5
- package/dist/{app-kfvgbvzy.js → app-7k8s9w5p.js} +9 -9
- package/dist/{assistant-z7zdy9ja.js → assistant-wswt2y7p.js} +28 -28
- package/dist/{boot-reembed-j8xa181e.js → boot-reembed-9apm9qw6.js} +7 -7
- package/dist/{boot-reembed-5c2z0mbe.js → boot-reembed-whzbdthj.js} +7 -7
- package/dist/{boot-scrub-sweep-hnkn3rgt.js → boot-scrub-sweep-ndzmvmgt.js} +8 -8
- package/dist/{boot-seeds-kwjq4d2z.js → boot-seeds-e9sta0yt.js} +13 -11
- package/dist/{claude-adapter-9vmvpvgd.js → claude-adapter-4mqd3r6r.js} +11 -11
- package/dist/{claude-managed-adapter-k9qae05j.js → claude-managed-adapter-9wswb52m.js} +3 -3
- package/dist/{claude-managed-setup-q54zx5q2.js → claude-managed-setup-tpe1effx.js} +4 -4
- package/dist/{claude-sdk-session-9jye6cxr.js → claude-sdk-session-q49ghmh9.js} +10340 -10197
- package/dist/{cli-ke896fcf.js → cli-01xvj1rq.js} +10 -10
- package/dist/{cli-tr4sxp68.js → cli-035vekcw.js} +2 -2
- package/dist/{cli-z5jjpaxw.js → cli-0pd8hzhc.js} +3 -3
- package/dist/{cli-n5vwg842.js → cli-0veb92a3.js} +6 -1
- package/dist/{cli-wayw93nt.js → cli-10jesr25.js} +3 -3
- package/dist/{cli-se8w6wny.js → cli-1y7yywmj.js} +1 -1
- package/dist/{cli-v1yb0rqb.js → cli-20krdhz5.js} +47 -19
- package/dist/{cli-d5f9ed5c.js → cli-29rja109.js} +39 -20
- package/dist/{cli-apx87jne.js → cli-2pwa0q4d.js} +15 -4
- package/dist/{cli-ee9gtz70.js → cli-4mengw46.js} +348 -25
- package/dist/{cli-03gedhng.js → cli-5twdke5x.js} +6 -6
- package/dist/{cli-6razj6t4.js → cli-711vwbxn.js} +2 -2
- package/dist/{cli-s2sp957d.js → cli-78vv7446.js} +1 -1
- package/dist/{cli-3fbmmawm.js → cli-7n6s5man.js} +4 -4
- package/dist/{cli-hnhtk6cj.js → cli-8wwxw8a3.js} +1 -1
- package/dist/{cli-hnmhtzvh.js → cli-9jzdj90z.js} +2 -0
- package/dist/{cli-skxvvpap.js → cli-cvv9k0hh.js} +28 -25
- package/dist/{cli-74ahz61x.js → cli-d9286m9h.js} +2 -2
- package/dist/{cli-4jxm2qtz.js → cli-gxzkg1de.js} +28 -16
- package/dist/{cli-zkpks3r3.js → cli-h1arqn31.js} +1 -1
- package/dist/{cli-r62a35ea.js → cli-hqr88ew6.js} +1 -1
- package/dist/{cli-ra0xzggj.js → cli-j67mh2vg.js} +2 -2
- package/dist/{cli-29jc5q7w.js → cli-ksphgh48.js} +2 -2
- package/dist/{cli-eejsgvm4.js → cli-mgz4nbgg.js} +4 -4
- package/dist/{cli-vatyhshy.js → cli-mz5as5ha.js} +4 -4
- package/dist/{cli-f7kr7svz.js → cli-n8ndw8j7.js} +2 -2
- package/dist/{cli-1e4b6bzh.js → cli-naqzr17j.js} +7 -7
- package/dist/{cli-q0ke1yww.js → cli-nm1p8jfr.js} +6 -6
- package/dist/{cli-m50t4309.js → cli-p3zbc1rw.js} +5 -5
- package/dist/{cli-cgtksawx.js → cli-pe05f8yk.js} +23 -3
- package/dist/{cli-vdach5a9.js → cli-qmzsbkvy.js} +4 -4
- package/dist/{cli-n3hzg89b.js → cli-qnm4e9e4.js} +57 -579
- package/dist/{cli-s4setq91.js → cli-qqy4q48b.js} +3 -3
- package/dist/{cli-k382tshb.js → cli-r35rw5de.js} +8 -7
- package/dist/{cli-k3dp5tpf.js → cli-r610p9bs.js} +2 -2
- package/dist/{cli-e6mpjhed.js → cli-rc6pf5kv.js} +1 -1
- package/dist/{cli-3dj8k4vy.js → cli-rdxm77k3.js} +1 -1
- package/dist/{cli-tqaazemc.js → cli-s69nfyeh.js} +1 -1
- package/dist/{cli-6jyj59nb.js → cli-sawv63e8.js} +2 -2
- package/dist/{cli-q50942rs.js → cli-sg19gbf0.js} +1 -1
- package/dist/{cli-p1x85d2t.js → cli-shk7r5cs.js} +12 -9
- package/dist/{cli-przpmdgr.js → cli-sksetb2q.js} +11 -1
- package/dist/{cli-d2qr7aj4.js → cli-t02z59f2.js} +1 -1
- package/dist/{cli-j0pfptdp.js → cli-v7g551te.js} +39 -43
- package/dist/{cli-bzszjf2p.js → cli-vgqzp4em.js} +519 -126
- package/dist/cli-wfnemr1t.js +86 -0
- package/dist/{cli-ne236afy.js → cli-wms6x9db.js} +8 -8
- package/dist/{cli-5hkm7whv.js → cli-x1r545x8.js} +1 -1
- package/dist/{cli-kt6zqg0e.js → cli-xczsgshn.js} +2 -2
- package/dist/{cli-ef1hg1wv.js → cli-y3167wt1.js} +430 -238
- package/dist/{cli-tsde1hdr.js → cli-yjmqpxt3.js} +12 -11
- package/dist/{cli-9nfx44eh.js → cli-z780ec7c.js} +1 -1
- package/dist/{cli-r2pnvj8g.js → cli-zm9t2vde.js} +2 -2
- package/dist/cli.js +24 -23
- package/dist/{codex-adapter-y81sssk0.js → codex-adapter-4z16v9n0.js} +8 -8
- package/dist/{codex-login-f2rcfpp9.js → codex-login-pkepy2j1.js} +2 -2
- package/dist/{codex-session-runner-8vx4j6a9.js → codex-session-runner-rvms9y2x.js} +8 -8
- package/dist/{commands-kcpvpcrr.js → commands-2a9rtyat.js} +8 -8
- package/dist/{controller-ywrmt6yd.js → controller-3k3madbn.js} +4 -4
- package/dist/{controllers-connect-kwpvj3rf.js → controllers-connect-v9yn8zb9.js} +30 -30
- package/dist/{cursor-adapter-cehv1qns.js → cursor-adapter-98gbhyzn.js} +4 -4
- package/dist/{db-ww5d7jq3.js → db-eywwsnjw.js} +9 -7
- package/dist/{db-k8ankj20.js → db-ncypk9np.js} +9 -9
- package/dist/{devin-adapter-x1022qb1.js → devin-adapter-zeq8b9gz.js} +2 -2
- package/dist/{dispatcher-e8gq5m68.js → dispatcher-w94kc575.js} +26 -26
- package/dist/{docker-ff6y1190.js → docker-b1e4cqab.js} +2 -2
- package/dist/{dsh-adapter-3epjm3af.js → dsh-adapter-47ztej36.js} +4 -4
- package/dist/{e2b-8ygwhg0f.js → e2b-bbt8ey45.js} +3 -3
- package/dist/{e2b-swhnpw3n.js → e2b-dp804sen.js} +3 -3
- package/dist/{event-bus-6axnngdj.js → event-bus-j2jdg3qb.js} +2 -2
- package/dist/{external-channel-h3359cwn.js → external-channel-zrkg8ar9.js} +8 -8
- package/dist/{grok-adapter-0d4x7gkx.js → grok-adapter-s9ncp9vy.js} +11 -11
- package/dist/{handlers-an1gzv21.js → handlers-kqftvjgw.js} +29 -29
- package/dist/{heartbeat-6dakeb61.js → heartbeat-9q96p8v9.js} +31 -31
- package/dist/{hook-6v6cv6d5.js → hook-56qe10dp.js} +7 -7
- package/dist/{hook-sa5xye4y.js → hook-yy6rw42k.js} +8 -8
- package/dist/{http-r7f3eky0.js → http-r2dnb8f8.js} +963 -75
- package/dist/{index-7ep4gzdk.js → index-3gtmkhc6.js} +26 -26
- package/dist/{index-v1v6n7hg.js → index-3k5hkz7r.js} +31 -31
- package/dist/{index-rmpw9a8q.js → index-5e101240.js} +26 -26
- package/dist/{index-s2wxgzr0.js → index-5wyfjc2c.js} +392 -15
- package/dist/{index-zk96aq1k.js → index-vxvhxxbn.js} +7 -7
- package/dist/{keepalive-be9vwy5h.js → keepalive-psj6j54c.js} +9 -9
- package/dist/{lead-0861cwrf.js → lead-hajz4688.js} +43 -43
- package/dist/{maintenance-mzzgc8bn.js → maintenance-ps02sb5m.js} +7 -7
- package/dist/{node-mn1zs7an.js → node-mwb5r697.js} +1 -1
- package/dist/{oauth-refresh-sweep-cpdnfwdj.js → oauth-refresh-sweep-0284h93b.js} +8 -8
- package/dist/{onboard-fh8qs4r3.js → onboard-rbf6bg01.js} +2 -2
- package/dist/{opencode-adapter-nm42wwj8.js → opencode-adapter-13dcatgq.js} +8 -8
- package/dist/{otel-impl-x6c1c9g4.js → otel-impl-zvqcbhp7.js} +2 -2
- package/dist/{pi-mono-adapter-9n2ya240.js → pi-mono-adapter-xxy47q4v.js} +7 -7
- package/dist/{pricing-refresh-qb0rv0ap.js → pricing-refresh-kefx9v3t.js} +9 -9
- package/dist/{rbac-roles-1gzdwbh1.js → rbac-roles-3m36rxm2.js} +8 -8
- package/dist/{rbac-roles-3mraey52.js → rbac-roles-z76xv0hq.js} +9 -9
- package/dist/{render-v2-agpa45zz.js → render-v2-wz73bs25.js} +9 -9
- package/dist/{resume-x0ydy9s9.js → resume-k4wzge8w.js} +14 -14
- package/dist/{setup-as9ga0d6.js → setup-2fp2pypp.js} +2 -2
- package/dist/{work-objects-y5g1y7ev.js → work-objects-6k0ywz3m.js} +29 -29
- package/dist/{worker-860c3e38.js → worker-5v3rxsdh.js} +43 -43
- package/dist/{x-m477fr0h.js → x-4mtdeym1.js} +2 -2
- package/openapi.json +777 -6
- package/package.json +8 -7
- package/src/agentmail/handlers.ts +21 -11
- package/src/apps/definition.ts +16 -0
- package/src/azure-devops/api.ts +52 -2
- package/src/azure-devops/boards-sync.ts +50 -20
- package/src/azure-devops/index.ts +1 -0
- package/src/azure-devops/outbound.ts +95 -0
- package/src/be/boot-seeds.ts +11 -0
- package/src/be/db/model-profiles.ts +315 -0
- package/src/be/db/tasks/read.ts +7 -0
- package/src/be/db.ts +53 -47
- package/src/be/migrations/219_relabel_pi_context_compactions.sql +22 -0
- package/src/be/migrations/220_model_profiles.sql +137 -0
- package/src/be/model-tier-resolution.ts +145 -11
- package/src/be/model-validation.ts +8 -1
- package/src/be/scripts/typecheck.ts +12 -0
- package/src/be/secret-registry.ts +6 -2
- package/src/be/seed-model-profiles.ts +41 -0
- package/src/be/seed-scripts/catalog/heartbeat-jev-gate.ts +368 -0
- package/src/be/seed-scripts/index.ts +9 -0
- package/src/be/swarm-config-guard.ts +1 -0
- package/src/commands/runner.ts +3 -17
- package/src/extensions/catalog.generated.json +17 -0
- package/src/extensions/contract-types.generated.ts +1 -1
- package/src/extensions/loader.ts +10 -5
- package/src/heartbeat/heartbeat.ts +2 -1
- package/src/http/agents.ts +49 -2
- package/src/http/all-routes.ts +1 -0
- package/src/http/apps.ts +30 -3
- package/src/http/config.ts +26 -2
- package/src/http/core.ts +4 -0
- package/src/http/index.ts +11 -1
- package/src/http/model-profiles.ts +303 -0
- package/src/http/pages-public.ts +11 -5
- package/src/http/poll.ts +16 -5
- package/src/http/script-runs.ts +1 -1
- package/src/http/scripts.ts +25 -2
- package/src/http/tasks.ts +18 -0
- package/src/http/webhooks.ts +1 -2
- package/src/pages/feedback-overlay.ts +65 -6
- package/src/providers/acp-targets.ts +1 -1
- package/src/providers/amp-adapter.ts +1 -1
- package/src/providers/dsh-adapter.ts +1 -1
- package/src/providers/pi-mono-extension.ts +17 -3
- package/src/providers/reasoning-effort.ts +10 -0
- package/src/rbac/legacy-policy.ts +8 -0
- package/src/rbac/permissions.ts +5 -0
- package/src/scheduler/deferred-task-waits.ts +18 -5
- package/src/scheduler/scheduler.ts +7 -1
- package/src/scripts-runtime/executors/quickjs-prelude.ts +75 -6
- package/src/scripts-runtime/executors/quickjs-runner.ts +85 -8
- package/src/scripts-runtime/executors/quickjs-web-shims.ts +1041 -0
- package/src/scripts-runtime/extract-args-schema.ts +9 -8
- package/src/scripts-runtime/quickjs-compat.ts +464 -0
- package/src/scripts-runtime/sdk-allowlist.ts +4 -0
- package/src/scripts-runtime/types/stdlib.d.ts +12 -0
- package/src/scripts-runtime/types/swarm-extension.d.ts +3 -0
- package/src/scripts-runtime/types/swarm-sdk.d.ts +12 -0
- package/src/server.ts +10 -1
- package/src/slack/blocks.ts +4 -2
- package/src/slack/reaction-shortcode.ts +14 -0
- package/src/slack/render-v2.ts +12 -7
- package/src/slack/responses.ts +5 -4
- package/src/slack/thread-buffer.ts +41 -22
- package/src/tasks/worker-follow-up.ts +11 -2
- package/src/tests/agentmail-filters.test.ts +47 -0
- package/src/tests/apps-spike2.test.ts +35 -0
- package/src/tests/azure-devops-outbound.test.ts +420 -0
- package/src/tests/db-task-read-contract.test.ts +2 -0
- package/src/tests/db-task-write-contract.test.ts +1 -0
- package/src/tests/deferred-task-wake.test.ts +78 -1
- package/src/tests/docs-index-footprint.test.js +2 -2
- package/src/tests/dsh-adapter.test.ts +1 -1
- package/src/tests/env-flag.test.ts +1 -0
- package/src/tests/extensions-catalog.test.ts +1 -0
- package/src/tests/extensions-example-comms-adherence-gate.test.ts +413 -0
- package/src/tests/extensions-loader.test.ts +15 -0
- package/src/tests/heartbeat-jev-gate-credentials.test.ts +168 -0
- package/src/tests/heartbeat-jev-gate.test.ts +385 -0
- package/src/tests/model-profile-backcompat.test.ts +327 -0
- package/src/tests/model-profile-providers.test.ts +302 -0
- package/src/tests/model-profile-runtime-secrets.test.ts +223 -0
- package/src/tests/model-profiles-api.test.ts +614 -0
- package/src/tests/pages-feedback-keys.test.ts +121 -0
- package/src/tests/pages-feedback.test.ts +3 -0
- package/src/tests/pi-context-compaction.test.ts +193 -0
- package/src/tests/profile-claim.test.ts +269 -0
- package/src/tests/quickjs-compat.test.ts +213 -0
- package/src/tests/rbac-engine.test.ts +21 -0
- package/src/tests/script-connections.test.ts +4 -0
- package/src/tests/script-executor-conformance.test.ts +252 -1
- package/src/tests/script-executor-quickjs.test.ts +96 -0
- package/src/tests/script-runs-http.test.ts +89 -0
- package/src/tests/scripts-http.test.ts +16 -0
- package/src/tests/secret-scrubber.test.ts +10 -0
- package/src/tests/seed-model-profiles.test.ts +174 -0
- package/src/tests/seed-scripts.test.ts +2 -2
- package/src/tests/skill-invocations.test.ts +4 -2
- package/src/tests/slack-blocks.test.ts +36 -0
- package/src/tests/slack-inline-output.test.ts +14 -0
- package/src/tests/slack-reaction-config.test.ts +20 -0
- package/src/tests/slack-render-v2.test.ts +37 -0
- package/src/tests/slack-thread-buffer.test.ts +98 -1
- package/src/tests/workflow-llm.test.ts +61 -0
- package/src/tools/model-profiles/list-model-profiles.ts +54 -0
- package/src/tools/request-human-input.ts +11 -3
- package/src/tools/script-apis.ts +5 -1
- package/src/tools/script-upsert.ts +4 -1
- package/src/tools/send-task.ts +24 -1
- package/src/tools/system-one-decide.ts +104 -0
- package/src/tools/task-action.ts +22 -1
- package/src/tools/tool-config.ts +6 -0
- package/src/types.ts +36 -1
- package/src/utils/secret-scrubber.ts +19 -12
- package/src/utils/vcs-repo-match.ts +29 -0
- package/src/workflows/executors/raw-llm.ts +8 -3
- package/src/workflows/executors/system-one-decision.ts +21 -11
- package/src/workflows/executors/workflow-llm.ts +22 -0
- package/src/workflows/index.ts +21 -12
- package/templates/extensions/README.md +1 -0
- package/templates/extensions/comms-adherence-gate/README.md +88 -0
- package/templates/extensions/comms-adherence-gate/hooks.ts +489 -0
- package/templates/extensions/comms-adherence-gate/manifest.json +8 -0
- package/dist/cli-5t4f29ez.js +0 -33
|
@@ -37,7 +37,7 @@ import type {
|
|
|
37
37
|
} from "./types";
|
|
38
38
|
|
|
39
39
|
// npm's `latest` is older and lacks the JSON/stdin contract used here.
|
|
40
|
-
export const DSH_PACKAGE = "@deepseek-ai/dsh@0.2.1-alpha.
|
|
40
|
+
export const DSH_PACKAGE = "@deepseek-ai/dsh@0.2.1-alpha.5";
|
|
41
41
|
|
|
42
42
|
export function checkDshCredentials(env: Record<string, string | undefined>): CredStatus {
|
|
43
43
|
return env.DEEPSEEK_API_KEY?.trim() || env.OPENROUTER_API_KEY?.trim()
|
|
@@ -653,9 +653,22 @@ export function createSwarmHooksExtension(config: SwarmHooksConfig): ExtensionFa
|
|
|
653
653
|
// === session_compact → PreCompact ===
|
|
654
654
|
// Re-anchor the model on its task once compaction has summarized the
|
|
655
655
|
// history. `context` fires before every LLM call, so it cannot carry this.
|
|
656
|
-
pi.on("session_compact", async () => {
|
|
656
|
+
pi.on("session_compact", async (event, ctx) => {
|
|
657
657
|
if (!config.taskId) return;
|
|
658
658
|
|
|
659
|
+
// The one real compaction signal: shaped like the claude runner's rows.
|
|
660
|
+
fireAndForget(`${config.apiUrl}/api/tasks/${config.taskId}/context`, {
|
|
661
|
+
method: "POST",
|
|
662
|
+
headers: apiHeaders(config),
|
|
663
|
+
body: JSON.stringify({
|
|
664
|
+
eventType: "compaction",
|
|
665
|
+
sessionId: `pi-${config.taskId}`,
|
|
666
|
+
compactTrigger: event.reason === "manual" ? "manual" : "auto",
|
|
667
|
+
preCompactTokens: event.compactionEntry?.tokensBefore,
|
|
668
|
+
contextTotalTokens: ctx.getContextUsage?.()?.contextWindow,
|
|
669
|
+
}),
|
|
670
|
+
});
|
|
671
|
+
|
|
659
672
|
const taskDetails = await fetchTaskDetails(config);
|
|
660
673
|
if (taskDetails) {
|
|
661
674
|
const reminder = await renderGoalReminder(taskDetails);
|
|
@@ -667,14 +680,15 @@ export function createSwarmHooksExtension(config: SwarmHooksConfig): ExtensionFa
|
|
|
667
680
|
pi.on("context", async (_event, ctx) => {
|
|
668
681
|
if (!config.taskId) return undefined;
|
|
669
682
|
|
|
670
|
-
//
|
|
683
|
+
// Per-call usage snapshot: "progress", as the claude/codex runner posts
|
|
684
|
+
// per-turn usage. Only session_compact reports a compaction.
|
|
671
685
|
const usage = ctx.getContextUsage?.();
|
|
672
686
|
if (usage) {
|
|
673
687
|
fireAndForget(`${config.apiUrl}/api/tasks/${config.taskId}/context`, {
|
|
674
688
|
method: "POST",
|
|
675
689
|
headers: apiHeaders(config),
|
|
676
690
|
body: JSON.stringify({
|
|
677
|
-
eventType: "
|
|
691
|
+
eventType: "progress",
|
|
678
692
|
sessionId: `pi-${config.taskId}`,
|
|
679
693
|
contextUsedTokens: usage.tokens ?? undefined,
|
|
680
694
|
contextTotalTokens: usage.contextWindow,
|
|
@@ -257,6 +257,16 @@ const REASONING_HARNESSES: ReadonlySet<string> = new Set<ReasoningHarness>([
|
|
|
257
257
|
"grok",
|
|
258
258
|
]);
|
|
259
259
|
|
|
260
|
+
/**
|
|
261
|
+
* The levels a session on this harness and model accepts; empty for a harness
|
|
262
|
+
* this feature does not cover. Unlike `effectiveReasoningEffort`, a supported
|
|
263
|
+
* level with no transport effect (Opencode `off`) is still listed.
|
|
264
|
+
*/
|
|
265
|
+
export function supportedReasoningLevels(harness: string, model: string): ReasoningEffort[] {
|
|
266
|
+
if (!REASONING_HARNESSES.has(harness)) return [];
|
|
267
|
+
return reasoningCapability(harness as ReasoningHarness, model).levels;
|
|
268
|
+
}
|
|
269
|
+
|
|
260
270
|
/**
|
|
261
271
|
* The effort a session on this harness and model actually runs at: `level`
|
|
262
272
|
* when the adapter applies it, null when it is unset or dropped (the harness
|
|
@@ -49,6 +49,13 @@ const operatorOrUser: LegacyRule = {
|
|
|
49
49
|
evaluate: (principal) => principal.kind === "operator" || principal.kind === "user",
|
|
50
50
|
};
|
|
51
51
|
|
|
52
|
+
/** The shared API key only: swarm-wide settings that no session user or agent may change. */
|
|
53
|
+
const operatorOnly: LegacyRule = {
|
|
54
|
+
name: "operator-only",
|
|
55
|
+
denyReason: "requires operator authentication",
|
|
56
|
+
evaluate: (principal) => principal.kind === "operator",
|
|
57
|
+
};
|
|
58
|
+
|
|
52
59
|
const leadOrOperatorOrUser: LegacyRule = {
|
|
53
60
|
name: "lead-or-operator-or-user",
|
|
54
61
|
denyReason: "requires lead agent, operator, or user authentication",
|
|
@@ -313,6 +320,7 @@ export const LEGACY_POLICY = {
|
|
|
313
320
|
// Lead tokens retain config.read.secrets. This exception does not grant config administration.
|
|
314
321
|
"models.catalog.write": leadOrOperator,
|
|
315
322
|
"models.harness-support.write": agentOrOperator,
|
|
323
|
+
"models.profiles.write": operatorOnly,
|
|
316
324
|
"skill.create.swarm": leadOnly,
|
|
317
325
|
"skill.install.any": leadOnly,
|
|
318
326
|
"skill.install.global": leadOnly,
|
package/src/rbac/permissions.ts
CHANGED
|
@@ -219,6 +219,11 @@ export const PERMISSIONS = {
|
|
|
219
219
|
"Record whether a pinned harness CLI version accepts a model (a worker reports its own probe results; the operator may correct any row).",
|
|
220
220
|
namespace: "models",
|
|
221
221
|
},
|
|
222
|
+
"models.profiles.write": {
|
|
223
|
+
description:
|
|
224
|
+
"Create, update, or delete model profiles (named harness + model + effort). Operator (shared API key) only.",
|
|
225
|
+
namespace: "models",
|
|
226
|
+
},
|
|
222
227
|
"config.read.secrets": {
|
|
223
228
|
description: "Read unmasked secret config values.",
|
|
224
229
|
namespace: "config",
|
|
@@ -98,13 +98,12 @@ async function prepareDeferredWakeTask(
|
|
|
98
98
|
*/
|
|
99
99
|
export const DEFERRED_WAIT_WOKE_EVENT = "task_deferred_wait_woke";
|
|
100
100
|
|
|
101
|
-
/** Undefined means a normal schedule; an empty
|
|
102
|
-
export async function
|
|
101
|
+
/** Undefined means a normal schedule; an empty preparation means the wait is not ready. */
|
|
102
|
+
export async function prepareDeferredTaskWait(
|
|
103
103
|
scheduleId: string,
|
|
104
104
|
reason: WakeReason,
|
|
105
105
|
extraTags: string[] = [],
|
|
106
|
-
|
|
107
|
-
): Promise<{ task?: AgentTask } | undefined> {
|
|
106
|
+
): Promise<{ task?: PreparedTaskCreate } | undefined> {
|
|
108
107
|
const client = getDbClient();
|
|
109
108
|
// Keep ordinary schedule dispatch off the write-lock path.
|
|
110
109
|
if (
|
|
@@ -117,8 +116,22 @@ export async function dispatchDeferredTaskWait(
|
|
|
117
116
|
// first; the transaction below re-reads the wait and its member set before claiming.
|
|
118
117
|
// The prepared description carries the cause seen here; for an `any` wait a member
|
|
119
118
|
// that settles between this read and the claim is missing from that text only.
|
|
120
|
-
|
|
119
|
+
return { task: await prepareDeferredWakeTask(scheduleId, reason, extraTags) };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** Undefined means a normal schedule; an empty result means another caller won. */
|
|
123
|
+
export async function dispatchDeferredTaskWait(
|
|
124
|
+
scheduleId: string,
|
|
125
|
+
reason: WakeReason,
|
|
126
|
+
extraTags: string[] = [],
|
|
127
|
+
triggerTaskId?: string,
|
|
128
|
+
prepared?: { task?: PreparedTaskCreate },
|
|
129
|
+
): Promise<{ task?: AgentTask } | undefined> {
|
|
130
|
+
const preparation = prepared ?? (await prepareDeferredTaskWait(scheduleId, reason, extraTags));
|
|
131
|
+
if (!preparation) return undefined;
|
|
132
|
+
const preparedForWait = preparation.task;
|
|
121
133
|
if (!preparedForWait) return {};
|
|
134
|
+
const client = getDbClient();
|
|
122
135
|
return client.transaction(async () => {
|
|
123
136
|
const resolved = await resolveWaitCause(scheduleId, reason);
|
|
124
137
|
if (!resolved) return {};
|
|
@@ -38,6 +38,7 @@ import { scrubObject, scrubSecrets } from "../utils/secret-scrubber";
|
|
|
38
38
|
import {
|
|
39
39
|
dispatchDeferredTaskWait,
|
|
40
40
|
initDeferredTaskWaits,
|
|
41
|
+
prepareDeferredTaskWait,
|
|
41
42
|
reapSpentDeferredSchedules,
|
|
42
43
|
reconcileDeferredTaskWaits,
|
|
43
44
|
stopDeferredTaskWaits,
|
|
@@ -303,6 +304,9 @@ async function throwIfWorkflowNeedsSetup(scheduleId: string, runIds: string[]):
|
|
|
303
304
|
* recovery pass, or schedule edit invalidates it before any target is dispatched.
|
|
304
305
|
*/
|
|
305
306
|
async function claimScheduleOccurrence(snapshot: ScheduledTask, extraTags: string[]) {
|
|
307
|
+
// Run extension pre-hooks before opening the claim transaction. The snapshot
|
|
308
|
+
// guard below still prevents a moved ceiling from consuming this preparation.
|
|
309
|
+
const preparedWait = await prepareDeferredTaskWait(snapshot.id, "ceiling", extraTags);
|
|
306
310
|
return getDbClient().transaction(async () => {
|
|
307
311
|
const current = await getScheduledTaskById(snapshot.id);
|
|
308
312
|
if (!current?.enabled || !current.nextRunAt || current.nextRunAt !== snapshot.nextRunAt) {
|
|
@@ -313,7 +317,9 @@ async function claimScheduleOccurrence(snapshot: ScheduledTask, extraTags: strin
|
|
|
313
317
|
// Event-driven waits already atomically create their child and consume the
|
|
314
318
|
// ceiling. Keep that transaction (and its afterCommit emits) intact: disabling
|
|
315
319
|
// a one-time schedule first would prevent its wake-up task from being created.
|
|
316
|
-
const deferred =
|
|
320
|
+
const deferred = preparedWait
|
|
321
|
+
? await dispatchDeferredTaskWait(schedule.id, "ceiling", extraTags, undefined, preparedWait)
|
|
322
|
+
: undefined;
|
|
317
323
|
if (deferred) {
|
|
318
324
|
return {
|
|
319
325
|
schedule,
|
|
@@ -5,11 +5,14 @@
|
|
|
5
5
|
*
|
|
6
6
|
* - `__host_call(path, argsJson)`: async host call, resolves to a JSON envelope.
|
|
7
7
|
* - `__host_sync(op, json)`: sync host call, returns a JSON envelope.
|
|
8
|
+
* `crypto.random` returns that many random bytes.
|
|
8
9
|
* - `__host_log(stream, text)`: append to the captured stdout or stderr.
|
|
9
10
|
* - `__host_sleep(ms)`: resolves after `ms` on the host clock.
|
|
10
11
|
* - `__host_done(json)`: reports the final outcome of the run.
|
|
11
12
|
*
|
|
12
13
|
* Every value crosses the boundary as JSON. The sandbox has no other way out.
|
|
14
|
+
* `SANDBOX_WEB_SHIMS` (quickjs-web-shims.ts) runs first and adds URL,
|
|
15
|
+
* URLSearchParams, TextEncoder and TextDecoder.
|
|
13
16
|
* `__swarm_shape` (injected before this file) lists the configured api/mcp
|
|
14
17
|
* connections so `ctx.api.<slug>` matches the native executor exactly.
|
|
15
18
|
*/
|
|
@@ -64,6 +67,39 @@ const setTimeout = (fn, ms, ...args) => {
|
|
|
64
67
|
const clearTimeout = (id) => {
|
|
65
68
|
timers.delete(id);
|
|
66
69
|
};
|
|
70
|
+
// Shares the timer ids with setTimeout, so either clear function stops either.
|
|
71
|
+
const setInterval = (fn, ms, ...args) => {
|
|
72
|
+
const id = ++timerSeq;
|
|
73
|
+
const delay = Math.max(1, Number(ms) || 0);
|
|
74
|
+
timers.add(id);
|
|
75
|
+
const tick = () =>
|
|
76
|
+
__host_sleep(delay).then(() => {
|
|
77
|
+
if (!timers.has(id)) return;
|
|
78
|
+
tick();
|
|
79
|
+
fn(...args);
|
|
80
|
+
});
|
|
81
|
+
tick();
|
|
82
|
+
return id;
|
|
83
|
+
};
|
|
84
|
+
const clearInterval = clearTimeout;
|
|
85
|
+
|
|
86
|
+
// ── crypto (bytes come from the host CSPRNG) ──
|
|
87
|
+
const INTEGER_ARRAYS = [Int8Array, Uint8Array, Uint8ClampedArray, Int16Array, Uint16Array, Int32Array, Uint32Array, BigInt64Array, BigUint64Array];
|
|
88
|
+
const getRandomValues = (array) => {
|
|
89
|
+
if (!INTEGER_ARRAYS.some((type) => array instanceof type)) {
|
|
90
|
+
throw new TypeError("crypto.getRandomValues: the argument must be an integer TypedArray");
|
|
91
|
+
}
|
|
92
|
+
new Uint8Array(array.buffer, array.byteOffset, array.byteLength).set(hostSync("crypto.random", array.byteLength));
|
|
93
|
+
return array;
|
|
94
|
+
};
|
|
95
|
+
const randomUUID = () => {
|
|
96
|
+
const bytes = hostSync("crypto.random", 16);
|
|
97
|
+
bytes[6] = (bytes[6] & 0x0f) | 0x40;
|
|
98
|
+
bytes[8] = (bytes[8] & 0x3f) | 0x80;
|
|
99
|
+
const hex = bytes.map((b) => (b + 256).toString(16).slice(1)).join("");
|
|
100
|
+
return hex.slice(0, 8) + "-" + hex.slice(8, 12) + "-" + hex.slice(12, 16) + "-" + hex.slice(16, 20) + "-" + hex.slice(20);
|
|
101
|
+
};
|
|
102
|
+
const crypto = Object.freeze({ getRandomValues, randomUUID });
|
|
67
103
|
|
|
68
104
|
// ── Redacted ──
|
|
69
105
|
const redactedEntries = new WeakMap();
|
|
@@ -115,7 +151,7 @@ const config = Object.freeze({
|
|
|
115
151
|
},
|
|
116
152
|
});
|
|
117
153
|
|
|
118
|
-
// ── fetch
|
|
154
|
+
// ── fetch ──
|
|
119
155
|
class Headers {
|
|
120
156
|
constructor(init) {
|
|
121
157
|
this._map = new Map();
|
|
@@ -165,14 +201,44 @@ class Response {
|
|
|
165
201
|
return JSON.parse(this._body);
|
|
166
202
|
}
|
|
167
203
|
}
|
|
204
|
+
const BASE64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
205
|
+
const toBase64 = (bytes) => {
|
|
206
|
+
let out = "";
|
|
207
|
+
let i = 0;
|
|
208
|
+
for (; i + 2 < bytes.length; i += 3) {
|
|
209
|
+
const n = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2];
|
|
210
|
+
out += BASE64[n >> 18] + BASE64[(n >> 12) & 63] + BASE64[(n >> 6) & 63] + BASE64[n & 63];
|
|
211
|
+
}
|
|
212
|
+
if (i < bytes.length) {
|
|
213
|
+
const n = (bytes[i] << 16) | ((bytes[i + 1] || 0) << 8);
|
|
214
|
+
out += BASE64[n >> 18] + BASE64[(n >> 12) & 63] + (i + 1 < bytes.length ? BASE64[(n >> 6) & 63] : "=") + "=";
|
|
215
|
+
}
|
|
216
|
+
return out;
|
|
217
|
+
};
|
|
218
|
+
// Binary bodies cross the host boundary as base64; the host decodes them.
|
|
219
|
+
const encodeBody = (body, headers) => {
|
|
220
|
+
if (body == null) return {};
|
|
221
|
+
if (typeof body === "string") return { body };
|
|
222
|
+
if (body instanceof globalThis.URLSearchParams) {
|
|
223
|
+
if (!headers.has("content-type")) headers.set("content-type", "application/x-www-form-urlencoded;charset=UTF-8");
|
|
224
|
+
return { body: body.toString() };
|
|
225
|
+
}
|
|
226
|
+
if (body instanceof ArrayBuffer) return { bodyBase64: toBase64(new Uint8Array(body)) };
|
|
227
|
+
if (ArrayBuffer.isView(body)) {
|
|
228
|
+
return { bodyBase64: toBase64(new Uint8Array(body.buffer, body.byteOffset, body.byteLength)) };
|
|
229
|
+
}
|
|
230
|
+
throw new TypeError(
|
|
231
|
+
"quickjs executor: fetch body must be a string, Uint8Array, ArrayBuffer or URLSearchParams (JSON.stringify objects first)",
|
|
232
|
+
);
|
|
233
|
+
};
|
|
168
234
|
const toRequest = (input, init) => {
|
|
169
235
|
const options = init || {};
|
|
170
236
|
const url = typeof input === "string" ? input : input && (input.href || input.url) ? input.href || input.url : String(input);
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
const headers = options.headers == null ? undefined :
|
|
175
|
-
const request = { url, method: options.method, headers, body
|
|
237
|
+
const headerMap = new Headers(options.headers);
|
|
238
|
+
const body = encodeBody(options.body, headerMap);
|
|
239
|
+
const headerList = Array.from(headerMap.entries());
|
|
240
|
+
const headers = options.headers == null && headerList.length === 0 ? undefined : headerList;
|
|
241
|
+
const request = { url, method: options.method, headers, ...body };
|
|
176
242
|
if (options.retries !== undefined) request.retries = options.retries;
|
|
177
243
|
if (options.timeoutMs !== undefined) request.timeoutMs = options.timeoutMs;
|
|
178
244
|
return request;
|
|
@@ -223,6 +289,9 @@ const define = (name, value) =>
|
|
|
223
289
|
define("console", console);
|
|
224
290
|
define("setTimeout", setTimeout);
|
|
225
291
|
define("clearTimeout", clearTimeout);
|
|
292
|
+
define("setInterval", setInterval);
|
|
293
|
+
define("clearInterval", clearInterval);
|
|
294
|
+
define("crypto", crypto);
|
|
226
295
|
define("fetch", fetch);
|
|
227
296
|
define("Headers", Headers);
|
|
228
297
|
define("Response", Response);
|
|
@@ -13,6 +13,7 @@ import { table } from "../stdlib/table";
|
|
|
13
13
|
import { SwarmConfig } from "../swarm-config";
|
|
14
14
|
import { bundleForQuickJS, USER_SCRIPT_FILE } from "./quickjs-bundle";
|
|
15
15
|
import { SANDBOX_EPILOGUE, SANDBOX_PRELUDE } from "./quickjs-prelude";
|
|
16
|
+
import { SANDBOX_WEB_SHIMS } from "./quickjs-web-shims";
|
|
16
17
|
import type { SourceMapReader } from "./sourcemap";
|
|
17
18
|
import type {
|
|
18
19
|
ExecutorOutput,
|
|
@@ -37,6 +38,13 @@ const BUNDLE_FILE = USER_SCRIPT_FILE.replace(/\.ts$/, ".js");
|
|
|
37
38
|
const MAX_CONCURRENT_HOST_CALLS = 16;
|
|
38
39
|
// Host calls and timers that one run can have in flight or queued.
|
|
39
40
|
const MAX_PENDING_HOST_CALLS = 1024;
|
|
41
|
+
// A binary fetch body crosses the bridge as base64 and is decoded on the host.
|
|
42
|
+
// One body decodes to at most this many bytes, and the binary bodies a run has
|
|
43
|
+
// queued or in flight total at most MAX_PENDING_BINARY_BODY_BYTES. Both are
|
|
44
|
+
// checked before decoding, so a rejected request never reaches fetch.
|
|
45
|
+
const MAX_BINARY_BODY_BYTES = 16 * 1024 * 1024;
|
|
46
|
+
const MAX_BINARY_BODY_BASE64_LENGTH = Math.ceil(MAX_BINARY_BODY_BYTES / 3) * 4;
|
|
47
|
+
const MAX_PENDING_BINARY_BODY_BYTES = 64 * 1024 * 1024;
|
|
40
48
|
// After a run ends, its host calls are aborted. Wait this long for them to settle.
|
|
41
49
|
const HOST_CALL_SETTLE_MS = 1_000;
|
|
42
50
|
|
|
@@ -46,6 +54,9 @@ export type QuickJSJobResult = {
|
|
|
46
54
|
leakedHostCalls: number;
|
|
47
55
|
};
|
|
48
56
|
const MAX_STACK_BYTES = 1024 * 1024;
|
|
57
|
+
// Bun's getRandomValues has no 64 KiB quota, so neither does the sandbox. This
|
|
58
|
+
// bound only keeps one sync host call's JSON reply small.
|
|
59
|
+
const MAX_RANDOM_BYTES = 1024 * 1024;
|
|
49
60
|
const TIMEOUT_EXIT_CODE = 124;
|
|
50
61
|
|
|
51
62
|
type ErrorPayload = { name: string; message: string; stack: string };
|
|
@@ -126,12 +137,51 @@ type SandboxRequest = {
|
|
|
126
137
|
method?: string;
|
|
127
138
|
headers?: [string, string][];
|
|
128
139
|
body?: string;
|
|
140
|
+
/** Binary body, base64-encoded by the sandbox. */
|
|
141
|
+
bodyBase64?: string;
|
|
129
142
|
retries?: number;
|
|
130
143
|
timeoutMs?: number;
|
|
131
144
|
};
|
|
132
145
|
|
|
146
|
+
const FETCH_PATHS = new Set(["fetch", "stdlib.fetch", "stdlib.fetchJson"]);
|
|
147
|
+
const BASE64_RE = /^[A-Za-z0-9+/]*={0,2}$/;
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Checks a sandbox fetch request's base64 body without decoding it and returns
|
|
151
|
+
* its decoded size (0 when there is none). The sandbox controls the value, so
|
|
152
|
+
* the type, alphabet and size are checked before the host allocates anything.
|
|
153
|
+
*/
|
|
154
|
+
function binaryBodySize(request: unknown): number {
|
|
155
|
+
if (!request || typeof request !== "object") return 0;
|
|
156
|
+
const encoded = (request as { bodyBase64?: unknown }).bodyBase64;
|
|
157
|
+
if (encoded === undefined) return 0;
|
|
158
|
+
if (typeof encoded !== "string") throw new TypeError("fetch bodyBase64 must be a string");
|
|
159
|
+
if (encoded.length > MAX_BINARY_BODY_BASE64_LENGTH) {
|
|
160
|
+
throw new RangeError(
|
|
161
|
+
`fetch binary body exceeds the ${MAX_BINARY_BODY_BYTES}-byte limit of the quickjs executor`,
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
if (encoded.length % 4 !== 0 || !BASE64_RE.test(encoded)) {
|
|
165
|
+
throw new TypeError("fetch bodyBase64 is not valid base64");
|
|
166
|
+
}
|
|
167
|
+
const padding = encoded.endsWith("==") ? 2 : encoded.endsWith("=") ? 1 : 0;
|
|
168
|
+
const size = (encoded.length / 4) * 3 - padding;
|
|
169
|
+
if (size > MAX_BINARY_BODY_BYTES) {
|
|
170
|
+
throw new RangeError(
|
|
171
|
+
`fetch binary body exceeds the ${MAX_BINARY_BODY_BYTES}-byte limit of the quickjs executor`,
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
return size;
|
|
175
|
+
}
|
|
176
|
+
|
|
133
177
|
function requestInit(request: SandboxRequest): RequestInit {
|
|
134
|
-
|
|
178
|
+
if (request.bodyBase64 === undefined) {
|
|
179
|
+
return { method: request.method, headers: request.headers, body: request.body };
|
|
180
|
+
}
|
|
181
|
+
// Already checked by binaryBodySize when the call was accepted.
|
|
182
|
+
const bytes = Buffer.from(request.bodyBase64, "base64");
|
|
183
|
+
const body = new Uint8Array(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
184
|
+
return { method: request.method, headers: request.headers, body };
|
|
135
185
|
}
|
|
136
186
|
|
|
137
187
|
async function serializeResponse(response: Response, operation: string) {
|
|
@@ -223,6 +273,16 @@ function dispatchSync(config: SwarmConfig, op: string, value: unknown): unknown
|
|
|
223
273
|
return Redacted.meta(redactedFor(config, value));
|
|
224
274
|
case "config.has":
|
|
225
275
|
return typeof value === "string" && config.get(value) !== undefined;
|
|
276
|
+
case "crypto.random": {
|
|
277
|
+
if (
|
|
278
|
+
!Number.isInteger(value) ||
|
|
279
|
+
(value as number) < 0 ||
|
|
280
|
+
(value as number) > MAX_RANDOM_BYTES
|
|
281
|
+
) {
|
|
282
|
+
throw new Error(`crypto.random needs a byte count between 0 and ${MAX_RANDOM_BYTES}`);
|
|
283
|
+
}
|
|
284
|
+
return Array.from(crypto.getRandomValues(new Uint8Array(value as number)));
|
|
285
|
+
}
|
|
226
286
|
case "table":
|
|
227
287
|
return table(value as Array<Record<string, unknown>>);
|
|
228
288
|
default:
|
|
@@ -287,6 +347,7 @@ class HostCallScope {
|
|
|
287
347
|
private readonly waiters: Array<() => void> = [];
|
|
288
348
|
private readonly outerFetch = globalThis.fetch;
|
|
289
349
|
private active = 0;
|
|
350
|
+
private bodyBytes = 0;
|
|
290
351
|
|
|
291
352
|
constructor() {
|
|
292
353
|
const outer = this.outerFetch;
|
|
@@ -306,8 +367,11 @@ class HostCallScope {
|
|
|
306
367
|
return this.controller.signal;
|
|
307
368
|
}
|
|
308
369
|
|
|
309
|
-
/**
|
|
310
|
-
|
|
370
|
+
/**
|
|
371
|
+
* Track a host call. `limited` calls share the per-run concurrency budget.
|
|
372
|
+
* `bodyBytes` counts against the per-run binary body budget until it settles.
|
|
373
|
+
*/
|
|
374
|
+
call<T>(run: () => Promise<T>, limited: boolean, bodyBytes = 0): Promise<T> {
|
|
311
375
|
if (this.ops.size >= MAX_PENDING_HOST_CALLS) {
|
|
312
376
|
return Promise.reject(
|
|
313
377
|
new Error(
|
|
@@ -315,12 +379,21 @@ class HostCallScope {
|
|
|
315
379
|
),
|
|
316
380
|
);
|
|
317
381
|
}
|
|
382
|
+
if (this.bodyBytes + bodyBytes > MAX_PENDING_BINARY_BODY_BYTES) {
|
|
383
|
+
return Promise.reject(
|
|
384
|
+
new RangeError(
|
|
385
|
+
`Pending fetch binary bodies in one script run exceed ${MAX_PENDING_BINARY_BODY_BYTES} bytes`,
|
|
386
|
+
),
|
|
387
|
+
);
|
|
388
|
+
}
|
|
389
|
+
this.bodyBytes += bodyBytes;
|
|
318
390
|
const op = limited ? this.limited(run) : run();
|
|
319
391
|
this.ops.add(op);
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
392
|
+
const settle = () => {
|
|
393
|
+
this.ops.delete(op);
|
|
394
|
+
this.bodyBytes -= bodyBytes;
|
|
395
|
+
};
|
|
396
|
+
void op.then(settle, settle);
|
|
324
397
|
return op;
|
|
325
398
|
}
|
|
326
399
|
|
|
@@ -491,7 +564,10 @@ async function evaluateJob(
|
|
|
491
564
|
define("__host_call", (pathHandle, argsHandle) => {
|
|
492
565
|
const path = ctx.getString(pathHandle);
|
|
493
566
|
const callArgs = JSON.parse(ctx.getString(argsHandle)) as unknown[];
|
|
494
|
-
return bridge(
|
|
567
|
+
return bridge(async () => {
|
|
568
|
+
const bodyBytes = FETCH_PATHS.has(path) ? binaryBodySize(callArgs?.[0]) : 0;
|
|
569
|
+
return host.call(() => dispatchCall(hostCtx, path, callArgs, host.signal), true, bodyBytes);
|
|
570
|
+
});
|
|
495
571
|
});
|
|
496
572
|
define("__host_sync", (opHandle, valueHandle) => {
|
|
497
573
|
let envelope: unknown;
|
|
@@ -538,6 +614,7 @@ async function evaluateJob(
|
|
|
538
614
|
const init = `globalThis.__swarm_shape = ${JSON.stringify(shape)};\nglobalThis.__swarm_args = ${JSON.stringify(args)};`;
|
|
539
615
|
const ok =
|
|
540
616
|
evaluate(init, "swarm-init.js") &&
|
|
617
|
+
evaluate(SANDBOX_WEB_SHIMS, "swarm-web-shims.js") &&
|
|
541
618
|
evaluate(SANDBOX_PRELUDE, "swarm-prelude.js") &&
|
|
542
619
|
evaluate(bundle.code, BUNDLE_FILE) &&
|
|
543
620
|
evaluate(SANDBOX_EPILOGUE, "swarm-epilogue.js");
|