@desplega.ai/agent-swarm 2.6.0 → 2.7.1
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-8dn5tq56.js} +6 -6
- package/dist/{actions-fbqma8aa.js → actions-3r6gw7dy.js} +18 -18
- package/dist/{amp-adapter-en0jy6hs.js → amp-adapter-wjtq413p.js} +3 -3
- package/dist/{anthropic-messages-txs190dd.js → anthropic-messages-n4cby1rz.js} +4 -4
- package/dist/{app-k8a5cgpp.js → app-d53yhykk.js} +3 -3
- package/dist/{assistant-a16433gr.js → assistant-ksrawj3k.js} +20 -20
- package/dist/{boot-reembed-6c1sjfnr.js → boot-reembed-fxfw17m2.js} +11 -3
- package/dist/{boot-reembed-f4nn561n.js → boot-reembed-k5y5xbj0.js} +15 -8
- package/dist/{boot-scrub-sweep-d4w9gajf.js → boot-scrub-sweep-se41j9fd.js} +3 -3
- package/dist/{boot-seeds-9bmkspyw.js → boot-seeds-r3kwwv8b.js} +4 -4
- package/dist/{claude-adapter-nys1kjxa.js → claude-adapter-ahmyf231.js} +8 -8
- package/dist/{claude-managed-adapter-55207973.js → claude-managed-adapter-5knamegc.js} +3 -3
- package/dist/{claude-managed-setup-3cm35gey.js → claude-managed-setup-mdc7dy6b.js} +3 -3
- package/dist/{claude-sdk-session-nw5yd8cp.js → claude-sdk-session-n5wen9zk.js} +8729 -8600
- package/dist/{cli-683y0b3a.js → cli-0dqtv2jg.js} +2 -2
- package/dist/{cli-a34pr3w1.js → cli-0tww7dnz.js} +2 -2
- package/dist/{cli-w2656ka1.js → cli-0w4e4v58.js} +7 -7
- package/dist/{cli-5sckpqcx.js → cli-2sjm3t4r.js} +1 -1
- package/dist/{cli-gf2850q7.js → cli-3cywb44q.js} +18 -1
- package/dist/{cli-8wkb09q5.js → cli-3k9jej0c.js} +2 -2
- package/dist/{cli-3aqd4jaa.js → cli-3v2cyqb8.js} +3 -3
- package/dist/{cli-emhjcj2y.js → cli-5qt9vzmq.js} +89 -20
- package/dist/{cli-abkm71vr.js → cli-5rp1w4cr.js} +1 -1
- package/dist/{cli-gf95q3bm.js → cli-6vwjsetx.js} +4 -4
- package/dist/{cli-k21gx5ht.js → cli-6w2yrrc0.js} +10 -10
- package/dist/{cli-m70bajzr.js → cli-75tdydgv.js} +1 -1
- package/dist/{cli-5rj83bam.js → cli-8feswf2v.js} +47 -36
- package/dist/{cli-sxgtf5fw.js → cli-8wd3mbyj.js} +1 -1
- package/dist/{cli-ca6banwz.js → cli-9csfthbw.js} +10 -8
- package/dist/{cli-dqygeeh5.js → cli-b41p764a.js} +31 -3
- package/dist/{cli-m5a9m0ak.js → cli-c1t533hh.js} +3 -3
- package/dist/{cli-2sjhpsns.js → cli-c28jxpcv.js} +1 -1
- package/dist/{cli-bqztfyve.js → cli-cb50g9cd.js} +1 -1
- package/dist/{cli-ag2yrnt5.js → cli-d8gaphtr.js} +2 -2
- package/dist/{cli-9abdgn5t.js → cli-dgtfysne.js} +3 -3
- package/dist/{cli-nq677csy.js → cli-dscnhn18.js} +1 -1
- package/dist/{cli-3fwqy71j.js → cli-dv11pgpv.js} +39 -32
- package/dist/{cli-v49fkghp.js → cli-ekptgk2m.js} +6 -6
- package/dist/{cli-c2habvsf.js → cli-f3mzy662.js} +17 -17
- package/dist/{cli-zqne1qcj.js → cli-fk7k8s9x.js} +2 -2
- package/dist/{cli-1tem6ns9.js → cli-gt4h8cxw.js} +3 -2
- package/dist/{cli-vbhgkw9h.js → cli-h3e7bhvn.js} +3 -3
- package/dist/{cli-ffvsj2qw.js → cli-hkxqdmnb.js} +3 -3
- package/dist/{cli-a2rr0fmg.js → cli-hpkvgdad.js} +7 -4
- package/dist/{cli-art7c1zh.js → cli-hq2v4rr5.js} +37 -9
- package/dist/{cli-sqma6vw1.js → cli-k1zb7raw.js} +5 -5
- package/dist/{cli-4rvr7zfx.js → cli-k4awgvzm.js} +5 -0
- package/dist/{cli-3ndmpxxv.js → cli-k7d3hvya.js} +2 -2
- package/dist/{cli-3gfvmzht.js → cli-kbtdftbw.js} +74 -26
- package/dist/{cli-hfeb68z4.js → cli-kj69b9tw.js} +34 -6
- package/dist/{cli-az25fn5f.js → cli-kk9jy4nm.js} +2 -2
- package/dist/{cli-39aet655.js → cli-m52t4cta.js} +1 -1
- package/dist/{cli-cz37jqss.js → cli-mdadewqp.js} +5 -5
- package/dist/{cli-esakngh8.js → cli-nf6k60sn.js} +1 -1
- package/dist/{cli-j6rktc91.js → cli-nzdf09js.js} +1 -1
- package/dist/{cli-rr7zx19j.js → cli-prcyzecg.js} +12 -12
- package/dist/{cli-4hxrf0p4.js → cli-pzafyqtk.js} +393 -62
- package/dist/{cli-g8ka5j4j.js → cli-qvaa1gjp.js} +173 -52
- package/dist/{cli-d2dpf95n.js → cli-rg4b2g6j.js} +1 -1
- package/dist/{cli-kf4jree9.js → cli-rm66vqdz.js} +1 -1
- package/dist/{cli-4rf0f5vg.js → cli-s504c3w7.js} +5 -3
- package/dist/{cli-nn6vx2mq.js → cli-swkx9jm2.js} +7 -5
- package/dist/{cli-g20jjwt4.js → cli-thsaq4pd.js} +16 -16
- package/dist/{cli-aj8djyqt.js → cli-twjnyvs8.js} +2 -2
- package/dist/{cli-03gg9pd1.js → cli-typdpe8g.js} +4 -4
- package/dist/{cli-mtnj4fwa.js → cli-vg8pwmsd.js} +6 -6
- package/dist/{cli-gnsb1zxa.js → cli-vhpckzvp.js} +1 -1
- package/dist/{cli-7enjwya9.js → cli-vz78s5wm.js} +1 -1
- package/dist/{cli-dv97kdzv.js → cli-wdxgsdz5.js} +2 -2
- package/dist/{cli-7a6p4e65.js → cli-wtjg6je3.js} +1 -1
- package/dist/{cli-6y51ryva.js → cli-x3cngb1h.js} +44 -12
- package/dist/{cli-n7asmce6.js → cli-xbz6mcg4.js} +3 -3
- package/dist/{cli-24j1jeeb.js → cli-y4tdxsqb.js} +3 -3
- package/dist/{cli-mvjz5an3.js → cli-yqrsp3ny.js} +7 -7
- package/dist/cli.js +31 -21
- package/dist/{codex-adapter-d95q4qsn.js → codex-adapter-7wepzq15.js} +6 -6
- package/dist/{codex-hook-brpnycn5.js → codex-hook-pbdh4xhx.js} +2 -2
- package/dist/{codex-session-runner-gdwd8h0c.js → codex-session-runner-02sb49e4.js} +6 -6
- package/dist/{commands-jqmgk54b.js → commands-3rcspxct.js} +2 -2
- package/dist/{controller-z3jq589f.js → controller-8ar4avd6.js} +3 -3
- package/dist/{controllers-connect-r2za39ef.js → controllers-connect-ajvh9h0v.js} +19 -19
- package/dist/{cursor-adapter-4mr4jgkw.js → cursor-adapter-gd0sa7bh.js} +1 -1
- package/dist/{db-w36wwb5g.js → db-dv9j7ts5.js} +2 -2
- package/dist/{db-ppv2tze7.js → db-z26vgdvj.js} +3 -3
- package/dist/{dispatcher-pv5h77vf.js → dispatcher-8m0htjq8.js} +17 -17
- package/dist/{dsh-adapter-d4pdpb3r.js → dsh-adapter-y8gyk130.js} +1 -1
- package/dist/{e2b-fn3zacvt.js → e2b-74vg9b7e.js} +1 -1
- package/dist/{e2b-222hsajc.js → e2b-jyay8wzn.js} +1 -1
- package/dist/{easter-egg-3d-4sp4dvmc.js → easter-egg-3d-28f880xx.js} +1 -1
- package/dist/{execute-jev839y1.js → execute-9seb79vh.js} +1 -1
- package/dist/{external-channel-y6nkzhnn.js → external-channel-smz7jyth.js} +2 -2
- package/dist/{getMachineId-bsd-brn0xym3.js → getMachineId-bsd-whfre6gr.js} +3 -3
- package/dist/{getMachineId-darwin-t339sfxm.js → getMachineId-darwin-5zjynb19.js} +3 -3
- package/dist/{getMachineId-linux-5eq95d8b.js → getMachineId-linux-8mksrrsq.js} +3 -3
- package/dist/{getMachineId-unsupported-qavrtc7j.js → getMachineId-unsupported-fxtqsdt2.js} +3 -3
- package/dist/{getMachineId-win-q7fe1wx7.js → getMachineId-win-nzfmz857.js} +3 -3
- package/dist/{grok-adapter-t08b9ntm.js → grok-adapter-z0nnvtbn.js} +7 -7
- package/dist/handlers-pspryh6g.js +41 -0
- package/dist/{heartbeat-vn31xwdk.js → heartbeat-f928aqyp.js} +20 -20
- package/dist/{hook-cpjs8q51.js → hook-d7c605r4.js} +7 -7
- package/dist/{hook-6d0zbvvt.js → hook-q9hv5xf9.js} +5 -5
- package/dist/{http-9c6medda.js → http-q6j4k989.js} +1000 -251
- package/dist/{index-k0yt1tvq.js → index-5nb266jr.js} +17 -17
- package/dist/{index-ysd45kgc.js → index-8fxt5eah.js} +10 -10
- package/dist/{index-zgvqzjsw.js → index-cekzwhez.js} +5 -5
- package/dist/{index-38t35h0n.js → index-ch0c1xff.js} +17 -17
- package/dist/{index-pkma1yf8.js → index-ebnjhxh1.js} +1 -1
- package/dist/{index-yqgbfgaj.js → index-ed8dy8rn.js} +20 -20
- package/dist/{index-qhjf2htm.js → index-ejqdkkqx.js} +262 -96
- package/dist/{index-dr94zbnh.js → index-farm1zsn.js} +32661 -29443
- package/dist/{index-g6k9tctj.js → index-yn1yxg85.js} +1 -1
- package/dist/{keepalive-em0117bp.js → keepalive-zzc3hks1.js} +3 -3
- package/dist/lead-c0pjcaw4.js +79 -0
- package/dist/{maintenance-kbme3qy9.js → maintenance-kqsxzpzx.js} +1 -1
- package/dist/{oauth-refresh-sweep-xysj4yse.js → oauth-refresh-sweep-7qt0g55z.js} +8 -3
- package/dist/{onboard-p6q0vz97.js → onboard-k7x13fqg.js} +121 -8
- package/dist/{opencode-adapter-esv3k9yn.js → opencode-adapter-tqvrtter.js} +5 -5
- package/dist/{otel-impl-54qhqh1z.js → otel-impl-sqkf6k3b.js} +97 -97
- package/dist/{pi-mono-adapter-mymbwhj0.js → pi-mono-adapter-m2qnafxm.js} +89 -14
- package/dist/{pricing-refresh-yb75wv33.js → pricing-refresh-wkpwhgvs.js} +3 -3
- package/dist/{rbac-roles-dexhc9x8.js → rbac-roles-dpn0c0wj.js} +7 -4
- package/dist/{rbac-roles-1vp2mw4p.js → rbac-roles-wk7xd6a6.js} +2 -2
- package/dist/{render-v2-tjay5hhf.js → render-v2-ex2m4g2k.js} +3 -3
- package/dist/{resume-8ayf37g5.js → resume-725g9b0b.js} +7 -7
- package/dist/{runtime-hqe5cqcd.js → runtime-kajas15g.js} +1 -1
- package/dist/{session-access-fdhp987s.js → session-access-0j2ezjje.js} +8 -4
- package/dist/{session-source-backfill-qgtc86z1.js → session-source-backfill-62202awe.js} +2 -2
- package/dist/{setup-4rrmqcq3.js → setup-9sj11ax6.js} +1 -1
- package/dist/{virtual-modules-52km00d6.js → virtual-modules-c6fcq6zr.js} +2 -2
- package/dist/{work-objects-p28fgd70.js → work-objects-0y07495y.js} +20 -20
- package/dist/worker-d4q9sqbc.js +79 -0
- package/openapi.json +1536 -407
- package/package.json +7 -5
- package/src/be/automation-preflight.ts +2 -0
- package/src/be/connector-codes.ts +65 -22
- 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/boot-reembed.ts +4 -1
- package/src/be/memory/constants.ts +6 -0
- package/src/be/memory/embedding-recovery.ts +48 -0
- package/src/be/memory/index-content.ts +5 -2
- package/src/be/memory/providers/openai-embedding.ts +65 -14
- package/src/be/memory/task-learning.ts +257 -0
- package/src/be/memory/types.ts +13 -2
- package/src/be/migrations/245_script_executor.sql +3 -0
- package/src/be/migrations/246_connector_code_kind.sql +11 -0
- package/src/be/oauth-refresh-sweep.ts +2 -1
- package/src/be/onboarding.ts +125 -15
- package/src/be/rbac-roles.ts +4 -1
- package/src/be/scripts/boot-reembed.ts +2 -0
- package/src/be/scripts/db.ts +14 -4
- package/src/be/scripts/embeddings.ts +4 -2
- package/src/be/scripts/run-saved.ts +1 -0
- package/src/be/scripts/typecheck.ts +9 -3
- package/src/be/seed/agent-fs-provision.ts +75 -14
- package/src/be/seed-skills/index.ts +7 -0
- package/src/be/swarm-config-guard.ts +3 -0
- package/src/cli.tsx +9 -0
- package/src/comb/config.ts +6 -1
- package/src/commands/onboard/agent-fs-version.ts +5 -0
- package/src/commands/onboard/compose-generator.ts +117 -4
- package/src/commands/onboard/env-generator.ts +9 -0
- package/src/commands/onboard/tei-image.ts +12 -0
- package/src/commands/onboard/types.ts +7 -0
- package/src/commands/onboard.tsx +11 -1
- package/src/commands/provider-credentials.ts +2 -1
- package/src/commands/runner.ts +14 -1
- package/src/heartbeat/heartbeat.ts +14 -1
- package/src/heartbeat/script-runner-probe.ts +47 -0
- package/src/http/agentfs-proxy.ts +369 -0
- package/src/http/all-routes.ts +1 -0
- package/src/http/api-keys.ts +24 -9
- package/src/http/codex-oauth-device.ts +8 -1
- package/src/http/fs.ts +64 -10
- package/src/http/idempotency-key.ts +1 -1
- package/src/http/inbox-state.ts +27 -1
- package/src/http/index.ts +7 -4
- package/src/http/memory.ts +7 -4
- package/src/http/onboarding.ts +139 -11
- package/src/http/pages-public.ts +38 -5
- package/src/http/scripts.ts +27 -0
- package/src/http/session-access.ts +24 -0
- package/src/http/sessions.ts +10 -6
- package/src/http/tasks.ts +85 -11
- package/src/http/telemetry-ui.ts +1 -1
- package/src/http/user-roles.ts +4 -1
- package/src/http/users.ts +272 -3
- package/src/http/utils.ts +22 -3
- package/src/http/workflows.ts +10 -0
- package/src/http/x.ts +5 -0
- package/src/prompts/base-prompt.ts +45 -10
- package/src/prompts/session-templates.ts +43 -5
- package/src/providers/amp-adapter.ts +1 -1
- package/src/providers/codex-adapter.ts +6 -1
- package/src/providers/cursor-adapter.ts +1 -1
- package/src/providers/dsh-adapter.ts +1 -1
- package/src/providers/pi-mono-adapter.ts +99 -14
- package/src/rbac/legacy-policy.ts +1 -0
- package/src/rbac/permissions.ts +4 -0
- 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/agent-fs-provision-seeder.test.ts +261 -92
- package/src/tests/agentfs-proxy.test.ts +1106 -0
- package/src/tests/api-key-status-by-suffix.test.ts +242 -0
- package/src/tests/base-prompt.test.ts +139 -4
- package/src/tests/codex-adapter.test.ts +46 -8
- package/src/tests/codex-oauth-device.test.ts +12 -1
- package/src/tests/comb-config-guard.test.ts +20 -0
- package/src/tests/comb-links.test.ts +79 -1
- package/src/tests/comb-status.test.ts +44 -0
- 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-footprint.test.js +3 -3
- 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/fixtures/onboard/baseline-states.ts +68 -0
- package/src/tests/fixtures/onboard/content.compose.yml.txt +190 -0
- package/src/tests/fixtures/onboard/content.env.txt +29 -0
- package/src/tests/fixtures/onboard/dev.compose.yml.txt +155 -0
- package/src/tests/fixtures/onboard/dev.env.txt +28 -0
- package/src/tests/fixtures/onboard/full-all.compose.yml.txt +582 -0
- package/src/tests/fixtures/onboard/full-all.env.txt +59 -0
- package/src/tests/fixtures/onboard/full.compose.yml.txt +435 -0
- package/src/tests/fixtures/onboard/full.env.txt +36 -0
- package/src/tests/fixtures/onboard/research.compose.yml.txt +155 -0
- package/src/tests/fixtures/onboard/research.env.txt +28 -0
- package/src/tests/fixtures/onboard/solo.compose.yml.txt +85 -0
- package/src/tests/fixtures/onboard/solo.env.txt +26 -0
- package/src/tests/heartbeat-checklist.test.ts +89 -0
- package/src/tests/heartbeat-script-runner-probe.test.ts +83 -0
- package/src/tests/http-log-scrubbing.test.ts +34 -1
- package/src/tests/http-task-write-ownership.test.ts +5 -1
- package/src/tests/invites-http.test.ts +464 -0
- package/src/tests/memory-embedding-input-cap.test.ts +122 -1
- package/src/tests/memory-embedding-recovery.test.ts +137 -0
- package/src/tests/oauth-refresh-sweep.test.ts +34 -1
- package/src/tests/onboard-compose.test.ts +162 -0
- package/src/tests/onboard-env.test.ts +24 -0
- package/src/tests/onboard-non-interactive.test.ts +29 -0
- package/src/tests/onboard-tei-image.test.ts +36 -0
- package/src/tests/onboarding.test.ts +451 -4
- package/src/tests/openai-embedding-provider.test.ts +105 -2
- 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-mono-session-cost-on-abort.test.ts +270 -0
- package/src/tests/pi-native-mcp.test.ts +13 -1
- package/src/tests/pi-tool-exposure.test.ts +8 -8
- package/src/tests/preload-env.test.ts +4 -0
- package/src/tests/preload.ts +4 -0
- package/src/tests/probe-tool-schema-tokens.test.ts +144 -0
- package/src/tests/prompt-template-session.test.ts +20 -3
- package/src/tests/rbac-admission-e2e.test.ts +22 -6
- package/src/tests/rbac-admission.test.ts +342 -4
- package/src/tests/rbac-engine.test.ts +1 -0
- package/src/tests/rbac-roles.test.ts +9 -1
- 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/send-task-requester-gate.test.ts +64 -3
- package/src/tests/sessions-move.test.ts +39 -1
- package/src/tests/store-progress-learning.test.ts +381 -0
- package/src/tests/ui-logs-parser.test.ts +180 -0
- package/src/tests/whoami-route.test.ts +41 -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/inject-learning.ts +1 -1
- package/src/tools/memory-edit.ts +1 -1
- package/src/tools/memory-search.ts +1 -1
- 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 +71 -1
- package/src/tools/utils.ts +12 -1
- package/src/tools/workflows/trigger-workflow.ts +104 -10
- package/src/types.ts +12 -1
- package/src/utils/constants.ts +39 -0
- package/src/utils/credentials.ts +5 -2
- package/src/utils/task-attachment-links.ts +15 -3
- 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
- package/dist/lead-23e7z59t.js +0 -79
- package/dist/worker-e3w7vye1.js +0 -79
- package/dist/{azure-openai-responses-9kfwhmn7.js → azure-openai-responses-43asa23s.js} +0 -0
- package/dist/{node-vxx896fa.js → node-t28cc56b.js} +0 -0
- package/dist/{openai-codex-responses-79s8xnbh.js → openai-codex-responses-jhr3sjtm.js} +3 -3
- package/dist/{openai-completions-tdbyts0x.js → openai-completions-gs6yn37k.js} +3 -3
- /package/dist/{openai-responses-dm3t5vqp.js → openai-responses-jqm9vwwk.js} +0 -0
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `PAGES_FRAME_ANCESTORS` widens `frame-ancestors` on `/p/:id` for PUBLIC
|
|
3
|
+
* pages only, so an outside site (for example a blog) can iframe a chart.
|
|
4
|
+
* Authed and password pages keep the app-origin-only header, and an unset
|
|
5
|
+
* env leaves every header unchanged.
|
|
6
|
+
*/
|
|
7
|
+
import { afterAll, afterEach, beforeAll, describe, expect, test } from "bun:test";
|
|
8
|
+
import crypto from "node:crypto";
|
|
9
|
+
import { unlink } from "node:fs/promises";
|
|
10
|
+
import {
|
|
11
|
+
createServer as createHttpServer,
|
|
12
|
+
type IncomingMessage,
|
|
13
|
+
type Server,
|
|
14
|
+
type ServerResponse,
|
|
15
|
+
} from "node:http";
|
|
16
|
+
import { closeDb, initDb } from "../be/db";
|
|
17
|
+
import { handlePages } from "../http/pages";
|
|
18
|
+
import { getPageFrameAncestors, handlePagesPublic } from "../http/pages-public";
|
|
19
|
+
import { getPathSegments, parseQueryParams } from "../http/utils";
|
|
20
|
+
import { signPageSession } from "../utils/page-session";
|
|
21
|
+
import { listenOnFreePort } from "./test-net";
|
|
22
|
+
|
|
23
|
+
const TEST_DB_PATH = "./test-pages-frame-ancestors.sqlite";
|
|
24
|
+
const EMBEDDERS = "https://www.agent-swarm.dev https://agent-swarm.dev";
|
|
25
|
+
let BASE = "";
|
|
26
|
+
|
|
27
|
+
function createTestServer(): Server {
|
|
28
|
+
return createHttpServer(async (req: IncomingMessage, res: ServerResponse) => {
|
|
29
|
+
const pathSegments = getPathSegments(req.url || "");
|
|
30
|
+
const queryParams = parseQueryParams(req.url || "");
|
|
31
|
+
const myAgentId = req.headers["x-agent-id"] as string | undefined;
|
|
32
|
+
if (await handlePagesPublic(req, res, pathSegments, queryParams)) return;
|
|
33
|
+
if (await handlePages(req, res, pathSegments, queryParams, myAgentId)) return;
|
|
34
|
+
res.writeHead(404);
|
|
35
|
+
res.end("not found");
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function frameAncestors(res: Response): string {
|
|
40
|
+
const csp = res.headers.get("content-security-policy") ?? "";
|
|
41
|
+
return csp.split(";").find((d) => d.trim().startsWith("frame-ancestors ")) ?? "";
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
describe("PAGES_FRAME_ANCESTORS on /p/:id", () => {
|
|
45
|
+
let server: Server;
|
|
46
|
+
const agentId = crypto.randomUUID();
|
|
47
|
+
const headers = { "Content-Type": "application/json", "X-Agent-ID": agentId };
|
|
48
|
+
const ORIG = {
|
|
49
|
+
PAGES_FRAME_ANCESTORS: process.env.PAGES_FRAME_ANCESTORS,
|
|
50
|
+
APP_URL: process.env.APP_URL,
|
|
51
|
+
DASHBOARD_URL: process.env.DASHBOARD_URL,
|
|
52
|
+
PAGE_SESSION_SECRET: process.env.PAGE_SESSION_SECRET,
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
function restore(key: keyof typeof ORIG): void {
|
|
56
|
+
if (ORIG[key] === undefined) delete process.env[key];
|
|
57
|
+
else process.env[key] = ORIG[key];
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async function createPage(
|
|
61
|
+
slug: string,
|
|
62
|
+
authMode: "public" | "authed" | "password",
|
|
63
|
+
): Promise<string> {
|
|
64
|
+
const post = await fetch(`${BASE}/api/pages`, {
|
|
65
|
+
method: "POST",
|
|
66
|
+
headers,
|
|
67
|
+
body: JSON.stringify({
|
|
68
|
+
slug,
|
|
69
|
+
title: slug,
|
|
70
|
+
contentType: "text/html",
|
|
71
|
+
authMode,
|
|
72
|
+
...(authMode === "password" ? { password: "swordfish" } : {}),
|
|
73
|
+
body: "<!doctype html><html><head></head><body><h1>chart</h1></body></html>",
|
|
74
|
+
}),
|
|
75
|
+
});
|
|
76
|
+
expect(post.status).toBe(201);
|
|
77
|
+
const { id } = (await post.json()) as { id: string };
|
|
78
|
+
return id;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
beforeAll(async () => {
|
|
82
|
+
process.env.PAGE_SESSION_SECRET = "example-test-frame-ancestors-secret";
|
|
83
|
+
process.env.APP_URL = "https://app.example.test";
|
|
84
|
+
delete process.env.DASHBOARD_URL;
|
|
85
|
+
for (const suffix of ["", "-wal", "-shm"]) {
|
|
86
|
+
try {
|
|
87
|
+
await unlink(`${TEST_DB_PATH}${suffix}`);
|
|
88
|
+
} catch {}
|
|
89
|
+
}
|
|
90
|
+
initDb(TEST_DB_PATH);
|
|
91
|
+
server = createTestServer();
|
|
92
|
+
const port = await listenOnFreePort(server);
|
|
93
|
+
BASE = `http://localhost:${port}`;
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
afterEach(() => restore("PAGES_FRAME_ANCESTORS"));
|
|
97
|
+
|
|
98
|
+
afterAll(async () => {
|
|
99
|
+
await new Promise<void>((resolve) => server.close(() => resolve()));
|
|
100
|
+
closeDb();
|
|
101
|
+
for (const key of Object.keys(ORIG) as (keyof typeof ORIG)[]) restore(key);
|
|
102
|
+
for (const suffix of ["", "-wal", "-shm"]) {
|
|
103
|
+
try {
|
|
104
|
+
await unlink(`${TEST_DB_PATH}${suffix}`);
|
|
105
|
+
} catch {}
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test("public page gets the configured origins", async () => {
|
|
110
|
+
process.env.PAGES_FRAME_ANCESTORS = EMBEDDERS;
|
|
111
|
+
const id = await createPage("public-embed", "public");
|
|
112
|
+
const res = await fetch(`${BASE}/p/${id}`);
|
|
113
|
+
expect(res.status).toBe(200);
|
|
114
|
+
const fa = frameAncestors(res);
|
|
115
|
+
expect(fa).toContain("'self'");
|
|
116
|
+
expect(fa).toContain("https://app.example.test");
|
|
117
|
+
expect(fa).toContain("https://www.agent-swarm.dev");
|
|
118
|
+
expect(fa).toContain("https://agent-swarm.dev");
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
test("authed page keeps the app-origin-only header", async () => {
|
|
122
|
+
const id = await createPage("authed-embed", "authed");
|
|
123
|
+
const exp = Math.floor(Date.now() / 1000) + 3600;
|
|
124
|
+
const cookie = `page_session_${id}=${await signPageSession({ pageId: id, exp })}`;
|
|
125
|
+
|
|
126
|
+
const before = frameAncestors(await fetch(`${BASE}/p/${id}`, { headers: { cookie } }));
|
|
127
|
+
process.env.PAGES_FRAME_ANCESTORS = EMBEDDERS;
|
|
128
|
+
const res = await fetch(`${BASE}/p/${id}`, { headers: { cookie } });
|
|
129
|
+
expect(res.status).toBe(200);
|
|
130
|
+
expect(frameAncestors(res)).toBe(before);
|
|
131
|
+
expect(frameAncestors(res)).not.toContain("agent-swarm.dev");
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
test("password page keeps the app-origin-only header", async () => {
|
|
135
|
+
process.env.PAGES_FRAME_ANCESTORS = EMBEDDERS;
|
|
136
|
+
const id = await createPage("password-embed", "password");
|
|
137
|
+
const res = await fetch(`${BASE}/p/${id}?key=swordfish`);
|
|
138
|
+
expect(res.status).toBe(200);
|
|
139
|
+
expect(frameAncestors(res)).not.toContain("agent-swarm.dev");
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
test("unset env leaves the public header unchanged", async () => {
|
|
143
|
+
const id = await createPage("public-unset", "public");
|
|
144
|
+
delete process.env.PAGES_FRAME_ANCESTORS;
|
|
145
|
+
const unset = frameAncestors(await fetch(`${BASE}/p/${id}`));
|
|
146
|
+
process.env.PAGES_FRAME_ANCESTORS = "";
|
|
147
|
+
const blank = frameAncestors(await fetch(`${BASE}/p/${id}`));
|
|
148
|
+
expect(unset).toContain("https://app.example.test");
|
|
149
|
+
expect(unset).not.toContain("agent-swarm.dev");
|
|
150
|
+
expect(blank).toBe(unset);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
test("parser accepts comma or space lists and drops invalid entries", () => {
|
|
154
|
+
process.env.PAGES_FRAME_ANCESTORS =
|
|
155
|
+
"https://www.agent-swarm.dev/, https://*.example.com:8443 blog.example.org,* 'none' https://evil.test;script-src javascript:alert(1) https://x.test/path";
|
|
156
|
+
expect(getPageFrameAncestors()).toEqual([
|
|
157
|
+
"https://www.agent-swarm.dev",
|
|
158
|
+
"https://*.example.com:8443",
|
|
159
|
+
"blog.example.org",
|
|
160
|
+
]);
|
|
161
|
+
});
|
|
162
|
+
});
|
|
@@ -104,7 +104,8 @@ describe("PiMonoAdapter.createSession — codemode", () => {
|
|
|
104
104
|
process.env.OPENROUTER_BASE_URL = "http://127.0.0.1:9/api/v1";
|
|
105
105
|
delete process.env.PI_CODEMODE;
|
|
106
106
|
delete process.env.PI_CODEMODE_MODELS;
|
|
107
|
-
|
|
107
|
+
// Deferral off, so every swarm tool shows up as an active tool name.
|
|
108
|
+
process.env.PI_TOOL_DEFERRAL = "false";
|
|
108
109
|
});
|
|
109
110
|
afterEach(() => {
|
|
110
111
|
for (const key of envKeys) {
|
|
@@ -1859,3 +1859,57 @@ describe("PiMonoSession context-bound note", () => {
|
|
|
1859
1859
|
expect(await lateSteers([assistantTurn(usage(5000))])).toEqual([HELD_NOTE_TEXT]);
|
|
1860
1860
|
});
|
|
1861
1861
|
});
|
|
1862
|
+
|
|
1863
|
+
describe("PiMonoSession — per-call usage line", () => {
|
|
1864
|
+
function assistantEnd(content: unknown[], usage?: unknown): AgentSessionEvent {
|
|
1865
|
+
return {
|
|
1866
|
+
type: "message_end",
|
|
1867
|
+
message: { role: "assistant", content, stopReason: "stop", ...(usage ? { usage } : {}) },
|
|
1868
|
+
} as unknown as AgentSessionEvent;
|
|
1869
|
+
}
|
|
1870
|
+
|
|
1871
|
+
test("one call_usage raw_log line per assistant message_end with usage", async () => {
|
|
1872
|
+
const { events } = await runWithEvents([
|
|
1873
|
+
assistantEnd([{ type: "toolCall", id: "call-1", name: "bash", arguments: {} }], {
|
|
1874
|
+
input: 120,
|
|
1875
|
+
cacheRead: 9_000,
|
|
1876
|
+
cacheWrite: 400,
|
|
1877
|
+
output: 60,
|
|
1878
|
+
reasoning: 12,
|
|
1879
|
+
totalTokens: 9_580,
|
|
1880
|
+
}),
|
|
1881
|
+
{
|
|
1882
|
+
type: "message_end",
|
|
1883
|
+
message: { role: "user", content: [{ type: "text", text: "tool output" }] },
|
|
1884
|
+
} as unknown as AgentSessionEvent,
|
|
1885
|
+
// No usage (some providers omit it): no line.
|
|
1886
|
+
assistantEnd([{ type: "text", text: "thinking out loud" }]),
|
|
1887
|
+
assistantEnd([{ type: "text", text: "Done" }], { input: 5, cacheRead: 9_500, output: 3 }),
|
|
1888
|
+
]);
|
|
1889
|
+
|
|
1890
|
+
const raw = events.flatMap((e) =>
|
|
1891
|
+
e.type === "raw_log" ? [JSON.parse(e.content) as Record<string, unknown>] : [],
|
|
1892
|
+
);
|
|
1893
|
+
const usageLines = raw.filter((line) => line.type === "call_usage");
|
|
1894
|
+
expect(usageLines).toEqual([
|
|
1895
|
+
{
|
|
1896
|
+
type: "call_usage",
|
|
1897
|
+
harness: "pi",
|
|
1898
|
+
usage: { input: 120, cacheRead: 9_000, cacheWrite: 400, output: 60, reasoning: 12 },
|
|
1899
|
+
},
|
|
1900
|
+
{
|
|
1901
|
+
type: "call_usage",
|
|
1902
|
+
harness: "pi",
|
|
1903
|
+
usage: { input: 5, cacheRead: 9_500, cacheWrite: 0, output: 3, reasoning: 0 },
|
|
1904
|
+
},
|
|
1905
|
+
]);
|
|
1906
|
+
// The usage line precedes the call's own text line.
|
|
1907
|
+
const done = raw.findIndex(
|
|
1908
|
+
(line) => line.type === "assistant" && JSON.stringify(line).includes('"Done"'),
|
|
1909
|
+
);
|
|
1910
|
+
expect(done).toBeGreaterThan(0);
|
|
1911
|
+
expect(raw[done - 1]).toEqual(usageLines[1]);
|
|
1912
|
+
// Token counts only: no message content rides on the line.
|
|
1913
|
+
expect(JSON.stringify(usageLines)).not.toContain("tool output");
|
|
1914
|
+
});
|
|
1915
|
+
});
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
|
2
|
+
import { mkdirSync, rmSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import type { AgentSession } from "@earendil-works/pi-coding-agent";
|
|
5
|
+
import {
|
|
6
|
+
type ReconcileActiveTasksOptions,
|
|
7
|
+
type RunnerState,
|
|
8
|
+
type RunningTask,
|
|
9
|
+
reconcileActiveTasks,
|
|
10
|
+
} from "../commands/runner";
|
|
11
|
+
import { PiMonoSession } from "../providers/pi-mono-adapter";
|
|
12
|
+
import type { CostData, ProviderEvent, ProviderSessionConfig } from "../providers/types";
|
|
13
|
+
|
|
14
|
+
type AgentSessionEvent = Parameters<Parameters<AgentSession["subscribe"]>[0]>[0];
|
|
15
|
+
|
|
16
|
+
const tmpLogDir = `/tmp/pi-mono-cost-abort-test-${Date.now()}`;
|
|
17
|
+
|
|
18
|
+
beforeAll(() => {
|
|
19
|
+
mkdirSync(tmpLogDir, { recursive: true });
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
afterAll(() => {
|
|
23
|
+
rmSync(tmpLogDir, { recursive: true, force: true });
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
function makeSessionConfig(logFile: string): ProviderSessionConfig {
|
|
27
|
+
return {
|
|
28
|
+
prompt: "Coordinate sub-agents and complete the lead task",
|
|
29
|
+
systemPrompt: "",
|
|
30
|
+
model: "anthropic/claude-opus-4-6",
|
|
31
|
+
role: "lead",
|
|
32
|
+
agentId: "lead-agent-id",
|
|
33
|
+
taskId: "lead-task-id",
|
|
34
|
+
apiUrl: "http://localhost:3013",
|
|
35
|
+
apiKey: "example-test-key",
|
|
36
|
+
cwd: "/tmp",
|
|
37
|
+
logFile,
|
|
38
|
+
iteration: 1,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
describe("PiMonoSession & runner — long lead session cost preservation (#2073)", () => {
|
|
43
|
+
test("abort() unblocks waitForIdle() and preserves cost after terminal store-progress", async () => {
|
|
44
|
+
const listeners: Array<(event: AgentSessionEvent) => void> = [];
|
|
45
|
+
const mockSession = {
|
|
46
|
+
sessionId: "lead-pi-session-1",
|
|
47
|
+
isStreaming: true,
|
|
48
|
+
model: { provider: "anthropic", id: "claude-opus-4-6" },
|
|
49
|
+
subscribe(listener: (event: AgentSessionEvent) => void) {
|
|
50
|
+
listeners.push(listener);
|
|
51
|
+
return () => {
|
|
52
|
+
const idx = listeners.indexOf(listener);
|
|
53
|
+
if (idx >= 0) listeners.splice(idx, 1);
|
|
54
|
+
};
|
|
55
|
+
},
|
|
56
|
+
async prompt() {
|
|
57
|
+
for (const l of listeners) {
|
|
58
|
+
l({
|
|
59
|
+
type: "tool_execution_start",
|
|
60
|
+
toolCallId: "sp-1",
|
|
61
|
+
toolName: "agent-swarm_store-progress",
|
|
62
|
+
args: { status: "completed", output: "All sub-tasks merged" },
|
|
63
|
+
} as unknown as AgentSessionEvent);
|
|
64
|
+
l({
|
|
65
|
+
type: "tool_execution_end",
|
|
66
|
+
toolCallId: "sp-1",
|
|
67
|
+
toolName: "agent-swarm_store-progress",
|
|
68
|
+
result: { success: true },
|
|
69
|
+
isError: false,
|
|
70
|
+
} as unknown as AgentSessionEvent);
|
|
71
|
+
}
|
|
72
|
+
// Simulate a streaming turn that never emits agent_end before runner aborts it
|
|
73
|
+
},
|
|
74
|
+
getContextUsage: () => null,
|
|
75
|
+
getSessionStats: () => ({
|
|
76
|
+
tokens: { input: 42000, output: 3800, cacheRead: 120000, cacheWrite: 15000, total: 180800 },
|
|
77
|
+
cost: 1.85,
|
|
78
|
+
userMessages: 1,
|
|
79
|
+
assistantMessages: 14,
|
|
80
|
+
}),
|
|
81
|
+
abort: async () => {},
|
|
82
|
+
dispose: () => {},
|
|
83
|
+
} as unknown as AgentSession;
|
|
84
|
+
|
|
85
|
+
const logFile = join(tmpLogDir, `lead-abort-idle-${Date.now()}.log`);
|
|
86
|
+
const session = new PiMonoSession(mockSession, makeSessionConfig(logFile), false);
|
|
87
|
+
const events: ProviderEvent[] = [];
|
|
88
|
+
session.onEvent((e) => events.push(e));
|
|
89
|
+
|
|
90
|
+
// Allow prompt() to emit store-progress and enter waitForIdle()
|
|
91
|
+
await Bun.sleep(20);
|
|
92
|
+
|
|
93
|
+
// Mid-session result snapshot should already be emitted on terminal store-progress
|
|
94
|
+
const resultEventsBeforeAbort = events.filter((e) => e.type === "result");
|
|
95
|
+
expect(resultEventsBeforeAbort.length).toBeGreaterThanOrEqual(1);
|
|
96
|
+
|
|
97
|
+
// Runner reconciliation aborts the session because the task is now completed server-side
|
|
98
|
+
await session.abort();
|
|
99
|
+
|
|
100
|
+
const result = await session.waitForCompletion();
|
|
101
|
+
expect(result.exitCode).toBe(0);
|
|
102
|
+
expect(result.isError).toBe(false);
|
|
103
|
+
expect(result.cost).toBeDefined();
|
|
104
|
+
expect(result.cost?.totalCostUsd).toBe(1.85);
|
|
105
|
+
expect(result.cost?.inputTokens).toBe(42000);
|
|
106
|
+
expect(result.cost?.outputTokens).toBe(3800);
|
|
107
|
+
expect(result.cost?.cacheReadTokens).toBe(120000);
|
|
108
|
+
expect(result.cost?.cacheWriteTokens).toBe(15000);
|
|
109
|
+
expect(result.cost?.numTurns).toBe(15);
|
|
110
|
+
expect(result.cost?.provider).toBe("pi");
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test("prompt() rejection on abort after terminal store-progress still returns cost and exitCode 0", async () => {
|
|
114
|
+
const listeners: Array<(event: AgentSessionEvent) => void> = [];
|
|
115
|
+
let rejectPrompt: (err: Error) => void = () => {};
|
|
116
|
+
const promptBlocked = new Promise<void>((_, reject) => {
|
|
117
|
+
rejectPrompt = reject;
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
const mockSession = {
|
|
121
|
+
sessionId: "lead-pi-session-2",
|
|
122
|
+
isStreaming: true,
|
|
123
|
+
model: { provider: "anthropic", id: "claude-opus-4-6" },
|
|
124
|
+
subscribe(listener: (event: AgentSessionEvent) => void) {
|
|
125
|
+
listeners.push(listener);
|
|
126
|
+
return () => {
|
|
127
|
+
const idx = listeners.indexOf(listener);
|
|
128
|
+
if (idx >= 0) listeners.splice(idx, 1);
|
|
129
|
+
};
|
|
130
|
+
},
|
|
131
|
+
async prompt() {
|
|
132
|
+
for (const l of listeners) {
|
|
133
|
+
l({
|
|
134
|
+
type: "tool_execution_start",
|
|
135
|
+
toolCallId: "sp-2",
|
|
136
|
+
toolName: "store-progress",
|
|
137
|
+
args: { status: "completed", output: "Done" },
|
|
138
|
+
} as unknown as AgentSessionEvent);
|
|
139
|
+
l({
|
|
140
|
+
type: "tool_execution_end",
|
|
141
|
+
toolCallId: "sp-2",
|
|
142
|
+
toolName: "store-progress",
|
|
143
|
+
result: { ok: true },
|
|
144
|
+
isError: false,
|
|
145
|
+
} as unknown as AgentSessionEvent);
|
|
146
|
+
}
|
|
147
|
+
await promptBlocked;
|
|
148
|
+
},
|
|
149
|
+
getContextUsage: () => null,
|
|
150
|
+
getSessionStats: () => ({
|
|
151
|
+
tokens: { input: 18500, output: 2100, cacheRead: 50000, cacheWrite: 6000, total: 76600 },
|
|
152
|
+
cost: 0.92,
|
|
153
|
+
userMessages: 1,
|
|
154
|
+
assistantMessages: 8,
|
|
155
|
+
}),
|
|
156
|
+
abort: async () => {
|
|
157
|
+
rejectPrompt(new Error("Request aborted"));
|
|
158
|
+
},
|
|
159
|
+
dispose: () => {},
|
|
160
|
+
} as unknown as AgentSession;
|
|
161
|
+
|
|
162
|
+
const logFile = join(tmpLogDir, `lead-abort-throw-${Date.now()}.log`);
|
|
163
|
+
const session = new PiMonoSession(mockSession, makeSessionConfig(logFile), false);
|
|
164
|
+
|
|
165
|
+
await Bun.sleep(20);
|
|
166
|
+
await session.abort();
|
|
167
|
+
|
|
168
|
+
const result = await session.waitForCompletion();
|
|
169
|
+
expect(result.exitCode).toBe(0);
|
|
170
|
+
expect(result.isError).toBe(false);
|
|
171
|
+
expect(result.cost).toBeDefined();
|
|
172
|
+
expect(result.cost?.totalCostUsd).toBe(0.92);
|
|
173
|
+
expect(result.cost?.inputTokens).toBe(18500);
|
|
174
|
+
expect(result.cost?.outputTokens).toBe(2100);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
test("unexpected throw in runSession attaches partial session cost with isError: true", async () => {
|
|
178
|
+
const mockSession = {
|
|
179
|
+
sessionId: "lead-pi-session-3",
|
|
180
|
+
isStreaming: false,
|
|
181
|
+
model: { provider: "anthropic", id: "claude-opus-4-6" },
|
|
182
|
+
subscribe: () => () => {},
|
|
183
|
+
async prompt() {
|
|
184
|
+
throw new Error("MCP transport disconnected after 10 turns");
|
|
185
|
+
},
|
|
186
|
+
getContextUsage: () => null,
|
|
187
|
+
getSessionStats: () => ({
|
|
188
|
+
tokens: { input: 9000, output: 1100, cacheRead: 4000, cacheWrite: 1000, total: 15100 },
|
|
189
|
+
cost: 0.41,
|
|
190
|
+
userMessages: 1,
|
|
191
|
+
assistantMessages: 5,
|
|
192
|
+
}),
|
|
193
|
+
abort: async () => {},
|
|
194
|
+
dispose: () => {},
|
|
195
|
+
} as unknown as AgentSession;
|
|
196
|
+
|
|
197
|
+
const logFile = join(tmpLogDir, `lead-unexpected-throw-${Date.now()}.log`);
|
|
198
|
+
const session = new PiMonoSession(mockSession, makeSessionConfig(logFile), false);
|
|
199
|
+
|
|
200
|
+
const result = await session.waitForCompletion();
|
|
201
|
+
expect(result.exitCode).toBe(1);
|
|
202
|
+
expect(result.isError).toBe(true);
|
|
203
|
+
expect(result.cost).toBeDefined();
|
|
204
|
+
expect(result.cost?.isError).toBe(true);
|
|
205
|
+
expect(result.cost?.totalCostUsd).toBe(0.41);
|
|
206
|
+
expect(result.cost?.inputTokens).toBe(9000);
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
test("reconcileActiveTasks preserves task.lastCost when settling an unresponsive aborted session", async () => {
|
|
210
|
+
const snapshotCost: CostData = {
|
|
211
|
+
sessionId: "runner-session-1",
|
|
212
|
+
taskId: "task-2073",
|
|
213
|
+
agentId: "lead-agent-id",
|
|
214
|
+
totalCostUsd: 2.34,
|
|
215
|
+
inputTokens: 64000,
|
|
216
|
+
outputTokens: 5200,
|
|
217
|
+
cacheReadTokens: 150000,
|
|
218
|
+
cacheWriteTokens: 20000,
|
|
219
|
+
durationMs: 1_200_000,
|
|
220
|
+
numTurns: 22,
|
|
221
|
+
model: "anthropic/claude-opus-4-6",
|
|
222
|
+
isError: false,
|
|
223
|
+
provider: "pi",
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
const task: RunningTask = {
|
|
227
|
+
taskId: "task-2073",
|
|
228
|
+
session: {
|
|
229
|
+
sessionId: "pi-session-2073",
|
|
230
|
+
onEvent: () => {},
|
|
231
|
+
waitForCompletion: () => new Promise(() => {}),
|
|
232
|
+
abort: async () => {},
|
|
233
|
+
},
|
|
234
|
+
logFile: "/tmp/task-2073.log",
|
|
235
|
+
startTime: new Date(1_000),
|
|
236
|
+
promise: new Promise(() => {}),
|
|
237
|
+
result: null,
|
|
238
|
+
harnessProvider: "pi",
|
|
239
|
+
hasLocalEnvironment: true,
|
|
240
|
+
abortRequestedAt: 10_000,
|
|
241
|
+
serverTerminalStatus: "completed",
|
|
242
|
+
lastCost: { value: snapshotCost },
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
const state: RunnerState = {
|
|
246
|
+
activeTasks: new Map([["task-2073", task]]),
|
|
247
|
+
deliveryMode: "poll",
|
|
248
|
+
maxConcurrent: 2,
|
|
249
|
+
startedAt: 0,
|
|
250
|
+
tasksProcessed: 0,
|
|
251
|
+
harnessProvider: "pi",
|
|
252
|
+
codexCreditsExhaustedCooldownMs: 60_000,
|
|
253
|
+
modelWindowBlocks: new Map(),
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
const opts: ReconcileActiveTasksOptions = {
|
|
257
|
+
cancelledSignaled: new Set(),
|
|
258
|
+
isCancelled: async () => false,
|
|
259
|
+
fetchStatus: async () => "completed",
|
|
260
|
+
intervalMs: 30_000,
|
|
261
|
+
abortGraceMs: 10_000,
|
|
262
|
+
now: () => 25_000,
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
await reconcileActiveTasks(state, "lead", opts);
|
|
266
|
+
|
|
267
|
+
expect(task.result).not.toBeNull();
|
|
268
|
+
expect(task.result?.cost).toEqual(snapshotCost);
|
|
269
|
+
});
|
|
270
|
+
});
|
|
@@ -30,6 +30,17 @@ describe("toPiMcpServers", () => {
|
|
|
30
30
|
});
|
|
31
31
|
});
|
|
32
32
|
|
|
33
|
+
test("deferred maps every installed server to deferred exposure", () => {
|
|
34
|
+
const servers = toPiMcpServers(
|
|
35
|
+
{
|
|
36
|
+
local: { command: "context-mode" },
|
|
37
|
+
remote: { url: "https://mcp.example.com/mcp" },
|
|
38
|
+
},
|
|
39
|
+
{ deferred: true },
|
|
40
|
+
);
|
|
41
|
+
expect(Object.values(servers).map((s) => s.exposure)).toEqual(["deferred", "deferred"]);
|
|
42
|
+
});
|
|
43
|
+
|
|
33
44
|
test("no installed servers maps to an empty set", () => {
|
|
34
45
|
expect(toPiMcpServers(null)).toEqual({});
|
|
35
46
|
});
|
|
@@ -70,7 +81,8 @@ describe("PiMonoAdapter.createSession — native MCP for installed servers", ()
|
|
|
70
81
|
process.env.OPENROUTER_API_KEY = "example-test-key";
|
|
71
82
|
// The session prompts on creation; keep that request off the network.
|
|
72
83
|
process.env.OPENROUTER_BASE_URL = "http://127.0.0.1:9/api/v1";
|
|
73
|
-
|
|
84
|
+
// Direct exposure, so installed tools show up as active tool names.
|
|
85
|
+
process.env.PI_TOOL_DEFERRAL = "false";
|
|
74
86
|
mkdirSync(join(dir, "agent"), { recursive: true });
|
|
75
87
|
});
|
|
76
88
|
afterEach(() => {
|
|
@@ -176,8 +176,8 @@ describe("pi resource loader trust", () => {
|
|
|
176
176
|
});
|
|
177
177
|
|
|
178
178
|
describe("PI_TOOL_DEFERRAL flag", () => {
|
|
179
|
-
test("defaults
|
|
180
|
-
expect(isPiToolDeferralEnabled({})).toBe(
|
|
179
|
+
test("defaults on and parses boolean literals", () => {
|
|
180
|
+
expect(isPiToolDeferralEnabled({})).toBe(true);
|
|
181
181
|
expect(isPiToolDeferralEnabled({ PI_TOOL_DEFERRAL: "true" })).toBe(true);
|
|
182
182
|
expect(isPiToolDeferralEnabled({ PI_TOOL_DEFERRAL: "1" })).toBe(true);
|
|
183
183
|
expect(isPiToolDeferralEnabled({ PI_TOOL_DEFERRAL: "false" })).toBe(false);
|
|
@@ -187,9 +187,9 @@ describe("PI_TOOL_DEFERRAL flag", () => {
|
|
|
187
187
|
"traits.hasToolSearch follows the flag live",
|
|
188
188
|
withEnv("PI_TOOL_DEFERRAL", undefined, () => {
|
|
189
189
|
const adapter = new PiMonoAdapter();
|
|
190
|
-
expect(adapter.traits.hasToolSearch).toBe(false);
|
|
191
|
-
process.env.PI_TOOL_DEFERRAL = "true";
|
|
192
190
|
expect(adapter.traits.hasToolSearch).toBe(true);
|
|
191
|
+
process.env.PI_TOOL_DEFERRAL = "false";
|
|
192
|
+
expect(adapter.traits.hasToolSearch).toBe(false);
|
|
193
193
|
}),
|
|
194
194
|
);
|
|
195
195
|
});
|
|
@@ -259,8 +259,8 @@ describe("PiMonoAdapter.createSession — tool deferral wiring", () => {
|
|
|
259
259
|
}
|
|
260
260
|
|
|
261
261
|
test(
|
|
262
|
-
"flag
|
|
263
|
-
withEnv("PI_TOOL_DEFERRAL",
|
|
262
|
+
"flag unset: non-core tools deferred and tool_search enabled by default",
|
|
263
|
+
withEnv("PI_TOOL_DEFERRAL", undefined, async () => {
|
|
264
264
|
const opts = await captureSessionOptions();
|
|
265
265
|
const tools = opts.customTools as ToolDefinition[];
|
|
266
266
|
expect(Object.fromEntries(tools.map((t) => [t.name, t.exposure]))).toEqual({
|
|
@@ -274,8 +274,8 @@ describe("PiMonoAdapter.createSession — tool deferral wiring", () => {
|
|
|
274
274
|
);
|
|
275
275
|
|
|
276
276
|
test(
|
|
277
|
-
"flag
|
|
278
|
-
withEnv("PI_TOOL_DEFERRAL",
|
|
277
|
+
"flag false: every tool declared and no tool_search",
|
|
278
|
+
withEnv("PI_TOOL_DEFERRAL", "false", async () => {
|
|
279
279
|
const opts = await captureSessionOptions();
|
|
280
280
|
const tools = opts.customTools as ToolDefinition[];
|
|
281
281
|
expect(tools.map((t) => t.exposure)).toEqual([undefined, undefined, undefined]);
|
|
@@ -16,11 +16,15 @@ test(
|
|
|
16
16
|
"COMB_ENABLED",
|
|
17
17
|
"APP_URL",
|
|
18
18
|
"AGENT_FS_API_URL",
|
|
19
|
+
"AGENT_FS_PROXY",
|
|
19
20
|
"AGENT_FS_LIVE_URL",
|
|
21
|
+
"AGENT_FS_LINK_TARGET",
|
|
20
22
|
"AGENT_FS_DEFAULT_ORG_ID",
|
|
21
23
|
"AGENT_FS_DEFAULT_DRIVE_ID",
|
|
22
24
|
"SWARM_ORG_NAME",
|
|
23
25
|
"SWARM_ORG_WEBSITE",
|
|
26
|
+
"EMBEDDING_QUERY_PREFIX",
|
|
27
|
+
"EMBEDDING_DOCUMENT_PREFIX",
|
|
24
28
|
];
|
|
25
29
|
try {
|
|
26
30
|
await Bun.write(
|
package/src/tests/preload.ts
CHANGED
|
@@ -32,11 +32,15 @@ for (const key of Object.keys(process.env)) {
|
|
|
32
32
|
"COMB_ENABLED",
|
|
33
33
|
"APP_URL",
|
|
34
34
|
"AGENT_FS_API_URL",
|
|
35
|
+
"AGENT_FS_PROXY",
|
|
35
36
|
"AGENT_FS_LIVE_URL",
|
|
37
|
+
"AGENT_FS_LINK_TARGET",
|
|
36
38
|
"AGENT_FS_DEFAULT_ORG_ID",
|
|
37
39
|
"AGENT_FS_DEFAULT_DRIVE_ID",
|
|
38
40
|
"SWARM_ORG_NAME",
|
|
39
41
|
"SWARM_ORG_WEBSITE",
|
|
42
|
+
"EMBEDDING_QUERY_PREFIX",
|
|
43
|
+
"EMBEDDING_DOCUMENT_PREFIX",
|
|
40
44
|
].includes(key)
|
|
41
45
|
) {
|
|
42
46
|
delete process.env[key];
|