@desplega.ai/agent-swarm 1.164.1 → 1.166.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-yzfxn8vj.js +23 -0
- package/dist/{actions-hp2h32dx.js → actions-qz3m692w.js} +24 -28
- package/dist/{amp-adapter-zfd7djyx.js → amp-adapter-j1xbmsqz.js} +7 -7
- package/dist/{app-w2d26h4f.js → app-43vrdmv8.js} +9 -10
- package/dist/{assistant-45pc4en7.js → assistant-ertffmka.js} +25 -29
- package/dist/{boot-reembed-k1q2s4fc.js → boot-reembed-998jtpdg.js} +12 -15
- package/dist/{boot-reembed-6rhcpsjf.js → boot-reembed-s7k7c6v2.js} +13 -15
- package/dist/boot-scrub-sweep-nepsk4t1.js +211 -0
- package/dist/{boot-seeds-ex93edx7.js → boot-seeds-vvzs1zhk.js} +12 -12
- package/dist/{claude-adapter-hcxd0szm.js → claude-adapter-2zvmekrp.js} +15 -17
- package/dist/{claude-managed-adapter-fz7xps2r.js → claude-managed-adapter-s49wbk8t.js} +5 -5
- package/dist/{claude-managed-setup-xx6f15h9.js → claude-managed-setup-cvhqq8qn.js} +4 -4
- package/dist/{claude-sdk-session-6nb9raem.js → claude-sdk-session-kkbxtw5q.js} +2624 -2606
- package/dist/{cli-etbrt8x1.js → cli-0nhdwxd5.js} +4 -4
- package/dist/{cli-jeefgppq.js → cli-0qjcsxgz.js} +7 -7
- package/dist/{cli-6behs2bt.js → cli-32bca7nr.js} +1 -1
- package/dist/{cli-gt8v96qx.js → cli-3ddybbbx.js} +4 -4
- package/dist/{acp-adapter-rxvek4ym.js → cli-3qn4r334.js} +317 -35
- package/dist/{cli-1d3w4xm3.js → cli-4r1sjfc4.js} +14 -6
- package/dist/{cli-691f2rxm.js → cli-66zh26sp.js} +68 -16
- package/dist/{cli-et8wdyqh.js → cli-68r1j4sg.js} +9 -9
- package/dist/{cli-xb7kyanp.js → cli-8kgvmkak.js} +3 -3
- package/dist/{cli-fdpkw8vk.js → cli-8t229cmr.js} +8 -8
- package/dist/{cli-gc54qc0p.js → cli-aqcey741.js} +1 -1
- package/dist/{cli-4q4cmr4t.js → cli-cpfmqz3n.js} +8 -8
- package/dist/{cli-a9bw0xxt.js → cli-df56tqdw.js} +997 -408
- package/dist/{cli-n7h1kqpf.js → cli-dmtrxqhg.js} +6 -1
- package/dist/{cli-xfb7333f.js → cli-dw2q9f7g.js} +5 -5
- package/dist/{cli-4drstbb9.js → cli-ecknp7p1.js} +81 -23
- package/dist/{cli-v0pvvhfq.js → cli-efnd2kce.js} +36 -1
- package/dist/{cli-1wetf34q.js → cli-f2n17zhp.js} +1 -1
- package/dist/{cli-2c8eky5y.js → cli-feh3dkah.js} +1 -1
- package/dist/cli-fn697bnt.js +126 -0
- package/dist/{cli-v5pr2fgt.js → cli-gt8azyet.js} +3 -3
- package/dist/{cli-dbszf3rw.js → cli-h3x613ze.js} +4 -4
- package/dist/{cli-nwhb8y2e.js → cli-hf5s9pgf.js} +1 -1
- package/dist/cli-j2jbr8af.js +516 -0
- package/dist/{cli-agjzafns.js → cli-jhm6hzjn.js} +593 -10
- package/dist/{cli-j0tmc908.js → cli-jvj6a0cv.js} +484 -477
- package/dist/{cli-xxz89c2n.js → cli-mb75wpxf.js} +1 -1
- package/dist/{cli-07s39g1n.js → cli-mv2520nm.js} +1 -1
- package/dist/{cli-v90vfwyb.js → cli-mwegs4af.js} +5 -5
- package/dist/{cli-bz1gbac9.js → cli-p4ztb80h.js} +2 -2
- package/dist/{cli-1wd1n2ya.js → cli-rfphkzh5.js} +1 -1
- package/dist/{cli-976r1zqx.js → cli-rggdq8wq.js} +5 -5
- package/dist/{cli-3ajw5v5v.js → cli-rsy933yz.js} +6 -6
- package/dist/{cli-yj790e19.js → cli-tadfgd0t.js} +8 -1
- package/dist/{cli-cn53e7vc.js → cli-ttbsse3f.js} +6 -1
- package/dist/{cli-dyqsrqcf.js → cli-v2a8x45d.js} +4 -4
- package/dist/{cli-wbvnn6tk.js → cli-v6vv7dy8.js} +91 -9
- package/dist/{cli-z2ap2c0c.js → cli-vzc73wet.js} +90 -55
- package/dist/cli-wt7ccy4r.js +2654 -0
- package/dist/{cli-4cn19jvn.js → cli-x7kpr72a.js} +3 -3
- package/dist/{cli-003785v9.js → cli-xvc3q9br.js} +1 -1
- package/dist/{cli-v0e7jpr5.js → cli-xvdgasgy.js} +13 -4
- package/dist/{cli-asqeg4ve.js → cli-y59h2pk9.js} +2 -2
- package/dist/{cli-0wn3ypqv.js → cli-yshaazs1.js} +14121 -3478
- package/dist/{cli-wg8yjqtw.js → cli-ysnyhws9.js} +16 -3
- package/dist/cli-zp67qg5x.js +92 -0
- package/dist/{cli-g3qgrzck.js → cli-zsh093hk.js} +71 -3
- package/dist/{cli-ma3zvh39.js → cli-zy7vwsne.js} +6066 -5829
- package/dist/cli.js +79 -26
- package/dist/{codex-adapter-pcxdn10v.js → codex-adapter-dw1wxqt8.js} +10 -10
- package/dist/{codex-hook-b1fb7c2k.js → codex-hook-d2rkvf39.js} +1 -1
- package/dist/{codex-login-059nm145.js → codex-login-terbdsqq.js} +2 -2
- package/dist/{codex-session-runner-xzwwddp8.js → codex-session-runner-3cgb8y1g.js} +10 -10
- package/dist/{commands-98at8mkn.js → commands-mhxam6z2.js} +9 -9
- package/dist/{cursor-adapter-dkg6qnkp.js → cursor-adapter-2qda3st7.js} +6 -6
- package/dist/{db-01g8wtvw.js → db-sjks418k.js} +11 -9
- package/dist/{devin-adapter-qmttt2fz.js → devin-adapter-bffjj5n4.js} +2 -2
- package/dist/{dispatcher-nn2k1wxd.js → dispatcher-1es759rn.js} +22 -26
- package/dist/{dsh-adapter-w4d4vwdq.js → dsh-adapter-bek4cyw9.js} +6 -6
- package/dist/{e2b-9e4g2hvt.js → e2b-nd50eccg.js} +1 -1
- package/dist/{event-bus-z7v2t26e.js → event-bus-vnfthafg.js} +2 -2
- package/dist/grok-adapter-ar9cw9jg.js +29 -0
- package/dist/{handlers-b0kqsbg8.js → handlers-1gkbhktr.js} +25 -29
- package/dist/{hook-sap7v1aj.js → hook-ds2zxq9v.js} +16 -18
- package/dist/{hook-pz50gqep.js → hook-vxa3tcsy.js} +10 -12
- package/dist/{http-ncr8rmd0.js → http-3h21z9md.js} +679 -227
- package/dist/{index-c4aqcyz1.js → index-9b65j1m5.js} +11 -11
- package/dist/{index-r46s01nq.js → index-bn6cy8ps.js} +22 -26
- package/dist/{index-38kqdt3e.js → index-dkfxpkmm.js} +23 -27
- package/dist/{index-630kj5vd.js → index-f8xnqrye.js} +55 -43
- package/dist/{index-fdk1t4d6.js → index-n0rxa4ca.js} +11962 -9900
- package/dist/{index-9nrj7797.js → index-n99fycq7.js} +22 -26
- package/dist/{keepalive-31613r2h.js → keepalive-3xspa9jw.js} +14 -11
- package/dist/{lead-mx59tr5s.js → lead-6qphc124.js} +43 -43
- package/dist/{maintenance-55ff39ca.js → maintenance-0teem1bb.js} +10 -12
- package/dist/{node-f0ykebbv.js → node-9sg7ajby.js} +25 -7
- package/dist/{oauth-refresh-sweep-anma954w.js → oauth-refresh-sweep-sh9q25xp.js} +13 -10
- package/dist/{onboard-b2fy8ftb.js → onboard-54qmnqxe.js} +4 -3
- package/dist/{opencode-adapter-p1s92tmy.js → opencode-adapter-wjfa2wvd.js} +6 -6
- package/dist/{otel-impl-9brvt7xv.js → otel-impl-ycqzp2a1.js} +2 -2
- package/dist/{pi-mono-adapter-kz4rrwvd.js → pi-mono-adapter-e77e9zst.js} +6 -6
- package/dist/{pricing-refresh-7w8mbmva.js → pricing-refresh-atnkngqs.js} +10 -10
- package/dist/{rbac-roles-ejp7qwvb.js → rbac-roles-9pjphh9q.js} +10 -10
- package/dist/{rbac-roles-nhk4r7bc.js → rbac-roles-j78wdksm.js} +10 -10
- package/dist/{render-v2-2gzt8skq.js → render-v2-pxjs49fx.js} +9 -10
- package/dist/{setup-5et8t412.js → setup-ymej0xtx.js} +2 -2
- package/dist/{work-objects-tb0er7qa.js → work-objects-3nh4ymzb.js} +26 -30
- package/dist/{worker-62cv075d.js → worker-qa9fkz5s.js} +43 -43
- package/dist/{x-vbxb31jr.js → x-ma4gnfgy.js} +2 -2
- package/openapi.json +457 -31
- package/package.json +14 -6
- package/plugin/commands/create-pr.md +7 -1
- package/plugin/pi-skills/create-pr/SKILL.md +7 -1
- package/src/agentmail/app.ts +5 -4
- package/src/be/automation-preflight.ts +11 -9
- package/src/be/boot-scrub-sweep.ts +302 -0
- package/src/be/connector-codes.ts +81 -0
- package/src/be/crypto/key-bootstrap.ts +1 -1
- package/src/be/db/approvals.ts +15 -1
- package/src/be/db/codex-oauth-device-flows.ts +40 -0
- package/src/be/db/config-secrets.ts +31 -0
- package/src/be/db/runtime.ts +4 -0
- package/src/be/db/script-runs.ts +15 -0
- package/src/be/db/tasks/write.ts +1 -1
- package/src/be/db-queries/mcp-oauth.ts +5 -0
- package/src/be/db-queries/oauth.ts +77 -0
- package/src/be/db.ts +162 -59
- package/src/be/events.ts +17 -1
- package/src/be/memory/index-content.ts +2 -2
- package/src/be/memory/providers/sqlite-store.ts +63 -11
- package/src/be/memory/types.ts +14 -0
- package/src/be/migrations/197_connector_codes.sql +23 -0
- package/src/be/migrations/198_permission_audit_guest.sql +33 -0
- package/src/be/migrations/200_backfill_auto_memory_expiry.sql +20 -0
- package/src/be/migrations/201_workflow_replay_columns.sql +10 -0
- package/src/be/migrations/202_codex_oauth_device_flows.sql +24 -0
- package/src/be/model-tier-resolution.ts +2 -1
- package/src/be/model-validation.ts +1 -1
- package/src/be/pricing-normalize.ts +3 -0
- package/src/be/rbac-audit.ts +2 -1
- package/src/be/scripts/db.ts +28 -4
- package/src/be/scripts/source-secrets.ts +37 -0
- package/src/be/scripts/typecheck.ts +23 -2
- package/src/be/scrub-json.ts +32 -0
- package/src/be/sealed-json.ts +59 -0
- package/src/be/secret-registry.ts +131 -0
- package/src/be/seed-pricing.ts +14 -0
- package/src/be/users.ts +7 -1
- package/src/be/workflow-replay.ts +77 -0
- package/src/cli.tsx +7 -0
- package/src/commands/provider-credentials.ts +46 -8
- package/src/commands/runner.ts +2 -10
- package/src/extensions/contract-types.generated.ts +1 -1
- package/src/github/app.ts +4 -4
- package/src/heartbeat/heartbeat.ts +20 -8
- package/src/hooks/pr-body-guard.ts +1 -1
- package/src/http/agents.ts +1 -0
- package/src/http/approval-requests.ts +1 -0
- package/src/http/apps.ts +3 -0
- package/src/http/auth.ts +4 -1
- package/src/http/codex-oauth-device.ts +8 -10
- package/src/http/codex-oauth-keep-warm.ts +2 -2
- package/src/http/config-values.ts +59 -0
- package/src/http/config.ts +5 -0
- package/src/http/db-query-bounded.ts +3 -0
- package/src/http/db-query-shared.ts +107 -15
- package/src/http/db-query.ts +10 -2
- package/src/http/extensions.ts +1 -0
- package/src/http/index.ts +23 -6
- package/src/http/integrations.ts +1 -38
- package/src/http/ip-rate-limit.ts +82 -0
- package/src/http/kv.ts +38 -4
- package/src/http/mcp-servers.ts +1 -0
- package/src/http/memory.ts +9 -0
- package/src/http/models-catalog.ts +2 -0
- package/src/http/operator-actor.ts +4 -0
- package/src/http/page-proxy.ts +22 -0
- package/src/http/pages.ts +4 -2
- package/src/http/realtime.ts +1 -1
- package/src/http/request-principal.ts +14 -1
- package/src/http/script-connections.ts +8 -0
- package/src/http/script-runs.ts +12 -4
- package/src/http/scripts.ts +15 -0
- package/src/http/session-cost-recompute.ts +3 -2
- package/src/http/tasks.ts +9 -0
- package/src/http/users.ts +226 -0
- package/src/http/workflows.ts +3 -2
- package/src/http.ts +1 -0
- package/src/integrations/kapso/client.ts +3 -1
- package/src/jira/app.ts +2 -2
- package/src/kv-view.ts +324 -0
- package/src/linear/app.ts +2 -2
- package/src/oauth/mcp-wrapper.ts +1 -1
- package/src/prompts/memories.ts +42 -1
- package/src/providers/acp-adapter.ts +85 -24
- package/src/providers/acp-target-catalog.ts +54 -1
- package/src/providers/acp-targets.ts +358 -4
- package/src/providers/amp-adapter.ts +1 -1
- package/src/providers/cursor-adapter.ts +1 -1
- package/src/providers/grok-adapter.ts +186 -0
- package/src/providers/index.ts +5 -1
- package/src/providers/pricing-sources.md +6 -0
- package/src/providers/reasoning-effort.ts +17 -1
- package/src/providers/types.ts +2 -1
- package/src/rbac/can.ts +4 -3
- package/src/rbac/legacy-policy.ts +3 -3
- package/src/rbac/types.ts +6 -0
- package/src/script-workflows/supervisor.ts +11 -2
- package/src/scripts-runtime/eval-harness.ts +2 -12
- package/src/scripts-runtime/executors/quickjs-bundle.ts +152 -0
- package/src/scripts-runtime/executors/quickjs-prelude.ts +285 -0
- package/src/scripts-runtime/executors/quickjs-runner.ts +580 -0
- package/src/scripts-runtime/executors/quickjs-worker.ts +81 -0
- package/src/scripts-runtime/executors/quickjs.ts +196 -0
- package/src/scripts-runtime/executors/registry.ts +2 -0
- package/src/scripts-runtime/executors/sourcemap.ts +79 -0
- package/src/scripts-runtime/script-error-hints.ts +14 -0
- package/src/scripts-runtime/swarm-sdk.ts +9 -2
- package/src/scripts-runtime/types/stdlib.d.ts +27 -8
- package/src/scripts-runtime/types/swarm-extension.d.ts +3 -0
- package/src/scripts-runtime/types/swarm-sdk.d.ts +27 -8
- package/src/server-user.ts +2 -20
- package/src/server.ts +3 -1
- package/src/slack/app.ts +4 -0
- package/src/slack/config.ts +3 -1
- package/src/slack/egress-scrub.ts +87 -0
- package/src/tests/acp-adapter.test.ts +191 -2
- package/src/tests/boot-scrub-sweep.test.ts +390 -0
- package/src/tests/budget-user-scope.test.ts +7 -21
- package/src/tests/check-pr-body.test.ts +149 -6
- package/src/tests/check-text-columns.test.ts +145 -0
- package/src/tests/claude-adapter-binary.test.ts +1 -1
- package/src/tests/claude-sdk-process.fixture.ts +13 -2
- package/src/tests/codex-oauth-device.test.ts +23 -37
- package/src/tests/codex-oauth-keep-warm.test.ts +29 -0
- package/src/tests/console-scrub.test.ts +141 -0
- package/src/tests/credential-check.test.ts +90 -0
- package/src/tests/db-query-denylist.test.ts +202 -0
- package/src/tests/db-task-read-contract.test.ts +1 -0
- package/src/tests/db-task-write-contract.test.ts +1 -0
- package/src/tests/docs-index-footprint.test.js +3 -3
- package/src/tests/entrypoint-acp-target-binary.test.ts +44 -1
- package/src/tests/events-scrub.test.ts +88 -0
- package/src/tests/explicit-model-validation.test.ts +81 -0
- package/src/tests/extensions-pre-task.test.ts +33 -1
- package/src/tests/fixtures/grok/acp-updates-recorded.jsonl +53 -0
- package/src/tests/fixtures/grok/prompt-response-meta.json +36 -0
- package/src/tests/fixtures/secret-corpus.ts +320 -0
- package/src/tests/grok-adapter.test.ts +647 -0
- package/src/tests/heartbeat-checklist.test.ts +44 -0
- package/src/tests/integration-disable-flags.test.ts +156 -0
- package/src/tests/kv-http.test.ts +34 -0
- package/src/tests/kv-tool.test.ts +274 -1
- package/src/tests/kv-view.test.ts +147 -0
- package/src/tests/mcp-user-route.test.ts +257 -15
- package/src/tests/memory-injection-outcome.test.ts +143 -0
- package/src/tests/model-tier-resolution.test.ts +76 -3
- package/src/tests/page-guest-session.test.ts +424 -0
- package/src/tests/page-proxy.test.ts +1 -1
- package/src/tests/page-viewer-auth.test.ts +22 -1
- package/src/tests/pr-body-leaks.test.ts +157 -2
- package/src/tests/preload.ts +10 -0
- package/src/tests/provider-registration.test.ts +3 -1
- package/src/tests/rbac-audit.test.ts +32 -0
- package/src/tests/rbac-mcp-admission-e2e.test.ts +4 -62
- package/src/tests/script-connections.test.ts +9 -0
- package/src/tests/script-executor-conformance.test.ts +3 -0
- package/src/tests/script-executor-quickjs.test.ts +442 -0
- package/src/tests/script-executor-registry.test.ts +6 -1
- package/src/tests/script-runs-http.test.ts +126 -1
- package/src/tests/script-runs-scrub.test.ts +275 -0
- package/src/tests/scripts-http.test.ts +82 -1
- package/src/tests/scripts-mcp-e2e.test.ts +42 -6
- package/src/tests/scripts-runtime.test.ts +37 -0
- package/src/tests/scrubbed-text-12e.test.ts +289 -0
- package/src/tests/sealed-key-guard.test.ts +166 -0
- package/src/tests/secret-canary-sweep.test.ts +958 -0
- package/src/tests/secret-registry.test.ts +355 -0
- package/src/tests/secret-rules.test.ts +230 -0
- package/src/tests/secret-scrubber.test.ts +317 -1
- package/src/tests/session-costs-golden.test.ts +17 -0
- package/src/tests/slack-egress-scrub.test.ts +216 -0
- package/src/tests/swarm-tool-result-gate.test.ts +143 -30
- package/src/tests/task-tools-ctx.test.ts +3 -0
- package/src/tests/user-token-routes.test.ts +342 -3
- package/src/tests/workflow-replay.test.ts +518 -0
- package/src/tests/workflow-scrub.test.ts +242 -0
- package/src/tests/workflow-system-one-decision-providers.test.ts +737 -0
- package/src/tools/db-query.ts +1 -1
- package/src/tools/inject-learning.ts +1 -1
- package/src/tools/kv/kv-get.ts +136 -6
- package/src/tools/send-task.ts +61 -17
- package/src/tools/utils.ts +186 -43
- package/src/tools/workflows/get-workflow-run.ts +3 -2
- package/src/types.ts +16 -1
- package/src/utils/console-scrub-install.ts +5 -0
- package/src/utils/console-scrub.ts +57 -0
- package/src/utils/credentials.ts +8 -0
- package/src/utils/page-session.ts +6 -2
- package/src/utils/pr-body-leaks.ts +149 -10
- package/src/utils/request-auth-context.ts +2 -0
- package/src/utils/secret-rules.generated.ts +2055 -0
- package/src/utils/secret-scrubber.ts +662 -36
- package/src/workflows/engine.ts +39 -4
- package/src/workflows/executors/notify.ts +6 -1
- package/src/workflows/executors/system-one-decision.ts +31 -12
- package/src/workflows/executors/system-one-openai-wire.ts +168 -0
- package/src/workflows/executors/system-one-providers.ts +112 -11
- package/src/workflows/executors/system-one-wire.ts +79 -0
- package/src/workflows/recovery.ts +10 -1
- package/src/workflows/resume.ts +62 -3
- package/src/workflows/retry-poller.ts +3 -0
- package/templates/ai-toolbox.manifest.json +2 -2
- package/templates/skills/agent-browser/SKILL.md +5 -5
- package/templates/skills/agent-browser/config.json +1 -1
- package/templates/skills/agent-browser/content.md +4 -4
- package/templates/skills/artifacts/content.md +17 -7
- package/templates/skills/jira-interaction/config.json +1 -1
- package/templates/skills/jira-interaction/content.md +56 -205
- package/templates/skills/linear-interaction/config.json +1 -1
- package/templates/skills/linear-interaction/content.md +31 -50
- package/templates/skills/pages/content.md +8 -4
- package/templates/skills/qa/content.md +2 -2
- package/templates/skills/workflow-iterate/content.md +4 -4
- package/tsconfig.json +3 -3
- package/dist/boot-scrub-logs-t8xg7wd7.js +0 -99
- package/dist/cli-0a7r5331.js +0 -168
- package/dist/cli-93rxw9yd.js +0 -951
- package/dist/cli-cvk6yzw5.js +0 -40
- package/dist/cli-dgnj0zcc.js +0 -40
- package/dist/cli-qt5wzq6v.js +0 -256
- package/dist/cli-v7fqehqd.js +0 -9281
- package/dist/cli-vbr859rz.js +0 -572
- package/src/be/boot-scrub-logs.ts +0 -132
package/src/tools/utils.ts
CHANGED
|
@@ -20,6 +20,7 @@ import type {
|
|
|
20
20
|
import * as z from "zod";
|
|
21
21
|
import { sweepExpiredKvPrefix, upsertKv } from "../be/db";
|
|
22
22
|
import { MCP_OVERFLOW_NAMESPACE, mcpOverflowNamespace } from "../kv-overflow";
|
|
23
|
+
import { joinKvPath, type KvShapeEntry, splitKvPath, summarizeKvShape } from "../kv-view";
|
|
23
24
|
import { withSpan } from "../otel";
|
|
24
25
|
import type { PermissionVerb } from "../rbac/permissions";
|
|
25
26
|
import { SCRIPT_LONG_TIMEOUT_HINT_MS } from "../scripts-runtime/executors/types";
|
|
@@ -138,6 +139,8 @@ export type SwarmToolTruncation = {
|
|
|
138
139
|
originalBytes: number;
|
|
139
140
|
limitBytes: number;
|
|
140
141
|
retrieval: string;
|
|
142
|
+
/** Biggest branches of the stored value; each `path` is a kv-get `path`. */
|
|
143
|
+
shape?: KvShapeEntry[];
|
|
141
144
|
};
|
|
142
145
|
|
|
143
146
|
export type SwarmToolResult<TData extends SwarmToolData = SwarmToolData> = {
|
|
@@ -153,6 +156,12 @@ export type SwarmToolResult<TData extends SwarmToolData = SwarmToolData> = {
|
|
|
153
156
|
nudge?: string;
|
|
154
157
|
/** Ctx-control metadata attached centrally when the full wire result is spilled. */
|
|
155
158
|
truncation?: SwarmToolTruncation;
|
|
159
|
+
/**
|
|
160
|
+
* Set by ctx-control only. Keeps `details` out of structuredContent when
|
|
161
|
+
* that channel already carries `data` verbatim and the duplicate rendering
|
|
162
|
+
* is what pushes it over the size budget. The text channel keeps `details`.
|
|
163
|
+
*/
|
|
164
|
+
omitStructuredDetails?: boolean;
|
|
156
165
|
/**
|
|
157
166
|
* Skip the finalize pipeline's secret scrubbing for this result. ONLY for
|
|
158
167
|
* deliberate credential-reveal branches (oauth-access-token, script-apis
|
|
@@ -186,6 +195,16 @@ const swarmToolTruncationSchema = z.looseObject({
|
|
|
186
195
|
originalBytes: z.number(),
|
|
187
196
|
limitBytes: z.number(),
|
|
188
197
|
retrieval: z.string(),
|
|
198
|
+
shape: z
|
|
199
|
+
.array(
|
|
200
|
+
z.looseObject({
|
|
201
|
+
path: z.string().optional(),
|
|
202
|
+
type: z.string().optional(),
|
|
203
|
+
bytes: z.number().optional(),
|
|
204
|
+
items: z.number().optional(),
|
|
205
|
+
}),
|
|
206
|
+
)
|
|
207
|
+
.optional(),
|
|
189
208
|
});
|
|
190
209
|
|
|
191
210
|
export const swarmToolEnvelopeShape = {
|
|
@@ -341,15 +360,21 @@ export const NUDGES: Record<string, (result: SwarmToolResult) => string | undefi
|
|
|
341
360
|
const rendered =
|
|
342
361
|
typeof entry.value === "string" ? entry.value : (JSON.stringify(entry.value) ?? "");
|
|
343
362
|
return rendered.length > MCP_RESULT_WIRE_LIMIT_BYTES
|
|
344
|
-
? "Large value — your harness may truncate this result;
|
|
363
|
+
? "Large value — your harness may truncate this result; re-call kv-get with path/offset/limit for a bounded slice, or process it in a script via ctx.swarm.kv_get."
|
|
345
364
|
: undefined;
|
|
346
365
|
},
|
|
347
366
|
};
|
|
348
367
|
|
|
368
|
+
/**
|
|
369
|
+
* Per-channel budget: every harness reads ONE channel (text or structured,
|
|
370
|
+
* runbook §3), so each channel must fit on its own. See `wireChannelBytes`.
|
|
371
|
+
*/
|
|
349
372
|
export const MCP_RESULT_WIRE_LIMIT_BYTES = 10_000;
|
|
350
373
|
export { MCP_OVERFLOW_NAMESPACE, mcpOverflowNamespace };
|
|
351
374
|
export const MCP_OVERFLOW_TTL_MS = 24 * 60 * 60 * 1_000;
|
|
352
|
-
|
|
375
|
+
// Floor for the prose preview while the array path sizes its arrays; the
|
|
376
|
+
// preview then grows into whatever budget the arrays leave.
|
|
377
|
+
const MCP_PROSE_PREVIEW_FLOOR_CHARS = 1_200;
|
|
353
378
|
|
|
354
379
|
type FinalizeContext = {
|
|
355
380
|
toolName: string;
|
|
@@ -391,7 +416,14 @@ function composeWireResult(r: SwarmToolResult): CallToolResult {
|
|
|
391
416
|
// Payload LAST: harnesses truncate oversized text from the tail (or keep
|
|
392
417
|
// head+tail), so message and nudge lead and a cut lands inside the payload
|
|
393
418
|
// rendering — a truncated JSON prefix still shows its first key values.
|
|
394
|
-
|
|
419
|
+
// A spill pointer follows the bounded preview; the text channel renders it
|
|
420
|
+
// once, the structured channel carries only the `truncation` object.
|
|
421
|
+
const text = [
|
|
422
|
+
r.message,
|
|
423
|
+
r.nudge,
|
|
424
|
+
normalizedDetails ?? dataFallback,
|
|
425
|
+
r.truncation ? renderTruncationPointer(r.truncation) : undefined,
|
|
426
|
+
]
|
|
395
427
|
.filter((part): part is string => Boolean(part?.trim()))
|
|
396
428
|
.join("\n\n");
|
|
397
429
|
const structuredContent: Record<string, unknown> = {
|
|
@@ -399,7 +431,7 @@ function composeWireResult(r: SwarmToolResult): CallToolResult {
|
|
|
399
431
|
success: r.ok,
|
|
400
432
|
message: r.message,
|
|
401
433
|
};
|
|
402
|
-
if (normalizedDetails) structuredContent.details = normalizedDetails;
|
|
434
|
+
if (normalizedDetails && !r.omitStructuredDetails) structuredContent.details = normalizedDetails;
|
|
403
435
|
if (r.nudge) structuredContent.nudge = r.nudge;
|
|
404
436
|
if (r.truncation) structuredContent.truncation = r.truncation;
|
|
405
437
|
|
|
@@ -410,20 +442,121 @@ function composeWireResult(r: SwarmToolResult): CallToolResult {
|
|
|
410
442
|
};
|
|
411
443
|
}
|
|
412
444
|
|
|
445
|
+
function renderTruncationPointer(t: SwarmToolTruncation): string {
|
|
446
|
+
const shape = t.shape?.length ? `\nShape: ${renderShape(t.shape)}` : "";
|
|
447
|
+
return `Full value: ${t.fullValueAt} (${t.originalBytes} bytes)${shape}\nRetrieval: ${t.retrieval}`;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* Bytes the model sees from one wire result: the larger of the two channels.
|
|
452
|
+
* pi/opencode/claude-managed read only `content.text`; Codex reads only the
|
|
453
|
+
* JSON-encoded `structuredContent` (runbook §3). Summing both would spill a
|
|
454
|
+
* result at half the payload any single harness actually receives.
|
|
455
|
+
*/
|
|
456
|
+
export function wireChannelBytes(wire: {
|
|
457
|
+
content?: ReadonlyArray<{ type: string; text?: string }>;
|
|
458
|
+
structuredContent?: unknown;
|
|
459
|
+
}): number {
|
|
460
|
+
const text = (wire.content ?? [])
|
|
461
|
+
.map((block) => (block.type === "text" ? (block.text ?? "") : ""))
|
|
462
|
+
.join("");
|
|
463
|
+
return Math.max(
|
|
464
|
+
Buffer.byteLength(text, "utf8"),
|
|
465
|
+
Buffer.byteLength(JSON.stringify(wire.structuredContent ?? {}), "utf8"),
|
|
466
|
+
);
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
/** `wireChannelBytes` of the wire result a SwarmToolResult composes to. */
|
|
470
|
+
export function swarmToolResultBytes(r: SwarmToolResult): number {
|
|
471
|
+
return wireChannelBytes(composeWireResult(r));
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
function fitsWireLimit(r: SwarmToolResult): boolean {
|
|
475
|
+
return swarmToolResultBytes(r) <= MCP_RESULT_WIRE_LIMIT_BYTES;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/** A prefix of `source` that never ends on a lone high surrogate. */
|
|
479
|
+
function safePrefix(source: string, chars: number): string {
|
|
480
|
+
const end = Math.min(chars, source.length);
|
|
481
|
+
const code = source.charCodeAt(end - 1);
|
|
482
|
+
return code >= 0xd800 && code <= 0xdbff ? source.slice(0, end - 1) : source.slice(0, end);
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
function previewWithMarker(source: string, chars: number): string {
|
|
486
|
+
const preview = safePrefix(source, chars);
|
|
487
|
+
const omitted = source.length - preview.length;
|
|
488
|
+
return omitted > 0 ? `${preview}\n… [truncated ${omitted} chars]` : preview;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
/**
|
|
492
|
+
* Largest preview of `source` whose rendered result still fits the per-channel
|
|
493
|
+
* budget (binary search on characters). Falls back to the empty preview when
|
|
494
|
+
* nothing fits, so the pointer still goes out.
|
|
495
|
+
*/
|
|
496
|
+
function fillPreviewToBudget(
|
|
497
|
+
source: string,
|
|
498
|
+
render: (preview: string) => SwarmToolResult,
|
|
499
|
+
): SwarmToolResult {
|
|
500
|
+
let low = 0;
|
|
501
|
+
let high = source.length;
|
|
502
|
+
while (low < high) {
|
|
503
|
+
const mid = Math.ceil((low + high) / 2);
|
|
504
|
+
if (fitsWireLimit(render(previewWithMarker(source, mid)))) low = mid;
|
|
505
|
+
else high = mid - 1;
|
|
506
|
+
}
|
|
507
|
+
return render(previewWithMarker(source, low));
|
|
508
|
+
}
|
|
509
|
+
|
|
413
510
|
function overflowKey(toolName: string, value: string): string {
|
|
414
511
|
const safeToolName = toolName.replace(/[^a-zA-Z0-9._-]/g, "-").slice(0, 80);
|
|
415
512
|
const hash = new Bun.CryptoHasher("sha256").update(value).digest("hex");
|
|
416
513
|
return `v1/${safeToolName}/${hash}`;
|
|
417
514
|
}
|
|
418
515
|
|
|
419
|
-
|
|
516
|
+
/**
|
|
517
|
+
* Point the model at a targeted fetch: the biggest array in the shape, else the
|
|
518
|
+
* biggest object or string, which kv-get returns as a bounded, pageable slice.
|
|
519
|
+
* A shape of only scalar leaves (a wide object) points at their parent object,
|
|
520
|
+
* since kv-get rejects offset on a scalar.
|
|
521
|
+
*/
|
|
522
|
+
function overflowRetrieval(namespace: string, key: string, shape: KvShapeEntry[]): string {
|
|
523
|
+
const target =
|
|
524
|
+
shape.find((entry) => entry.type === "array") ??
|
|
525
|
+
shape.find((entry) => entry.type === "object" || entry.type === "string") ??
|
|
526
|
+
(shape[0] && { path: joinKvPath(splitKvPath(shape[0].path).slice(0, -1)) });
|
|
527
|
+
if (!target) {
|
|
528
|
+
return (
|
|
529
|
+
`kv-get(${JSON.stringify({ namespace, key })}) returns the full value ` +
|
|
530
|
+
`(your harness may truncate it); in a script, ctx.swarm.kv_get keeps it out of your context.`
|
|
531
|
+
);
|
|
532
|
+
}
|
|
420
533
|
return (
|
|
421
|
-
`kv-get(${JSON.stringify({ namespace, key })}) returns
|
|
422
|
-
`(
|
|
423
|
-
`
|
|
534
|
+
`kv-get(${JSON.stringify({ namespace, key, path: target.path, offset: 0 })}) returns a ` +
|
|
535
|
+
`bounded page (nextOffset pages on; any Shape path works; no path = the full value). ` +
|
|
536
|
+
`ctx.swarm.kv_get takes the same args in a script.`
|
|
424
537
|
);
|
|
425
538
|
}
|
|
426
539
|
|
|
540
|
+
function formatBytes(bytes: number): string {
|
|
541
|
+
return bytes >= 1_024 ? `${(bytes / 1_024).toFixed(1)}KB` : `${bytes}B`;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
function renderShape(shape: KvShapeEntry[]): string {
|
|
545
|
+
return shape
|
|
546
|
+
.map((entry) => {
|
|
547
|
+
const count =
|
|
548
|
+
entry.items === undefined
|
|
549
|
+
? ""
|
|
550
|
+
: entry.type === "array"
|
|
551
|
+
? `[${entry.items}]`
|
|
552
|
+
: entry.type === "object"
|
|
553
|
+
? `{${entry.items} keys}`
|
|
554
|
+
: ` ${entry.items} chars`;
|
|
555
|
+
return `${entry.path} ${entry.type}${count} ${formatBytes(entry.bytes)}`;
|
|
556
|
+
})
|
|
557
|
+
.join("; ");
|
|
558
|
+
}
|
|
559
|
+
|
|
427
560
|
function canonicalOverflowPayload(toolName: string, result: SwarmToolResult): string {
|
|
428
561
|
// `allowSecretEgress` deliberately bypasses the normal wire scrubber. Never
|
|
429
562
|
// persist that escape hatch: overflow KV is readable by authenticated agents,
|
|
@@ -549,15 +682,13 @@ function arrayPreservingOverflowResult(
|
|
|
549
682
|
data: SwarmToolData,
|
|
550
683
|
targets: ArrayTarget[],
|
|
551
684
|
truncation: SwarmToolTruncation,
|
|
552
|
-
|
|
685
|
+
detailsPreview: string | undefined,
|
|
553
686
|
): SwarmToolResult {
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
)} chars]\n${pointer}`
|
|
560
|
-
: `JSON payload truncated in place:\n${JSON.stringify(data, null, 2)}\n${pointer}`;
|
|
687
|
+
// The structured channel carries the shortened data verbatim, so `details`
|
|
688
|
+
// (tool prose, or the data rendered for text-only harnesses) stays out of
|
|
689
|
+
// it and each channel spends its own budget on payload.
|
|
690
|
+
const details =
|
|
691
|
+
detailsPreview ?? `JSON payload truncated in place:\n${JSON.stringify(data, null, 2)}`;
|
|
561
692
|
return {
|
|
562
693
|
ok: result.ok,
|
|
563
694
|
message: arrayTruncationMessage(result.message, targets),
|
|
@@ -565,6 +696,7 @@ function arrayPreservingOverflowResult(
|
|
|
565
696
|
data,
|
|
566
697
|
nudge: result.nudge,
|
|
567
698
|
truncation,
|
|
699
|
+
omitStructuredDetails: true,
|
|
568
700
|
};
|
|
569
701
|
}
|
|
570
702
|
|
|
@@ -576,7 +708,6 @@ function arrayPreservingOverflowResult(
|
|
|
576
708
|
function truncateArraysInPlace(
|
|
577
709
|
result: SwarmToolResult,
|
|
578
710
|
truncation: SwarmToolTruncation,
|
|
579
|
-
pointer: string,
|
|
580
711
|
): SwarmToolResult | undefined {
|
|
581
712
|
if (!result.data) return undefined;
|
|
582
713
|
const data = cloneResultData(result.data) as SwarmToolData;
|
|
@@ -590,10 +721,15 @@ function truncateArraysInPlace(
|
|
|
590
721
|
Buffer.byteLength(JSON.stringify(a.originalItems), "utf8"),
|
|
591
722
|
);
|
|
592
723
|
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
724
|
+
// Size the arrays against a fixed prose floor, then let the prose preview
|
|
725
|
+
// grow into whatever budget the arrays leave.
|
|
726
|
+
const prose = result.details?.trim() || undefined;
|
|
727
|
+
const renderWith = (preview: string | undefined) =>
|
|
728
|
+
arrayPreservingOverflowResult(result, data, targets, truncation, preview);
|
|
729
|
+
const render = () =>
|
|
730
|
+
renderWith(prose ? previewWithMarker(prose, MCP_PROSE_PREVIEW_FLOOR_CHARS) : undefined);
|
|
731
|
+
const fits = () => fitsWireLimit(render());
|
|
732
|
+
const finish = () => (prose ? fillPreviewToBudget(prose, renderWith) : render());
|
|
597
733
|
|
|
598
734
|
for (const target of targets) {
|
|
599
735
|
setArrayLength(target, 0);
|
|
@@ -608,10 +744,10 @@ function truncateArraysInPlace(
|
|
|
608
744
|
else high = mid - 1;
|
|
609
745
|
}
|
|
610
746
|
setArrayLength(target, low);
|
|
611
|
-
return
|
|
747
|
+
return finish();
|
|
612
748
|
}
|
|
613
749
|
|
|
614
|
-
if (fits()) return
|
|
750
|
+
if (fits()) return finish();
|
|
615
751
|
|
|
616
752
|
// A large scalar sibling can keep the result oversized even after every
|
|
617
753
|
// array is emptied. Prefer dropping those non-array branches over dropping
|
|
@@ -630,7 +766,7 @@ function truncateArraysInPlace(
|
|
|
630
766
|
}
|
|
631
767
|
setArrayLength(target, low);
|
|
632
768
|
}
|
|
633
|
-
return
|
|
769
|
+
return finish();
|
|
634
770
|
}
|
|
635
771
|
|
|
636
772
|
const ctxControlMiddleware: FinalizeMiddleware = async (result, ctx) => {
|
|
@@ -640,21 +776,30 @@ const ctxControlMiddleware: FinalizeMiddleware = async (result, ctx) => {
|
|
|
640
776
|
if (ctx.callOrigin === "script-sdk" || ctx.callOrigin === "extension") return result;
|
|
641
777
|
if (CTX_CONTROL_EXEMPT_TOOLS.has(ctx.toolName)) return result;
|
|
642
778
|
|
|
643
|
-
const
|
|
644
|
-
const fullWireJson = JSON.stringify(fullWire);
|
|
645
|
-
const fullWireBytes = Buffer.byteLength(fullWireJson, "utf8");
|
|
779
|
+
const fullWireBytes = swarmToolResultBytes(result);
|
|
646
780
|
if (fullWireBytes <= MCP_RESULT_WIRE_LIMIT_BYTES) {
|
|
647
781
|
return result;
|
|
648
782
|
}
|
|
649
783
|
|
|
784
|
+
// Lossless first: tools like script-run put the payload in `data` AND a
|
|
785
|
+
// rendering of it in `details`, so structuredContent carries it twice. That
|
|
786
|
+
// channel stays self-sufficient with `data` alone; drop the duplicate there
|
|
787
|
+
// before spilling anything.
|
|
788
|
+
if (result.details?.trim() && result.data && Object.keys(result.data).length > 0) {
|
|
789
|
+
const deduped: SwarmToolResult = { ...result, omitStructuredDetails: true };
|
|
790
|
+
if (fitsWireLimit(deduped)) return deduped;
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
const storedValue = canonicalOverflowPayload(ctx.toolName, result);
|
|
794
|
+
// Paths are relative to the stored value, so each one is a kv-get `path`.
|
|
795
|
+
const shape = summarizeKvShape(JSON.parse(storedValue));
|
|
650
796
|
let fullValueAt: string;
|
|
651
797
|
let retrieval: string;
|
|
652
798
|
if (ctx.agentId) {
|
|
653
|
-
const storedValue = canonicalOverflowPayload(ctx.toolName, result);
|
|
654
799
|
const key = overflowKey(ctx.toolName, storedValue);
|
|
655
800
|
const namespace = mcpOverflowNamespace(ctx.agentId);
|
|
656
801
|
fullValueAt = `kv://${namespace}/${key}`;
|
|
657
|
-
retrieval = overflowRetrieval(namespace, key);
|
|
802
|
+
retrieval = overflowRetrieval(namespace, key, shape);
|
|
658
803
|
const expiresAt = Date.now() + MCP_OVERFLOW_TTL_MS;
|
|
659
804
|
|
|
660
805
|
// The public KV write surfaces cap values at 2 MiB, while the SQLite TEXT
|
|
@@ -680,28 +825,26 @@ const ctxControlMiddleware: FinalizeMiddleware = async (result, ctx) => {
|
|
|
680
825
|
originalBytes: fullWireBytes,
|
|
681
826
|
limitBytes: MCP_RESULT_WIRE_LIMIT_BYTES,
|
|
682
827
|
retrieval,
|
|
828
|
+
...(shape.length > 0 ? { shape } : {}),
|
|
683
829
|
};
|
|
684
|
-
const
|
|
685
|
-
`Full value: ${fullValueAt}\nRetrieval: ${retrieval}\n` +
|
|
686
|
-
`Truncation: ${JSON.stringify(truncation)}`;
|
|
687
|
-
const arrayPreservingResult = truncateArraysInPlace(result, truncation, pointer);
|
|
830
|
+
const arrayPreservingResult = truncateArraysInPlace(result, truncation);
|
|
688
831
|
if (arrayPreservingResult) return arrayPreservingResult;
|
|
689
832
|
|
|
690
|
-
const
|
|
691
|
-
const details = normalizedDetails
|
|
692
|
-
? `${normalizedDetails.slice(0, MCP_PROSE_PREVIEW_CHARS)}\n… [truncated ${Math.max(
|
|
693
|
-
normalizedDetails.length - MCP_PROSE_PREVIEW_CHARS,
|
|
694
|
-
0,
|
|
695
|
-
)} chars]\n${pointer}`
|
|
696
|
-
: `JSON payload omitted because the composed result exceeded ${MCP_RESULT_WIRE_LIMIT_BYTES} bytes.\n${pointer}`;
|
|
697
|
-
|
|
698
|
-
return {
|
|
833
|
+
const spilled = (details: string): SwarmToolResult => ({
|
|
699
834
|
ok: result.ok,
|
|
700
835
|
message: result.message,
|
|
701
836
|
details,
|
|
702
837
|
nudge: result.nudge,
|
|
703
838
|
truncation,
|
|
704
|
-
};
|
|
839
|
+
});
|
|
840
|
+
const prose = result.details?.trim() || undefined;
|
|
841
|
+
if (prose) return fillPreviewToBudget(prose, spilled);
|
|
842
|
+
// Data without arrays to shorten: show the head of its JSON rendering as
|
|
843
|
+
// labelled prose. Structured `data` stays omitted, since a cut JSON value
|
|
844
|
+
// there would read as a real (wrong) value.
|
|
845
|
+
const json = result.data ? JSON.stringify(result.data, null, 2) : "";
|
|
846
|
+
if (!json) return spilled("Payload omitted.");
|
|
847
|
+
return fillPreviewToBudget(json, (preview) => spilled(`JSON payload head:\n${preview}`));
|
|
705
848
|
};
|
|
706
849
|
|
|
707
850
|
// Ordered and security-sensitive: ctx-control runs only after the result and
|
|
@@ -54,11 +54,12 @@ export const registerGetWorkflowRunTool = (server: McpServer) => {
|
|
|
54
54
|
},
|
|
55
55
|
async ({ id }) => {
|
|
56
56
|
try {
|
|
57
|
-
|
|
57
|
+
// Display view: the scrubbed context and step outputs, never replay state.
|
|
58
|
+
const run = await getWorkflowRun(id, "display");
|
|
58
59
|
if (!run) {
|
|
59
60
|
return toolErr(`Workflow run not found: ${id}`, { data: { steps: [] } });
|
|
60
61
|
}
|
|
61
|
-
const steps = await getWorkflowRunStepsByRunId(id);
|
|
62
|
+
const steps = await getWorkflowRunStepsByRunId(id, "display");
|
|
62
63
|
return toolOk(`Run ${id} status: ${run.status}.`, {
|
|
63
64
|
details: renderSteps(steps),
|
|
64
65
|
data: { run, steps },
|
package/src/types.ts
CHANGED
|
@@ -175,6 +175,15 @@ export const DEFAULT_MODEL_TIER_MAP: Record<ProviderName, Record<ModelTier, stri
|
|
|
175
175
|
smart: "high",
|
|
176
176
|
ultra: "ultra",
|
|
177
177
|
},
|
|
178
|
+
// xAI's text models as `grok models` lists them for an API key, cheapest
|
|
179
|
+
// first: grok-build-0.1 ($1/$2 per 1M), grok-4.3 ($1.25/$2.50), then the
|
|
180
|
+
// grok-4.6/4.7 frontier pair ($2/$6).
|
|
181
|
+
grok: {
|
|
182
|
+
smol: "grok-build-0.1",
|
|
183
|
+
regular: "grok-4.3",
|
|
184
|
+
smart: "grok-4.6",
|
|
185
|
+
ultra: "grok-4.7",
|
|
186
|
+
},
|
|
178
187
|
// ACP has no portable tier-to-model mapping. Operators may set an explicit
|
|
179
188
|
// MODEL_OVERRIDE, which the adapter applies through an advertised `model`
|
|
180
189
|
// config option with a target-specific startup fallback.
|
|
@@ -413,6 +422,7 @@ export const ProviderNameSchema = z.enum([
|
|
|
413
422
|
"dsh",
|
|
414
423
|
"amp",
|
|
415
424
|
"cursor",
|
|
425
|
+
"grok",
|
|
416
426
|
]);
|
|
417
427
|
export type ProviderName = z.infer<typeof ProviderNameSchema>;
|
|
418
428
|
|
|
@@ -510,6 +520,9 @@ export const PROVIDER_STEER_CAPABILITIES: Record<ProviderName, SteerMode[]> = {
|
|
|
510
520
|
// `run.steer()` reaches the in-flight run; a message the SDK reverts to a
|
|
511
521
|
// follow-up, and every queued one, starts the next run on the same agent.
|
|
512
522
|
cursor: ["steer", "queue"],
|
|
523
|
+
// Grok runs on the ACP client: one `session/prompt` turn, interrupted only
|
|
524
|
+
// by `session/cancel`. No Grok surface (ACP or headless) exposes a steer.
|
|
525
|
+
grok: [],
|
|
513
526
|
};
|
|
514
527
|
|
|
515
528
|
export type DevinProviderMeta = {
|
|
@@ -532,6 +545,7 @@ export type ProviderMetaMap = {
|
|
|
532
545
|
dsh: NoProviderMeta;
|
|
533
546
|
amp: NoProviderMeta;
|
|
534
547
|
cursor: NoProviderMeta;
|
|
548
|
+
grok: NoProviderMeta;
|
|
535
549
|
};
|
|
536
550
|
|
|
537
551
|
export const FollowUpConfigSchema = z
|
|
@@ -1307,7 +1321,7 @@ export type AcpSessionConfigOption = z.infer<typeof AcpSessionConfigOptionSchema
|
|
|
1307
1321
|
|
|
1308
1322
|
export const AgentAcpStatusSchema = z
|
|
1309
1323
|
.object({
|
|
1310
|
-
target: z.enum(["opencode", "custom"]),
|
|
1324
|
+
target: z.enum(["opencode", "gemini", "copilot", "custom"]),
|
|
1311
1325
|
configOptions: z.array(AcpSessionConfigOptionSchema),
|
|
1312
1326
|
reportedAt: z.number(),
|
|
1313
1327
|
})
|
|
@@ -3476,6 +3490,7 @@ export const PricingProviderSchema = z.enum([
|
|
|
3476
3490
|
"dsh",
|
|
3477
3491
|
"amp",
|
|
3478
3492
|
"cursor",
|
|
3493
|
+
"grok",
|
|
3479
3494
|
]);
|
|
3480
3495
|
export type PricingProvider = z.infer<typeof PricingProviderSchema>;
|
|
3481
3496
|
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { format } from "node:util";
|
|
2
|
+
import { scrubSecrets } from "./secret-scrubber";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Process-wide console scrub: every `console.log/info/warn/error/debug` line
|
|
6
|
+
* is formatted, scrubbed, and handed to the previous method as one string.
|
|
7
|
+
*
|
|
8
|
+
* Install it before anything else wraps the console (the OTel log bridge, the
|
|
9
|
+
* Ink console patch), so every later wrapper sees only scrubbed text.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const METHODS = ["log", "info", "warn", "error", "debug"] as const;
|
|
13
|
+
type ConsoleMethod = (typeof METHODS)[number];
|
|
14
|
+
|
|
15
|
+
const SCRUB_FAILED = "[console-scrub] failed to scrub log line";
|
|
16
|
+
const NESTED_SUPPRESSED = "[console-scrub] suppressed a log line written while formatting another";
|
|
17
|
+
|
|
18
|
+
let restore: (() => void) | undefined;
|
|
19
|
+
|
|
20
|
+
export function installConsoleScrub(): void {
|
|
21
|
+
if (restore) return;
|
|
22
|
+
const originals = new Map<ConsoleMethod, (...args: unknown[]) => void>();
|
|
23
|
+
let scrubbing = false;
|
|
24
|
+
for (const method of METHODS) {
|
|
25
|
+
const original = console[method];
|
|
26
|
+
originals.set(method, original);
|
|
27
|
+
console[method] = (...args: unknown[]) => {
|
|
28
|
+
if (args.length === 0) return original.call(console);
|
|
29
|
+
// `format` runs custom inspectors synchronously, and one that logs lands
|
|
30
|
+
// here mid-scrub. Its arguments are unscrubbed and formatting them again
|
|
31
|
+
// could recurse, so fail closed with a fixed line instead.
|
|
32
|
+
if (scrubbing) return original.call(console, NESTED_SUPPRESSED);
|
|
33
|
+
scrubbing = true;
|
|
34
|
+
let line: string;
|
|
35
|
+
try {
|
|
36
|
+
// `format` keeps %s/%d substitution, inspects objects and prints
|
|
37
|
+
// Error stacks, so the scrub sees exactly what would be printed.
|
|
38
|
+
line = scrubSecrets(format(...args));
|
|
39
|
+
} catch {
|
|
40
|
+
// Never fall back to the raw arguments.
|
|
41
|
+
line = SCRUB_FAILED;
|
|
42
|
+
} finally {
|
|
43
|
+
scrubbing = false;
|
|
44
|
+
}
|
|
45
|
+
original.call(console, line);
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
restore = () => {
|
|
49
|
+
for (const [method, original] of originals) console[method] = original;
|
|
50
|
+
restore = undefined;
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Restore the console methods captured at install time (tests only). */
|
|
55
|
+
export function uninstallConsoleScrub(): void {
|
|
56
|
+
restore?.();
|
|
57
|
+
}
|
package/src/utils/credentials.ts
CHANGED
|
@@ -13,6 +13,7 @@ export const CREDENTIAL_POOL_VARS = [
|
|
|
13
13
|
"DEEPSEEK_API_KEY",
|
|
14
14
|
"AMP_API_KEY",
|
|
15
15
|
"CURSOR_API_KEY",
|
|
16
|
+
"XAI_API_KEY",
|
|
16
17
|
] as const;
|
|
17
18
|
|
|
18
19
|
/**
|
|
@@ -34,6 +35,7 @@ export const PROVIDER_CREDENTIAL_VARS: Record<string, readonly string[]> = {
|
|
|
34
35
|
dsh: ["OPENROUTER_API_KEY", "DEEPSEEK_API_KEY"],
|
|
35
36
|
amp: ["AMP_API_KEY"],
|
|
36
37
|
cursor: ["CURSOR_API_KEY"],
|
|
38
|
+
grok: ["XAI_API_KEY", "OPENROUTER_API_KEY"],
|
|
37
39
|
opencode: ["OPENROUTER_API_KEY", "ANTHROPIC_API_KEY", "OPENAI_API_KEY"],
|
|
38
40
|
};
|
|
39
41
|
|
|
@@ -69,6 +71,10 @@ export function getModelAwareCredentialVars(provider: string, model?: string): r
|
|
|
69
71
|
if (provider === "dsh" && model) {
|
|
70
72
|
return model.startsWith("openrouter/") ? ["OPENROUTER_API_KEY"] : ["DEEPSEEK_API_KEY"];
|
|
71
73
|
}
|
|
74
|
+
// grok routes `openrouter/<id>` to OpenRouter and every other model to xAI.
|
|
75
|
+
if (provider === "grok" && model) {
|
|
76
|
+
return model.startsWith("openrouter/") ? ["OPENROUTER_API_KEY"] : ["XAI_API_KEY"];
|
|
77
|
+
}
|
|
72
78
|
if (!SLASH_MODEL_PROVIDERS.has(provider) || !model) return base;
|
|
73
79
|
if (model.includes("/")) {
|
|
74
80
|
return base.filter((v) => v !== "OPENAI_API_KEY");
|
|
@@ -100,6 +106,8 @@ export function deriveProviderFromKeyType(keyType: string): string {
|
|
|
100
106
|
return "amp";
|
|
101
107
|
case "CURSOR_API_KEY":
|
|
102
108
|
return "cursor";
|
|
109
|
+
case "XAI_API_KEY":
|
|
110
|
+
return "grok";
|
|
103
111
|
case "DEVIN_API_KEY":
|
|
104
112
|
return "devin";
|
|
105
113
|
default:
|
|
@@ -47,6 +47,8 @@ export interface PageSessionPayload {
|
|
|
47
47
|
uid?: string;
|
|
48
48
|
/** Display name for a user or generated guest session. */
|
|
49
49
|
name?: string;
|
|
50
|
+
/** Set only when the shared swarm key launched the session. A session with neither this nor `uid` is a guest. */
|
|
51
|
+
op?: true;
|
|
50
52
|
}
|
|
51
53
|
|
|
52
54
|
/** base64url encode a byte buffer (no padding). */
|
|
@@ -212,7 +214,8 @@ export async function verifyPageSession(
|
|
|
212
214
|
return null;
|
|
213
215
|
}
|
|
214
216
|
|
|
215
|
-
const candidate = payload as { uid?: unknown; name?: unknown };
|
|
217
|
+
const candidate = payload as { uid?: unknown; name?: unknown; op?: unknown };
|
|
218
|
+
if (candidate.op !== undefined && candidate.op !== true) return null;
|
|
216
219
|
if (candidate.uid !== undefined && typeof candidate.uid !== "string") return null;
|
|
217
220
|
if (candidate.name !== undefined && typeof candidate.name !== "string") return null;
|
|
218
221
|
|
|
@@ -286,7 +289,7 @@ const PAGE_SESSION_TTL_SECONDS = 3600;
|
|
|
286
289
|
*/
|
|
287
290
|
export async function issuePageSessionCookie(
|
|
288
291
|
pageId: string,
|
|
289
|
-
opts: { dev: boolean; uid?: string; name?: string },
|
|
292
|
+
opts: { dev: boolean; uid?: string; name?: string; op?: true },
|
|
290
293
|
): Promise<string> {
|
|
291
294
|
const exp = Math.floor(Date.now() / 1000) + PAGE_SESSION_TTL_SECONDS;
|
|
292
295
|
const token = await signPageSession({
|
|
@@ -294,6 +297,7 @@ export async function issuePageSessionCookie(
|
|
|
294
297
|
exp,
|
|
295
298
|
...(opts.uid ? { uid: opts.uid } : {}),
|
|
296
299
|
...(opts.name ? { name: opts.name } : {}),
|
|
300
|
+
...(opts.op ? { op: true as const } : {}),
|
|
297
301
|
});
|
|
298
302
|
const attrs = [
|
|
299
303
|
`page_session=${token}`,
|