@desplega.ai/agent-swarm 1.173.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/{acp-adapter-ccm9xrep.js → acp-adapter-kr4abrsk.js} +17 -16
- package/dist/{actions-sgeyv08k.js → actions-eh4g6w2r.js} +39 -34
- package/dist/{amp-adapter-gpxp3kc9.js → amp-adapter-7h7wdt8q.js} +8 -7
- package/dist/{app-km9qnv2f.js → app-7k8s9w5p.js} +18 -14
- package/dist/{assistant-5r8tcvvd.js → assistant-wswt2y7p.js} +40 -35
- package/dist/{boot-reembed-16rv9843.js → boot-reembed-9apm9qw6.js} +10 -9
- package/dist/{boot-reembed-swwzdgd7.js → boot-reembed-whzbdthj.js} +10 -9
- package/dist/{boot-scrub-sweep-zkr6vkqb.js → boot-scrub-sweep-ndzmvmgt.js} +11 -10
- package/dist/{boot-seeds-d8sp49t2.js → boot-seeds-e9sta0yt.js} +16 -13
- package/dist/{claude-adapter-yzzzh8jp.js → claude-adapter-4mqd3r6r.js} +19 -18
- package/dist/{claude-managed-adapter-xgxhqrt2.js → claude-managed-adapter-9wswb52m.js} +7 -5
- package/dist/{claude-managed-setup-bnmjarjc.js → claude-managed-setup-tpe1effx.js} +6 -4
- package/dist/{claude-sdk-session-4whxere4.js → claude-sdk-session-q49ghmh9.js} +12402 -11850
- package/dist/{cli-ne7smj1b.js → cli-01xvj1rq.js} +10 -10
- package/dist/{cli-nwrdx840.js → cli-035vekcw.js} +3 -3
- package/dist/{cli-9xf7fdtp.js → cli-0pd8hzhc.js} +29 -6
- package/dist/{cli-tmhsxhb8.js → cli-0veb92a3.js} +28 -6
- package/dist/{cli-c3rk1v0x.js → cli-10jesr25.js} +3 -3
- package/dist/{cli-6thk4jz3.js → cli-1y7yywmj.js} +6 -6
- package/dist/{cli-6r2f7edk.js → cli-20krdhz5.js} +47 -19
- package/dist/{cli-rfs4vddv.js → cli-29rja109.js} +39 -20
- package/dist/cli-2m1ktfzz.js +17795 -0
- package/dist/{cli-na2pxe4f.js → cli-2pwa0q4d.js} +15 -4
- package/dist/{cli-7a93cv8c.js → cli-4mengw46.js} +441 -43
- package/dist/{cli-94b48jhk.js → cli-5twdke5x.js} +16 -14
- package/dist/{cli-7a0sde2x.js → cli-6cagzc96.js} +9 -0
- package/dist/cli-6cvfxs8r.js +47 -0
- package/dist/{cli-w1apjecb.js → cli-711vwbxn.js} +2 -2
- package/dist/{cli-r4fqfpwf.js → cli-78vv7446.js} +50 -27
- package/dist/{cli-srqq3gc5.js → cli-7n6s5man.js} +5 -5
- package/dist/{cli-m89t9jdf.js → cli-7x0g4sn1.js} +15416 -16024
- package/dist/{cli-2bw3w47j.js → cli-8wwxw8a3.js} +1 -1
- package/dist/{cli-yatxqynm.js → cli-9ha6hs2d.js} +1 -290
- package/dist/{cli-ywm1azg9.js → cli-9jzdj90z.js} +8 -4
- package/dist/cli-9traj3dn.js +638 -0
- package/dist/{cli-smcrrmhy.js → cli-agzebbd5.js} +9 -9
- package/dist/{cli-c5km5znr.js → cli-c8r3kzzb.js} +1 -1
- package/dist/cli-cvv9k0hh.js +10143 -0
- package/dist/{cli-anzdk0z4.js → cli-d9286m9h.js} +2 -2
- package/dist/cli-e3g5mbkz.js +776 -0
- package/dist/cli-f527ce74.js +781 -0
- package/dist/{cli-gmdajfw5.js → cli-gxzkg1de.js} +5020 -1729
- package/dist/{cli-451ks6aw.js → cli-h1arqn31.js} +1 -1
- package/dist/cli-hjwmhcg1.js +10622 -0
- package/dist/{cli-jpc4r224.js → cli-hqr88ew6.js} +19 -3
- package/dist/{cli-h7k0xtry.js → cli-hvqbtna4.js} +4 -1256
- package/dist/{cli-cd0hv9nh.js → cli-j67mh2vg.js} +2 -2
- package/dist/cli-jta2eyzc.js +619 -0
- package/dist/{cli-wd0hz6ah.js → cli-kbrt8gqa.js} +9 -91
- package/dist/cli-kceah1eh.js +92 -0
- package/dist/{cli-d603qf86.js → cli-ksphgh48.js} +10 -8
- package/dist/{cli-3nxdmjrc.js → cli-mgz4nbgg.js} +4 -4
- package/dist/{cli-saqyage0.js → cli-mz5as5ha.js} +179 -27
- package/dist/cli-n2wq4j7m.js +6985 -0
- package/dist/{cli-ppy4nnnh.js → cli-n8ndw8j7.js} +2 -2
- package/dist/{cli-1w0vj3gw.js → cli-naqzr17j.js} +7 -7
- package/dist/{cli-0a7mm3zp.js → cli-nm1p8jfr.js} +25 -21
- package/dist/{cli-wrh08zjd.js → cli-p3zbc1rw.js} +5 -18
- package/dist/{cli-hsem16hb.js → cli-pe05f8yk.js} +29 -6
- package/dist/{cli-5wpw4rab.js → cli-qmzsbkvy.js} +5 -5
- package/dist/{cli-bv2etw2y.js → cli-qnm4e9e4.js} +99 -590
- package/dist/{cli-1x7da0de.js → cli-qqy4q48b.js} +8 -8
- package/dist/{cli-r44t9wqc.js → cli-r106725k.js} +2 -2
- package/dist/{cli-5er5gjbm.js → cli-r35rw5de.js} +15 -15
- package/dist/{cli-v3zhnxsb.js → cli-r610p9bs.js} +2 -2
- package/dist/{cli-7jykv7ef.js → cli-rc6pf5kv.js} +1 -1
- package/dist/{cli-x119kdba.js → cli-rdxm77k3.js} +1 -1
- package/dist/cli-rhs9pz33.js +294 -0
- package/dist/{cli-0p3enk35.js → cli-s69nfyeh.js} +9 -1
- package/dist/{cli-0kxf81jj.js → cli-sawv63e8.js} +2 -2
- package/dist/{cli-e3pmyt39.js → cli-sg19gbf0.js} +1 -1
- package/dist/{cli-d161g69q.js → cli-shk7r5cs.js} +57 -6
- package/dist/{cli-7pz3b7x5.js → cli-sksetb2q.js} +36 -1
- package/dist/{cli-014vns2y.js → cli-stfs0k5s.js} +208 -525
- package/dist/{cli-db3m6zdt.js → cli-t02z59f2.js} +1 -1
- package/dist/{cli-b07hr3bt.js → cli-txqb9s38.js} +2 -2
- package/dist/{cli-s8z6x4b4.js → cli-v7g551te.js} +225 -113
- package/dist/{cli-rfz2ve55.js → cli-vgqzp4em.js} +3341 -955
- package/dist/{cli-7avy4v8w.js → cli-w1q7h2r2.js} +5 -3
- package/dist/{cli-rcp5aqdw.js → cli-w7m81tb7.js} +1 -1
- package/dist/cli-wfnemr1t.js +86 -0
- package/dist/cli-wmgrkscj.js +23254 -0
- package/dist/{cli-qm1448f9.js → cli-wms6x9db.js} +8 -8
- package/dist/{cli-tadfgd0t.js → cli-x1r545x8.js} +33 -4
- package/dist/{cli-xc7p7s3g.js → cli-xczsgshn.js} +6 -6
- package/dist/{cli-fmhj1ehj.js → cli-y3167wt1.js} +882 -271
- package/dist/{cli-r0byd61h.js → cli-yjmqpxt3.js} +17 -16
- package/dist/{cli-81r1wjyv.js → cli-z780ec7c.js} +1 -1
- package/dist/{cli-phcqp1b9.js → cli-zm9t2vde.js} +2 -2
- package/dist/cli.js +34 -49
- package/dist/{codex-adapter-r3dn4fp5.js → codex-adapter-4z16v9n0.js} +15 -14
- package/dist/{codex-login-841kv5fk.js → codex-login-pkepy2j1.js} +3 -2
- package/dist/{codex-session-runner-fwt793h4.js → codex-session-runner-rvms9y2x.js} +15 -14
- package/dist/{commands-z8fq9qna.js → commands-2a9rtyat.js} +11 -10
- package/dist/{controller-hq5ggpcp.js → controller-3k3madbn.js} +5 -4
- package/dist/{controllers-connect-3mf2btyj.js → controllers-connect-v9yn8zb9.js} +42 -37
- package/dist/{cursor-adapter-q8pgv5qa.js → cursor-adapter-98gbhyzn.js} +7 -6
- package/dist/{db-qfpq4bt7.js → db-eywwsnjw.js} +30 -9
- package/dist/{db-yyhqqrbf.js → db-ncypk9np.js} +12 -11
- package/dist/{devin-adapter-xgtj680j.js → devin-adapter-zeq8b9gz.js} +3 -2
- package/dist/{dispatcher-x0x7qfcq.js → dispatcher-w94kc575.js} +38 -33
- package/dist/{docker-f503k9p0.js → docker-b1e4cqab.js} +3 -2
- package/dist/{dsh-adapter-34etvb5m.js → dsh-adapter-47ztej36.js} +7 -6
- package/dist/{e2b-66xra4az.js → e2b-bbt8ey45.js} +4 -3
- package/dist/{e2b-xwhyj8cw.js → e2b-dp804sen.js} +4 -3
- package/dist/{event-bus-vjtv82gm.js → event-bus-j2jdg3qb.js} +3 -2
- package/dist/{external-channel-h1fjecwe.js → external-channel-zrkg8ar9.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-s9ncp9vy.js} +18 -17
- package/dist/{handlers-znb9n7d4.js → handlers-kqftvjgw.js} +41 -36
- package/dist/{heartbeat-bm4qkn8d.js → heartbeat-9q96p8v9.js} +43 -38
- package/dist/{hook-254r4mza.js → hook-56qe10dp.js} +11 -23
- package/dist/{hook-xaa3q80s.js → hook-yy6rw42k.js} +12 -11
- package/dist/{http-3dgrgp8y.js → http-r2dnb8f8.js} +2411 -275
- package/dist/{index-fx3jqq1e.js → index-3gtmkhc6.js} +38 -33
- package/dist/{index-bdsqa799.js → index-3k5hkz7r.js} +43 -38
- package/dist/index-52j1aja4.js +3465 -0
- package/dist/{index-g2byveez.js → index-5e101240.js} +38 -33
- package/dist/{index-2ybk7khy.js → index-5wyfjc2c.js} +845 -683
- 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-jd7sejm2.js → index-vxvhxxbn.js} +10 -9
- package/dist/index-we27kd31.js +12810 -0
- package/dist/{keepalive-6ywhpja1.js → keepalive-psj6j54c.js} +12 -11
- package/dist/{lead-cw45n21s.js → lead-hajz4688.js} +61 -56
- package/dist/{maintenance-ss1a1ge9.js → maintenance-ps02sb5m.js} +10 -9
- package/dist/{node-fkajaq96.js → node-mwb5r697.js} +2 -1
- package/dist/{oauth-refresh-sweep-3c6f8vyy.js → oauth-refresh-sweep-0284h93b.js} +11 -10
- package/dist/{onboard-8hzdhc1k.js → onboard-rbf6bg01.js} +11 -3
- package/dist/{opencode-adapter-qbchbcfz.js → opencode-adapter-13dcatgq.js} +18 -13
- package/dist/{otel-impl-e6mnvc4v.js → otel-impl-zvqcbhp7.js} +242 -868
- package/dist/{pi-mono-adapter-3c9g9snh.js → pi-mono-adapter-xxy47q4v.js} +244 -106
- package/dist/{pricing-refresh-gedkjdw0.js → pricing-refresh-kefx9v3t.js} +12 -11
- package/dist/{rbac-roles-dt4vh1yy.js → rbac-roles-3m36rxm2.js} +11 -10
- package/dist/{rbac-roles-eszdg6pm.js → rbac-roles-z76xv0hq.js} +12 -11
- package/dist/{render-v2-907cy6sa.js → render-v2-wz73bs25.js} +18 -14
- package/dist/{resume-42stcaz0.js → resume-k4wzge8w.js} +25 -21
- package/dist/{runtime-qfry88v2.js → runtime-sa2vr16g.js} +2 -2
- package/dist/{setup-adbc1c10.js → setup-2fp2pypp.js} +2 -2
- package/dist/{virtual-modules-rtmt494q.js → virtual-modules-9gkxqrap.js} +6 -5
- package/dist/{work-objects-wpj8ck95.js → work-objects-6k0ywz3m.js} +41 -36
- package/dist/{worker-bzdra9rf.js → worker-5v3rxsdh.js} +61 -56
- package/dist/{x-qfmygqqv.js → x-4mtdeym1.js} +3 -2
- package/openapi.json +1279 -329
- package/package.json +15 -15
- package/src/agentmail/handlers.ts +21 -11
- package/src/apps/definition.ts +16 -0
- package/src/azure-devops/api.ts +52 -2
- package/src/azure-devops/boards-sync.ts +50 -20
- package/src/azure-devops/index.ts +1 -0
- package/src/azure-devops/outbound.ts +95 -0
- package/src/be/boot-seeds.ts +11 -0
- package/src/be/config-refs.ts +60 -0
- package/src/be/db/model-profiles.ts +315 -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 +237 -0
- package/src/be/db/tasks/write.ts +7 -0
- package/src/be/db.ts +87 -36
- 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/migrations/219_relabel_pi_context_compactions.sql +22 -0
- package/src/be/migrations/220_model_profiles.sql +137 -0
- package/src/be/model-catalog-store.ts +5 -1
- package/src/be/model-tier-resolution.ts +155 -10
- package/src/be/model-validation.ts +12 -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 +22 -8
- package/src/be/secret-registry.ts +6 -2
- 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-model-profiles.ts +41 -0
- package/src/be/seed-pricing.ts +19 -2
- package/src/be/seed-scripts/catalog/heartbeat-jev-gate.ts +368 -0
- 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-scripts/index.ts +9 -0
- 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 +53 -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 +193 -71
- package/src/e2b/env.ts +5 -0
- package/src/extensions/catalog.generated.json +17 -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/loader.ts +10 -5
- package/src/extensions/post-bridge.ts +40 -0
- package/src/github/handlers.ts +11 -4
- package/src/heartbeat/heartbeat.ts +2 -1
- package/src/heartbeat/templates.ts +8 -5
- package/src/hooks/hook.ts +0 -26
- package/src/http/agents.ts +49 -2
- package/src/http/all-routes.ts +1 -1
- package/src/http/apps.ts +30 -3
- package/src/http/config.ts +62 -19
- package/src/http/core.ts +8 -0
- package/src/http/index.ts +25 -3
- package/src/http/integrations.ts +88 -1
- package/src/http/mcp.ts +15 -5
- package/src/http/model-profiles.ts +303 -0
- package/src/http/page-proxy.ts +2 -4
- package/src/http/pages-public.ts +13 -7
- package/src/http/pages.ts +3 -13
- package/src/http/poll.ts +42 -71
- package/src/http/requested-by.ts +54 -0
- package/src/http/script-runs.ts +1 -1
- package/src/http/scripts.ts +26 -2
- package/src/http/stats.ts +1 -30
- package/src/http/tasks.ts +79 -5
- package/src/http/utils.ts +14 -0
- package/src/http/webhooks.ts +160 -3
- 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/{artifact-sdk → pages}/feedback-overlay.ts +65 -6
- 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 +56 -3
- 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/pi-mono-extension.ts +17 -3
- package/src/providers/pricing-sources.md +33 -2
- package/src/providers/reasoning-effort.ts +37 -0
- package/src/providers/types.ts +6 -2
- package/src/rbac/legacy-policy.ts +16 -2
- package/src/rbac/permissions.ts +26 -0
- package/src/scheduler/deferred-task-waits.ts +18 -5
- package/src/scheduler/scheduler.ts +11 -3
- package/src/scripts-runtime/executors/quickjs-prelude.ts +75 -6
- package/src/scripts-runtime/executors/quickjs-runner.ts +85 -8
- package/src/scripts-runtime/executors/quickjs-web-shims.ts +1041 -0
- package/src/scripts-runtime/extract-args-schema.ts +9 -8
- package/src/scripts-runtime/loader.ts +19 -7
- package/src/scripts-runtime/quickjs-compat.ts +464 -0
- package/src/scripts-runtime/sdk-allowlist.ts +12 -6
- package/src/scripts-runtime/swarm-sdk.ts +0 -11
- package/src/scripts-runtime/types/stdlib.d.ts +39 -15
- package/src/scripts-runtime/types/swarm-extension.d.ts +66 -1
- package/src/scripts-runtime/types/swarm-sdk.d.ts +39 -15
- package/src/server.ts +62 -19
- package/src/slack/blocks.ts +4 -2
- package/src/slack/event-dedup.ts +1 -1
- package/src/slack/reaction-shortcode.ts +14 -0
- package/src/slack/render-v2.ts +12 -7
- package/src/slack/responses.ts +5 -4
- package/src/slack/thread-buffer.ts +41 -22
- package/src/tasks/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 +15 -33
- package/src/tests/acp-adapter.test.ts +42 -0
- package/src/tests/agentmail-filters.test.ts +47 -0
- package/src/tests/api-drain-worker-e2e.test.ts +1 -6
- package/src/tests/apps-spike2.test.ts +35 -0
- package/src/tests/azure-devops-outbound.test.ts +420 -0
- 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 +22 -0
- package/src/tests/db-task-write-contract.test.ts +11 -0
- package/src/tests/deferred-task-wake.test.ts +78 -1
- 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/env-flag.test.ts +1 -0
- package/src/tests/explicit-model-validation.test.ts +105 -1
- package/src/tests/extensions-catalog.test.ts +1 -0
- package/src/tests/extensions-example-comms-adherence-gate.test.ts +413 -0
- package/src/tests/extensions-example-route-chat-channel.test.ts +154 -0
- package/src/tests/extensions-loader.test.ts +15 -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/heartbeat-jev-gate-credentials.test.ts +168 -0
- package/src/tests/heartbeat-jev-gate.test.ts +385 -0
- 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-profile-backcompat.test.ts +327 -0
- package/src/tests/model-profile-providers.test.ts +302 -0
- package/src/tests/model-profile-runtime-secrets.test.ts +223 -0
- package/src/tests/model-profiles-api.test.ts +614 -0
- package/src/tests/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-keys.test.ts +121 -0
- package/src/tests/pages-feedback.test.ts +4 -1
- package/src/tests/paused-tasks-requested-by.test.ts +102 -0
- package/src/tests/pi-context-compaction.test.ts +193 -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/profile-claim.test.ts +269 -0
- package/src/tests/prompt-template-remaining.test.ts +35 -0
- package/src/tests/quickjs-compat.test.ts +213 -0
- package/src/tests/rbac-engine.test.ts +32 -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 +6 -0
- package/src/tests/script-executor-conformance.test.ts +252 -1
- package/src/tests/script-executor-quickjs.test.ts +96 -0
- package/src/tests/script-runs-http.test.ts +89 -0
- package/src/tests/scripts-http.test.ts +217 -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 +96 -0
- package/src/tests/seed-model-profiles.test.ts +174 -0
- package/src/tests/seed-scripts.test.ts +2 -2
- 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/skill-invocations.test.ts +4 -2
- package/src/tests/slack-blocks.test.ts +36 -0
- package/src/tests/slack-inline-output.test.ts +14 -0
- package/src/tests/slack-reaction-config.test.ts +20 -0
- package/src/tests/slack-render-v2.test.ts +37 -0
- package/src/tests/slack-thread-buffer.test.ts +98 -1
- package/src/tests/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-llm.test.ts +61 -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/model-profiles/list-model-profiles.ts +54 -0
- package/src/tools/register-chat-channel.ts +139 -0
- package/src/tools/request-human-input.ts +11 -3
- package/src/tools/script-apis.ts +5 -1
- package/src/tools/script-upsert.ts +4 -1
- package/src/tools/send-task.ts +48 -1
- 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/system-one-decide.ts +104 -0
- package/src/tools/task-action.ts +22 -1
- package/src/tools/tool-config.ts +14 -6
- package/src/types.ts +48 -3
- 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 +86 -10
- package/src/utils/task-tool-manifest.ts +19 -2
- package/src/utils/vcs-repo-match.ts +29 -0
- package/src/workflows/executors/raw-llm.ts +8 -3
- package/src/workflows/executors/swarm-script.ts +1 -0
- package/src/workflows/executors/system-one-decision.ts +21 -11
- package/src/workflows/executors/workflow-llm.ts +22 -0
- package/src/workflows/index.ts +21 -12
- package/templates/extensions/README.md +1 -0
- package/templates/extensions/comms-adherence-gate/README.md +88 -0
- package/templates/extensions/comms-adherence-gate/hooks.ts +489 -0
- package/templates/extensions/comms-adherence-gate/manifest.json +8 -0
- package/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-42qhyvwx.js +0 -33
- 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
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
// Model profiles CRUD (runbooks/model-tiers.md § Model profiles).
|
|
2
|
+
//
|
|
3
|
+
// A profile is a named harness + model + optional effort. Lead picks one per
|
|
4
|
+
// task (`profile` on send-task / POST /api/tasks) or sets it as an agent's
|
|
5
|
+
// default (PATCH /api/agents/{id}/runtime { profile }); claim resolves it.
|
|
6
|
+
// Reads are open to every caller; writes are operator-only (`models.profiles.write`).
|
|
7
|
+
|
|
8
|
+
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
9
|
+
import { isAlias } from "@desplega/model-catalog";
|
|
10
|
+
import { z } from "zod";
|
|
11
|
+
import { resolveHttpAuditUserId } from "../be/audit-user";
|
|
12
|
+
import {
|
|
13
|
+
createModelProfile,
|
|
14
|
+
deleteModelProfile,
|
|
15
|
+
getModelProfileByName,
|
|
16
|
+
listModelProfiles,
|
|
17
|
+
modelProfileReferences,
|
|
18
|
+
updateModelProfile,
|
|
19
|
+
} from "../be/db/model-profiles";
|
|
20
|
+
import { explicitModelError } from "../be/model-validation";
|
|
21
|
+
import { supportedReasoningLevels } from "../providers/reasoning-effort";
|
|
22
|
+
import { can } from "../rbac";
|
|
23
|
+
import {
|
|
24
|
+
ModelProfileNameSchema,
|
|
25
|
+
ModelProfileSchema,
|
|
26
|
+
type ProviderName,
|
|
27
|
+
ProviderNameSchema,
|
|
28
|
+
type ReasoningEffort,
|
|
29
|
+
ReasoningEffortSchema,
|
|
30
|
+
} from "../types";
|
|
31
|
+
import { agentFirstPrincipal } from "./request-principal";
|
|
32
|
+
import { route } from "./route-def";
|
|
33
|
+
import { jsonError } from "./utils";
|
|
34
|
+
|
|
35
|
+
const listProfiles = route({
|
|
36
|
+
method: "get",
|
|
37
|
+
path: "/api/model-profiles",
|
|
38
|
+
pattern: ["api", "model-profiles"],
|
|
39
|
+
summary: "List model profiles",
|
|
40
|
+
description:
|
|
41
|
+
"Every model profile, ordered by harness then name. Filter by `harness` to see what an agent on that harness can run.",
|
|
42
|
+
tags: ["Models"],
|
|
43
|
+
query: z.object({ harness: ProviderNameSchema.optional() }),
|
|
44
|
+
responses: {
|
|
45
|
+
200: {
|
|
46
|
+
description: "Model profiles",
|
|
47
|
+
schema: z.object({ profiles: z.array(ModelProfileSchema) }),
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const getProfile = route({
|
|
53
|
+
method: "get",
|
|
54
|
+
path: "/api/model-profiles/{name}",
|
|
55
|
+
pattern: ["api", "model-profiles", null],
|
|
56
|
+
summary: "Get one model profile",
|
|
57
|
+
tags: ["Models"],
|
|
58
|
+
params: z.object({ name: z.string() }),
|
|
59
|
+
responses: {
|
|
60
|
+
200: { description: "The model profile", schema: ModelProfileSchema },
|
|
61
|
+
404: { description: "No profile with that name" },
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
const createProfile = route({
|
|
66
|
+
method: "post",
|
|
67
|
+
path: "/api/model-profiles",
|
|
68
|
+
pattern: ["api", "model-profiles"],
|
|
69
|
+
summary: "Create a model profile",
|
|
70
|
+
description:
|
|
71
|
+
"A profile runs on one harness. `model` must run on that harness and be in the model catalog unless `allowCustomModel` is true (a gateway id, a fresh launch); a `latest:` alias is accepted and resolves at claim. `effort` must be a level the harness and model support. Operator only (the shared API key).",
|
|
72
|
+
tags: ["Models"],
|
|
73
|
+
rbac: { permission: "models.profiles.write" },
|
|
74
|
+
body: z.object({
|
|
75
|
+
name: ModelProfileNameSchema,
|
|
76
|
+
harness: ProviderNameSchema,
|
|
77
|
+
model: z.string().trim().min(1),
|
|
78
|
+
effort: ReasoningEffortSchema.nullable().optional(),
|
|
79
|
+
allowCustomModel: z.boolean().optional().default(false),
|
|
80
|
+
description: z.string().trim().max(500).nullable().optional(),
|
|
81
|
+
}),
|
|
82
|
+
responses: {
|
|
83
|
+
201: { description: "Profile created", schema: ModelProfileSchema },
|
|
84
|
+
400: { description: "Validation error" },
|
|
85
|
+
403: { description: "Forbidden" },
|
|
86
|
+
409: { description: "A profile with that name exists" },
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
const updateProfile = route({
|
|
91
|
+
method: "patch",
|
|
92
|
+
path: "/api/model-profiles/{name}",
|
|
93
|
+
pattern: ["api", "model-profiles", null],
|
|
94
|
+
summary: "Update a model profile",
|
|
95
|
+
description:
|
|
96
|
+
"Changes the model, effort, custom-model flag or description; the name and harness are fixed. Omit a field to keep it; `effort: null` clears it. Applies to tasks claimed after the change. Operator only (the shared API key).",
|
|
97
|
+
tags: ["Models"],
|
|
98
|
+
rbac: { permission: "models.profiles.write" },
|
|
99
|
+
params: z.object({ name: z.string() }),
|
|
100
|
+
body: z.object({
|
|
101
|
+
model: z.string().trim().min(1).optional(),
|
|
102
|
+
effort: ReasoningEffortSchema.nullable().optional(),
|
|
103
|
+
allowCustomModel: z.boolean().optional(),
|
|
104
|
+
description: z.string().trim().max(500).nullable().optional(),
|
|
105
|
+
}),
|
|
106
|
+
responses: {
|
|
107
|
+
200: { description: "Profile updated", schema: ModelProfileSchema },
|
|
108
|
+
400: { description: "Validation error" },
|
|
109
|
+
403: { description: "Forbidden" },
|
|
110
|
+
404: { description: "No profile with that name" },
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
const deleteProfile = route({
|
|
115
|
+
method: "delete",
|
|
116
|
+
path: "/api/model-profiles/{name}",
|
|
117
|
+
pattern: ["api", "model-profiles", null],
|
|
118
|
+
summary: "Delete a model profile",
|
|
119
|
+
description:
|
|
120
|
+
"Refused (409) for a shipped `<harness>-<tier>` seed (edit it instead; it would come back at the next boot), and while an agent has the profile as its default or a task holds it (unfinished, or superseded and still owed its resume). Operator only (the shared API key).",
|
|
121
|
+
tags: ["Models"],
|
|
122
|
+
rbac: { permission: "models.profiles.write" },
|
|
123
|
+
params: z.object({ name: z.string() }),
|
|
124
|
+
responses: {
|
|
125
|
+
200: { description: "Profile deleted", schema: z.object({ deleted: z.literal(true) }) },
|
|
126
|
+
403: { description: "Forbidden" },
|
|
127
|
+
404: { description: "No profile with that name" },
|
|
128
|
+
409: { description: "The profile is a seed or still in use" },
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
/** Why `harness` + `model` + `effort` is not a valid profile, or null. */
|
|
133
|
+
async function profileValueError(input: {
|
|
134
|
+
harness: ProviderName;
|
|
135
|
+
model: string;
|
|
136
|
+
effort: ReasoningEffort | null | undefined;
|
|
137
|
+
allowCustomModel: boolean;
|
|
138
|
+
}): Promise<string | null> {
|
|
139
|
+
const modelError = await explicitModelError({
|
|
140
|
+
model: input.model,
|
|
141
|
+
allowCustomModel: input.allowCustomModel,
|
|
142
|
+
harnessProvider: input.harness,
|
|
143
|
+
});
|
|
144
|
+
if (modelError) return modelError;
|
|
145
|
+
// The levels the harness and model accept (as PATCH /runtime checks them); a
|
|
146
|
+
// supported level may still be a transport no-op, like Opencode `off`. An
|
|
147
|
+
// alias is judged once it resolves.
|
|
148
|
+
if (
|
|
149
|
+
input.effort &&
|
|
150
|
+
!isAlias(input.model) &&
|
|
151
|
+
!supportedReasoningLevels(input.harness, input.model).includes(input.effort)
|
|
152
|
+
) {
|
|
153
|
+
return `Effort "${input.effort}" is not supported by ${input.model} on ${input.harness}.`;
|
|
154
|
+
}
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** Gate a write on `models.profiles.write`; writes a 403 and returns false on denial. */
|
|
159
|
+
async function canWriteProfiles(
|
|
160
|
+
req: IncomingMessage,
|
|
161
|
+
res: ServerResponse,
|
|
162
|
+
myAgentId: string | undefined,
|
|
163
|
+
): Promise<boolean> {
|
|
164
|
+
const decision = can({
|
|
165
|
+
principal: await agentFirstPrincipal(req, myAgentId),
|
|
166
|
+
verb: "models.profiles.write",
|
|
167
|
+
resource: { kind: "none" },
|
|
168
|
+
source: "http",
|
|
169
|
+
});
|
|
170
|
+
if (!decision.allow) {
|
|
171
|
+
jsonError(res, "Writing model profiles requires the operator", 403);
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
174
|
+
return true;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export async function handleModelProfiles(
|
|
178
|
+
req: IncomingMessage,
|
|
179
|
+
res: ServerResponse,
|
|
180
|
+
pathSegments: string[],
|
|
181
|
+
queryParams: URLSearchParams,
|
|
182
|
+
myAgentId: string | undefined,
|
|
183
|
+
): Promise<boolean> {
|
|
184
|
+
if (listProfiles.match(req.method, pathSegments)) {
|
|
185
|
+
const parsed = await listProfiles.parse(req, res, pathSegments, queryParams);
|
|
186
|
+
if (!parsed) return true;
|
|
187
|
+
listProfiles.respond(res, 200, {
|
|
188
|
+
profiles: await listModelProfiles({ harness: parsed.query.harness }),
|
|
189
|
+
});
|
|
190
|
+
return true;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (getProfile.match(req.method, pathSegments)) {
|
|
194
|
+
const parsed = await getProfile.parse(req, res, pathSegments, queryParams);
|
|
195
|
+
if (!parsed) return true;
|
|
196
|
+
const profile = await getModelProfileByName(parsed.params.name);
|
|
197
|
+
if (!profile) {
|
|
198
|
+
jsonError(res, "Model profile not found", 404);
|
|
199
|
+
return true;
|
|
200
|
+
}
|
|
201
|
+
getProfile.respond(res, 200, profile);
|
|
202
|
+
return true;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (createProfile.match(req.method, pathSegments)) {
|
|
206
|
+
const parsed = await createProfile.parse(req, res, pathSegments, queryParams);
|
|
207
|
+
if (!parsed) return true;
|
|
208
|
+
if (!(await canWriteProfiles(req, res, myAgentId))) return true;
|
|
209
|
+
const body = parsed.body;
|
|
210
|
+
const error = await profileValueError({ ...body, effort: body.effort ?? null });
|
|
211
|
+
if (error) {
|
|
212
|
+
jsonError(res, error, 400);
|
|
213
|
+
return true;
|
|
214
|
+
}
|
|
215
|
+
const created = await createModelProfile(body, {
|
|
216
|
+
userId: await resolveHttpAuditUserId(req, myAgentId),
|
|
217
|
+
});
|
|
218
|
+
if (!created) {
|
|
219
|
+
jsonError(res, `Model profile "${body.name}" already exists`, 409);
|
|
220
|
+
return true;
|
|
221
|
+
}
|
|
222
|
+
createProfile.respond(res, 201, created);
|
|
223
|
+
return true;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (updateProfile.match(req.method, pathSegments)) {
|
|
227
|
+
const parsed = await updateProfile.parse(req, res, pathSegments, queryParams);
|
|
228
|
+
if (!parsed) return true;
|
|
229
|
+
if (!(await canWriteProfiles(req, res, myAgentId))) return true;
|
|
230
|
+
const current = await getModelProfileByName(parsed.params.name);
|
|
231
|
+
if (!current) {
|
|
232
|
+
jsonError(res, "Model profile not found", 404);
|
|
233
|
+
return true;
|
|
234
|
+
}
|
|
235
|
+
const body = parsed.body;
|
|
236
|
+
const error = await profileValueError({
|
|
237
|
+
harness: current.harness,
|
|
238
|
+
model: body.model ?? current.model,
|
|
239
|
+
effort: body.effort === undefined ? current.effort : body.effort,
|
|
240
|
+
allowCustomModel: body.allowCustomModel ?? current.allowCustomModel,
|
|
241
|
+
});
|
|
242
|
+
if (error) {
|
|
243
|
+
jsonError(res, error, 400);
|
|
244
|
+
return true;
|
|
245
|
+
}
|
|
246
|
+
const updated = await updateModelProfile(current.name, body, {
|
|
247
|
+
userId: await resolveHttpAuditUserId(req, myAgentId),
|
|
248
|
+
});
|
|
249
|
+
if (!updated) {
|
|
250
|
+
jsonError(res, "Model profile not found", 404);
|
|
251
|
+
return true;
|
|
252
|
+
}
|
|
253
|
+
updateProfile.respond(res, 200, updated);
|
|
254
|
+
return true;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if (deleteProfile.match(req.method, pathSegments)) {
|
|
258
|
+
const parsed = await deleteProfile.parse(req, res, pathSegments, queryParams);
|
|
259
|
+
if (!parsed) return true;
|
|
260
|
+
if (!(await canWriteProfiles(req, res, myAgentId))) return true;
|
|
261
|
+
const current = await getModelProfileByName(parsed.params.name);
|
|
262
|
+
if (!current) {
|
|
263
|
+
jsonError(res, "Model profile not found", 404);
|
|
264
|
+
return true;
|
|
265
|
+
}
|
|
266
|
+
if (current.seededFrom?.startsWith("tier:")) {
|
|
267
|
+
jsonError(
|
|
268
|
+
res,
|
|
269
|
+
`"${current.name}" is a shipped tier profile and comes back at the next boot; edit it instead.`,
|
|
270
|
+
409,
|
|
271
|
+
);
|
|
272
|
+
return true;
|
|
273
|
+
}
|
|
274
|
+
const refs = await modelProfileReferences(current.name);
|
|
275
|
+
if (refs.agentIds.length > 0 || refs.openTaskCount > 0) {
|
|
276
|
+
jsonError(
|
|
277
|
+
res,
|
|
278
|
+
`"${current.name}" is in use: default of ${refs.agentIds.length} agent(s), held by ${refs.openTaskCount} unfinished or awaiting-resume task(s).`,
|
|
279
|
+
409,
|
|
280
|
+
);
|
|
281
|
+
return true;
|
|
282
|
+
}
|
|
283
|
+
// The count above is for the message; the delete re-checks in the same
|
|
284
|
+
// statement (migration 220 trigger), so a reference written since is a 409.
|
|
285
|
+
const outcome = await deleteModelProfile(current.name);
|
|
286
|
+
if (outcome === "in-use") {
|
|
287
|
+
jsonError(
|
|
288
|
+
res,
|
|
289
|
+
`"${current.name}" is in use by a task or an agent default created just now.`,
|
|
290
|
+
409,
|
|
291
|
+
);
|
|
292
|
+
return true;
|
|
293
|
+
}
|
|
294
|
+
if (outcome === "missing") {
|
|
295
|
+
jsonError(res, "Model profile not found", 404);
|
|
296
|
+
return true;
|
|
297
|
+
}
|
|
298
|
+
deleteProfile.respond(res, 200, { deleted: true });
|
|
299
|
+
return true;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
return false;
|
|
303
|
+
}
|
package/src/http/page-proxy.ts
CHANGED
|
@@ -6,10 +6,8 @@ import { route } from "./route-def";
|
|
|
6
6
|
import { jsonError } from "./utils";
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
|
-
* `/@swarm/api/*` proxy.
|
|
10
|
-
*
|
|
11
|
-
* authenticates via the page-session cookie instead of basic-auth + a per-
|
|
12
|
-
* artifact tunnel.
|
|
9
|
+
* `/@swarm/api/*` proxy. Lives on the MAIN API server and authenticates via
|
|
10
|
+
* the page-session cookie.
|
|
13
11
|
*
|
|
14
12
|
* Flow:
|
|
15
13
|
* 1. Browser hits `/@swarm/api/<rest>` from inside an iframe of `/p/:id`.
|
package/src/http/pages-public.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*
|
|
8
8
|
* Scope of THIS module (step-3):
|
|
9
9
|
* - `auth_mode === 'public'`: ungated. HTML responses inline-inject the
|
|
10
|
-
* `BROWSER_SDK_JS` constant from `src/
|
|
10
|
+
* `BROWSER_SDK_JS` constant from `src/pages/browser-sdk.ts` (reused
|
|
11
11
|
* verbatim — no token-injection hook on the client). SVG responses serve
|
|
12
12
|
* raw bytes with a restrictive CSP and no injected scripts. JSON responses
|
|
13
13
|
* 302-redirect to the SPA `/pages/:id` route (the JSON renderer lives
|
|
@@ -23,9 +23,13 @@
|
|
|
23
23
|
*/
|
|
24
24
|
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
25
25
|
import { z } from "zod";
|
|
26
|
-
import { BROWSER_SDK_JS, SWARM_UI_JS } from "../artifact-sdk/browser-sdk";
|
|
27
|
-
import { feedbackOverlayScripts, isPageFeedbackRequested } from "../artifact-sdk/feedback-overlay";
|
|
28
26
|
import { getPage, incrementPageViewCount } from "../be/db";
|
|
27
|
+
import { BROWSER_SDK_JS, SWARM_UI_JS } from "../pages/browser-sdk";
|
|
28
|
+
import {
|
|
29
|
+
feedbackKeyGuardScript,
|
|
30
|
+
feedbackOverlayScripts,
|
|
31
|
+
isPageFeedbackRequested,
|
|
32
|
+
} from "../pages/feedback-overlay";
|
|
29
33
|
import { type Page, PageSchema } from "../types";
|
|
30
34
|
import { getAppUrl, getConfiguredAppUrls } from "../utils/constants";
|
|
31
35
|
import { getNodeEnv } from "../utils/node-env";
|
|
@@ -170,8 +174,8 @@ const PAGE_HEAD_DEFAULTS = `<base target="_blank">
|
|
|
170
174
|
}
|
|
171
175
|
</style>`;
|
|
172
176
|
|
|
173
|
-
function injectBrowserSdk(html: string): string {
|
|
174
|
-
const injection = `${PAGE_HEAD_DEFAULTS}<script>${BROWSER_SDK_JS}</script><script>${SWARM_UI_JS}</script>`;
|
|
177
|
+
function injectBrowserSdk(html: string, headFirst = ""): string {
|
|
178
|
+
const injection = `${headFirst}${PAGE_HEAD_DEFAULTS}<script>${BROWSER_SDK_JS}</script><script>${SWARM_UI_JS}</script>`;
|
|
175
179
|
// Use the first occurrence of `<head>` (case-insensitive). A page that
|
|
176
180
|
// doesn't have a `<head>` element (raw fragment) still gets the SDK at the
|
|
177
181
|
// front of the document.
|
|
@@ -646,10 +650,12 @@ export async function handlePagesPublic(
|
|
|
646
650
|
// text/html — inject SDK + serve. Append the self-print snippet when the
|
|
647
651
|
// export button requested it, and the feedback overlay when
|
|
648
652
|
// `?__swarm-feedback` is on (the overlay hides itself in print).
|
|
653
|
+
// The feedback key guard goes first in <head> so it runs before page scripts.
|
|
654
|
+
const feedback = isPageFeedbackRequested(queryParams);
|
|
649
655
|
const html =
|
|
650
|
-
injectBrowserSdk(page.body) +
|
|
656
|
+
injectBrowserSdk(page.body, feedback ? feedbackKeyGuardScript() : "") +
|
|
651
657
|
(wantsPrint ? PRINT_AUTOTRIGGER_SCRIPT : "") +
|
|
652
|
-
(
|
|
658
|
+
(feedback ? feedbackOverlayScripts(page.id) : "");
|
|
653
659
|
const headers: Record<string, string> = {
|
|
654
660
|
"Content-Type": "text/html; charset=utf-8",
|
|
655
661
|
"Cache-Control": "no-store",
|
package/src/http/pages.ts
CHANGED
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
updatePage,
|
|
20
20
|
withFavoriteFlags,
|
|
21
21
|
} from "../be/db";
|
|
22
|
+
import { getPageEditCounter } from "../be/db/page-versions";
|
|
22
23
|
import { snapshotPage } from "../pages/version";
|
|
23
24
|
import { resolveTemplate } from "../prompts/resolver";
|
|
24
25
|
// Side-effect import: registers `task.page.feedback`.
|
|
@@ -227,7 +228,7 @@ const updatePageRoute = route({
|
|
|
227
228
|
/**
|
|
228
229
|
* POST /api/pages/:id/feedback — turn the element comments collected by the
|
|
229
230
|
* in-page feedback overlay (`?__swarm-feedback`, see
|
|
230
|
-
* `src/
|
|
231
|
+
* `src/pages/feedback-overlay.ts`) into one task for the lead. In the
|
|
231
232
|
* dashboard the SPA calls it with its bearer for the page it shows. A page
|
|
232
233
|
* opened directly reaches it through the `/@swarm/api/*` page proxy, which
|
|
233
234
|
* pins `X-Page-Id` to the cookie's page, so a page can only file feedback on
|
|
@@ -482,17 +483,6 @@ function withShareUrls<T extends { id: string; slug: string }>(
|
|
|
482
483
|
};
|
|
483
484
|
}
|
|
484
485
|
|
|
485
|
-
/**
|
|
486
|
-
* Compute the page's "edit counter" — `MAX(page_versions.version) + 1`. Means
|
|
487
|
-
* "this is the N-th edit since the page was created". After the first PUT the
|
|
488
|
-
* value is 2 (one snapshot row → version 1 → counter becomes 2). This is the
|
|
489
|
-
* value POST and PUT return as `version` on the wire.
|
|
490
|
-
*/
|
|
491
|
-
async function pageEditCounter(pageId: string): Promise<number> {
|
|
492
|
-
const versions = await getPageVersions(pageId);
|
|
493
|
-
return versions.length > 0 ? versions[0]!.version + 1 : 1;
|
|
494
|
-
}
|
|
495
|
-
|
|
496
486
|
/**
|
|
497
487
|
* Viewer and page-supplied text sits inside a `<page_feedback>` block in the
|
|
498
488
|
* task prompt. Defuse any tag of that name so the text cannot close the block
|
|
@@ -843,7 +833,7 @@ export async function handlePages(
|
|
|
843
833
|
updatePageRoute.respond(res, 200, {
|
|
844
834
|
id: updated.id,
|
|
845
835
|
key: updated.key,
|
|
846
|
-
version: await
|
|
836
|
+
version: await getPageEditCounter(updated.id),
|
|
847
837
|
});
|
|
848
838
|
return true;
|
|
849
839
|
}
|
package/src/http/poll.ts
CHANGED
|
@@ -19,7 +19,6 @@ import {
|
|
|
19
19
|
getTaskAttachments,
|
|
20
20
|
getTaskById,
|
|
21
21
|
getUnassignedTaskIdsForAgent,
|
|
22
|
-
getUserById,
|
|
23
22
|
hasCapacity,
|
|
24
23
|
isExtensionAgent,
|
|
25
24
|
recordBudgetRefusalNotification,
|
|
@@ -27,7 +26,6 @@ import {
|
|
|
27
26
|
updateAgentStatusFromCapacity,
|
|
28
27
|
upsertChannelActivityCursor,
|
|
29
28
|
} from "../be/db";
|
|
30
|
-
import { renderIdentity, resolveIdentity } from "../be/identity";
|
|
31
29
|
import {
|
|
32
30
|
parseModelTierOverridesHeader,
|
|
33
31
|
recordClaimModelResolution,
|
|
@@ -37,19 +35,16 @@ import { poolTaskRunsOnHarness } from "../be/model-validation";
|
|
|
37
35
|
import { getRuntimeInstanceById, touchRuntimeInstance } from "../be/multi-runtime";
|
|
38
36
|
import { hasCapability } from "../server";
|
|
39
37
|
import { fetchChannelActivity } from "../slack/channel-activity";
|
|
38
|
+
import { type TaskModelPinFields, taskModelPin } from "../tasks/model-pin";
|
|
40
39
|
import { emitTaskTelemetry, resolveTriggerSurface } from "../telemetry-trigger";
|
|
41
|
-
import {
|
|
42
|
-
AgentTaskSchema,
|
|
43
|
-
BudgetRefusedTriggerSchema,
|
|
44
|
-
TaskAttachmentSchema,
|
|
45
|
-
UserCommsPrefsSchema,
|
|
46
|
-
UserSchema,
|
|
47
|
-
} from "../types";
|
|
40
|
+
import { AgentTaskSchema, BudgetRefusedTriggerSchema, TaskAttachmentSchema } from "../types";
|
|
48
41
|
import { ensure } from "../utils/business-use";
|
|
49
42
|
import { isLeadChannelMonitoringEnabled } from "../utils/lead-monitor-channels";
|
|
50
43
|
import { isMultiRuntimeEnabled } from "../utils/multi-runtime";
|
|
51
|
-
import {
|
|
44
|
+
import { getRequestAuth, type HttpRequestAuth } from "../utils/request-auth-context";
|
|
52
45
|
import { isRuntimePoolsEnabled } from "../utils/runtime-pools-flag";
|
|
46
|
+
import { readsRepoSecrets } from "./config";
|
|
47
|
+
import { buildTaskRequestedBy, TaskRequestedBySchema } from "./requested-by";
|
|
53
48
|
import { route, runtimeInstanceHeader } from "./route-def";
|
|
54
49
|
import { jsonError } from "./utils";
|
|
55
50
|
|
|
@@ -100,27 +95,11 @@ const PollTriggerAttachmentSchema = TaskAttachmentSchema.pick({
|
|
|
100
95
|
sizeBytes: true,
|
|
101
96
|
});
|
|
102
97
|
|
|
103
|
-
// Requester projection sent on `task_assigned` triggers — either the resolved
|
|
104
|
-
// user's identity fields, or (when no `requestedByUserId` is recorded) just a
|
|
105
|
-
// rendered `name` for the UNKNOWN-identity sentinel.
|
|
106
|
-
const PollRequestedBySchema = UserSchema.pick({
|
|
107
|
-
id: true,
|
|
108
|
-
name: true,
|
|
109
|
-
email: true,
|
|
110
|
-
role: true,
|
|
111
|
-
notes: true,
|
|
112
|
-
}).extend({
|
|
113
|
-
// Absent for the UNKNOWN-identity sentinel (Slack-only requester, no users row).
|
|
114
|
-
id: z.string().optional(),
|
|
115
|
-
// Structured communication preferences from `users.metadata.comms`.
|
|
116
|
-
comms: UserCommsPrefsSchema.optional(),
|
|
117
|
-
});
|
|
118
|
-
|
|
119
98
|
const PollTaskOfferedTriggerSchema = z.object({
|
|
120
99
|
type: z.literal("task_offered"),
|
|
121
100
|
taskId: z.string(),
|
|
122
101
|
task: AgentTaskSchema,
|
|
123
|
-
requestedBy:
|
|
102
|
+
requestedBy: TaskRequestedBySchema.optional(),
|
|
124
103
|
});
|
|
125
104
|
|
|
126
105
|
const PollTaskAssignedTriggerSchema = z.object({
|
|
@@ -132,7 +111,7 @@ const PollTaskAssignedTriggerSchema = z.object({
|
|
|
132
111
|
task: AgentTaskSchema.extend({
|
|
133
112
|
attachments: z.array(PollTriggerAttachmentSchema),
|
|
134
113
|
}),
|
|
135
|
-
requestedBy:
|
|
114
|
+
requestedBy: TaskRequestedBySchema.optional(),
|
|
136
115
|
});
|
|
137
116
|
|
|
138
117
|
// `budget_refused` reuses the canonical `BudgetRefusedTriggerSchema` entity
|
|
@@ -204,38 +183,6 @@ const pollTriggers = route({
|
|
|
204
183
|
const CHANNEL_ACTIVITY_INTERVAL_MS = 60_000; // Check at most once per 60s
|
|
205
184
|
let lastChannelActivityCheckAt = 0;
|
|
206
185
|
|
|
207
|
-
function getRequesterNotes(notes: string | undefined): string | undefined {
|
|
208
|
-
return typeof notes === "string" && notes.trim().length > 0 ? notes : undefined;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
/**
|
|
212
|
-
* Resolve the requester projection for a task trigger. If the task carries a
|
|
213
|
-
* machine-recorded external id (today: the Slack user field) but no
|
|
214
|
-
* requestedByUserId, render the explicit UNKNOWN sentinel instead of silently
|
|
215
|
-
* omitting the section: never a substituted human (Rule 33 /
|
|
216
|
-
* provenance-or-silence).
|
|
217
|
-
*/
|
|
218
|
-
async function buildTriggerRequestedBy(task: {
|
|
219
|
-
requestedByUserId?: string | null;
|
|
220
|
-
slackUserId?: string | null;
|
|
221
|
-
}): Promise<z.infer<typeof PollRequestedBySchema> | undefined> {
|
|
222
|
-
const user = task.requestedByUserId ? await getUserById(task.requestedByUserId) : undefined;
|
|
223
|
-
if (user) {
|
|
224
|
-
return {
|
|
225
|
-
id: user.id,
|
|
226
|
-
name: user.name,
|
|
227
|
-
email: user.email,
|
|
228
|
-
role: user.role,
|
|
229
|
-
notes: getRequesterNotes(user.notes),
|
|
230
|
-
comms: getUserCommsPrefs(user),
|
|
231
|
-
};
|
|
232
|
-
}
|
|
233
|
-
if (task.slackUserId) {
|
|
234
|
-
return { name: renderIdentity(await resolveIdentity("slack", task.slackUserId)) };
|
|
235
|
-
}
|
|
236
|
-
return undefined;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
186
|
/**
|
|
240
187
|
* Slim attachment projection for the `task_assigned` poll trigger — just
|
|
241
188
|
* enough (id, name, mimeType, sizeBytes) for the worker to build a one-shot
|
|
@@ -256,16 +203,40 @@ async function attachmentsForTrigger(
|
|
|
256
203
|
/**
|
|
257
204
|
* Claim-time model resolution (runbooks/model-tiers.md). Never blocks a claim:
|
|
258
205
|
* a resolution failure leaves the worker on its own local resolution.
|
|
206
|
+
*
|
|
207
|
+
* The trigger's `model` is the task's own pin, not the column: a re-claimed
|
|
208
|
+
* row carries the model its previous session reported, and the worker treats
|
|
209
|
+
* `task.model` as a pin that beats the agent's current MODEL_OVERRIDE. The
|
|
210
|
+
* previous claim record is overwritten for the same reason.
|
|
259
211
|
*/
|
|
260
212
|
async function claimModelFields(
|
|
261
|
-
task:
|
|
262
|
-
|
|
263
|
-
|
|
213
|
+
task: TaskModelPinFields & {
|
|
214
|
+
id: string;
|
|
215
|
+
profile?: string | null;
|
|
216
|
+
effort?: string | null;
|
|
217
|
+
vcsRepo?: string | null;
|
|
218
|
+
},
|
|
219
|
+
agent: Parameters<typeof recordClaimModelResolution>[1] & { isLead?: boolean },
|
|
220
|
+
auth: HttpRequestAuth | null,
|
|
221
|
+
): Promise<Awaited<ReturnType<typeof recordClaimModelResolution>> & { model: string | undefined }> {
|
|
222
|
+
const pin = taskModelPin(task);
|
|
223
|
+
const cleared = {
|
|
224
|
+
model: pin,
|
|
225
|
+
resolvedModel: undefined,
|
|
226
|
+
modelSource: undefined,
|
|
227
|
+
modelAlias: undefined,
|
|
228
|
+
resolvedProfile: undefined,
|
|
229
|
+
};
|
|
264
230
|
try {
|
|
265
|
-
return
|
|
231
|
+
return {
|
|
232
|
+
...cleared,
|
|
233
|
+
...(await recordClaimModelResolution({ ...task, model: pin }, agent, {
|
|
234
|
+
includeRepoSecrets: readsRepoSecrets(auth, agent),
|
|
235
|
+
})),
|
|
236
|
+
};
|
|
266
237
|
} catch (error) {
|
|
267
238
|
console.warn(`[/api/poll] model resolution failed for task ${task.id}:`, error);
|
|
268
|
-
return
|
|
239
|
+
return cleared;
|
|
269
240
|
}
|
|
270
241
|
}
|
|
271
242
|
|
|
@@ -422,7 +393,7 @@ export async function handlePoll(
|
|
|
422
393
|
const claimedTask = await claimOfferedTask(firstOfferedTask.id, myAgentId);
|
|
423
394
|
if (claimedTask) {
|
|
424
395
|
await recordRuntimeTask(claimedTask.id);
|
|
425
|
-
const offeredRequestedBy = await
|
|
396
|
+
const offeredRequestedBy = await buildTaskRequestedBy(claimedTask);
|
|
426
397
|
return {
|
|
427
398
|
trigger: {
|
|
428
399
|
type: "task_offered",
|
|
@@ -519,8 +490,8 @@ export async function handlePoll(
|
|
|
519
490
|
});
|
|
520
491
|
|
|
521
492
|
// Resolve requesting user if available (UNKNOWN sentinel handling
|
|
522
|
-
// lives in
|
|
523
|
-
const assignedRequestedBy = await
|
|
493
|
+
// lives in buildTaskRequestedBy).
|
|
494
|
+
const assignedRequestedBy = await buildTaskRequestedBy(pendingTask);
|
|
524
495
|
// The surface that started the whole chain, so the worker can tag
|
|
525
496
|
// its session events with it (workers cannot read the task tree).
|
|
526
497
|
const assignedTriggerSurface = await resolveTriggerSurface(
|
|
@@ -535,7 +506,7 @@ export async function handlePoll(
|
|
|
535
506
|
triggerSurface: assignedTriggerSurface,
|
|
536
507
|
task: {
|
|
537
508
|
...pendingTask,
|
|
538
|
-
...(await claimModelFields(pendingTask, agent)),
|
|
509
|
+
...(await claimModelFields(pendingTask, agent, getRequestAuth(req))),
|
|
539
510
|
status: "in_progress",
|
|
540
511
|
attachments: await attachmentsForTrigger(pendingTask.id),
|
|
541
512
|
},
|
|
@@ -667,7 +638,7 @@ export async function handlePoll(
|
|
|
667
638
|
agentId: myAgentId,
|
|
668
639
|
});
|
|
669
640
|
});
|
|
670
|
-
const claimedRequestedBy = await
|
|
641
|
+
const claimedRequestedBy = await buildTaskRequestedBy(claimed);
|
|
671
642
|
const claimedTriggerSurface = await resolveTriggerSurface(
|
|
672
643
|
claimed.id,
|
|
673
644
|
claimed.source,
|
|
@@ -679,7 +650,7 @@ export async function handlePoll(
|
|
|
679
650
|
triggerSurface: claimedTriggerSurface,
|
|
680
651
|
task: {
|
|
681
652
|
...claimed,
|
|
682
|
-
...(await claimModelFields(claimed, agent)),
|
|
653
|
+
...(await claimModelFields(claimed, agent, getRequestAuth(req))),
|
|
683
654
|
attachments: await attachmentsForTrigger(claimed.id),
|
|
684
655
|
},
|
|
685
656
|
...(claimedRequestedBy && { requestedBy: claimedRequestedBy }),
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { getUserById } from "../be/db";
|
|
3
|
+
import { renderIdentity, resolveIdentity } from "../be/identity";
|
|
4
|
+
import { UserCommsPrefsSchema, UserSchema } from "../types";
|
|
5
|
+
import { getUserCommsPrefs } from "../utils/requester-comms";
|
|
6
|
+
|
|
7
|
+
// Requester projection sent with a task to the worker that runs it (poll
|
|
8
|
+
// triggers, paused-task resume) — either the resolved user's identity fields,
|
|
9
|
+
// or (when no `requestedByUserId` is recorded) just a rendered `name` for the
|
|
10
|
+
// UNKNOWN-identity sentinel.
|
|
11
|
+
export const TaskRequestedBySchema = UserSchema.pick({
|
|
12
|
+
id: true,
|
|
13
|
+
name: true,
|
|
14
|
+
email: true,
|
|
15
|
+
role: true,
|
|
16
|
+
notes: true,
|
|
17
|
+
}).extend({
|
|
18
|
+
// Absent for the UNKNOWN-identity sentinel (Slack-only requester, no users row).
|
|
19
|
+
id: z.string().optional(),
|
|
20
|
+
// Structured communication preferences from `users.metadata.comms`.
|
|
21
|
+
comms: UserCommsPrefsSchema.optional(),
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
function getRequesterNotes(notes: string | undefined): string | undefined {
|
|
25
|
+
return typeof notes === "string" && notes.trim().length > 0 ? notes : undefined;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Resolve the requester projection for a task. If the task carries a
|
|
30
|
+
* machine-recorded external id (today: the Slack user field) but no
|
|
31
|
+
* requestedByUserId, render the explicit UNKNOWN sentinel instead of silently
|
|
32
|
+
* omitting the section: never a substituted human (Rule 33 /
|
|
33
|
+
* provenance-or-silence).
|
|
34
|
+
*/
|
|
35
|
+
export async function buildTaskRequestedBy(task: {
|
|
36
|
+
requestedByUserId?: string | null;
|
|
37
|
+
slackUserId?: string | null;
|
|
38
|
+
}): Promise<z.infer<typeof TaskRequestedBySchema> | undefined> {
|
|
39
|
+
const user = task.requestedByUserId ? await getUserById(task.requestedByUserId) : undefined;
|
|
40
|
+
if (user) {
|
|
41
|
+
return {
|
|
42
|
+
id: user.id,
|
|
43
|
+
name: user.name,
|
|
44
|
+
email: user.email,
|
|
45
|
+
role: user.role,
|
|
46
|
+
notes: getRequesterNotes(user.notes),
|
|
47
|
+
comms: getUserCommsPrefs(user),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
if (task.slackUserId) {
|
|
51
|
+
return { name: renderIdentity(await resolveIdentity("slack", task.slackUserId)) };
|
|
52
|
+
}
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
package/src/http/script-runs.ts
CHANGED
|
@@ -611,7 +611,7 @@ export async function handleScriptRuns(
|
|
|
611
611
|
if (!parsed) return true;
|
|
612
612
|
const result = await executeRawLlm(parsed.body);
|
|
613
613
|
if (result.status === "failed") {
|
|
614
|
-
|
|
614
|
+
jsonError(res, result.error, 500);
|
|
615
615
|
return true;
|
|
616
616
|
}
|
|
617
617
|
rawLlmRoute.respond(res, 200, result.output);
|