@desplega.ai/agent-swarm 1.152.2 → 1.154.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 +2 -2
- package/dist/{acp-adapter-pfeje2v5.js → acp-adapter-eb2aj8xy.js} +4 -4
- package/dist/{actions-0sttc7f5.js → actions-kpnwp1kz.js} +31 -27
- package/dist/{anthropic-messages-2y8nktar.js → anthropic-messages-9vqsbznt.js} +112 -84
- package/dist/{app-p8teva95.js → app-zk1apkf6.js} +10 -7
- package/dist/{assistant-r45jnsrt.js → assistant-kwq4y50q.js} +32 -28
- package/dist/{azure-openai-responses-f38x2avq.js → azure-openai-responses-04qwdwfq.js} +27 -13
- package/dist/{boot-reembed-je26h5y7.js → boot-reembed-22hd3jjn.js} +9 -7
- package/dist/{boot-reembed-1qmybsd4.js → boot-reembed-qe168424.js} +10 -8
- package/dist/{boot-scrub-logs-gte3h1mm.js → boot-scrub-logs-pkdpsrjd.js} +7 -5
- package/dist/{claude-adapter-csc1mzka.js → claude-adapter-3fx90nww.js} +18 -16
- package/dist/{claude-managed-adapter-dsmkcrjy.js → claude-managed-adapter-ea2aga6w.js} +3 -3
- package/dist/{claude-sdk-session-fcd6f078.js → claude-sdk-session-z7fahf1k.js} +18 -16
- package/dist/{cli-r2fpe7cx.js → cli-00w2c66p.js} +61 -55
- package/dist/{cli-wbyk465z.js → cli-09y3tjxz.js} +1 -1
- package/dist/{cli-ht5rs63n.js → cli-0e31nm4n.js} +16 -7
- package/dist/cli-0x5qzvkg.js +4279 -0
- package/dist/{cli-d3p82erb.js → cli-18amys3y.js} +9 -9
- package/dist/{cli-4ahpr4sw.js → cli-1jpjafw4.js} +5 -5
- package/dist/{cli-jbmc0r7r.js → cli-243tzws8.js} +1 -1
- package/dist/{cli-ch38q5z4.js → cli-24k806sc.js} +6789 -2542
- package/dist/cli-3bkz2p7j.js +2828 -0
- package/dist/cli-3er6gcsn.js +1849 -0
- package/dist/cli-4k7jazkq.js +3339 -0
- package/dist/{cli-c4eeb5q2.js → cli-5b6b62tz.js} +1 -1
- package/dist/{cli-qd65dvr6.js → cli-5vs0nh43.js} +65 -44
- package/dist/{cli-z3wkng35.js → cli-6r3cgpk2.js} +224 -156
- package/dist/{cli-pgj4a5fb.js → cli-71cj99hx.js} +4 -3
- package/dist/cli-8sea27q4.js +14075 -0
- package/dist/{cli-jvy9qgjs.js → cli-913yakhv.js} +22 -7
- package/dist/{cli-pf9q3696.js → cli-927erqqt.js} +5 -2
- package/dist/cli-95jnpnqg.js +230 -0
- package/dist/{cli-wpv5rgg2.js → cli-abp3g4ax.js} +15 -2
- package/dist/{cli-ckz5t2hb.js → cli-acy6bfhr.js} +10 -7
- package/dist/{cli-4yc5d8fk.js → cli-avkd6mqv.js} +50 -0
- package/dist/{cli-8wrympd7.js → cli-bzr0ftht.js} +26 -7
- package/dist/{cli-qn042eh7.js → cli-c1v10xb2.js} +194 -1913
- package/dist/cli-cgj4hc12.js +18902 -0
- package/dist/{cli-2nnd56zt.js → cli-e0pg4zeh.js} +3 -2
- package/dist/{cli-r84ctdxn.js → cli-e31ehqwy.js} +2 -2
- package/dist/{cli-gphgnvpf.js → cli-e3229na9.js} +3 -3
- package/dist/{cli-gpqcs935.js → cli-e3cyf59d.js} +2 -2
- package/dist/{cli-s25avqg9.js → cli-e781c6f9.js} +1 -0
- package/dist/{cli-s2qjdd6z.js → cli-eetrvv91.js} +1 -1
- package/dist/{cli-j4nv2wqy.js → cli-fdzg6jhf.js} +10 -4
- package/dist/{cli-fm4ytsdk.js → cli-g2h29qar.js} +2 -2
- package/dist/{cli-zvgj5zg8.js → cli-ge67p080.js} +302 -3497
- package/dist/cli-h38x0qhg.js +6912 -0
- package/dist/{cli-9xvdm3bx.js → cli-h60qfasa.js} +742 -278
- package/dist/{cli-jew3jda5.js → cli-hws0vn86.js} +2 -2
- package/dist/{cli-j8dggbcr.js → cli-k0g1sjzm.js} +23 -31
- package/dist/{cli-xw7vak96.js → cli-kp3qs53m.js} +5 -5
- package/dist/{cli-rgvp6r2x.js → cli-mcnhgnn1.js} +193 -4
- package/dist/{cli-zvkq5csn.js → cli-myaxtgh8.js} +7 -6
- package/dist/{cli-56h18ng2.js → cli-n62atwh9.js} +1 -1
- package/dist/cli-n7h1kqpf.js +29 -0
- package/dist/{cli-fmy65jdn.js → cli-pf1v8xhb.js} +7 -5
- package/dist/{cli-xy1cchaf.js → cli-q85qhckh.js} +238 -11
- package/dist/{cli-3bpzj8me.js → cli-r1d3dzt6.js} +2 -2
- package/dist/{pi-mono-adapter-jcqt71cg.js → cli-rxkqvngf.js} +67343 -101083
- package/dist/{cli-kj76p1ez.js → cli-scwq51gn.js} +13 -7
- package/dist/{cli-b9dd0h3k.js → cli-stwsmd6k.js} +3 -2
- package/dist/{cli-c2mk1kgf.js → cli-v4en75c5.js} +8 -1
- package/dist/{cli-z61ykvam.js → cli-wg04s9kr.js} +4 -3
- package/dist/cli-yn0a417a.js +4069 -0
- package/dist/{cli-zfzznn7c.js → cli-ytxs7mn1.js} +26 -2
- package/dist/{cli-4j8k1m4w.js → cli-zt1227k1.js} +1162 -192
- package/dist/cli.js +28 -35
- package/dist/{codex-adapter-fxmpsabw.js → codex-adapter-ttdm995v.js} +12 -11
- package/dist/{codex-hook-8jexc34x.js → codex-hook-8gx7bcz0.js} +3 -3
- package/dist/{codex-session-runner-rr1e47ea.js → codex-session-runner-3v8nh4c8.js} +12 -11
- package/dist/{commands-0f5rxz51.js → commands-1cxwbhhq.js} +7 -5
- package/dist/{db-r3jxf3wy.js → db-y9zwsr79.js} +25 -5
- package/dist/{dispatcher-06vph6q4.js → dispatcher-9m9zss36.js} +30 -26
- package/dist/dsh-adapter-gsb2qqf9.js +19 -0
- package/dist/{e2b-413b8t4v.js → e2b-93x1k0j5.js} +6 -7
- package/dist/{google-generative-ai-ndjmj5fa.js → google-generative-ai-gwwrnzqn.js} +33 -73
- package/dist/{google-vertex-fr665tqs.js → google-vertex-5wfawpwz.js} +38 -68
- package/dist/{handlers-0hgtc910.js → handlers-36fm1bwn.js} +33 -29
- package/dist/{hook-9kvrv1er.js → hook-pntw4r8h.js} +12 -10
- package/dist/{hook-fbfhkat3.js → hook-vwq8y26p.js} +13 -11
- package/dist/{http-xbyx8k5j.js → http-20jesakk.js} +357 -261
- package/dist/{index-jsw3aynb.js → index-6artrehg.js} +30 -26
- package/dist/{cli-pdz9qde4.js → index-9ch1n761.js} +10 -11
- package/dist/{index-rnyahfn5.js → index-9n5z2492.js} +30 -26
- package/dist/{index-66xz5e9j.js → index-h5gs261z.js} +521 -9
- package/dist/{index-tfsdxyv6.js → index-j5wrf1yw.js} +40 -25
- package/dist/{index-2sp0384z.js → index-kkeftv5h.js} +738 -21
- package/dist/{index-sgk50mbg.js → index-m2xqpz5r.js} +31 -27
- package/dist/{index-gpfn1741.js → index-m9ks8a7t.js} +9 -7
- package/dist/index-w097eg0x.js +5 -0
- package/dist/jiti-loader-b4837mtm.js +34 -0
- package/dist/jiti-static-loader-q2g5ds0y.js +31267 -0
- package/dist/{keepalive-s3xpr7sw.js → keepalive-pwxvxd2c.js} +9 -7
- package/dist/{lead-rq4n5r16.js → lead-m789wqa3.js} +46 -41
- package/dist/{maintenance-f0atj1ab.js → maintenance-bvg6w7e2.js} +10 -8
- package/dist/{mistral-conversations-qhr84ph3.js → mistral-conversations-zx0kn554.js} +24 -18
- package/dist/{node-x1g4v0pq.js → node-5qtycf6a.js} +16 -11
- package/dist/{oauth-refresh-sweep-q0t3bxre.js → oauth-refresh-sweep-6v4bd737.js} +8 -6
- package/dist/{onboard-br5j1kch.js → onboard-snckpcj1.js} +115 -35
- package/dist/{openai-codex-responses-wy6a43wd.js → openai-codex-responses-4vjqzvjp.js} +33 -27
- package/dist/{openai-completions-32rdwmze.js → openai-completions-pxsmym7g.js} +52 -64
- package/dist/{openai-responses-6xwf7bzg.js → openai-responses-tmn0rqp0.js} +23 -24
- package/dist/{opencode-adapter-28axqa09.js → opencode-adapter-qbmv4tpy.js} +6 -6
- package/dist/{otel-impl-vdqge2nt.js → otel-impl-e0ytens5.js} +4 -4
- package/dist/{pi-messages-b1mbr6gp.js → pi-messages-txpjj39q.js} +3 -3
- package/dist/pi-mono-adapter-c4ggemhz.js +1316 -0
- package/dist/{pricing-refresh-hb3691fd.js → pricing-refresh-52g2ywrw.js} +9 -7
- package/dist/{rbac-roles-t78xt39b.js → rbac-roles-5455kfdn.js} +8 -6
- package/dist/{rbac-roles-jh280k9g.js → rbac-roles-hbf0xa52.js} +9 -7
- package/dist/{render-v2-7p8r2ymg.js → render-v2-npm8xdzh.js} +16 -9
- package/dist/{seed-pricing-hf889enh.js → seed-pricing-650wbr1n.js} +8 -6
- package/dist/{setup-w6mb03jv.js → setup-k1mew5rf.js} +4 -5
- package/dist/virtual-modules-2q5b2pk4.js +54 -0
- package/dist/{work-objects-nt8gy1kj.js → work-objects-2ea3kvwg.js} +33 -29
- package/dist/{worker-dwy617t0.js → worker-ec6hvc4m.js} +46 -41
- package/openapi.json +109 -16
- package/package.json +7 -6
- package/plugin/opencode-plugins/lib/memory-raters.ts +7 -0
- package/src/be/automation-preflight.ts +6 -4
- package/src/be/db/agents.ts +75 -2
- package/src/be/db/tasks/read.ts +2 -0
- package/src/be/db/tasks/write.ts +7 -1
- package/src/be/db.ts +140 -18
- package/src/be/extensions/db.ts +35 -4
- package/src/be/memory/raters/llm.ts +2 -6
- package/src/be/memory/raters/noop.ts +2 -3
- package/src/be/memory/raters/registry.ts +4 -10
- package/src/be/migrations/157_defer_provenance_and_marker.sql +27 -0
- package/src/be/models-catalog.ts +2 -0
- package/src/be/modelsdev-cache.json +213 -0
- package/src/be/rbac-audit.ts +2 -0
- package/src/be/seed/agent-fs-provision.ts +23 -0
- package/src/be/seed-pricing.ts +3 -1
- package/src/be/swarm-config-guard.ts +1 -0
- package/src/commands/onboard/compose-generator.ts +16 -1
- package/src/commands/onboard/env-generator.ts +14 -1
- package/src/commands/onboard/manifest.ts +1 -0
- package/src/commands/onboard/steps/integration-slack.tsx +56 -16
- package/src/commands/onboard/steps/review.tsx +5 -0
- package/src/commands/onboard/types.ts +6 -0
- package/src/commands/onboard.tsx +5 -1
- package/src/commands/provider-credentials.ts +14 -2
- package/src/commands/runner.ts +18 -2
- package/src/extensions/identity.ts +15 -7
- package/src/extensions/lifecycle.ts +15 -21
- package/src/hooks/codex-hook.ts +7 -1
- package/src/http/agents.ts +12 -4
- package/src/http/config.ts +1 -1
- package/src/http/core.ts +5 -3
- package/src/http/extensions.ts +108 -34
- package/src/http/poll.ts +7 -0
- package/src/http/schedules.ts +10 -0
- package/src/http/script-runs.ts +35 -26
- package/src/http/status.ts +24 -11
- package/src/http/tasks.ts +6 -1
- package/src/prompts/base-prompt.ts +15 -7
- package/src/prompts/memories.ts +5 -7
- package/src/prompts/session-templates.ts +3 -2
- package/src/prompts/steering-delivery.ts +2 -0
- package/src/providers/claude-managed-models.ts +8 -0
- package/src/providers/codex-adapter.ts +3 -6
- package/src/providers/codex-models.ts +15 -1
- package/src/providers/dsh-adapter.ts +248 -0
- package/src/providers/index.ts +5 -1
- package/src/providers/modelsdev-reasoning.json +50 -0
- package/src/providers/pi-mono-extension.ts +2 -5
- package/src/rbac/legacy-policy.ts +15 -1
- package/src/rbac/types.ts +2 -0
- package/src/server.ts +7 -1
- package/src/slack/ack.ts +43 -8
- package/src/slack/app.ts +32 -10
- package/src/slack/config.ts +29 -0
- package/src/slack/handlers.ts +7 -1
- package/src/slack/render-v2.ts +186 -17
- package/src/slack/responses.ts +1 -3
- package/src/slack/task-output.ts +66 -8
- package/src/slack/thread-buffer.ts +27 -3
- package/src/telemetry.ts +2 -3
- package/src/tests/agent-fs-provision-seeder.test.ts +56 -2
- package/src/tests/agents-harness-provider.test.ts +19 -0
- package/src/tests/agents-list-model-display.test.ts +60 -1
- package/src/tests/base-prompt.test.ts +25 -0
- package/src/tests/claude-managed-adapter.test.ts +6 -0
- package/src/tests/codex-gpt6-release.test.ts +97 -0
- package/src/tests/codex-steering-hook.test.ts +2 -2
- package/src/tests/config-api-only-keys.test.ts +9 -0
- package/src/tests/context-window.test.ts +1 -0
- package/src/tests/db-task-read-contract.test.ts +9 -0
- package/src/tests/db-task-write-contract.test.ts +9 -0
- package/src/tests/defer-task.test.ts +115 -16
- package/src/tests/dsh-adapter.test.ts +276 -0
- package/src/tests/e2e-nightly-report.test.ts +3 -3
- package/src/tests/e2e-redact.test.ts +2 -0
- package/src/tests/env-flag.test.ts +6 -0
- package/src/tests/extension-agent-visibility.test.ts +474 -0
- package/src/tests/extensions-db.test.ts +41 -0
- package/src/tests/extensions-http.test.ts +148 -4
- package/src/tests/extensions-lifecycle.test.ts +21 -1
- package/src/tests/extensions-mcp-tools.test.ts +9 -5
- package/src/tests/fetch-resolved-env.test.ts +43 -0
- package/src/tests/internal-ai/complete-structured.test.ts +32 -9
- package/src/tests/internal-ai/credentials.test.ts +4 -4
- package/src/tests/internal-ai/models.test.ts +20 -0
- package/src/tests/memory-rate-tool.test.ts +7 -10
- package/src/tests/memory-rater-defaults.test.ts +23 -0
- package/src/tests/memory-rater-e2e.test.ts +2 -3
- package/src/tests/migration-046-budgets.test.ts +2 -0
- package/src/tests/model-control.test.ts +2 -2
- package/src/tests/onboard-compose.test.ts +14 -0
- package/src/tests/onboard-env.test.ts +17 -0
- package/src/tests/onboard-manifest.test.ts +12 -0
- package/src/tests/opus-5-5-registry.test.ts +48 -0
- package/src/tests/pricing-refresh.test.ts +1 -1
- package/src/tests/pricing-routes.test.ts +15 -3
- package/src/tests/provider-registration.test.ts +5 -2
- package/src/tests/rbac-admission-e2e.test.ts +54 -0
- package/src/tests/rbac-e2e-helpers.ts +2 -0
- package/src/tests/rbac-engine.test.ts +32 -0
- package/src/tests/rbac-wire-e2e.test.ts +33 -0
- package/src/tests/reasoning-effort.test.ts +8 -0
- package/src/tests/reload-config.test.ts +33 -0
- package/src/tests/script-connections.test.ts +3 -0
- package/src/tests/session-costs-model-key-normalize.test.ts +1 -0
- package/src/tests/slack-config.test.ts +136 -0
- package/src/tests/slack-mode-lifecycle.test.ts +69 -0
- package/src/tests/slack-render-v2.test.ts +446 -16
- package/src/tests/slack-steering.test.ts +69 -1
- package/src/tests/slack-task-output.test.ts +33 -12
- package/src/tests/status.test.ts +22 -2
- package/src/tests/steering-core.test.ts +35 -0
- package/src/tests/steering-delivery.test.ts +39 -0
- package/src/tests/steering-transport.test.ts +2 -2
- package/src/tests/task-tool-preload-mcp.test.ts +3 -1
- package/src/tests/telemetry-init.test.ts +18 -0
- package/src/tests/workflow-live-recovery.test.ts +126 -0
- package/src/tests/workflow-llm.test.ts +3 -3
- package/src/tools/defer-task.ts +64 -26
- package/src/tools/extension-common.ts +4 -0
- package/src/tools/extension-install.ts +1 -1
- package/src/tools/extension-list.ts +4 -3
- package/src/tools/join-swarm.ts +1 -1
- package/src/tools/schedules/create-schedule.ts +11 -1
- package/src/tools/schedules/patch-schedule.ts +5 -0
- package/src/tools/schedules/update-schedule.ts +5 -0
- package/src/tools/send-task.ts +6 -0
- package/src/tools/utils.ts +1 -1
- package/src/types.ts +30 -8
- package/src/utils/context-window.ts +1 -0
- package/src/utils/credentials.ts +8 -0
- package/src/utils/internal-ai/complete-structured.ts +8 -0
- package/src/utils/internal-ai/models.ts +5 -5
- package/src/utils/memory-raters.ts +3 -0
- package/src/workflows/engine.ts +28 -0
- package/src/workflows/recovery.ts +7 -4
- package/templates/schedules/daily-status-report/content.md +25 -12
- package/templates/schedules/daily-workflow-health-audit/content.md +1 -1
- package/templates/skills/swarm-extensions/content.md +9 -5
- package/dist/cli-4wppfkcm.js +0 -16384
- package/dist/cli-55qrx3q8.js +0 -6041
- package/dist/cli-cg4vnyh4.js +0 -305
- package/dist/cli-jyetr64j.js +0 -52
- package/dist/cli-pp9ax9pa.js +0 -520
- package/dist/cli-qa66qaf0.js +0 -6076
- package/dist/cli-qghpvca2.js +0 -590
- package/dist/cli-rembwgt8.js +0 -861
- package/dist/cli-tcvj7j2r.js +0 -1971
- package/dist/cli-vfcth11k.js +0 -36
- package/dist/cli-w1d1bgkk.js +0 -1422
- package/dist/cli-y6x0gqtg.js +0 -5977
- package/dist/es5-tj3tn4jj.js +0 -844
- package/dist/index-1kq5hw2p.js +0 -467
- package/dist/index-1p8wdstt.js +0 -7
- package/dist/index-8fdjzb2f.js +0 -100
- package/dist/index-bxmwzbgw.js +0 -365
- package/dist/index-nzerxd5z.js +0 -7
- package/dist/index-pyymnzat.js +0 -1168
- package/dist/index-qv6atbab.js +0 -619
- package/dist/index-rja32qa7.js +0 -801
- package/dist/index-tzg5srfp.js +0 -211
- package/dist/index-vhncw983.js +0 -90
- package/dist/index-xk036zsk.js +0 -859
- package/dist/index-zyazbzcb.js +0 -6
- package/dist/{claude-managed-setup-48dwg9bv.js → claude-managed-setup-f5pmsyrp.js} +3 -3
package/src/slack/task-output.ts
CHANGED
|
@@ -1,6 +1,22 @@
|
|
|
1
1
|
import type { AgentTask } from "../types";
|
|
2
2
|
|
|
3
|
-
/**
|
|
3
|
+
/**
|
|
4
|
+
* Legacy deferral output, written before `defer-task` started storing the
|
|
5
|
+
* human-facing card directly:
|
|
6
|
+
* `Deferred until {date} {time} ([shortId](url)) -> {note excerpt}`
|
|
7
|
+
* Group 1 is the ETA; the schedule link and the note excerpt are dropped.
|
|
8
|
+
*/
|
|
9
|
+
const LEGACY_DEFERRAL_NOTICE = /^Deferred until ([^\n(]+?) \([^\n]*?\) -> .*$/;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* The Slack-facing form of a task's stored output.
|
|
13
|
+
*
|
|
14
|
+
* For a deferral this is the stored text verbatim — `defer-task` already
|
|
15
|
+
* writes the human card (`Checking back today at 18:38`, or
|
|
16
|
+
* `Waiting on Researcher — or today at 18:38 at the latest`). Rows written
|
|
17
|
+
* before that change carried the agent's internal handoff note plus a raw
|
|
18
|
+
* schedule UUID link instead, so they are rewritten to the ETA alone.
|
|
19
|
+
*/
|
|
4
20
|
export function slackTaskOutput(
|
|
5
21
|
task: Pick<AgentTask, "output" | "tags" | "outputSchema" | "status">,
|
|
6
22
|
): string | undefined {
|
|
@@ -9,13 +25,55 @@ export function slackTaskOutput(
|
|
|
9
25
|
return output;
|
|
10
26
|
}
|
|
11
27
|
// Only match the engine-authored line from defer-task, not continuation results.
|
|
12
|
-
const
|
|
13
|
-
if (!
|
|
14
|
-
const
|
|
15
|
-
return
|
|
28
|
+
const legacy = output?.match(LEGACY_DEFERRAL_NOTICE);
|
|
29
|
+
if (!legacy) return output;
|
|
30
|
+
const eta = legacy[1]?.trim();
|
|
31
|
+
return eta ? `Checking back ${eta}` : output;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* A task that ENDED in a deferral. It is stored `completed` — `defer-task`
|
|
36
|
+
* ends the task and books a wake-up — but to a human reading the thread the
|
|
37
|
+
* work is still outstanding, so every glyph and card treats it as waiting.
|
|
38
|
+
*
|
|
39
|
+
* `deferredAt` is the exact signal. The `deferred` tag is not: `defer-task`
|
|
40
|
+
* puts it on both the deferring task and the wake-up schedule, so a wake-up
|
|
41
|
+
* child that simply finished is tag-identical to one that deferred again.
|
|
42
|
+
* The tag is still consulted for rows written before `deferredAt` existed,
|
|
43
|
+
* narrowed to tasks the scheduler did not create (`taskType !== "deferred"`),
|
|
44
|
+
* where it is unambiguous.
|
|
45
|
+
*/
|
|
46
|
+
export function isDeferredTask(
|
|
47
|
+
task: Pick<AgentTask, "tags" | "status" | "deferredAt" | "taskType">,
|
|
48
|
+
): boolean {
|
|
49
|
+
if (task.status !== "completed") return false;
|
|
50
|
+
if (task.deferredAt) return true;
|
|
51
|
+
return !!task.tags?.includes("deferred") && task.taskType !== "deferred";
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** The wake-up tasks `defer-task` booked to continue `task`. */
|
|
55
|
+
export function deferralWakes<T extends Pick<AgentTask, "parentTaskId" | "taskType">>(
|
|
56
|
+
task: Pick<AgentTask, "id">,
|
|
57
|
+
threadTasks: readonly T[],
|
|
58
|
+
): T[] {
|
|
59
|
+
return threadTasks.filter(
|
|
60
|
+
(candidate) => candidate.parentTaskId === task.id && candidate.taskType === "deferred",
|
|
61
|
+
);
|
|
16
62
|
}
|
|
17
63
|
|
|
18
|
-
/**
|
|
19
|
-
|
|
20
|
-
|
|
64
|
+
/**
|
|
65
|
+
* A deferred task that is still parked: none of its wake-ups has settled.
|
|
66
|
+
* Once one has, the deferral is over — the answer (or the next deferral)
|
|
67
|
+
* lives on the wake-up, and the deferring task reads as the `completed` it
|
|
68
|
+
* is stored as.
|
|
69
|
+
*/
|
|
70
|
+
export function isAwaitingWake(
|
|
71
|
+
task: Pick<AgentTask, "id" | "tags" | "status" | "deferredAt" | "taskType">,
|
|
72
|
+
threadTasks: readonly Pick<AgentTask, "parentTaskId" | "taskType" | "status">[],
|
|
73
|
+
): boolean {
|
|
74
|
+
if (!isDeferredTask(task)) return false;
|
|
75
|
+
return !deferralWakes(task, threadTasks).some(
|
|
76
|
+
(wake) =>
|
|
77
|
+
wake.status === "completed" || wake.status === "failed" || wake.status === "cancelled",
|
|
78
|
+
);
|
|
21
79
|
}
|
|
@@ -4,11 +4,12 @@ import {
|
|
|
4
4
|
getLeadAgent,
|
|
5
5
|
getMostRecentTaskInThread,
|
|
6
6
|
} from "../be/db";
|
|
7
|
+
import { findUserByExternalId } from "../be/users";
|
|
7
8
|
import { createAdditiveBuffer } from "../tasks/additive-buffer";
|
|
8
9
|
import { slackContextKey } from "../tasks/context-key";
|
|
9
10
|
import { getSlackApp } from "./app";
|
|
10
11
|
import { buildBufferFlushBlocks } from "./blocks";
|
|
11
|
-
import { rewriteSlackMentions } from "./enrich";
|
|
12
|
+
import { resolveSlackUserId, rewriteSlackMentions } from "./enrich";
|
|
12
13
|
import type { SlackFile } from "./files";
|
|
13
14
|
import {
|
|
14
15
|
bufferedFileFailures,
|
|
@@ -204,6 +205,18 @@ async function slackFlush(
|
|
|
204
205
|
);
|
|
205
206
|
}
|
|
206
207
|
|
|
208
|
+
const requestedByUserId = app
|
|
209
|
+
? await resolveSlackUserId(app.client, originalRequesterId, {
|
|
210
|
+
sampleEventType: "buffered_thread_reply",
|
|
211
|
+
sampleContext: items[0]!.text,
|
|
212
|
+
})
|
|
213
|
+
: (await findUserByExternalId("slack", originalRequesterId))?.id;
|
|
214
|
+
|
|
215
|
+
// A steering row has one sender. Keep consecutive messages from that sender
|
|
216
|
+
// together, then flush the remaining senders in order after successful delivery.
|
|
217
|
+
const nextSenderIndex = items.findIndex((item) => item.userId !== originalRequesterId);
|
|
218
|
+
const steeringItems = nextSenderIndex === -1 ? items : items.slice(0, nextSenderIndex);
|
|
219
|
+
|
|
207
220
|
// Steering carries text only. As on the mention path, files need a follow-up
|
|
208
221
|
// task so its attachments exist before a worker can claim it.
|
|
209
222
|
const steering =
|
|
@@ -211,8 +224,15 @@ async function slackFlush(
|
|
|
211
224
|
? await requestSlackThreadSteering({
|
|
212
225
|
channelId,
|
|
213
226
|
threadTs,
|
|
214
|
-
message:
|
|
215
|
-
|
|
227
|
+
message:
|
|
228
|
+
nextSenderIndex === -1
|
|
229
|
+
? combinedText
|
|
230
|
+
: await rewriteSlackMentions(
|
|
231
|
+
steeringItems.map((item) => item.text).join("\n---\n"),
|
|
232
|
+
botUserId,
|
|
233
|
+
),
|
|
234
|
+
messageTimestamps: steeringItems.map((item) => item.ts),
|
|
235
|
+
requestedByUserId,
|
|
216
236
|
})
|
|
217
237
|
: null;
|
|
218
238
|
if (steering) {
|
|
@@ -239,6 +259,9 @@ async function slackFlush(
|
|
|
239
259
|
}
|
|
240
260
|
}
|
|
241
261
|
}
|
|
262
|
+
if (nextSenderIndex !== -1) {
|
|
263
|
+
await slackFlush(items.slice(nextSenderIndex), key, immediate);
|
|
264
|
+
}
|
|
242
265
|
return;
|
|
243
266
|
}
|
|
244
267
|
|
|
@@ -267,6 +290,7 @@ async function slackFlush(
|
|
|
267
290
|
slackThreadTs: threadTs,
|
|
268
291
|
slackTriggerMessageTs: items.at(-1)!.ts,
|
|
269
292
|
slackUserId: originalRequesterId,
|
|
293
|
+
requestedByUserId,
|
|
270
294
|
dependsOn,
|
|
271
295
|
parentTaskId: mostRecentTask?.id,
|
|
272
296
|
contextKey: slackContextKey({ channelId, threadTs }),
|
package/src/telemetry.ts
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { randomUUID } from "node:crypto";
|
|
10
10
|
import pkg from "../package.json";
|
|
11
|
+
import { isSlackConfigured } from "./slack/config";
|
|
11
12
|
import { isEnvFlagEnabled } from "./utils/env-flag";
|
|
12
13
|
|
|
13
14
|
const TELEMETRY_ENDPOINT = "https://proxy.desplega.sh/v1/events";
|
|
@@ -269,9 +270,7 @@ export function _hasEmbeddingKey(env: NodeJS.ProcessEnv = process.env): boolean
|
|
|
269
270
|
* Exported for tests; not part of the public API.
|
|
270
271
|
*/
|
|
271
272
|
export function _hasSlackChannel(env: NodeJS.ProcessEnv = process.env): boolean {
|
|
272
|
-
return (
|
|
273
|
-
!!(env.SLACK_BOT_TOKEN && env.SLACK_APP_TOKEN) && !isEnvFlagEnabled("SLACK_DISABLE", false, env)
|
|
274
|
-
);
|
|
273
|
+
return isSlackConfigured(env);
|
|
275
274
|
}
|
|
276
275
|
|
|
277
276
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { afterAll, afterEach, beforeAll, describe, expect, test } from "bun:test";
|
|
1
|
+
import { afterAll, afterEach, beforeAll, describe, expect, spyOn, test } from "bun:test";
|
|
2
2
|
import { unlink } from "node:fs/promises";
|
|
3
3
|
import {
|
|
4
4
|
closeDb,
|
|
@@ -110,6 +110,7 @@ function createFetchStub(
|
|
|
110
110
|
orgId: "personal-org",
|
|
111
111
|
});
|
|
112
112
|
}
|
|
113
|
+
if (url.pathname === "/auth/profile" && method === "PATCH") return Response.json(body);
|
|
113
114
|
if (url.pathname === "/auth/me" && method === "GET") {
|
|
114
115
|
return Response.json({
|
|
115
116
|
userId: "admin-user",
|
|
@@ -502,11 +503,64 @@ describe("agent-fs provisioning seeder", () => {
|
|
|
502
503
|
role: "editor",
|
|
503
504
|
});
|
|
504
505
|
|
|
506
|
+
expect(records.find((r) => r.path === "/auth/profile")).toMatchObject({
|
|
507
|
+
method: "PATCH",
|
|
508
|
+
body: { displayName: worker.name },
|
|
509
|
+
authorization: "Bearer example-afs-agent-key",
|
|
510
|
+
hasSignal: true,
|
|
511
|
+
});
|
|
505
512
|
records.length = 0;
|
|
506
513
|
const second = await ensureAgentFsCredentialsForAgent(worker.id);
|
|
507
514
|
|
|
508
515
|
expect(second.created).toBe(false);
|
|
509
|
-
expect(records).toEqual([
|
|
516
|
+
expect(records).toEqual([
|
|
517
|
+
{
|
|
518
|
+
method: "PATCH",
|
|
519
|
+
path: "/auth/profile",
|
|
520
|
+
body: { displayName: "Credential Worker" },
|
|
521
|
+
authorization: "Bearer example-afs-agent-key",
|
|
522
|
+
hasSignal: true,
|
|
523
|
+
},
|
|
524
|
+
]);
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
test("profile sync failures leave existing credentials usable and retry later", async () => {
|
|
528
|
+
process.env.AGENT_FS_API_URL = "https://agent-fs.example.test";
|
|
529
|
+
const worker = await createAgent({
|
|
530
|
+
name: "Legacy Worker",
|
|
531
|
+
description: "Profile compatibility",
|
|
532
|
+
role: "worker",
|
|
533
|
+
isLead: false,
|
|
534
|
+
status: "idle",
|
|
535
|
+
maxTasks: 1,
|
|
536
|
+
capabilities: [],
|
|
537
|
+
});
|
|
538
|
+
await upsertSwarmConfig({
|
|
539
|
+
scope: "agent",
|
|
540
|
+
scopeId: worker.id,
|
|
541
|
+
key: "AGENT_FS_API_KEY",
|
|
542
|
+
value: "example-profile-key",
|
|
543
|
+
isSecret: true,
|
|
544
|
+
});
|
|
545
|
+
const warn = spyOn(console, "warn").mockImplementation(() => {});
|
|
546
|
+
try {
|
|
547
|
+
for (const status of [404, 405, 500, 0]) {
|
|
548
|
+
setAgentFsProvisionFetchForTests((async (_input, init) => {
|
|
549
|
+
expect(init?.signal).toBeInstanceOf(AbortSignal);
|
|
550
|
+
if (!status) throw new DOMException("Timed out", "TimeoutError");
|
|
551
|
+
return Response.json({ error: "unsupported" }, { status });
|
|
552
|
+
}) as typeof fetch);
|
|
553
|
+
expect((await ensureAgentFsCredentialsForAgent(worker.id)).created).toBe(false);
|
|
554
|
+
}
|
|
555
|
+
expect(warn).toHaveBeenCalledTimes(4);
|
|
556
|
+
const records: RequestRecord[] = [];
|
|
557
|
+
setAgentFsProvisionFetchForTests(createFetchStub(records));
|
|
558
|
+
await ensureAgentFsCredentialsForAgent(worker.id);
|
|
559
|
+
expect(records[0].body).toEqual({ displayName: "Legacy Worker" });
|
|
560
|
+
expect(records[0].authorization).toBe("Bearer example-profile-key");
|
|
561
|
+
} finally {
|
|
562
|
+
warn.mockRestore();
|
|
563
|
+
}
|
|
510
564
|
});
|
|
511
565
|
|
|
512
566
|
test("recovers a lost agent credential by registering an alias after a 409", async () => {
|
|
@@ -333,6 +333,25 @@ describe("PATCH /api/agents/:id/harness-provider", () => {
|
|
|
333
333
|
});
|
|
334
334
|
|
|
335
335
|
describe("PATCH /api/agents/:id/runtime", () => {
|
|
336
|
+
test("accepts dsh and persists its harness and model for the worker", async () => {
|
|
337
|
+
const agent = await createAgent({
|
|
338
|
+
name: "dsh-worker",
|
|
339
|
+
isLead: false,
|
|
340
|
+
status: "idle",
|
|
341
|
+
capabilities: [],
|
|
342
|
+
});
|
|
343
|
+
const res = await fetch(`${baseUrl}/api/agents/${agent.id}/runtime`, {
|
|
344
|
+
method: "PATCH",
|
|
345
|
+
headers: { "Content-Type": "application/json" },
|
|
346
|
+
body: JSON.stringify({ harness_provider: "dsh", model: "deepseek-flash" }),
|
|
347
|
+
});
|
|
348
|
+
expect(res.status).toBe(200);
|
|
349
|
+
expect((await getAgentById(agent.id))?.harnessProvider).toBe("dsh");
|
|
350
|
+
const rows = await getSwarmConfigs({ scope: "agent", scopeId: agent.id });
|
|
351
|
+
expect(rows.find((row) => row.key === "HARNESS_PROVIDER")?.value).toBe("dsh");
|
|
352
|
+
expect(rows.find((row) => row.key === "MODEL_OVERRIDE")?.value).toBe("deepseek-flash");
|
|
353
|
+
});
|
|
354
|
+
|
|
336
355
|
test("updates harness_provider and agent-scoped runtime config rows", async () => {
|
|
337
356
|
const a = await createAgent({
|
|
338
357
|
name: "runtime-target-1",
|
|
@@ -1,9 +1,38 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import type { LiveModelsCatalog } from "../../apps/ui/src/lib/agent-runtime-models";
|
|
2
3
|
import {
|
|
3
4
|
getAgentModelDisplay,
|
|
4
5
|
getAgentModelPresentation,
|
|
5
6
|
} from "../../apps/ui/src/lib/agents-list-model-display";
|
|
6
7
|
|
|
8
|
+
const liveCatalog: LiveModelsCatalog = {
|
|
9
|
+
openrouter: {
|
|
10
|
+
id: "openrouter",
|
|
11
|
+
name: "OpenRouter",
|
|
12
|
+
models: {
|
|
13
|
+
"deepseek/deepseek-v4-pro": { id: "deepseek/deepseek-v4-pro", name: "DeepSeek V4 Pro" },
|
|
14
|
+
"deepseek/deepseek-v4.1-flash": {
|
|
15
|
+
id: "deepseek/deepseek-v4.1-flash",
|
|
16
|
+
name: "DeepSeek V4.1 Flash",
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
opencode: {
|
|
21
|
+
id: "opencode",
|
|
22
|
+
name: "OpenCode Zen",
|
|
23
|
+
models: {
|
|
24
|
+
"glm-5.3-flash": { id: "glm-5.3-flash", name: "GLM-5.3-Flash" },
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
anthropic: {
|
|
28
|
+
id: "anthropic",
|
|
29
|
+
name: "Anthropic",
|
|
30
|
+
models: {
|
|
31
|
+
"claude-opus-5-5": { id: "claude-opus-5-5", name: "Claude Opus 5.5" },
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
|
|
7
36
|
describe("agents list model display", () => {
|
|
8
37
|
test("shows configured and last-used models when they diverge", () => {
|
|
9
38
|
const display = getAgentModelDisplay("claude-opus-4-7", "claude-sonnet-4-6");
|
|
@@ -43,6 +72,31 @@ describe("agents list model display", () => {
|
|
|
43
72
|
});
|
|
44
73
|
});
|
|
45
74
|
|
|
75
|
+
test.each([
|
|
76
|
+
["openrouter/deepseek/deepseek-v4-pro", "DeepSeek V4 Pro", "OpenRouter"],
|
|
77
|
+
["openrouter/deepseek/deepseek-v4.1-flash", "DeepSeek V4.1 Flash", "OpenRouter"],
|
|
78
|
+
["opencode/glm-5.3-flash", "GLM-5.3-Flash", "OpenCode Zen"],
|
|
79
|
+
["claude-opus-5-5", "Claude Opus 5.5", "Anthropic"],
|
|
80
|
+
])("resolves %s from the live catalog", (model, label, provider) => {
|
|
81
|
+
expect(getAgentModelPresentation(model, liveCatalog)).toMatchObject({
|
|
82
|
+
raw: model,
|
|
83
|
+
label,
|
|
84
|
+
provider,
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test("title-cases an uncatalogued model id without a maintained label entry", () => {
|
|
89
|
+
expect(
|
|
90
|
+
getAgentModelPresentation("opencode/future-model-v8.2-preview", liveCatalog),
|
|
91
|
+
).toMatchObject({
|
|
92
|
+
label: "Future Model V8.2 Preview",
|
|
93
|
+
provider: null,
|
|
94
|
+
});
|
|
95
|
+
expect(
|
|
96
|
+
getAgentModelPresentation("openrouter/deepseek/deepseek-v5.1-flash", liveCatalog)?.label,
|
|
97
|
+
).toBe("Deepseek V5.1 Flash");
|
|
98
|
+
});
|
|
99
|
+
|
|
46
100
|
test("presents latest Anthropic direct model ids as readable labels", () => {
|
|
47
101
|
expect(getAgentModelPresentation("claude-fable-5-1")).toMatchObject({
|
|
48
102
|
label: "Claude Fable 5.1",
|
|
@@ -54,6 +108,11 @@ describe("agents list model display", () => {
|
|
|
54
108
|
provider: "Anthropic",
|
|
55
109
|
providerId: "anthropic",
|
|
56
110
|
});
|
|
111
|
+
expect(getAgentModelPresentation("claude-opus-5-5")).toMatchObject({
|
|
112
|
+
label: "Claude Opus 5.5",
|
|
113
|
+
provider: "Anthropic",
|
|
114
|
+
providerId: "anthropic",
|
|
115
|
+
});
|
|
57
116
|
expect(getAgentModelPresentation("claude-opus-5")).toMatchObject({
|
|
58
117
|
label: "Claude Opus 5",
|
|
59
118
|
provider: "Anthropic",
|
|
@@ -75,7 +134,7 @@ describe("agents list model display", () => {
|
|
|
75
134
|
providerId: "anthropic",
|
|
76
135
|
});
|
|
77
136
|
expect(getAgentModelPresentation("opus")).toMatchObject({
|
|
78
|
-
label: "Claude Opus 5",
|
|
137
|
+
label: "Claude Opus 5.5",
|
|
79
138
|
provider: "Anthropic",
|
|
80
139
|
providerId: "anthropic",
|
|
81
140
|
});
|
|
@@ -25,6 +25,8 @@ const ENV_KEYS = [
|
|
|
25
25
|
"SLACK_DISABLE",
|
|
26
26
|
"SLACK_BOT_TOKEN",
|
|
27
27
|
"SLACK_APP_TOKEN",
|
|
28
|
+
"SLACK_MODE",
|
|
29
|
+
"SLACK_SIGNING_SECRET",
|
|
28
30
|
"STEERING_ENABLED",
|
|
29
31
|
"AGENT_FS_API_URL",
|
|
30
32
|
"SCRIPTS_ONLY_MCP",
|
|
@@ -561,6 +563,29 @@ describe("getBasePrompt: slack section", () => {
|
|
|
561
563
|
expect(result).toContain(SLACK_HEADER);
|
|
562
564
|
});
|
|
563
565
|
|
|
566
|
+
test("a tool capability alone does not bypass the default socket credential gate", async () => {
|
|
567
|
+
const result = await getBasePrompt({
|
|
568
|
+
...minimalArgs,
|
|
569
|
+
serverCapabilities: ["core", "slack"],
|
|
570
|
+
});
|
|
571
|
+
expect(result).not.toContain(SLACK_HEADER);
|
|
572
|
+
});
|
|
573
|
+
|
|
574
|
+
test("supports the HTTP prompt contract without distributing Slack secrets to the worker", async () => {
|
|
575
|
+
process.env.SLACK_MODE = "http";
|
|
576
|
+
const result = await getBasePrompt({
|
|
577
|
+
...minimalArgs,
|
|
578
|
+
serverCapabilities: ["core", "slack"],
|
|
579
|
+
});
|
|
580
|
+
expect(result).toContain(SLACK_HEADER);
|
|
581
|
+
|
|
582
|
+
const withoutCapability = await getBasePrompt({
|
|
583
|
+
...minimalArgs,
|
|
584
|
+
serverCapabilities: ["core"],
|
|
585
|
+
});
|
|
586
|
+
expect(withoutCapability).not.toContain(SLACK_HEADER);
|
|
587
|
+
});
|
|
588
|
+
|
|
564
589
|
test("a scripts-only worker with a Slack task gets the scripts-only variant only", async () => {
|
|
565
590
|
enableSlack();
|
|
566
591
|
const result = await getBasePrompt({
|
|
@@ -898,6 +898,12 @@ describe("ClaudeManagedAdapter (Phase 4) — repo provisioning + cost data", ()
|
|
|
898
898
|
cacheReadPerMillion: 0.25,
|
|
899
899
|
cacheWritePerMillion: 12.5,
|
|
900
900
|
});
|
|
901
|
+
expect(CLAUDE_MANAGED_MODEL_PRICING["claude-opus-5-5"]).toEqual({
|
|
902
|
+
inputPerMillion: 4.0,
|
|
903
|
+
outputPerMillion: 20.0,
|
|
904
|
+
cacheReadPerMillion: 0.2,
|
|
905
|
+
cacheWritePerMillion: 5.0,
|
|
906
|
+
});
|
|
901
907
|
expect(CLAUDE_MANAGED_MODEL_PRICING["claude-opus-5"]).toEqual({
|
|
902
908
|
inputPerMillion: 5.0,
|
|
903
909
|
outputPerMillion: 25.0,
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { modelGroupsForHarness } from "../../apps/ui/src/lib/agent-runtime-models";
|
|
3
|
+
import { buildModelsCatalog } from "../be/models-catalog";
|
|
4
|
+
import cache from "../be/modelsdev-cache.json";
|
|
5
|
+
import { buildModelsDevSeedRows } from "../be/seed-pricing";
|
|
6
|
+
import {
|
|
7
|
+
CODEX_DEFAULT_MODEL,
|
|
8
|
+
CODEX_MODELS,
|
|
9
|
+
computeCodexCostUsd,
|
|
10
|
+
FALLBACK_CODEX_MODEL_PRICING,
|
|
11
|
+
getCodexContextWindow,
|
|
12
|
+
resolveCodexModel,
|
|
13
|
+
} from "../providers/codex-models";
|
|
14
|
+
import { applyReasoningEffort, reasoningCapability } from "../providers/reasoning-effort";
|
|
15
|
+
|
|
16
|
+
const releases = [
|
|
17
|
+
{ id: "gpt-6-sol", label: "GPT-6 Sol", input: 2, output: 10, read: 0.2, write: 2.5 },
|
|
18
|
+
{ id: "gpt-6-luna", label: "GPT-6 Luna", input: 0.1, output: 0.5, read: 0.01, write: 0.125 },
|
|
19
|
+
] as const;
|
|
20
|
+
|
|
21
|
+
for (const release of releases) {
|
|
22
|
+
describe(release.id, () => {
|
|
23
|
+
test("uses the exact bare CLI ID with verified context and base costs", () => {
|
|
24
|
+
expect(CODEX_MODELS).toContain(release.id);
|
|
25
|
+
expect(resolveCodexModel(release.id.toUpperCase())).toBe(release.id);
|
|
26
|
+
expect(getCodexContextWindow(release.id)).toBe(1_050_000);
|
|
27
|
+
expect(cache.openai.models[release.id].limit.output).toBe(128_000);
|
|
28
|
+
expect(FALLBACK_CODEX_MODEL_PRICING[release.id]).toEqual({
|
|
29
|
+
inputPerMillion: release.input,
|
|
30
|
+
cachedInputPerMillion: release.read,
|
|
31
|
+
outputPerMillion: release.output,
|
|
32
|
+
});
|
|
33
|
+
expect(computeCodexCostUsd(release.id, 1_000_000, 250_000, 100_000)).toBeCloseTo(
|
|
34
|
+
release.input * 0.75 + release.read * 0.25 + release.output * 0.1,
|
|
35
|
+
8,
|
|
36
|
+
);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test("exposes the full reasoning range and transports none/max correctly", () => {
|
|
40
|
+
expect(reasoningCapability("codex", release.id)).toEqual({
|
|
41
|
+
supported: true,
|
|
42
|
+
levels: ["off", "low", "medium", "high", "xhigh", "max"],
|
|
43
|
+
default: "medium",
|
|
44
|
+
});
|
|
45
|
+
for (const [effort, value] of [
|
|
46
|
+
["off", "none"],
|
|
47
|
+
["max", "max"],
|
|
48
|
+
] as const) {
|
|
49
|
+
expect(applyReasoningEffort("codex", release.id, effort)).toEqual({
|
|
50
|
+
kind: "codex-config",
|
|
51
|
+
config: { model_reasoning_effort: value },
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test("seeds all four token classes under canonical Codex IDs", () => {
|
|
57
|
+
const rows = buildModelsDevSeedRows(cache).filter(
|
|
58
|
+
(row) => row.provider === "codex" && row.model === release.id,
|
|
59
|
+
);
|
|
60
|
+
expect(
|
|
61
|
+
Object.fromEntries(rows.map((row) => [row.tokenClass, row.pricePerMillionUsd])),
|
|
62
|
+
).toEqual({
|
|
63
|
+
input: release.input,
|
|
64
|
+
output: release.output,
|
|
65
|
+
cached_input: release.read,
|
|
66
|
+
cache_write: release.write,
|
|
67
|
+
});
|
|
68
|
+
expect(cache.openai.models[release.id].cost.tiers).toEqual([
|
|
69
|
+
{
|
|
70
|
+
input: release.input * 2,
|
|
71
|
+
output: release.output * 1.5,
|
|
72
|
+
cache_read: release.read * 2,
|
|
73
|
+
cache_write: release.write * 2,
|
|
74
|
+
tier: { type: "context", size: 272000 },
|
|
75
|
+
},
|
|
76
|
+
]);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test("is available through the catalog and direct UI selector", () => {
|
|
80
|
+
const model = buildModelsCatalog(cache).openai?.models[release.id];
|
|
81
|
+
expect(model?.limit?.context).toBe(1_050_000);
|
|
82
|
+
expect(model?.cost).toEqual({ input: release.input, output: release.output });
|
|
83
|
+
const option = modelGroupsForHarness("codex", undefined, undefined)
|
|
84
|
+
.flatMap((group) => group.models)
|
|
85
|
+
.find((entry) => entry.id === release.id);
|
|
86
|
+
expect(option?.label).toBe(release.label);
|
|
87
|
+
expect(option?.reasoningLevels).toEqual(["off", "low", "medium", "high", "xhigh", "max"]);
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
test("release additions preserve rollout-safe defaults and aliases", () => {
|
|
93
|
+
expect(CODEX_DEFAULT_MODEL).toBe("gpt-5.6-terra");
|
|
94
|
+
expect(resolveCodexModel("sonnet")).toBe("gpt-5.6-terra");
|
|
95
|
+
expect(resolveCodexModel("haiku")).toBe("gpt-5.6-luna");
|
|
96
|
+
expect(resolveCodexModel("opus")).toBe("gpt-5.6-sol");
|
|
97
|
+
});
|
|
@@ -101,7 +101,7 @@ describe("codex steering hook", () => {
|
|
|
101
101
|
);
|
|
102
102
|
|
|
103
103
|
test("PostToolUse injects the envelope and marks the row delivered first", async () => {
|
|
104
|
-
const pending = message();
|
|
104
|
+
const pending = message({ createdByKind: "user", senderLabel: "Taras (user)" });
|
|
105
105
|
const deliveredCalls: string[] = [];
|
|
106
106
|
const output = await handleCodexHookEvent(
|
|
107
107
|
{ hook_event_name: "PostToolUse" },
|
|
@@ -116,7 +116,7 @@ describe("codex steering hook", () => {
|
|
|
116
116
|
additionalContext: string;
|
|
117
117
|
};
|
|
118
118
|
expect(hookOutput.hookEventName).toBe("PostToolUse");
|
|
119
|
-
expect(hookOutput.additionalContext).toContain(pending.body);
|
|
119
|
+
expect(hookOutput.additionalContext).toContain(`From Taras (user): ${pending.body}`);
|
|
120
120
|
expect(hookOutput.additionalContext).toContain(`[steering ${pending.id}]`);
|
|
121
121
|
expect(hookOutput.additionalContext).toContain("accept-steer");
|
|
122
122
|
});
|
|
@@ -50,6 +50,12 @@ beforeAll(async () => {
|
|
|
50
50
|
value: BOOTSTRAP_VALUE,
|
|
51
51
|
isSecret: true,
|
|
52
52
|
});
|
|
53
|
+
await upsertSwarmConfig({
|
|
54
|
+
scope: "global",
|
|
55
|
+
key: "SLACK_SIGNING_SECRET",
|
|
56
|
+
value: "synthetic-api-only-signing-secret",
|
|
57
|
+
isSecret: true,
|
|
58
|
+
});
|
|
53
59
|
await upsertSwarmConfig({
|
|
54
60
|
scope: "global",
|
|
55
61
|
key: "SOME_WORKER_SECRET",
|
|
@@ -86,6 +92,7 @@ describe("API-only config keys are never served over HTTP", () => {
|
|
|
86
92
|
test("GET /api/config/resolved WITHOUT agentId strips the bootstrap key", async () => {
|
|
87
93
|
const { configs } = await getJson("/api/config/resolved?includeSecrets=true");
|
|
88
94
|
expect(configs.some((c) => c.key === BOOTSTRAP_KEY)).toBe(false);
|
|
95
|
+
expect(configs.some((c) => c.key === "SLACK_SIGNING_SECRET")).toBe(false);
|
|
89
96
|
// A legitimate worker secret still comes through.
|
|
90
97
|
expect(configs.some((c) => c.key === "SOME_WORKER_SECRET")).toBe(true);
|
|
91
98
|
});
|
|
@@ -95,11 +102,13 @@ describe("API-only config keys are never served over HTTP", () => {
|
|
|
95
102
|
`/api/config/resolved?includeSecrets=true&agentId=${agentId}`,
|
|
96
103
|
);
|
|
97
104
|
expect(configs.some((c) => c.key === BOOTSTRAP_KEY)).toBe(false);
|
|
105
|
+
expect(configs.some((c) => c.key === "SLACK_SIGNING_SECRET")).toBe(false);
|
|
98
106
|
});
|
|
99
107
|
|
|
100
108
|
test("GET /api/config?scope=global strips the bootstrap key", async () => {
|
|
101
109
|
const { configs } = await getJson("/api/config?scope=global&includeSecrets=true");
|
|
102
110
|
expect(configs.some((c) => c.key === BOOTSTRAP_KEY)).toBe(false);
|
|
111
|
+
expect(configs.some((c) => c.key === "SLACK_SIGNING_SECRET")).toBe(false);
|
|
103
112
|
expect(configs.some((c) => c.key === "SOME_WORKER_SECRET")).toBe(true);
|
|
104
113
|
});
|
|
105
114
|
});
|
|
@@ -18,6 +18,7 @@ describe("getContextWindowSize", () => {
|
|
|
18
18
|
});
|
|
19
19
|
|
|
20
20
|
test("returns 1M for opus models", () => {
|
|
21
|
+
expect(getContextWindowSize("claude-opus-5-5")).toBe(1_000_000);
|
|
21
22
|
expect(getContextWindowSize("claude-opus-5")).toBe(1_000_000);
|
|
22
23
|
expect(getContextWindowSize("claude-opus-4-8")).toBe(1_000_000);
|
|
23
24
|
expect(getContextWindowSize("claude-opus-4-7")).toBe(1_000_000);
|
|
@@ -94,8 +94,12 @@ type:UpsertAssetKeyMappingInput
|
|
|
94
94
|
type:WorkflowFilters
|
|
95
95
|
type:WorkflowRunListOptions
|
|
96
96
|
type:WorkflowRunPage
|
|
97
|
+
value:EXTENSION_AGENT_ROLE
|
|
98
|
+
value:ExtensionAgentAssignmentError
|
|
97
99
|
value:KvTypeCollisionError
|
|
98
100
|
value:MAX_EMPTY_POLLS
|
|
101
|
+
value:NOT_EXTENSION_AGENT_SQL
|
|
102
|
+
value:ReservedAgentRoleError
|
|
99
103
|
value:SKILL_FILE_LIMITS
|
|
100
104
|
value:UNATTRIBUTED_USER_ID
|
|
101
105
|
value:__resetSqliteVecExtensionPathCacheForTests
|
|
@@ -195,6 +199,7 @@ value:emitBuiltInIntegrationConnectedOnce
|
|
|
195
199
|
value:emitMcpServerConnectedTelemetry
|
|
196
200
|
value:ensureSlackDelegationActivation
|
|
197
201
|
value:ensureSlackRenderV2Activation
|
|
202
|
+
value:extensionAgentAssignmentError
|
|
198
203
|
value:failPendingResumeIfUnclaimed
|
|
199
204
|
value:failTask
|
|
200
205
|
value:findCompletedTaskInThread
|
|
@@ -326,6 +331,7 @@ value:getRecentFailedTasks
|
|
|
326
331
|
value:getRecentlyCancelledTasksForAgent
|
|
327
332
|
value:getRecentlyFinishedWorkerTasks
|
|
328
333
|
value:getRemainingCapacity
|
|
334
|
+
value:getResolvableDeferralOutcomes
|
|
329
335
|
value:getResolvedConfig
|
|
330
336
|
value:getRetryableSteps
|
|
331
337
|
value:getRootTaskChain
|
|
@@ -418,6 +424,7 @@ value:installMcpServer
|
|
|
418
424
|
value:installSkill
|
|
419
425
|
value:installSystemDefaultSkillsForAgent
|
|
420
426
|
value:isAgentEligibleForTask
|
|
427
|
+
value:isExtensionAgent
|
|
421
428
|
value:isPendingSlackMessage
|
|
422
429
|
value:isPoolAffinityEnforcementEnabled
|
|
423
430
|
value:isSqliteVecAvailable
|
|
@@ -449,6 +456,7 @@ value:markInboxMessageDelegated
|
|
|
449
456
|
value:markInboxMessageRead
|
|
450
457
|
value:markInboxMessageResponded
|
|
451
458
|
value:markKeyRateLimited
|
|
459
|
+
value:markSlackDeferralResolved
|
|
452
460
|
value:markSlackRelayAttempted
|
|
453
461
|
value:markSlackRelayDelivered
|
|
454
462
|
value:markSlackTreeRendered
|
|
@@ -485,6 +493,7 @@ value:releaseStaleReviewingTasks
|
|
|
485
493
|
value:releaseTask
|
|
486
494
|
value:replaceTaskAttachment
|
|
487
495
|
value:reserveSlackMessage
|
|
496
|
+
value:reservedRoleViolation
|
|
488
497
|
value:resetEmptyPollCount
|
|
489
498
|
value:resetOrphanedInProgressTasksForAgent
|
|
490
499
|
value:resetPromptTemplateToDefault
|