@desplega.ai/agent-swarm 1.173.0 → 2.0.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 +1 -1
- package/dist/{acp-adapter-ccm9xrep.js → acp-adapter-sj2g0m84.js} +17 -16
- package/dist/{actions-sgeyv08k.js → actions-fegk44gr.js} +39 -34
- package/dist/{amp-adapter-gpxp3kc9.js → amp-adapter-ad8ft89x.js} +8 -7
- package/dist/{app-km9qnv2f.js → app-kfvgbvzy.js} +18 -14
- package/dist/{assistant-5r8tcvvd.js → assistant-z7zdy9ja.js} +40 -35
- package/dist/{boot-reembed-swwzdgd7.js → boot-reembed-5c2z0mbe.js} +10 -9
- package/dist/{boot-reembed-16rv9843.js → boot-reembed-j8xa181e.js} +10 -9
- package/dist/{boot-scrub-sweep-zkr6vkqb.js → boot-scrub-sweep-hnkn3rgt.js} +11 -10
- package/dist/{boot-seeds-d8sp49t2.js → boot-seeds-kwjq4d2z.js} +14 -13
- package/dist/{claude-adapter-yzzzh8jp.js → claude-adapter-9vmvpvgd.js} +19 -18
- package/dist/{claude-managed-adapter-xgxhqrt2.js → claude-managed-adapter-k9qae05j.js} +7 -5
- package/dist/{claude-managed-setup-bnmjarjc.js → claude-managed-setup-q54zx5q2.js} +6 -4
- package/dist/{claude-sdk-session-4whxere4.js → claude-sdk-session-9jye6cxr.js} +11881 -11472
- package/dist/{cli-94b48jhk.js → cli-03gedhng.js} +16 -14
- package/dist/{cli-1w0vj3gw.js → cli-1e4b6bzh.js} +7 -7
- package/dist/{cli-d603qf86.js → cli-29jc5q7w.js} +10 -8
- package/dist/cli-2m1ktfzz.js +17795 -0
- package/dist/{cli-x119kdba.js → cli-3dj8k4vy.js} +1 -1
- package/dist/{cli-srqq3gc5.js → cli-3fbmmawm.js} +5 -5
- package/dist/{cli-gmdajfw5.js → cli-4jxm2qtz.js} +4987 -1708
- package/dist/{cli-tadfgd0t.js → cli-5hkm7whv.js} +33 -4
- package/dist/{cli-42qhyvwx.js → cli-5t4f29ez.js} +3 -3
- package/dist/{cli-7a0sde2x.js → cli-6cagzc96.js} +9 -0
- package/dist/cli-6cvfxs8r.js +47 -0
- package/dist/{cli-0kxf81jj.js → cli-6jyj59nb.js} +2 -2
- package/dist/{cli-w1apjecb.js → cli-6razj6t4.js} +2 -2
- package/dist/{cli-anzdk0z4.js → cli-74ahz61x.js} +2 -2
- package/dist/{cli-m89t9jdf.js → cli-7x0g4sn1.js} +15416 -16024
- package/dist/{cli-yatxqynm.js → cli-9ha6hs2d.js} +1 -290
- package/dist/{cli-81r1wjyv.js → cli-9nfx44eh.js} +1 -1
- package/dist/cli-9traj3dn.js +638 -0
- package/dist/{cli-smcrrmhy.js → cli-agzebbd5.js} +9 -9
- package/dist/{cli-na2pxe4f.js → cli-apx87jne.js} +2 -2
- package/dist/{cli-rfz2ve55.js → cli-bzszjf2p.js} +2855 -862
- package/dist/{cli-c5km5znr.js → cli-c8r3kzzb.js} +1 -1
- package/dist/{cli-hsem16hb.js → cli-cgtksawx.js} +7 -4
- package/dist/{cli-db3m6zdt.js → cli-d2qr7aj4.js} +1 -1
- package/dist/{cli-rfs4vddv.js → cli-d5f9ed5c.js} +5 -5
- package/dist/cli-e3g5mbkz.js +776 -0
- package/dist/{cli-7jykv7ef.js → cli-e6mpjhed.js} +1 -1
- package/dist/{cli-7a93cv8c.js → cli-ee9gtz70.js} +98 -23
- package/dist/{cli-3nxdmjrc.js → cli-eejsgvm4.js} +4 -4
- package/dist/{cli-fmhj1ehj.js → cli-ef1hg1wv.js} +470 -51
- package/dist/cli-f527ce74.js +781 -0
- package/dist/{cli-ppy4nnnh.js → cli-f7kr7svz.js} +2 -2
- package/dist/cli-hjwmhcg1.js +10622 -0
- package/dist/{cli-2bw3w47j.js → cli-hnhtk6cj.js} +1 -1
- package/dist/{cli-ywm1azg9.js → cli-hnmhtzvh.js} +6 -4
- package/dist/{cli-h7k0xtry.js → cli-hvqbtna4.js} +4 -1256
- package/dist/{cli-s8z6x4b4.js → cli-j0pfptdp.js} +218 -102
- package/dist/cli-jta2eyzc.js +619 -0
- package/dist/{cli-5er5gjbm.js → cli-k382tshb.js} +10 -11
- package/dist/{cli-v3zhnxsb.js → cli-k3dp5tpf.js} +2 -2
- package/dist/{cli-wd0hz6ah.js → cli-kbrt8gqa.js} +9 -91
- package/dist/cli-kceah1eh.js +92 -0
- package/dist/{cli-ne7smj1b.js → cli-ke896fcf.js} +10 -10
- package/dist/{cli-xc7p7s3g.js → cli-kt6zqg0e.js} +6 -6
- package/dist/{cli-wrh08zjd.js → cli-m50t4309.js} +5 -18
- package/dist/cli-n2wq4j7m.js +6985 -0
- package/dist/{cli-bv2etw2y.js → cli-n3hzg89b.js} +46 -15
- package/dist/{cli-tmhsxhb8.js → cli-n5vwg842.js} +23 -6
- package/dist/{cli-qm1448f9.js → cli-ne236afy.js} +8 -8
- package/dist/{cli-d161g69q.js → cli-p1x85d2t.js} +50 -2
- package/dist/{cli-7pz3b7x5.js → cli-przpmdgr.js} +25 -0
- package/dist/{cli-0a7mm3zp.js → cli-q0ke1yww.js} +25 -21
- package/dist/{cli-e3pmyt39.js → cli-q50942rs.js} +1 -1
- package/dist/{cli-r44t9wqc.js → cli-r106725k.js} +2 -2
- package/dist/{cli-phcqp1b9.js → cli-r2pnvj8g.js} +2 -2
- package/dist/{cli-jpc4r224.js → cli-r62a35ea.js} +19 -3
- package/dist/{cli-cd0hv9nh.js → cli-ra0xzggj.js} +2 -2
- package/dist/cli-rhs9pz33.js +294 -0
- package/dist/{cli-r4fqfpwf.js → cli-s2sp957d.js} +50 -27
- package/dist/{cli-1x7da0de.js → cli-s4setq91.js} +8 -8
- package/dist/{cli-6thk4jz3.js → cli-se8w6wny.js} +6 -6
- package/dist/cli-skxvvpap.js +10140 -0
- package/dist/{cli-014vns2y.js → cli-stfs0k5s.js} +208 -525
- package/dist/{cli-0p3enk35.js → cli-tqaazemc.js} +9 -1
- package/dist/{cli-nwrdx840.js → cli-tr4sxp68.js} +3 -3
- package/dist/{cli-r0byd61h.js → cli-tsde1hdr.js} +15 -15
- package/dist/{cli-b07hr3bt.js → cli-txqb9s38.js} +2 -2
- package/dist/{cli-6r2f7edk.js → cli-v1yb0rqb.js} +6 -6
- package/dist/{cli-saqyage0.js → cli-vatyhshy.js} +179 -27
- package/dist/{cli-5wpw4rab.js → cli-vdach5a9.js} +5 -5
- package/dist/{cli-7avy4v8w.js → cli-w1q7h2r2.js} +5 -3
- package/dist/{cli-rcp5aqdw.js → cli-w7m81tb7.js} +1 -1
- package/dist/{cli-c3rk1v0x.js → cli-wayw93nt.js} +3 -3
- package/dist/cli-wmgrkscj.js +23254 -0
- package/dist/{cli-9xf7fdtp.js → cli-z5jjpaxw.js} +29 -6
- package/dist/{cli-451ks6aw.js → cli-zkpks3r3.js} +1 -1
- package/dist/cli.js +29 -45
- package/dist/{codex-adapter-r3dn4fp5.js → codex-adapter-y81sssk0.js} +15 -14
- package/dist/{codex-login-841kv5fk.js → codex-login-f2rcfpp9.js} +3 -2
- package/dist/{codex-session-runner-fwt793h4.js → codex-session-runner-8vx4j6a9.js} +15 -14
- package/dist/{commands-z8fq9qna.js → commands-kcpvpcrr.js} +11 -10
- package/dist/{controller-hq5ggpcp.js → controller-ywrmt6yd.js} +5 -4
- package/dist/{controllers-connect-3mf2btyj.js → controllers-connect-kwpvj3rf.js} +42 -37
- package/dist/{cursor-adapter-q8pgv5qa.js → cursor-adapter-cehv1qns.js} +7 -6
- package/dist/{db-yyhqqrbf.js → db-k8ankj20.js} +12 -11
- package/dist/{db-qfpq4bt7.js → db-ww5d7jq3.js} +28 -9
- package/dist/{devin-adapter-xgtj680j.js → devin-adapter-x1022qb1.js} +3 -2
- package/dist/{dispatcher-x0x7qfcq.js → dispatcher-e8gq5m68.js} +38 -33
- package/dist/{docker-f503k9p0.js → docker-ff6y1190.js} +3 -2
- package/dist/{dsh-adapter-34etvb5m.js → dsh-adapter-3epjm3af.js} +7 -6
- package/dist/{e2b-66xra4az.js → e2b-8ygwhg0f.js} +4 -3
- package/dist/{e2b-xwhyj8cw.js → e2b-swhnpw3n.js} +4 -3
- package/dist/{event-bus-vjtv82gm.js → event-bus-6axnngdj.js} +3 -2
- package/dist/{external-channel-h1fjecwe.js → external-channel-h3359cwn.js} +11 -10
- package/dist/{google-generative-ai-szdxbpqg.js → google-generative-ai-g8db7x0f.js} +4 -2
- package/dist/{google-vertex-qzw3p36d.js → google-vertex-r2692q0r.js} +4 -2
- package/dist/{grok-adapter-e7mr287e.js → grok-adapter-0d4x7gkx.js} +18 -17
- package/dist/{handlers-znb9n7d4.js → handlers-an1gzv21.js} +41 -36
- package/dist/{heartbeat-bm4qkn8d.js → heartbeat-6dakeb61.js} +43 -38
- package/dist/{hook-254r4mza.js → hook-6v6cv6d5.js} +11 -23
- package/dist/{hook-xaa3q80s.js → hook-sa5xye4y.js} +12 -11
- package/dist/{http-3dgrgp8y.js → http-r7f3eky0.js} +1499 -251
- package/dist/index-52j1aja4.js +3465 -0
- package/dist/{index-fx3jqq1e.js → index-7ep4gzdk.js} +38 -33
- package/dist/index-9gw58n4k.js +193 -0
- package/dist/index-9j87mcm8.js +56305 -0
- package/dist/index-bsmcresk.js +79424 -0
- package/dist/index-mh85qhec.js +475 -0
- package/dist/{index-g2byveez.js → index-rmpw9a8q.js} +38 -33
- package/dist/{index-2ybk7khy.js → index-s2wxgzr0.js} +468 -683
- package/dist/{index-bdsqa799.js → index-v1v6n7hg.js} +43 -38
- package/dist/index-we27kd31.js +12810 -0
- package/dist/{index-jd7sejm2.js → index-zk96aq1k.js} +10 -9
- package/dist/{keepalive-6ywhpja1.js → keepalive-be9vwy5h.js} +12 -11
- package/dist/{lead-cw45n21s.js → lead-0861cwrf.js} +61 -56
- package/dist/{maintenance-ss1a1ge9.js → maintenance-mzzgc8bn.js} +10 -9
- package/dist/{node-fkajaq96.js → node-mn1zs7an.js} +2 -1
- package/dist/{oauth-refresh-sweep-3c6f8vyy.js → oauth-refresh-sweep-cpdnfwdj.js} +11 -10
- package/dist/{onboard-8hzdhc1k.js → onboard-fh8qs4r3.js} +11 -3
- package/dist/{opencode-adapter-qbchbcfz.js → opencode-adapter-nm42wwj8.js} +18 -13
- package/dist/{otel-impl-e6mnvc4v.js → otel-impl-x6c1c9g4.js} +242 -868
- package/dist/{pi-mono-adapter-3c9g9snh.js → pi-mono-adapter-9n2ya240.js} +244 -106
- package/dist/{pricing-refresh-gedkjdw0.js → pricing-refresh-qb0rv0ap.js} +12 -11
- package/dist/{rbac-roles-dt4vh1yy.js → rbac-roles-1gzdwbh1.js} +11 -10
- package/dist/{rbac-roles-eszdg6pm.js → rbac-roles-3mraey52.js} +12 -11
- package/dist/{render-v2-907cy6sa.js → render-v2-agpa45zz.js} +18 -14
- package/dist/{resume-42stcaz0.js → resume-x0ydy9s9.js} +25 -21
- package/dist/{runtime-qfry88v2.js → runtime-sa2vr16g.js} +2 -2
- package/dist/{setup-adbc1c10.js → setup-as9ga0d6.js} +2 -2
- package/dist/{virtual-modules-rtmt494q.js → virtual-modules-9gkxqrap.js} +6 -5
- package/dist/{work-objects-wpj8ck95.js → work-objects-y5g1y7ev.js} +41 -36
- package/dist/{worker-bzdra9rf.js → worker-860c3e38.js} +61 -56
- package/dist/{x-qfmygqqv.js → x-m477fr0h.js} +3 -2
- package/openapi.json +523 -344
- package/package.json +10 -11
- package/src/be/config-refs.ts +60 -0
- package/src/be/db/page-versions.ts +15 -0
- package/src/be/db/runtime.ts +35 -8
- package/src/be/db/skill-name-resolution.ts +36 -0
- package/src/be/db/tasks/read.ts +230 -0
- package/src/be/db/tasks/write.ts +7 -0
- package/src/be/db.ts +52 -7
- package/src/be/migrations/215_swarm_config_export_to_env.sql +34 -0
- package/src/be/migrations/216_task_resolved_effort.sql +5 -0
- package/src/be/migrations/217_chat_sdk_state.sql +104 -0
- package/src/be/model-catalog-store.ts +5 -1
- package/src/be/model-tier-resolution.ts +12 -1
- package/src/be/model-validation.ts +5 -2
- package/src/be/models-catalog.ts +26 -1
- package/src/be/modelsdev-cache.json +198 -63
- package/src/be/modelsdev-cache.ts +9 -1
- package/src/be/script-credential-broker.ts +39 -2
- package/src/be/scripts/run-saved.ts +1 -0
- package/src/be/scripts/typecheck.ts +10 -8
- package/src/be/seed/runner.ts +13 -1
- package/src/be/seed/state-db.ts +5 -0
- package/src/be/seed/types.ts +6 -0
- package/src/be/seed-pricing.ts +19 -2
- package/src/be/seed-scripts/catalog/ops-catalog-audit.inline.ts +1 -1
- package/src/be/seed-scripts/catalog/ops-catalog-audit.ts +1 -1
- package/src/be/seed-skills/bundled-files.generated.json +0 -18
- package/src/be/seed-skills/index.ts +104 -3
- package/src/be/swarm-config-guard.ts +52 -0
- package/src/cli.tsx +0 -16
- package/src/commands/onboard/non-interactive.ts +4 -2
- package/src/commands/provider-credentials.ts +52 -6
- package/src/commands/runner.ts +190 -54
- package/src/e2b/env.ts +5 -0
- package/src/extensions/contract-types.generated.ts +1 -1
- package/src/extensions/contract.ts +45 -0
- package/src/extensions/dispatcher.ts +7 -0
- package/src/extensions/post-bridge.ts +40 -0
- package/src/github/handlers.ts +11 -4
- package/src/heartbeat/templates.ts +8 -5
- package/src/hooks/hook.ts +0 -26
- package/src/http/all-routes.ts +0 -1
- package/src/http/config.ts +36 -17
- package/src/http/core.ts +4 -0
- package/src/http/index.ts +14 -2
- package/src/http/integrations.ts +88 -1
- package/src/http/mcp.ts +15 -5
- package/src/http/page-proxy.ts +2 -4
- package/src/http/pages-public.ts +3 -3
- package/src/http/pages.ts +3 -13
- package/src/http/poll.ts +28 -68
- package/src/http/requested-by.ts +54 -0
- package/src/http/scripts.ts +1 -0
- package/src/http/stats.ts +1 -30
- package/src/http/tasks.ts +61 -5
- package/src/http/utils.ts +14 -0
- package/src/http/webhooks.ts +159 -1
- package/src/http/x.ts +1 -0
- package/src/integrations/chat-sdk/adapters.ts +129 -0
- package/src/integrations/chat-sdk/attachments.ts +232 -0
- package/src/integrations/chat-sdk/background.ts +36 -0
- package/src/integrations/chat-sdk/commands.ts +194 -0
- package/src/integrations/chat-sdk/config.ts +198 -0
- package/src/integrations/chat-sdk/discord-api.ts +63 -0
- package/src/integrations/chat-sdk/discord-gateway.ts +185 -0
- package/src/integrations/chat-sdk/handlers.ts +28 -0
- package/src/integrations/chat-sdk/inbound.ts +426 -0
- package/src/integrations/chat-sdk/limits.ts +48 -0
- package/src/integrations/chat-sdk/logger.ts +81 -0
- package/src/integrations/chat-sdk/outbound.ts +123 -0
- package/src/integrations/chat-sdk/relay.ts +172 -0
- package/src/integrations/chat-sdk/router.ts +169 -0
- package/src/integrations/chat-sdk/runtime.ts +351 -0
- package/src/integrations/chat-sdk/schema.ts +245 -0
- package/src/integrations/chat-sdk/state.ts +243 -0
- package/src/integrations/chat-sdk/status.ts +244 -0
- package/src/integrations/chat-sdk/steering.ts +58 -0
- package/src/integrations/chat-sdk/teams-origin.ts +77 -0
- package/src/integrations/chat-sdk/telegram-api.ts +56 -0
- package/src/integrations/chat-sdk/templates.ts +16 -0
- package/src/integrations/chat-sdk/test-hooks.ts +37 -0
- package/src/integrations/chat-sdk/thread-buffer.ts +100 -0
- package/src/integrations/chat-sdk/typing.ts +65 -0
- package/src/pages/version.ts +4 -5
- package/src/prompts/base-prompt.ts +111 -45
- package/src/providers/acp-target-catalog.ts +6 -0
- package/src/providers/acp-targets.ts +55 -2
- package/src/providers/amp-adapter.ts +1 -1
- package/src/providers/claude-adapter.ts +1 -1
- package/src/providers/claude-flag-settings.ts +17 -5
- package/src/providers/claude-managed-adapter.ts +1 -1
- package/src/providers/claude-session-events.ts +6 -1
- package/src/providers/dsh-adapter.ts +1 -1
- package/src/providers/opencode-adapter.ts +243 -26
- package/src/providers/opencode-swarm-plugin.ts +31 -0
- package/src/providers/pi-mono-adapter.ts +398 -153
- package/src/providers/pricing-sources.md +33 -2
- package/src/providers/reasoning-effort.ts +27 -0
- package/src/providers/types.ts +6 -2
- package/src/rbac/legacy-policy.ts +8 -2
- package/src/rbac/permissions.ts +21 -0
- package/src/scheduler/scheduler.ts +4 -2
- package/src/scripts-runtime/loader.ts +19 -7
- package/src/scripts-runtime/sdk-allowlist.ts +8 -6
- package/src/scripts-runtime/swarm-sdk.ts +0 -11
- package/src/scripts-runtime/types/stdlib.d.ts +27 -15
- package/src/scripts-runtime/types/swarm-extension.d.ts +63 -1
- package/src/scripts-runtime/types/swarm-sdk.d.ts +27 -15
- package/src/server.ts +52 -18
- package/src/slack/event-dedup.ts +1 -1
- package/src/tasks/additive-buffer.ts +4 -2
- package/src/tasks/context-key.ts +60 -1
- package/src/tasks/model-pin.ts +67 -0
- package/src/tasks/worker-follow-up.ts +4 -31
- package/src/tests/acp-adapter.test.ts +42 -0
- package/src/tests/api-drain-worker-e2e.test.ts +1 -6
- package/src/tests/base-prompt.test.ts +58 -54
- package/src/tests/chat-sdk-commands.test.ts +191 -0
- package/src/tests/chat-sdk-config.test.ts +334 -0
- package/src/tests/chat-sdk-discord-api.test.ts +68 -0
- package/src/tests/chat-sdk-discord-gateway.test.ts +252 -0
- package/src/tests/chat-sdk-discord-webhook.test.ts +126 -0
- package/src/tests/chat-sdk-fields-sync.test.ts +50 -0
- package/src/tests/chat-sdk-harness.ts +146 -0
- package/src/tests/chat-sdk-inbound.test.ts +363 -0
- package/src/tests/chat-sdk-relay.test.ts +470 -0
- package/src/tests/chat-sdk-reload.test.ts +128 -0
- package/src/tests/chat-sdk-shutdown.test.ts +220 -0
- package/src/tests/chat-sdk-slack.test.ts +268 -0
- package/src/tests/chat-sdk-state.test.ts +163 -0
- package/src/tests/chat-sdk-status-route.test.ts +382 -0
- package/src/tests/chat-sdk-teams.test.ts +277 -0
- package/src/tests/chat-sdk-tools.test.ts +520 -0
- package/src/tests/chat-sdk-typing.test.ts +120 -0
- package/src/tests/chat-sdk-webhook-telegram.test.ts +200 -0
- package/src/tests/claude-adapter-binary.test.ts +9 -4
- package/src/tests/claude-flag-settings.test.ts +11 -3
- package/src/tests/claude-managed-adapter.test.ts +1 -1
- package/src/tests/claude-sdk-lifecycle.test.ts +9 -3
- package/src/tests/claude-thinking-tokens.test.ts +57 -0
- package/src/tests/claude-worker-parity.test.ts +2 -7
- package/src/tests/config-env-export-chat-sdk.test.ts +11 -0
- package/src/tests/config-refs.test.ts +66 -0
- package/src/tests/context-key.test.ts +19 -0
- package/src/tests/cors-allowlist.test.ts +25 -0
- package/src/tests/create-page-tool.test.ts +18 -0
- package/src/tests/credential-check.test.ts +139 -0
- package/src/tests/credentials.test.ts +21 -0
- package/src/tests/db-runtime.test.ts +61 -0
- package/src/tests/db-task-read-contract.test.ts +20 -0
- package/src/tests/db-task-write-contract.test.ts +10 -0
- package/src/tests/docs-index-footprint.test.js +3 -3
- package/src/tests/dsh-adapter.test.ts +1 -1
- package/src/tests/enabled-capabilities.test.ts +20 -0
- package/src/tests/entrypoint-api-readiness.test.ts +14 -8
- package/src/tests/entrypoint-config-env-export.test.ts +5 -2
- package/src/tests/entrypoint-export-to-env.test.ts +78 -0
- package/src/tests/explicit-model-validation.test.ts +105 -1
- package/src/tests/extensions-example-route-chat-channel.test.ts +154 -0
- package/src/tests/extensions-post-bridge.test.ts +51 -0
- package/src/tests/fetch-resolved-env.test.ts +128 -0
- package/src/tests/fixtures/chat-sdk/telegram-private-text.json +10 -0
- package/src/tests/fixtures/extensions/route-chat-channel-to-agent/hooks.ts +27 -0
- package/src/tests/fixtures/extensions/route-chat-channel-to-agent/manifest.json +9 -0
- package/src/tests/fixtures/openai-compatible-registry-extension.child.ts +146 -0
- package/src/tests/fixtures/runner-legacy-resume-prompt.ts +122 -0
- package/src/tests/github-handlers.test.ts +35 -0
- package/src/tests/harness-provider-resolution.test.ts +12 -0
- package/src/tests/heartbeat-checklist.test.ts +3 -2
- package/src/tests/hook-runtime-close.test.ts +0 -2
- package/src/tests/http-api-integration.test.ts +0 -25
- package/src/tests/migration-046-budgets.test.ts +17 -16
- package/src/tests/migration-217-chat-sdk-state.test.ts +113 -0
- package/src/tests/model-tier-resolution.test.ts +93 -0
- package/src/tests/models-catalog.test.ts +42 -1
- package/src/tests/openai-compatible-providers.test.ts +134 -0
- package/src/tests/openai-compatible-registry-extension.test.ts +42 -0
- package/src/tests/opencode-adapter.test.ts +216 -2
- package/src/tests/opencode-helmcode-sdk.test.ts +201 -0
- package/src/tests/opencode-shell-env.test.ts +145 -0
- package/src/tests/openrouter-base-url.test.ts +101 -151
- package/src/tests/page-proxy.test.ts +1 -1
- package/src/tests/pages-browser-sdk.test.ts +76 -0
- package/src/tests/pages-feedback.test.ts +1 -1
- package/src/tests/paused-tasks-requested-by.test.ts +102 -0
- package/src/tests/pi-custom-provider-fake-server.test.ts +442 -0
- package/src/tests/pi-mono-adapter.test.ts +385 -5
- package/src/tests/pi-shell-env.test.ts +170 -0
- package/src/tests/prompt-template-remaining.test.ts +35 -0
- package/src/tests/rbac-engine.test.ts +11 -3
- package/src/tests/runner-exit-after-tasks.test.ts +0 -4
- package/src/tests/runner-legacy-resume-prompt.test.ts +64 -0
- package/src/tests/script-connections.test.ts +2 -0
- package/src/tests/scripts-http.test.ts +201 -1
- package/src/tests/scripts-only-gating.test.ts +2 -3
- package/src/tests/scripts-runtime.test.ts +58 -40
- package/src/tests/secret-scrubber.test.ts +86 -0
- package/src/tests/seed-skills-bundled-files.test.ts +45 -53
- package/src/tests/seed-skills-retire.test.ts +182 -0
- package/src/tests/send-task-requester-gate.test.ts +58 -0
- package/src/tests/session-cost-recompute-unit.test.ts +46 -0
- package/src/tests/skill-get-name-resolution.test.ts +137 -0
- package/src/tests/sonnet-5-5-registry.test.ts +9 -1
- package/src/tests/swarm-config-export-to-env.test.ts +267 -0
- package/src/tests/swarm-config-reserved-keys.test.ts +16 -0
- package/src/tests/swarm-diff.test.ts +1 -1
- package/src/tests/task-model-effort-rerun.test.ts +164 -0
- package/src/tests/task-session-prompts.test.ts +132 -0
- package/src/tests/task-tool-manifest.test.ts +14 -0
- package/src/tests/task-tools-ctx.test.ts +2 -0
- package/src/tests/tool-annotations.test.ts +6 -6
- package/src/tests/ui-logs-parser.test.ts +43 -0
- package/src/tests/used-by-route.test.ts +6 -0
- package/src/tests/vercel-ai-gateway.test.ts +149 -0
- package/src/tests/workflow-swarm-script.test.ts +38 -0
- package/src/tests/workflow-wait-filter.test.ts +5 -3
- package/src/tools/chat-access.ts +41 -0
- package/src/tools/chat-open-dm.ts +59 -0
- package/src/tools/chat-post.ts +74 -0
- package/src/tools/chat-read-thread.ts +100 -0
- package/src/tools/chat-reply.ts +75 -0
- package/src/tools/create-page.ts +3 -13
- package/src/tools/model-catalog/index.ts +2 -1
- package/src/tools/register-chat-channel.ts +139 -0
- package/src/tools/send-task.ts +24 -0
- package/src/tools/skills/skill-get.ts +17 -9
- package/src/tools/swarm-config/get-config.ts +2 -1
- package/src/tools/swarm-config/list-config.ts +2 -1
- package/src/tools/swarm-config/set-config.ts +16 -3
- package/src/tools/tool-config.ts +8 -6
- package/src/types.ts +12 -2
- package/src/utils/config-env-export.ts +74 -0
- package/src/utils/credentials.test.ts +92 -0
- package/src/utils/credentials.ts +52 -4
- package/src/utils/harness-provider.ts +15 -6
- package/src/utils/runtime-model-catalog.ts +11 -1
- package/src/utils/secret-scrubber.ts +71 -2
- package/src/utils/task-tool-manifest.ts +19 -2
- package/src/workflows/executors/swarm-script.ts +1 -0
- package/templates/skills/agent-browser/SKILL.md +1 -1
- package/templates/skills/agent-browser/content.md +1 -1
- package/templates/skills/apps/SKILL.md +8 -1
- package/templates/skills/apps/content.md +8 -1
- package/templates/skills/chat-sdk/config.json +14 -0
- package/templates/skills/chat-sdk/content.md +84 -0
- package/templates/skills/composio-gmail/content.md +7 -0
- package/templates/skills/composio-google-docs/content.md +1 -1
- package/templates/skills/email-domain-health/config.json +14 -0
- package/templates/skills/email-domain-health/content.md +131 -0
- package/templates/skills/kv-storage/content.md +2 -2
- package/templates/skills/pages/content.md +8 -10
- package/templates/skills/swarm-extensions/content.md +2 -0
- package/templates/skills/swarm-scripts/SKILL.md +4 -2
- package/templates/skills/swarm-scripts/config.json +1 -1
- package/templates/skills/swarm-scripts/content.md +4 -2
- package/templates/skills/workflow-iterate/content.md +38 -0
- package/dist/artifact-eczsknc3.js +0 -3843
- package/dist/cli-bd957dgb.js +0 -153
- package/dist/cli-q51yprfq.js +0 -1104
- package/dist/cli-t1ze64h3.js +0 -811
- package/dist/cli-tcc4wd5d.js +0 -56471
- package/dist/index-9ch1n761.js +0 -1102
- package/src/artifact-sdk/index.ts +0 -2
- package/src/artifact-sdk/localtunnel.d.ts +0 -20
- package/src/artifact-sdk/port.ts +0 -12
- package/src/artifact-sdk/server.ts +0 -183
- package/src/artifact-sdk/tunnel.ts +0 -19
- package/src/commands/artifact.ts +0 -249
- package/src/http/ecosystem.ts +0 -77
- package/src/tests/artifact-commands.test.ts +0 -92
- package/src/tests/artifact-sdk.test.ts +0 -793
- package/src/tests/register-service-security.test.ts +0 -194
- package/src/tools/list-services.ts +0 -107
- package/src/tools/register-service.ts +0 -177
- package/src/tools/unregister-service.ts +0 -73
- package/src/tools/update-service-status.ts +0 -96
- package/templates/skills/artifacts/config.json +0 -14
- package/templates/skills/artifacts/content.md +0 -344
- package/templates/skills/artifacts/files/examples/approval-flow.ts +0 -34
- package/templates/skills/artifacts/files/examples/hono-dashboard.ts +0 -31
- package/templates/skills/artifacts/files/examples/multi-artifact.ts +0 -20
- package/templates/skills/artifacts/files/examples/static-report.sh +0 -17
- /package/src/{artifact-sdk → pages}/browser-sdk.ts +0 -0
- /package/src/{artifact-sdk → pages}/feedback-overlay.ts +0 -0
|
@@ -6,8 +6,29 @@
|
|
|
6
6
|
* discovered at session creation and registered as custom tools.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
existsSync,
|
|
11
|
+
lstatSync,
|
|
12
|
+
readFileSync,
|
|
13
|
+
symlinkSync,
|
|
14
|
+
unlinkSync,
|
|
15
|
+
writeFileSync,
|
|
16
|
+
} from "node:fs";
|
|
17
|
+
import { mkdtemp, rm } from "node:fs/promises";
|
|
18
|
+
import { tmpdir } from "node:os";
|
|
10
19
|
import { join } from "node:path";
|
|
20
|
+
import {
|
|
21
|
+
ignoredBaseUrlVar,
|
|
22
|
+
type OpenAICompatibleProvider,
|
|
23
|
+
openAICompatibleKeyVars,
|
|
24
|
+
openAICompatibleProvider,
|
|
25
|
+
openAICompatibleProviders,
|
|
26
|
+
providerBaseUrl,
|
|
27
|
+
providerForModel,
|
|
28
|
+
providerHarnessModels,
|
|
29
|
+
providerKeyFromEnv,
|
|
30
|
+
providerKeyVars,
|
|
31
|
+
} from "@desplega/model-catalog";
|
|
11
32
|
import {
|
|
12
33
|
getBuiltinModel as getModel,
|
|
13
34
|
getBuiltinModels as getModels,
|
|
@@ -23,11 +44,14 @@ import type {
|
|
|
23
44
|
} from "@earendil-works/pi-coding-agent";
|
|
24
45
|
import {
|
|
25
46
|
type AgentSession,
|
|
47
|
+
type BashSpawnContext,
|
|
26
48
|
createAgentSession,
|
|
49
|
+
createBashToolDefinition,
|
|
27
50
|
createCodemodeExtension,
|
|
28
51
|
createMcpExtension,
|
|
29
52
|
createToolSearchExtension,
|
|
30
53
|
DefaultResourceLoader,
|
|
54
|
+
defineTool,
|
|
31
55
|
getAgentDir,
|
|
32
56
|
ModelRuntime,
|
|
33
57
|
SessionManager,
|
|
@@ -44,7 +68,8 @@ import { parseEnvFlag } from "../utils/env-flag";
|
|
|
44
68
|
import { fetchInstalledMcpServers } from "../utils/mcp-server-fetcher";
|
|
45
69
|
import { swarmRuntimeInstanceId } from "../utils/multi-runtime";
|
|
46
70
|
import { DEFAULT_OPENROUTER_BASE_URL, getOpenRouterBaseUrl } from "../utils/openrouter-base-url";
|
|
47
|
-
import {
|
|
71
|
+
import { snapshotCatalogRows } from "../utils/runtime-model-catalog";
|
|
72
|
+
import { registerEnvSecrets, scrubObject, scrubSecrets } from "../utils/secret-scrubber";
|
|
48
73
|
import { readPkgVersion } from "./harness-version";
|
|
49
74
|
import { createSwarmHooksExtension } from "./pi-mono-extension";
|
|
50
75
|
import { McpHttpClient, type McpTool } from "./pi-mono-mcp-client";
|
|
@@ -90,6 +115,11 @@ function modelToCredKeys(modelStr: string | undefined): string[] | null {
|
|
|
90
115
|
if (provider === "openrouter") return ["OPENROUTER_API_KEY"];
|
|
91
116
|
if (provider === "openai") return ["OPENAI_API_KEY"];
|
|
92
117
|
if (provider === "google") return ["GOOGLE_API_KEY", "GEMINI_API_KEY"];
|
|
118
|
+
// models.json custom providers (`writeSessionModelsJson`). The
|
|
119
|
+
// runner normalizes key aliases (HELMCODE_API_NAN → NAN_API_KEY), but a
|
|
120
|
+
// worker without that step (or a boot-time check) may only see the alias.
|
|
121
|
+
const direct = openAICompatibleProvider(provider);
|
|
122
|
+
if (direct) return providerKeyVars(direct);
|
|
93
123
|
}
|
|
94
124
|
// Bare model name with no provider prefix — adapter falls through to a
|
|
95
125
|
// best-effort resolution against multiple providers, so the boot loop
|
|
@@ -303,14 +333,25 @@ export async function checkPiMonoCredentials(
|
|
|
303
333
|
};
|
|
304
334
|
}
|
|
305
335
|
|
|
306
|
-
// Permissive case: any one supported key works.
|
|
307
|
-
|
|
336
|
+
// Permissive case: any one supported key works. A registry provider key
|
|
337
|
+
// (NaN, Helmcode) only serves `<id>/*` models, but it is still a usable pi
|
|
338
|
+
// credential (the task's explicit model is checked above once it is set).
|
|
339
|
+
if (
|
|
340
|
+
env.ANTHROPIC_API_KEY ||
|
|
341
|
+
env.OPENROUTER_API_KEY ||
|
|
342
|
+
env.OPENAI_API_KEY ||
|
|
343
|
+
openAICompatibleKeyVars().some((k) => env[k])
|
|
344
|
+
) {
|
|
308
345
|
return { ready: true, missing: [], satisfiedBy: "env" };
|
|
309
346
|
}
|
|
310
347
|
return {
|
|
311
348
|
ready: false,
|
|
312
349
|
missing: ["ANTHROPIC_API_KEY", "OPENROUTER_API_KEY", "OPENAI_API_KEY", authFile],
|
|
313
|
-
hint:
|
|
350
|
+
hint: `Set one of ANTHROPIC_API_KEY / OPENROUTER_API_KEY / OPENAI_API_KEY (any one suffices; ${openAICompatibleProviders()
|
|
351
|
+
.map((p) => p.envVar)
|
|
352
|
+
.join(" or ")} serve only ${openAICompatibleProviders()
|
|
353
|
+
.map((p) => `${p.id}/*`)
|
|
354
|
+
.join(" or ")} models), or run \`pi auth login\` to create ~/.pi/agent/auth.json.`,
|
|
314
355
|
};
|
|
315
356
|
}
|
|
316
357
|
|
|
@@ -433,13 +474,21 @@ function envHasAnthropicCred(env: Record<string, string | undefined>): boolean {
|
|
|
433
474
|
return !!(env.ANTHROPIC_API_KEY || env.ANTHROPIC_OAUTH_TOKEN);
|
|
434
475
|
}
|
|
435
476
|
|
|
436
|
-
const PI_RUNTIME_API_KEYS = [
|
|
477
|
+
const PI_RUNTIME_API_KEYS: readonly (readonly [envKey: string, provider: string])[] = [
|
|
437
478
|
["OPENROUTER_API_KEY", "openrouter"],
|
|
438
479
|
["ANTHROPIC_API_KEY", "anthropic"],
|
|
439
480
|
["OPENAI_API_KEY", "openai"],
|
|
440
481
|
["GEMINI_API_KEY", "google"],
|
|
441
482
|
["GOOGLE_API_KEY", "google"],
|
|
442
|
-
|
|
483
|
+
// models.json custom providers (the registry). A runtime key beats the
|
|
484
|
+
// file's `apiKey: "$NAN_API_KEY"`, which pi would otherwise read from the
|
|
485
|
+
// shared process.env, where a credential pool is still the raw comma list.
|
|
486
|
+
// Later rows overwrite earlier ones, so each provider's aliases come first
|
|
487
|
+
// and the canonical var (NAN_API_KEY over HELMCODE_API_NAN) wins.
|
|
488
|
+
...openAICompatibleProviders().flatMap((p) =>
|
|
489
|
+
[...p.aliasEnvVars, p.envVar].map((name) => [name, p.id] as const),
|
|
490
|
+
),
|
|
491
|
+
];
|
|
443
492
|
|
|
444
493
|
/**
|
|
445
494
|
* Build pi-coding-agent auth services from the runner's per-task resolved env.
|
|
@@ -451,8 +500,13 @@ const PI_RUNTIME_API_KEYS = [
|
|
|
451
500
|
*/
|
|
452
501
|
export async function createPiRuntimeAuth(
|
|
453
502
|
env: Record<string, string | undefined> = process.env,
|
|
503
|
+
modelsPath?: string,
|
|
454
504
|
): Promise<ModelRuntime> {
|
|
455
|
-
|
|
505
|
+
// A per-session models.json (`writeSessionModelsJson`) still shares the
|
|
506
|
+
// agent dir's catalog cache, which only holds built-in provider catalogs.
|
|
507
|
+
const modelRuntime = await ModelRuntime.create(
|
|
508
|
+
modelsPath ? { modelsPath, modelsStorePath: join(getAgentDir(), "models-store.json") } : {},
|
|
509
|
+
);
|
|
456
510
|
for (const [envKey, provider] of PI_RUNTIME_API_KEYS) {
|
|
457
511
|
const apiKey = env[envKey];
|
|
458
512
|
if (apiKey) {
|
|
@@ -464,11 +518,12 @@ export async function createPiRuntimeAuth(
|
|
|
464
518
|
}
|
|
465
519
|
|
|
466
520
|
/**
|
|
467
|
-
*
|
|
468
|
-
*
|
|
469
|
-
*
|
|
470
|
-
*
|
|
471
|
-
* part of pi's config
|
|
521
|
+
* Legacy sidecar marker. Earlier versions wrote the OpenRouter gateway into
|
|
522
|
+
* the shared `<agentDir>/models.json` and recorded here the `baseUrl` they
|
|
523
|
+
* wrote (and any user value they displaced). Nothing writes it any more;
|
|
524
|
+
* `writeSessionModelsJson` reads it to keep such a leftover gateway out of a
|
|
525
|
+
* default-gateway session. Lives next to models.json; NOT part of pi's config
|
|
526
|
+
* surface.
|
|
472
527
|
*/
|
|
473
528
|
const OPENROUTER_OVERRIDE_MARKER = ".agent-swarm-openrouter-override.json";
|
|
474
529
|
|
|
@@ -492,141 +547,183 @@ async function readJsonFile(path: string): Promise<Record<string, unknown> | und
|
|
|
492
547
|
}
|
|
493
548
|
|
|
494
549
|
/**
|
|
495
|
-
*
|
|
496
|
-
*
|
|
497
|
-
* that file at create time) composes every built-in OpenRouter model with
|
|
498
|
-
* the gateway `baseUrl` — see `src/utils/openrouter-base-url.ts` for the
|
|
499
|
-
* env contract.
|
|
500
|
-
*
|
|
501
|
-
* Gateway set (non-default): merge-preserving write — existing provider
|
|
502
|
-
* entries and other openrouter keys are kept, only
|
|
503
|
-
* `providers.openrouter.baseUrl` is set; a displaced user value is recorded
|
|
504
|
-
* in a sidecar marker. A corrupt existing file is overwritten with just the
|
|
505
|
-
* override — for gateway deployments, guaranteed rerouting wins over
|
|
506
|
-
* preserving unparseable config.
|
|
550
|
+
* The models.json `providers.<id>` block for one OpenAI-compatible registry
|
|
551
|
+
* provider, at `baseUrl` (the `<ID>_BASE_URL` override or the registry default).
|
|
507
552
|
*
|
|
508
|
-
*
|
|
509
|
-
*
|
|
510
|
-
*
|
|
511
|
-
*
|
|
512
|
-
*
|
|
513
|
-
*
|
|
514
|
-
* from
|
|
515
|
-
*
|
|
553
|
+
* `apiKey` is always the `"$VAR"` interpolation of the canonical env var, never
|
|
554
|
+
* a key value: pi reads a bare name as a literal key. The key a session really
|
|
555
|
+
* uses arrives as a runtime key (`createPiRuntimeAuth`), which beats this field.
|
|
556
|
+
* Costs are 0: the server reprices every session from the pricing table.
|
|
557
|
+
* A `modelsFromCatalog` provider (`vercel`) lists every agent chat model of its
|
|
558
|
+
* catalog snapshot section (`providerHarnessModels`).
|
|
559
|
+
* A `modelId` missing from the list (a catalog id outside it) gets a bare
|
|
560
|
+
* entry so pi still resolves it instead of falling back to its default model;
|
|
561
|
+
* pi fills the omitted limits with its own defaults.
|
|
516
562
|
*/
|
|
517
|
-
export
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
const
|
|
523
|
-
const
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
}
|
|
536
|
-
} catch (err) {
|
|
537
|
-
console.warn(
|
|
538
|
-
`[pi-mono] ${modelsPath} is unreadable/invalid (${err instanceof Error ? err.message : err}); rewriting with the OpenRouter gateway override`,
|
|
539
|
-
);
|
|
540
|
-
existing = {};
|
|
563
|
+
export function customProviderModelsBlock(
|
|
564
|
+
provider: OpenAICompatibleProvider,
|
|
565
|
+
modelId?: string,
|
|
566
|
+
baseUrl: string = provider.baseUrl,
|
|
567
|
+
) {
|
|
568
|
+
const zeroCost = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
|
|
569
|
+
const listed = providerHarnessModels(provider, snapshotCatalogRows(provider.id));
|
|
570
|
+
const models: Record<string, unknown>[] = listed.map((m) => ({
|
|
571
|
+
id: m.id,
|
|
572
|
+
name: m.name,
|
|
573
|
+
reasoning: m.reasoning,
|
|
574
|
+
input: [...m.input],
|
|
575
|
+
contextWindow: m.contextWindow,
|
|
576
|
+
maxTokens: m.maxOutputTokens,
|
|
577
|
+
cost: { ...zeroCost },
|
|
578
|
+
compat: { ...m.compat },
|
|
579
|
+
}));
|
|
580
|
+
if (modelId && !listed.some((m) => m.id === modelId)) {
|
|
581
|
+
models.push({ id: modelId, name: modelId, cost: { ...zeroCost } });
|
|
541
582
|
}
|
|
542
|
-
|
|
543
|
-
const providers =
|
|
544
|
-
typeof existing.providers === "object" && existing.providers !== null
|
|
545
|
-
? (existing.providers as Record<string, unknown>)
|
|
546
|
-
: {};
|
|
547
|
-
const openrouter =
|
|
548
|
-
typeof providers.openrouter === "object" && providers.openrouter !== null
|
|
549
|
-
? (providers.openrouter as Record<string, unknown>)
|
|
550
|
-
: {};
|
|
551
|
-
|
|
552
|
-
// Record what we displace, once — re-runs with the marker already present
|
|
553
|
-
// keep the ORIGINAL displaced value (the current baseUrl is then ours).
|
|
554
|
-
const priorMarker = (await readJsonFile(markerPath)) as OpenRouterOverrideMarker | undefined;
|
|
555
|
-
const currentBaseUrl = typeof openrouter.baseUrl === "string" ? openrouter.baseUrl : undefined;
|
|
556
|
-
const previous = priorMarker
|
|
557
|
-
? priorMarker.previous
|
|
558
|
-
: currentBaseUrl !== baseUrl
|
|
559
|
-
? currentBaseUrl
|
|
560
|
-
: undefined;
|
|
561
|
-
|
|
562
|
-
const next = {
|
|
563
|
-
...existing,
|
|
564
|
-
providers: {
|
|
565
|
-
...providers,
|
|
566
|
-
openrouter: { ...openrouter, baseUrl },
|
|
567
|
-
},
|
|
568
|
-
};
|
|
569
|
-
await Bun.write(modelsPath, `${JSON.stringify(next, null, 2)}\n`);
|
|
570
|
-
const marker: OpenRouterOverrideMarker = {
|
|
583
|
+
return {
|
|
571
584
|
baseUrl,
|
|
572
|
-
|
|
585
|
+
api: "openai-completions",
|
|
586
|
+
apiKey: `$${provider.envVar}`,
|
|
587
|
+
models,
|
|
573
588
|
};
|
|
574
|
-
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
592
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
/** The models.json one pi session loads; see `writeSessionModelsJson`. */
|
|
596
|
+
export interface SessionModelsJson {
|
|
597
|
+
/** Per-session models.json, or undefined to load the shared `<agentDir>/models.json`. */
|
|
598
|
+
modelsPath: string | undefined;
|
|
599
|
+
/** Remove the per-session file. Safe to call more than once. */
|
|
600
|
+
dispose(): Promise<void>;
|
|
575
601
|
}
|
|
576
602
|
|
|
577
603
|
/**
|
|
578
|
-
*
|
|
579
|
-
*
|
|
580
|
-
*
|
|
581
|
-
*
|
|
604
|
+
* Compose the models.json this session loads from the shared
|
|
605
|
+
* `<agentDir>/models.json` plus this session's env, and write it to a private
|
|
606
|
+
* temp dir. Concurrent sessions in one worker share `<agentDir>`, so writing
|
|
607
|
+
* per-task values into the shared file lets one session load another's
|
|
608
|
+
* endpoint (and send its key there). The shared file is only ever read: it
|
|
609
|
+
* holds what the operator wrote by hand.
|
|
610
|
+
*
|
|
611
|
+
* The private copy keeps every top-level key and provider of the shared file
|
|
612
|
+
* and adds, from `env` only:
|
|
613
|
+
* - `providers.openrouter.baseUrl`: the session's OpenRouter gateway, over a
|
|
614
|
+
* hand-authored one. With the default gateway, a value an earlier version
|
|
615
|
+
* wrote into the shared file (recorded in the legacy marker) is reverted, so
|
|
616
|
+
* a stale gateway never leaks in.
|
|
617
|
+
* - one block per registry provider (`nan`, `helmcode`, `vercel`) whose key
|
|
618
|
+
* (canonical var or alias) is set, at its `<ID>_BASE_URL` or the registry
|
|
619
|
+
* default. A block already in the shared file is user-authored and wins,
|
|
620
|
+
* except that a `baseUrlLocked` provider (`vercel`) always gets its one
|
|
621
|
+
* endpoint and API, with a warning when the shared block named another.
|
|
622
|
+
* - `model` (the task's model) adds a bare entry to its provider's block when
|
|
623
|
+
* its id is outside the table.
|
|
624
|
+
*
|
|
625
|
+
* Nothing to add (no registry key, default gateway, no override marker): no
|
|
626
|
+
* copy, the session loads the shared file as before. A corrupt shared file is
|
|
627
|
+
* replaced by an empty object in the copy; pi cannot load it either way.
|
|
582
628
|
*/
|
|
583
|
-
async function
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
const
|
|
590
|
-
|
|
591
|
-
const
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
typeof existing.providers === "object" && existing.providers !== null
|
|
598
|
-
? { ...(existing.providers as Record<string, unknown>) }
|
|
599
|
-
: undefined;
|
|
600
|
-
const openrouter =
|
|
601
|
-
providers && typeof providers.openrouter === "object" && providers.openrouter !== null
|
|
602
|
-
? { ...(providers.openrouter as Record<string, unknown>) }
|
|
603
|
-
: undefined;
|
|
604
|
-
if (!providers || !openrouter || openrouter.baseUrl !== marker.baseUrl) {
|
|
605
|
-
await removeMarker();
|
|
606
|
-
return;
|
|
629
|
+
export async function writeSessionModelsJson(
|
|
630
|
+
agentDir: string,
|
|
631
|
+
env: Record<string, string | undefined> = process.env,
|
|
632
|
+
model?: string,
|
|
633
|
+
): Promise<SessionModelsJson> {
|
|
634
|
+
const sharedPath = join(agentDir, "models.json");
|
|
635
|
+
const registry = openAICompatibleProviders();
|
|
636
|
+
const wanted = registry.filter((p) => providerKeyFromEnv(p, env) !== undefined);
|
|
637
|
+
const gateway = getOpenRouterBaseUrl(env as NodeJS.ProcessEnv);
|
|
638
|
+
const marker = (await readJsonFile(join(agentDir, OPENROUTER_OVERRIDE_MARKER))) as
|
|
639
|
+
| OpenRouterOverrideMarker
|
|
640
|
+
| undefined;
|
|
641
|
+
if (wanted.length === 0 && gateway === DEFAULT_OPENROUTER_BASE_URL && !marker) {
|
|
642
|
+
return { modelsPath: undefined, dispose: async () => {} };
|
|
607
643
|
}
|
|
608
644
|
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
645
|
+
let shared: Record<string, unknown> = {};
|
|
646
|
+
const sharedFile = Bun.file(sharedPath);
|
|
647
|
+
if (await sharedFile.exists()) {
|
|
648
|
+
try {
|
|
649
|
+
const parsed: unknown = JSON.parse(await sharedFile.text());
|
|
650
|
+
if (!isPlainObject(parsed)) throw new Error("not a JSON object");
|
|
651
|
+
shared = parsed;
|
|
652
|
+
} catch (err) {
|
|
653
|
+
console.warn(
|
|
654
|
+
scrubSecrets(
|
|
655
|
+
`[pi-mono] ${sharedPath} is unreadable/invalid (${err instanceof Error ? err.message : String(err)}); this session loads its own provider blocks only`,
|
|
656
|
+
),
|
|
657
|
+
);
|
|
658
|
+
}
|
|
613
659
|
}
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
}
|
|
617
|
-
|
|
660
|
+
|
|
661
|
+
const providers = isPlainObject(shared.providers) ? { ...shared.providers } : {};
|
|
662
|
+
const openrouter = isPlainObject(providers.openrouter) ? { ...providers.openrouter } : {};
|
|
663
|
+
if (gateway !== DEFAULT_OPENROUTER_BASE_URL) {
|
|
664
|
+
openrouter.baseUrl = gateway;
|
|
665
|
+
} else if (marker && openrouter.baseUrl === marker.baseUrl) {
|
|
666
|
+
if (marker.previous !== undefined) openrouter.baseUrl = marker.previous;
|
|
667
|
+
else delete openrouter.baseUrl;
|
|
618
668
|
}
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
669
|
+
if (Object.keys(openrouter).length > 0) providers.openrouter = openrouter;
|
|
670
|
+
else delete providers.openrouter;
|
|
671
|
+
|
|
672
|
+
const modelProvider = providerForModel(model);
|
|
673
|
+
const modelId = modelProvider && model ? model.slice(modelProvider.id.length + 1) : undefined;
|
|
674
|
+
for (const provider of wanted) {
|
|
675
|
+
const ignored = ignoredBaseUrlVar(provider, env);
|
|
676
|
+
if (ignored) {
|
|
677
|
+
console.warn(
|
|
678
|
+
scrubSecrets(
|
|
679
|
+
`[pi-mono] ${ignored} is ignored: ${provider.displayName} is only reached at ${provider.baseUrl}`,
|
|
680
|
+
),
|
|
681
|
+
);
|
|
682
|
+
}
|
|
683
|
+
const existing = providers[provider.id];
|
|
684
|
+
if (provider.baseUrlLocked && isPlainObject(existing)) {
|
|
685
|
+
if (existing.baseUrl !== provider.baseUrl || existing.api !== "openai-completions") {
|
|
686
|
+
console.warn(
|
|
687
|
+
scrubSecrets(
|
|
688
|
+
`[pi-mono] ${sharedPath} providers.${provider.id} points at ${String(existing.baseUrl)}; using ${provider.baseUrl}`,
|
|
689
|
+
),
|
|
690
|
+
);
|
|
691
|
+
}
|
|
692
|
+
providers[provider.id] = {
|
|
693
|
+
...customProviderModelsBlock(
|
|
694
|
+
provider,
|
|
695
|
+
modelProvider?.id === provider.id ? modelId : undefined,
|
|
696
|
+
),
|
|
697
|
+
...existing,
|
|
698
|
+
baseUrl: provider.baseUrl,
|
|
699
|
+
api: "openai-completions",
|
|
700
|
+
};
|
|
701
|
+
continue;
|
|
702
|
+
}
|
|
703
|
+
if (existing !== undefined) {
|
|
704
|
+
console.warn(
|
|
705
|
+
scrubSecrets(
|
|
706
|
+
`[pi-mono] ${sharedPath} already has a providers.${provider.id} block; using it as is`,
|
|
707
|
+
),
|
|
708
|
+
);
|
|
709
|
+
continue;
|
|
710
|
+
}
|
|
711
|
+
providers[provider.id] = customProviderModelsBlock(
|
|
712
|
+
provider,
|
|
713
|
+
modelProvider?.id === provider.id ? modelId : undefined,
|
|
714
|
+
providerBaseUrl(provider, env),
|
|
715
|
+
);
|
|
622
716
|
}
|
|
623
717
|
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
}
|
|
629
|
-
|
|
718
|
+
const next: Record<string, unknown> = { ...shared, providers };
|
|
719
|
+
if (Object.keys(providers).length === 0) delete next.providers;
|
|
720
|
+
const dir = await mkdtemp(join(tmpdir(), "pi-session-models-"));
|
|
721
|
+
const modelsPath = join(dir, "models.json");
|
|
722
|
+
await Bun.write(modelsPath, `${JSON.stringify(next, null, 2)}\n`);
|
|
723
|
+
return {
|
|
724
|
+
modelsPath,
|
|
725
|
+
dispose: () => rm(dir, { recursive: true, force: true }),
|
|
726
|
+
};
|
|
630
727
|
}
|
|
631
728
|
|
|
632
729
|
/**
|
|
@@ -639,10 +736,33 @@ async function revertOpenRouterModelsOverride(
|
|
|
639
736
|
* anthropic-provider env lookup from failing at session-start with
|
|
640
737
|
* "No API key found for anthropic" — see task 37a4a87a and the chronic
|
|
641
738
|
* weekly-fire pattern (2026-04-13 → 2026-05-11) tracked in HEARTBEAT.md.
|
|
739
|
+
*
|
|
740
|
+
* `provider/id` strings that pi-ai's builtin catalog does not know (the
|
|
741
|
+
* models.json custom providers of the registry, `nan` / `helmcode`) fall back to
|
|
742
|
+
* `modelRuntime.getModel(provider, id)` when a runtime is passed. When nothing
|
|
743
|
+
* resolves, a scrubbed warning names the model: the session then silently runs
|
|
744
|
+
* pi's own default model, which is otherwise invisible.
|
|
642
745
|
*/
|
|
643
746
|
export function resolveModel(
|
|
644
747
|
modelStr: string,
|
|
645
748
|
env: Record<string, string | undefined> = process.env,
|
|
749
|
+
modelRuntime?: Pick<ModelRuntime, "getModel">,
|
|
750
|
+
) {
|
|
751
|
+
const model = resolveModelInner(modelStr, env, modelRuntime);
|
|
752
|
+
if (modelStr && !model) {
|
|
753
|
+
console.warn(
|
|
754
|
+
scrubSecrets(
|
|
755
|
+
`[pi-mono] Model "${modelStr}" did not resolve to a builtin or models.json model; pi will fall back to its default model`,
|
|
756
|
+
),
|
|
757
|
+
);
|
|
758
|
+
}
|
|
759
|
+
return model;
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
function resolveModelInner(
|
|
763
|
+
modelStr: string,
|
|
764
|
+
env: Record<string, string | undefined>,
|
|
765
|
+
modelRuntime: Pick<ModelRuntime, "getModel"> | undefined,
|
|
646
766
|
) {
|
|
647
767
|
if (!modelStr) return undefined;
|
|
648
768
|
|
|
@@ -687,11 +807,17 @@ export function resolveModel(
|
|
|
687
807
|
const slashIdx = modelStr.indexOf("/");
|
|
688
808
|
const provider = modelStr.slice(0, slashIdx);
|
|
689
809
|
const modelId = modelStr.slice(slashIdx + 1);
|
|
810
|
+
let builtin: ReturnType<typeof getModel> | undefined;
|
|
690
811
|
try {
|
|
691
|
-
|
|
812
|
+
// pi-ai returns undefined (does not throw) for an unknown provider/id.
|
|
813
|
+
builtin = getModel(provider as "anthropic", modelId as never);
|
|
692
814
|
} catch {
|
|
693
|
-
|
|
815
|
+
builtin = undefined;
|
|
694
816
|
}
|
|
817
|
+
if (builtin) return builtin;
|
|
818
|
+
// Custom providers live only in models.json, so only the runtime's
|
|
819
|
+
// composed store knows them.
|
|
820
|
+
return modelRuntime?.getModel(provider, modelId);
|
|
695
821
|
}
|
|
696
822
|
|
|
697
823
|
// Try as a full model ID with common providers
|
|
@@ -786,6 +912,31 @@ function describeAssistantTurn(message: unknown): AssistantTurnShape {
|
|
|
786
912
|
|
|
787
913
|
const TERMINAL_STORE_PROGRESS_STATUSES = new Set(["completed", "failed"]);
|
|
788
914
|
|
|
915
|
+
/** Rewrite a pi session file with every line scrubbed. No-op when absent or clean. */
|
|
916
|
+
function scrubSessionFileSync(path: string | undefined): void {
|
|
917
|
+
if (!path || !existsSync(path)) return;
|
|
918
|
+
const text = readFileSync(path, "utf-8");
|
|
919
|
+
const scrubbed = scrubSecrets(text);
|
|
920
|
+
if (scrubbed !== text) writeFileSync(path, scrubbed);
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
/**
|
|
924
|
+
* pi persists every session entry to its own `sessions/<cwd>/*.jsonl`, an
|
|
925
|
+
* egress none of ours covers: a provider error that echoes the request's key
|
|
926
|
+
* lands there verbatim. Scrub each entry on its way to disk. The in-memory
|
|
927
|
+
* entries (the model's context) are left as they are. pi's first write flushes
|
|
928
|
+
* every entry held so far, so that file is scrubbed once right after it.
|
|
929
|
+
*/
|
|
930
|
+
export function scrubPiSessionPersistence(sessionManager: SessionManager): void {
|
|
931
|
+
const persist = sessionManager._persist.bind(sessionManager);
|
|
932
|
+
sessionManager._persist = (entry) => {
|
|
933
|
+
const file = sessionManager.getSessionFile();
|
|
934
|
+
const existed = file ? existsSync(file) : true;
|
|
935
|
+
persist(scrubObject(entry));
|
|
936
|
+
if (!existed) scrubSessionFileSync(file);
|
|
937
|
+
};
|
|
938
|
+
}
|
|
939
|
+
|
|
789
940
|
export class PiMonoSession implements ProviderSession {
|
|
790
941
|
private listeners: Array<(event: ProviderEvent) => void> = [];
|
|
791
942
|
private eventQueue: ProviderEvent[] = [];
|
|
@@ -840,14 +991,18 @@ export class PiMonoSession implements ProviderSession {
|
|
|
840
991
|
private emptyTurnReprompted = false;
|
|
841
992
|
/** `abort()` was called: never prompt a session someone is trying to stop. */
|
|
842
993
|
private abortRequested = false;
|
|
994
|
+
/** Removes this session's own models.json once the session ends. */
|
|
995
|
+
private disposeModels: () => Promise<void>;
|
|
843
996
|
|
|
844
997
|
constructor(
|
|
845
998
|
agentSession: AgentSession,
|
|
846
999
|
config: ProviderSessionConfig,
|
|
847
1000
|
createdSymlink: boolean,
|
|
848
1001
|
appliedReasoningEffort: ReasoningEffort | null = null,
|
|
1002
|
+
disposeModels: () => Promise<void> = async () => {},
|
|
849
1003
|
) {
|
|
850
1004
|
this.agentSession = agentSession;
|
|
1005
|
+
this.disposeModels = disposeModels;
|
|
851
1006
|
this.config = config;
|
|
852
1007
|
this.createdSymlink = createdSymlink;
|
|
853
1008
|
this.appliedReasoningEffort = appliedReasoningEffort;
|
|
@@ -885,12 +1040,10 @@ export class PiMonoSession implements ProviderSession {
|
|
|
885
1040
|
}
|
|
886
1041
|
|
|
887
1042
|
private emit(event: ProviderEvent): void {
|
|
888
|
-
// Scrub
|
|
889
|
-
//
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
? { ...event, content: scrubSecrets(event.content) }
|
|
893
|
-
: event;
|
|
1043
|
+
// Scrub every event before egress (log file write, listener dispatch,
|
|
1044
|
+
// downstream session-logs push + pretty-print): an error or result event
|
|
1045
|
+
// can carry a provider message that echoes the request's key.
|
|
1046
|
+
const scrubbed = scrubObject(event);
|
|
894
1047
|
|
|
895
1048
|
// Log all events
|
|
896
1049
|
this.logFileHandle.write(
|
|
@@ -1094,7 +1247,7 @@ export class PiMonoSession implements ProviderSession {
|
|
|
1094
1247
|
// message; anything else surfaces its raw error text.
|
|
1095
1248
|
if (this.terminalError) {
|
|
1096
1249
|
const classification = classifyAwsSdkError(this.terminalError);
|
|
1097
|
-
const message = classification?.message ?? this.terminalError;
|
|
1250
|
+
const message = scrubSecrets(classification?.message ?? this.terminalError);
|
|
1098
1251
|
const category = classification?.category;
|
|
1099
1252
|
this.emit({ type: "error", message, category });
|
|
1100
1253
|
return {
|
|
@@ -1143,7 +1296,7 @@ export class PiMonoSession implements ProviderSession {
|
|
|
1143
1296
|
sessionId: this._sessionId,
|
|
1144
1297
|
isError: true,
|
|
1145
1298
|
errorCategory: awsCatchError?.category,
|
|
1146
|
-
failureReason: awsCatchError?.message ?? errorMessage,
|
|
1299
|
+
failureReason: scrubSecrets(awsCatchError?.message ?? errorMessage),
|
|
1147
1300
|
appliedReasoningEffort: this.appliedReasoningEffort,
|
|
1148
1301
|
};
|
|
1149
1302
|
} finally {
|
|
@@ -1153,6 +1306,15 @@ export class PiMonoSession implements ProviderSession {
|
|
|
1153
1306
|
cleanupAgentsMdSymlink(this.config.cwd);
|
|
1154
1307
|
}
|
|
1155
1308
|
this.agentSession.dispose();
|
|
1309
|
+
// Entries pi wrote outside `_persist` (a rewrite on migration or
|
|
1310
|
+
// branching) are covered here.
|
|
1311
|
+
try {
|
|
1312
|
+
// Test doubles of AgentSession may carry no session manager.
|
|
1313
|
+
scrubSessionFileSync(this.agentSession.sessionManager?.getSessionFile());
|
|
1314
|
+
} catch (err) {
|
|
1315
|
+
console.warn(scrubSecrets(`[pi-mono] could not scrub the session file: ${String(err)}`));
|
|
1316
|
+
}
|
|
1317
|
+
await this.disposeModels();
|
|
1156
1318
|
}
|
|
1157
1319
|
}
|
|
1158
1320
|
|
|
@@ -1504,6 +1666,32 @@ export function piDefaultToolAdditions(features: PiSessionFeatures): string[] {
|
|
|
1504
1666
|
return additions;
|
|
1505
1667
|
}
|
|
1506
1668
|
|
|
1669
|
+
/** Keys pi computes for each Bash call (PATH carries pi's managed `rg`/`fd` bin dir). */
|
|
1670
|
+
const PI_SPAWN_ENV_KEYS = [
|
|
1671
|
+
"PATH",
|
|
1672
|
+
"PI_SESSION_ID",
|
|
1673
|
+
"PI_SESSION_FILE",
|
|
1674
|
+
"PI_PROVIDER",
|
|
1675
|
+
"PI_MODEL",
|
|
1676
|
+
"PI_REASONING_LEVEL",
|
|
1677
|
+
] as const;
|
|
1678
|
+
|
|
1679
|
+
/**
|
|
1680
|
+
* The env one pi Bash call runs with: the session's own env plus the keys pi
|
|
1681
|
+
* computed for the call. Nothing else from the process env gets through.
|
|
1682
|
+
*/
|
|
1683
|
+
export function buildPiShellEnv(
|
|
1684
|
+
sessionEnv: Record<string, string | undefined>,
|
|
1685
|
+
spawnEnv: BashSpawnContext["env"],
|
|
1686
|
+
): BashSpawnContext["env"] {
|
|
1687
|
+
const env: BashSpawnContext["env"] = { ...sessionEnv };
|
|
1688
|
+
for (const key of PI_SPAWN_ENV_KEYS) {
|
|
1689
|
+
if (spawnEnv[key] !== undefined) env[key] = spawnEnv[key];
|
|
1690
|
+
else if (key !== "PATH") delete env[key];
|
|
1691
|
+
}
|
|
1692
|
+
return env;
|
|
1693
|
+
}
|
|
1694
|
+
|
|
1507
1695
|
/**
|
|
1508
1696
|
* Builds and loads the resource loader for a pi SDK session.
|
|
1509
1697
|
*
|
|
@@ -1627,19 +1815,53 @@ export class PiMonoAdapter implements ProviderAdapter {
|
|
|
1627
1815
|
|
|
1628
1816
|
const sessionEnv = config.env ?? process.env;
|
|
1629
1817
|
|
|
1818
|
+
// A key picked from a pool for this task lives only in sessionEnv, so the
|
|
1819
|
+
// scrubber's process.env pass cannot see it. Register it before anything
|
|
1820
|
+
// (a provider error that echoes it, a log line) can reach an egress.
|
|
1821
|
+
registerEnvSecrets(sessionEnv);
|
|
1822
|
+
|
|
1630
1823
|
// 3. Resolve model
|
|
1631
|
-
//
|
|
1632
|
-
//
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
const
|
|
1824
|
+
// This session's own models.json: the shared file plus the gateway and
|
|
1825
|
+
// the registry's custom provider blocks from sessionEnv. Written BEFORE
|
|
1826
|
+
// ModelRuntime.create, which loads it exactly once. Nothing writes the
|
|
1827
|
+
// shared file, so concurrent sessions never read each other's endpoints.
|
|
1828
|
+
const sessionModels = await writeSessionModelsJson(getAgentDir(), sessionEnv, config.model);
|
|
1829
|
+
try {
|
|
1830
|
+
return await this.createSessionWithModels(config, sessionEnv, sessionModels, {
|
|
1831
|
+
createdSymlink,
|
|
1832
|
+
customTools,
|
|
1833
|
+
deferTools,
|
|
1834
|
+
piMcpServers,
|
|
1835
|
+
});
|
|
1836
|
+
} catch (err) {
|
|
1837
|
+
await sessionModels.dispose();
|
|
1838
|
+
throw err;
|
|
1839
|
+
}
|
|
1840
|
+
}
|
|
1841
|
+
|
|
1842
|
+
private async createSessionWithModels(
|
|
1843
|
+
config: ProviderSessionConfig,
|
|
1844
|
+
sessionEnv: Record<string, string | undefined>,
|
|
1845
|
+
sessionModels: SessionModelsJson,
|
|
1846
|
+
setup: {
|
|
1847
|
+
createdSymlink: boolean;
|
|
1848
|
+
customTools: ToolDefinition[];
|
|
1849
|
+
deferTools: boolean;
|
|
1850
|
+
piMcpServers: Record<string, McpServerConfig>;
|
|
1851
|
+
},
|
|
1852
|
+
): Promise<ProviderSession> {
|
|
1853
|
+
const { createdSymlink, customTools, deferTools, piMcpServers } = setup;
|
|
1854
|
+
// The runtime is created before resolving so custom providers, which only
|
|
1855
|
+
// exist in its composed store, can resolve.
|
|
1856
|
+
const modelRuntime = await createPiRuntimeAuth(sessionEnv, sessionModels.modelsPath);
|
|
1857
|
+
const resolvedModel = resolveModel(config.model, sessionEnv, modelRuntime);
|
|
1636
1858
|
// models.json overrides (e.g. the OpenRouter gateway baseUrl) only apply
|
|
1637
1859
|
// to models resolved through the runtime's composed store; the builtin
|
|
1638
1860
|
// catalog object from `resolveModel` carries the hardcoded upstream URL
|
|
1639
1861
|
// and would be used verbatim by the session.
|
|
1640
|
-
const model =
|
|
1641
|
-
? (modelRuntime.getModel(
|
|
1642
|
-
:
|
|
1862
|
+
const model = resolvedModel
|
|
1863
|
+
? (modelRuntime.getModel(resolvedModel.provider, resolvedModel.id) ?? resolvedModel)
|
|
1864
|
+
: resolvedModel;
|
|
1643
1865
|
|
|
1644
1866
|
// 4. Create swarm hooks extension
|
|
1645
1867
|
const swarmExtension = createSwarmHooksExtension({
|
|
@@ -1684,10 +1906,27 @@ export class PiMonoAdapter implements ProviderAdapter {
|
|
|
1684
1906
|
reasoningApplication.kind === "pi-session" ? reasoningApplication.sessionOptions : {};
|
|
1685
1907
|
const appliedReasoningEffort =
|
|
1686
1908
|
reasoningApplication.kind === "pi-session" ? (config.reasoningEffort ?? null) : null;
|
|
1909
|
+
// pi's built-in Bash tool spawns with a copy of process.env, which is
|
|
1910
|
+
// shared by every concurrent session and never sees this task's env (rows
|
|
1911
|
+
// admitted or withheld by fetchResolvedEnv after boot). A custom tool
|
|
1912
|
+
// named "bash" replaces the built-in, so every shell call, including
|
|
1913
|
+
// codemode's executeTool("bash"), runs with this session's env.
|
|
1914
|
+
const bashTool = defineTool(
|
|
1915
|
+
createBashToolDefinition(config.cwd, {
|
|
1916
|
+
commandPrefix: settingsManager.getShellCommandPrefix(),
|
|
1917
|
+
shellPath: settingsManager.getShellPath(),
|
|
1918
|
+
spawnHook: (context) => ({ ...context, env: buildPiShellEnv(sessionEnv, context.env) }),
|
|
1919
|
+
}),
|
|
1920
|
+
);
|
|
1921
|
+
// pi's own default session manager (same session dir), with its writes
|
|
1922
|
+
// to `sessions/*.jsonl` scrubbed.
|
|
1923
|
+
const sessionManager = SessionManager.create(config.cwd);
|
|
1924
|
+
scrubPiSessionPersistence(sessionManager);
|
|
1687
1925
|
const sessionOptions: CreateAgentSessionOptions = {
|
|
1688
1926
|
cwd: config.cwd,
|
|
1927
|
+
sessionManager,
|
|
1689
1928
|
model,
|
|
1690
|
-
customTools,
|
|
1929
|
+
customTools: [...customTools, bashTool],
|
|
1691
1930
|
resourceLoader,
|
|
1692
1931
|
settingsManager,
|
|
1693
1932
|
modelRuntime,
|
|
@@ -1700,7 +1939,13 @@ export class PiMonoAdapter implements ProviderAdapter {
|
|
|
1700
1939
|
const { session } = await createAgentSession(sessionOptions);
|
|
1701
1940
|
await session.bindExtensions({});
|
|
1702
1941
|
|
|
1703
|
-
return new PiMonoSession(
|
|
1942
|
+
return new PiMonoSession(
|
|
1943
|
+
session,
|
|
1944
|
+
config,
|
|
1945
|
+
createdSymlink,
|
|
1946
|
+
appliedReasoningEffort,
|
|
1947
|
+
sessionModels.dispose,
|
|
1948
|
+
);
|
|
1704
1949
|
}
|
|
1705
1950
|
|
|
1706
1951
|
async canResume(sessionId: string): Promise<boolean> {
|