@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/commands/runner.ts
CHANGED
|
@@ -610,7 +610,11 @@ async function pollAndStartSteeringDispatches(
|
|
|
610
610
|
prepared.push(item);
|
|
611
611
|
item.text =
|
|
612
612
|
session.deliverSteering && !state.outcomes.has(message.id)
|
|
613
|
-
? await renderSteeringDelivery(
|
|
613
|
+
? await renderSteeringDelivery(
|
|
614
|
+
message.id,
|
|
615
|
+
message.body,
|
|
616
|
+
message.senderLabel ?? message.createdByKind,
|
|
617
|
+
)
|
|
614
618
|
: undefined;
|
|
615
619
|
}
|
|
616
620
|
} catch (error) {
|
|
@@ -765,6 +769,9 @@ export interface ResolvedEnvResult {
|
|
|
765
769
|
resolvedProvider: ProviderName;
|
|
766
770
|
}
|
|
767
771
|
|
|
772
|
+
// Preserve the deployment value before config reloads mutate process.env.
|
|
773
|
+
const deploymentMemoryRaters = process.env.MEMORY_RATERS;
|
|
774
|
+
|
|
768
775
|
export async function fetchResolvedEnv(
|
|
769
776
|
apiUrl: string,
|
|
770
777
|
apiKey: string,
|
|
@@ -792,6 +799,12 @@ export async function fetchResolvedEnv(
|
|
|
792
799
|
configs: Array<{ key: string; value: string }>;
|
|
793
800
|
};
|
|
794
801
|
|
|
802
|
+
// A deleted row restores the deployment value (including unset), while
|
|
803
|
+
// an explicit empty row below remains the disable-all-raters override.
|
|
804
|
+
// Only reset after a successful fetch so outages retain the current value.
|
|
805
|
+
env.MEMORY_RATERS =
|
|
806
|
+
baseEnv === process.env ? deploymentMemoryRaters : baseEnv.MEMORY_RATERS;
|
|
807
|
+
|
|
795
808
|
if (data.configs?.length) {
|
|
796
809
|
scriptsOnlyConfigValue = data.configs.find(
|
|
797
810
|
(config) => config.key === "SCRIPTS_ONLY_MCP",
|
|
@@ -1060,7 +1073,10 @@ export function applyResolvedEnvToProcessEnv(
|
|
|
1060
1073
|
const changed: string[] = [];
|
|
1061
1074
|
for (const key of RELOADABLE_ENV_KEYS) {
|
|
1062
1075
|
const next = freshEnv[key];
|
|
1063
|
-
if (
|
|
1076
|
+
if (key === "MEMORY_RATERS" && next === undefined && process.env[key] !== undefined) {
|
|
1077
|
+
delete process.env[key];
|
|
1078
|
+
changed.push(key);
|
|
1079
|
+
} else if (next !== undefined && next !== process.env[key]) {
|
|
1064
1080
|
const previous = process.env[key];
|
|
1065
1081
|
process.env[key] = next;
|
|
1066
1082
|
changed.push(key);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createAgent,
|
|
3
|
+
EXTENSION_AGENT_ROLE,
|
|
3
4
|
getAllAgents,
|
|
4
5
|
getDbClient,
|
|
5
6
|
updateAgentMaxTasks,
|
|
@@ -14,7 +15,9 @@ function extensionAgentName(name: string): string {
|
|
|
14
15
|
export async function ensureExtensionAgent(name: string): Promise<string> {
|
|
15
16
|
const agentName = extensionAgentName(name);
|
|
16
17
|
return await getDbClient().transaction(async () => {
|
|
17
|
-
let agent = (await getAllAgents()).find(
|
|
18
|
+
let agent = (await getAllAgents({ includeExtensions: true })).find(
|
|
19
|
+
(candidate) => candidate.name === agentName,
|
|
20
|
+
);
|
|
18
21
|
if (!agent) {
|
|
19
22
|
agent = await createAgent({
|
|
20
23
|
name: agentName,
|
|
@@ -27,17 +30,22 @@ export async function ensureExtensionAgent(name: string): Promise<string> {
|
|
|
27
30
|
|
|
28
31
|
if (agent.status !== "offline") await updateAgentStatus(agent.id, "offline");
|
|
29
32
|
if (agent.maxTasks !== 0) await updateAgentMaxTasks(agent.id, 0);
|
|
30
|
-
await updateAgentProfile(
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
await updateAgentProfile(
|
|
34
|
+
agent.id,
|
|
35
|
+
{
|
|
36
|
+
description: `System agent for extension ${name}`,
|
|
37
|
+
role: EXTENSION_AGENT_ROLE,
|
|
38
|
+
capabilities: [],
|
|
39
|
+
},
|
|
40
|
+
undefined,
|
|
41
|
+
{ allowExtensionRole: true },
|
|
42
|
+
);
|
|
35
43
|
return agent.id;
|
|
36
44
|
});
|
|
37
45
|
}
|
|
38
46
|
|
|
39
47
|
export async function deactivateExtensionAgent(name: string): Promise<void> {
|
|
40
|
-
const agent = (await getAllAgents()).find(
|
|
48
|
+
const agent = (await getAllAgents({ includeExtensions: true })).find(
|
|
41
49
|
(candidate) => candidate.name === extensionAgentName(name),
|
|
42
50
|
);
|
|
43
51
|
if (agent && agent.status !== "offline") await updateAgentStatus(agent.id, "offline");
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
activateExtensionVersionSnapshot,
|
|
3
3
|
getExtensionById,
|
|
4
|
-
getExtensionFiles,
|
|
5
4
|
getExtensionVersion,
|
|
6
5
|
insertExtensionRun,
|
|
7
6
|
listExtensions,
|
|
@@ -66,28 +65,23 @@ function maxUpdatedAt(extensions: Extension[]): string {
|
|
|
66
65
|
}
|
|
67
66
|
|
|
68
67
|
async function loadableExtension(record: Extension): Promise<LoadableExtension> {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
return {
|
|
78
|
-
record: {
|
|
79
|
-
...record,
|
|
80
|
-
manifestJson: version.manifestJson,
|
|
81
|
-
contentHash: version.contentHash,
|
|
82
|
-
},
|
|
83
|
-
manifest: ExtensionManifestSchema.parse(JSON.parse(version.manifestJson)),
|
|
84
|
-
files: JSON.parse(version.filesJson) as Record<string, string>,
|
|
85
|
-
};
|
|
68
|
+
// Always load the immutable selected version. The mutable current-file projection
|
|
69
|
+
// can advance while an agent stages a draft after we read the extension row.
|
|
70
|
+
const version = await getExtensionVersion(record.id, record.activeVersion);
|
|
71
|
+
if (!version) {
|
|
72
|
+
throw new ExtensionLifecycleError(
|
|
73
|
+
`Extension version ${record.activeVersion} was not found`,
|
|
74
|
+
404,
|
|
75
|
+
);
|
|
86
76
|
}
|
|
87
77
|
return {
|
|
88
|
-
record
|
|
89
|
-
|
|
90
|
-
|
|
78
|
+
record: {
|
|
79
|
+
...record,
|
|
80
|
+
manifestJson: version.manifestJson,
|
|
81
|
+
contentHash: version.contentHash,
|
|
82
|
+
},
|
|
83
|
+
manifest: ExtensionManifestSchema.parse(JSON.parse(version.manifestJson)),
|
|
84
|
+
files: JSON.parse(version.filesJson) as Record<string, string>,
|
|
91
85
|
};
|
|
92
86
|
}
|
|
93
87
|
|
package/src/hooks/codex-hook.ts
CHANGED
|
@@ -100,7 +100,13 @@ export async function collectDeliverableSteering(
|
|
|
100
100
|
} catch {
|
|
101
101
|
continue;
|
|
102
102
|
}
|
|
103
|
-
delivered.push(
|
|
103
|
+
delivered.push(
|
|
104
|
+
await renderSteeringDelivery(
|
|
105
|
+
message.id,
|
|
106
|
+
message.body,
|
|
107
|
+
message.senderLabel ?? message.createdByKind,
|
|
108
|
+
),
|
|
109
|
+
);
|
|
104
110
|
}
|
|
105
111
|
return delivered;
|
|
106
112
|
}
|
package/src/http/agents.ts
CHANGED
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
computeContentHash,
|
|
6
6
|
createAgent,
|
|
7
7
|
deleteSwarmConfigByKey,
|
|
8
|
+
EXTENSION_AGENT_ROLE,
|
|
8
9
|
getAgentById,
|
|
9
10
|
getAgentWithTasks,
|
|
10
11
|
getAllAgents,
|
|
@@ -12,6 +13,7 @@ import {
|
|
|
12
13
|
getDbClient,
|
|
13
14
|
getResolvedConfig,
|
|
14
15
|
getSwarmConfigs,
|
|
16
|
+
ReservedAgentRoleError,
|
|
15
17
|
resetEmptyPollCount,
|
|
16
18
|
setAgentHarnessProvider,
|
|
17
19
|
updateAgentActivity,
|
|
@@ -181,7 +183,7 @@ const setAgentHarnessProviderRoute = route({
|
|
|
181
183
|
},
|
|
182
184
|
});
|
|
183
185
|
|
|
184
|
-
const LocalHarnessProviderSchema = z.enum(["claude", "codex", "pi", "opencode", "acp"]);
|
|
186
|
+
const LocalHarnessProviderSchema = z.enum(["claude", "codex", "pi", "opencode", "acp", "dsh"]);
|
|
185
187
|
const AcpRuntimeConfigSchema = z
|
|
186
188
|
.object({
|
|
187
189
|
target: z.enum(ACP_TARGET_IDS),
|
|
@@ -339,7 +341,7 @@ const listAgents = route({
|
|
|
339
341
|
pattern: ["api", "agents"],
|
|
340
342
|
summary: "List all agents",
|
|
341
343
|
description:
|
|
342
|
-
"Returns agents WITHOUT the six identity-markdown blobs (`claudeMd`/`soulMd`/`identityMd`/`toolsMd`/`heartbeatMd`/`setupScript`) by default — they bloat the list by ~16 KB/agent and the overview never renders them. Pass `fields=full` to restore them, or fetch a single agent via `GET /api/agents/{id}`.",
|
|
344
|
+
"Returns agents WITHOUT the six identity-markdown blobs (`claudeMd`/`soulMd`/`identityMd`/`toolsMd`/`heartbeatMd`/`setupScript`) by default — they bloat the list by ~16 KB/agent and the overview never renders them. Pass `fields=full` to restore them, or fetch a single agent via `GET /api/agents/{id}`. Extension identities (`ext:<name>`, role `extension`) are never listed; they are API principals, not workers.",
|
|
343
345
|
tags: ["Agents"],
|
|
344
346
|
query: z.object({
|
|
345
347
|
include: z.enum(["tasks"]).optional(),
|
|
@@ -578,6 +580,12 @@ export async function handleAgentRegister(
|
|
|
578
580
|
const parsed = await registerAgent.parse(req, res, pathSegments, new URLSearchParams());
|
|
579
581
|
if (!parsed) return true;
|
|
580
582
|
|
|
583
|
+
// Extension identities are created only by the extension lifecycle.
|
|
584
|
+
if (parsed.body.role === EXTENSION_AGENT_ROLE) {
|
|
585
|
+
jsonError(res, `Role "${EXTENSION_AGENT_ROLE}" is reserved for extension identities.`, 400);
|
|
586
|
+
return true;
|
|
587
|
+
}
|
|
588
|
+
|
|
581
589
|
const agentId = myAgentId || crypto.randomUUID();
|
|
582
590
|
// Read once so one mode applies consistently even if a config reload
|
|
583
591
|
// flips the flag mid-request.
|
|
@@ -877,7 +885,7 @@ export async function handleAgentsRest(
|
|
|
877
885
|
},
|
|
878
886
|
);
|
|
879
887
|
} catch (error) {
|
|
880
|
-
if (error instanceof HeartbeatExpiryError) {
|
|
888
|
+
if (error instanceof HeartbeatExpiryError || error instanceof ReservedAgentRoleError) {
|
|
881
889
|
jsonError(res, error.message, 400);
|
|
882
890
|
return true;
|
|
883
891
|
}
|
|
@@ -1046,7 +1054,7 @@ export async function handleAgentsRest(
|
|
|
1046
1054
|
})
|
|
1047
1055
|
)[0]?.value ?? "");
|
|
1048
1056
|
const allowedLevels =
|
|
1049
|
-
harness_provider === "acp"
|
|
1057
|
+
harness_provider === "acp" || harness_provider === "dsh"
|
|
1050
1058
|
? []
|
|
1051
1059
|
: reasoningCapability(harness_provider, modelForValidation ?? "").levels;
|
|
1052
1060
|
if (!allowedLevels.includes(reasoning_effort)) {
|
package/src/http/config.ts
CHANGED
|
@@ -37,7 +37,7 @@ const SECRETS_FORCE_MASK_NOTE =
|
|
|
37
37
|
// bootstrap admin key would let a compromised worker administer the shared org.
|
|
38
38
|
// The API materializes these into its own process.env at boot via
|
|
39
39
|
// getInjectableGlobalConfigs, so no HTTP consumer legitimately needs them.
|
|
40
|
-
const API_ONLY_CONFIG_KEYS = new Set(["API_AGENT_FS_API_KEY"]);
|
|
40
|
+
const API_ONLY_CONFIG_KEYS = new Set(["API_AGENT_FS_API_KEY", "SLACK_SIGNING_SECRET"]);
|
|
41
41
|
|
|
42
42
|
function stripApiOnlyKeys<T extends { key: string }>(configs: T[]): T[] {
|
|
43
43
|
return configs.filter((config) => !API_ONLY_CONFIG_KEYS.has(config.key));
|
package/src/http/core.ts
CHANGED
|
@@ -150,6 +150,10 @@ async function reloadGlobalConfigsAndIntegrationsInner(): Promise<ReloadConfigRe
|
|
|
150
150
|
|
|
151
151
|
const updated = await loadGlobalConfigsIntoEnv(true);
|
|
152
152
|
|
|
153
|
+
// Retire the old transport before any other integration can fail during
|
|
154
|
+
// reload. HTTP/invalid selection must never leave the previous socket live.
|
|
155
|
+
await stopSlackApp();
|
|
156
|
+
|
|
153
157
|
// File-storage provider selection reads process.env once and memoizes; the
|
|
154
158
|
// env we just (re)hydrated may flip it (local-fs → agent-fs after late
|
|
155
159
|
// provisioning, or a rotated bootstrap key). Reset so the next fs request
|
|
@@ -206,9 +210,7 @@ async function reloadGlobalConfigsAndIntegrationsInner(): Promise<ReloadConfigRe
|
|
|
206
210
|
resetJira();
|
|
207
211
|
if (await initJira()) integrations.push("jira");
|
|
208
212
|
|
|
209
|
-
await
|
|
210
|
-
await startSlackApp();
|
|
211
|
-
integrations.push("slack");
|
|
213
|
+
if (await startSlackApp()) integrations.push("slack");
|
|
212
214
|
|
|
213
215
|
return {
|
|
214
216
|
configsLoaded: updated.length,
|
package/src/http/extensions.ts
CHANGED
|
@@ -4,9 +4,11 @@ import { resolveHttpAuditUserId } from "../be/audit-user";
|
|
|
4
4
|
import { getAgentById } from "../be/db";
|
|
5
5
|
import {
|
|
6
6
|
deleteExtension,
|
|
7
|
+
ExtensionOwnershipError,
|
|
7
8
|
getExtensionById,
|
|
8
9
|
getExtensionByName,
|
|
9
10
|
getExtensionFiles,
|
|
11
|
+
type InstallExtensionResult,
|
|
10
12
|
installExtension,
|
|
11
13
|
listExtensionRuns,
|
|
12
14
|
listExtensions,
|
|
@@ -23,8 +25,15 @@ import {
|
|
|
23
25
|
reloadExtension,
|
|
24
26
|
} from "../extensions/lifecycle";
|
|
25
27
|
import { ExtensionConfigError } from "../extensions/loader";
|
|
26
|
-
import { can, type PermissionVerb, type RbacPrincipal } from "../rbac";
|
|
27
28
|
import {
|
|
29
|
+
can,
|
|
30
|
+
LEGACY_RULES,
|
|
31
|
+
type PermissionVerb,
|
|
32
|
+
type RbacPrincipal,
|
|
33
|
+
type RbacResource,
|
|
34
|
+
} from "../rbac";
|
|
35
|
+
import {
|
|
36
|
+
type Extension,
|
|
28
37
|
ExtensionInstallBodySchema,
|
|
29
38
|
ExtensionManifestSchema,
|
|
30
39
|
ExtensionRunSchema,
|
|
@@ -44,6 +53,8 @@ const installRoute = route({
|
|
|
44
53
|
pattern: ["api", "extensions", "install"],
|
|
45
54
|
operationId: "extensions_install",
|
|
46
55
|
summary: "Validate and install an extension bundle",
|
|
56
|
+
description:
|
|
57
|
+
"Any authenticated agent can install a disabled draft owned by its agent ID. Workers can update only their own bundles; activation remains lead/operator-only.",
|
|
47
58
|
tags: ["Extensions"],
|
|
48
59
|
body: ExtensionInstallBodySchema,
|
|
49
60
|
responses: {
|
|
@@ -153,6 +164,8 @@ const patchRoute = route({
|
|
|
153
164
|
pattern: ["api", "extensions", null],
|
|
154
165
|
operationId: "extensions_update",
|
|
155
166
|
summary: "Update extension priority, config, or description",
|
|
167
|
+
description:
|
|
168
|
+
"Workers may edit their own disabled extensions. Leads, operators, and dashboard users retain access to all extensions.",
|
|
156
169
|
tags: ["Extensions"],
|
|
157
170
|
params: idParamsSchema,
|
|
158
171
|
body: z
|
|
@@ -177,6 +190,8 @@ const deleteRoute = route({
|
|
|
177
190
|
pattern: ["api", "extensions", null],
|
|
178
191
|
operationId: "extensions_delete",
|
|
179
192
|
summary: "Uninstall a disabled extension",
|
|
193
|
+
description:
|
|
194
|
+
"Workers may uninstall their own disabled extensions. Leads, operators, and dashboard users retain access to all extensions.",
|
|
180
195
|
tags: ["Extensions"],
|
|
181
196
|
params: idParamsSchema,
|
|
182
197
|
responses: {
|
|
@@ -316,14 +331,23 @@ async function extensionPrincipal(
|
|
|
316
331
|
return { kind: "agent", agentId: "", isLead: false };
|
|
317
332
|
}
|
|
318
333
|
|
|
334
|
+
/** Restrict ordinary agents; retain the existing blanket access for elevated writers. */
|
|
335
|
+
function ownerOnlyAgentId(principal: RbacPrincipal): string | undefined {
|
|
336
|
+
return principal.kind === "agent" &&
|
|
337
|
+
!LEGACY_RULES["lead-or-operator-or-user"].evaluate(principal, undefined)
|
|
338
|
+
? principal.agentId
|
|
339
|
+
: undefined;
|
|
340
|
+
}
|
|
341
|
+
|
|
319
342
|
async function requirePermission(
|
|
320
343
|
req: IncomingMessage,
|
|
321
344
|
res: ServerResponse,
|
|
322
345
|
callerAgentId: string | undefined,
|
|
323
346
|
verb: Extract<PermissionVerb, "extension.write" | "extension.activate">,
|
|
347
|
+
resource: RbacResource = { kind: "none" },
|
|
324
348
|
): Promise<RbacPrincipal | null> {
|
|
325
349
|
const principal = await extensionPrincipal(req, callerAgentId);
|
|
326
|
-
const decision = can({ principal, verb, resource
|
|
350
|
+
const decision = can({ principal, verb, resource, source: "http" });
|
|
327
351
|
if (decision.allow) return principal;
|
|
328
352
|
jsonError(res, `Forbidden: ${decision.reason}`, 403);
|
|
329
353
|
return null;
|
|
@@ -331,11 +355,13 @@ async function requirePermission(
|
|
|
331
355
|
|
|
332
356
|
function respondLifecycleError(res: ServerResponse, error: unknown): void {
|
|
333
357
|
const status =
|
|
334
|
-
error instanceof
|
|
335
|
-
?
|
|
336
|
-
: error instanceof
|
|
337
|
-
?
|
|
338
|
-
:
|
|
358
|
+
error instanceof ExtensionOwnershipError
|
|
359
|
+
? 403
|
|
360
|
+
: error instanceof ExtensionConfigError
|
|
361
|
+
? 400
|
|
362
|
+
: error instanceof ExtensionLifecycleError
|
|
363
|
+
? error.status
|
|
364
|
+
: 500;
|
|
339
365
|
jsonError(res, scrubSecrets(error instanceof Error ? error.message : String(error)), status);
|
|
340
366
|
}
|
|
341
367
|
|
|
@@ -349,26 +375,41 @@ export async function handleExtensions(
|
|
|
349
375
|
if (installRoute.match(req.method, pathSegments)) {
|
|
350
376
|
const parsed = await installRoute.parse(req, res, pathSegments, queryParams);
|
|
351
377
|
if (!parsed) return true;
|
|
352
|
-
const
|
|
378
|
+
const existing = await getExtensionByName(parsed.body.manifest.name);
|
|
379
|
+
const principal = await requirePermission(req, res, agentId, "extension.write", {
|
|
380
|
+
kind: "extension",
|
|
381
|
+
extensionId: existing?.id,
|
|
382
|
+
createdByAgentId: existing?.createdByAgentId,
|
|
383
|
+
});
|
|
353
384
|
if (!principal) return true;
|
|
354
385
|
const validation = await validateBundle(parsed.body);
|
|
355
386
|
if (!validation.ok) {
|
|
356
387
|
json(res, { error: "extension_validation_failed", diagnostics: validation.diagnostics }, 400);
|
|
357
388
|
return true;
|
|
358
389
|
}
|
|
359
|
-
const existing = await getExtensionByName(validation.manifest.name);
|
|
360
390
|
const writerAgentId = principal.kind === "agent" ? principal.agentId : undefined;
|
|
361
391
|
const updatedBy = await resolveHttpAuditUserId(req, writerAgentId);
|
|
362
392
|
const shouldActivate = principal.kind !== "agent" && existing?.enabled === true;
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
393
|
+
// Enforce ownership again inside the upsert transaction: validation can yield
|
|
394
|
+
// while another agent installs the same name.
|
|
395
|
+
const ownerOnly = ownerOnlyAgentId(principal);
|
|
396
|
+
let result: InstallExtensionResult;
|
|
397
|
+
try {
|
|
398
|
+
result = await installExtension({
|
|
399
|
+
manifest: validation.manifest,
|
|
400
|
+
files: parsed.body.files,
|
|
401
|
+
priority: parsed.body.priority,
|
|
402
|
+
config: parsed.body.config,
|
|
403
|
+
agentId: writerAgentId,
|
|
404
|
+
createdBy: updatedBy,
|
|
405
|
+
activate: shouldActivate,
|
|
406
|
+
ownerOnly,
|
|
407
|
+
});
|
|
408
|
+
} catch (error) {
|
|
409
|
+
if (!(error instanceof ExtensionOwnershipError)) throw error;
|
|
410
|
+
jsonError(res, error.message, 403);
|
|
411
|
+
return true;
|
|
412
|
+
}
|
|
372
413
|
let extension = result.extension;
|
|
373
414
|
if (shouldActivate) {
|
|
374
415
|
try {
|
|
@@ -446,19 +487,42 @@ export async function handleExtensions(
|
|
|
446
487
|
if (patchRoute.match(req.method, pathSegments)) {
|
|
447
488
|
const parsed = await patchRoute.parse(req, res, pathSegments, queryParams);
|
|
448
489
|
if (!parsed) return true;
|
|
449
|
-
const
|
|
490
|
+
const stored = await getExtensionById(parsed.params.id);
|
|
491
|
+
if (!stored) {
|
|
492
|
+
jsonError(res, "Extension not found", 404);
|
|
493
|
+
return true;
|
|
494
|
+
}
|
|
495
|
+
const principal = await requirePermission(req, res, agentId, "extension.write", {
|
|
496
|
+
kind: "extension",
|
|
497
|
+
extensionId: stored.id,
|
|
498
|
+
createdByAgentId: stored.createdByAgentId,
|
|
499
|
+
});
|
|
450
500
|
if (!principal) return true;
|
|
451
501
|
const writerAgentId = principal.kind === "agent" ? principal.agentId : undefined;
|
|
452
|
-
const
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
502
|
+
const ownerOnly = ownerOnlyAgentId(principal);
|
|
503
|
+
// PATCH reloads live code; ordinary owners may edit only disabled drafts.
|
|
504
|
+
if (
|
|
505
|
+
stored.enabled &&
|
|
506
|
+
ownerOnly &&
|
|
507
|
+
!(await requirePermission(req, res, agentId, "extension.activate"))
|
|
508
|
+
) {
|
|
509
|
+
return true;
|
|
510
|
+
}
|
|
511
|
+
const config = parsed.body.config
|
|
512
|
+
? preserveRedactedConfigValues(parsed.body.config, stored.configJson)
|
|
513
|
+
: parsed.body.config;
|
|
514
|
+
let extension: Extension | null;
|
|
515
|
+
try {
|
|
516
|
+
extension = await updateExtensionMeta(parsed.params.id, {
|
|
517
|
+
...parsed.body,
|
|
518
|
+
...(config === undefined ? {} : { config }),
|
|
519
|
+
updatedBy: await resolveHttpAuditUserId(req, writerAgentId),
|
|
520
|
+
ownerOnly,
|
|
521
|
+
});
|
|
522
|
+
} catch (error) {
|
|
523
|
+
respondLifecycleError(res, error);
|
|
524
|
+
return true;
|
|
525
|
+
}
|
|
462
526
|
if (!extension) {
|
|
463
527
|
jsonError(res, "Extension not found", 404);
|
|
464
528
|
return true;
|
|
@@ -478,18 +542,28 @@ export async function handleExtensions(
|
|
|
478
542
|
if (deleteRoute.match(req.method, pathSegments)) {
|
|
479
543
|
const parsed = await deleteRoute.parse(req, res, pathSegments, queryParams);
|
|
480
544
|
if (!parsed) return true;
|
|
481
|
-
const
|
|
482
|
-
if (!
|
|
483
|
-
const extension = await getExtensionById(parsed.params.id);
|
|
484
|
-
if (!extension) {
|
|
545
|
+
const stored = await getExtensionById(parsed.params.id);
|
|
546
|
+
if (!stored) {
|
|
485
547
|
jsonError(res, "Extension not found", 404);
|
|
486
548
|
return true;
|
|
487
549
|
}
|
|
550
|
+
const principal = await requirePermission(req, res, agentId, "extension.write", {
|
|
551
|
+
kind: "extension",
|
|
552
|
+
extensionId: stored.id,
|
|
553
|
+
createdByAgentId: stored.createdByAgentId,
|
|
554
|
+
});
|
|
555
|
+
if (!principal) return true;
|
|
556
|
+
const extension = stored;
|
|
488
557
|
if (extension.enabled) {
|
|
489
558
|
jsonError(res, "Disable the extension before uninstalling it", 409);
|
|
490
559
|
return true;
|
|
491
560
|
}
|
|
492
|
-
|
|
561
|
+
try {
|
|
562
|
+
await deleteExtension(extension.id, ownerOnlyAgentId(principal));
|
|
563
|
+
} catch (error) {
|
|
564
|
+
respondLifecycleError(res, error);
|
|
565
|
+
return true;
|
|
566
|
+
}
|
|
493
567
|
deleteRoute.respond(res, 200, { deleted: true });
|
|
494
568
|
return true;
|
|
495
569
|
}
|
package/src/http/poll.ts
CHANGED
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
getUnassignedTaskIdsForAgent,
|
|
22
22
|
getUserById,
|
|
23
23
|
hasCapacity,
|
|
24
|
+
isExtensionAgent,
|
|
24
25
|
recordBudgetRefusalNotification,
|
|
25
26
|
startTask,
|
|
26
27
|
updateAgentStatusFromCapacity,
|
|
@@ -310,6 +311,12 @@ export async function handlePoll(
|
|
|
310
311
|
return { error: "Agent not found", status: 404 };
|
|
311
312
|
}
|
|
312
313
|
|
|
314
|
+
// Extension identities authenticate to the API but never execute
|
|
315
|
+
// work: no offers, no pending assignments, no pool claims.
|
|
316
|
+
if (isExtensionAgent(agent)) {
|
|
317
|
+
return { trigger: null };
|
|
318
|
+
}
|
|
319
|
+
|
|
313
320
|
// A process whose runtime has been retired must not be handed work: it
|
|
314
321
|
// would execute alongside whatever replaced it. Dispatch is gated on a
|
|
315
322
|
// live runtime identity rather than on X-Agent-ID alone, which only
|
package/src/http/schedules.ts
CHANGED
|
@@ -6,11 +6,13 @@ import { resolveHttpAuditUserId } from "../be/audit-user";
|
|
|
6
6
|
import {
|
|
7
7
|
createScheduledTask,
|
|
8
8
|
deleteScheduledTask,
|
|
9
|
+
extensionAgentAssignmentError,
|
|
9
10
|
getAgentById,
|
|
10
11
|
getScheduledTaskById,
|
|
11
12
|
getScheduledTaskByName,
|
|
12
13
|
getScheduledTasks,
|
|
13
14
|
getWorkflow,
|
|
15
|
+
isExtensionAgent,
|
|
14
16
|
updateScheduledTask,
|
|
15
17
|
withFavoriteFlags,
|
|
16
18
|
} from "../be/db";
|
|
@@ -355,6 +357,10 @@ export async function handleSchedules(
|
|
|
355
357
|
jsonError(res, "Target agent not found", 400);
|
|
356
358
|
return true;
|
|
357
359
|
}
|
|
360
|
+
if (isExtensionAgent(agent)) {
|
|
361
|
+
jsonError(res, extensionAgentAssignmentError(agent), 400);
|
|
362
|
+
return true;
|
|
363
|
+
}
|
|
358
364
|
}
|
|
359
365
|
|
|
360
366
|
const targetType = body.targetType ?? "agent-task";
|
|
@@ -583,6 +589,10 @@ export async function handleSchedules(
|
|
|
583
589
|
jsonError(res, "Target agent not found", 400);
|
|
584
590
|
return true;
|
|
585
591
|
}
|
|
592
|
+
if (isExtensionAgent(agent)) {
|
|
593
|
+
jsonError(res, extensionAgentAssignmentError(agent), 400);
|
|
594
|
+
return true;
|
|
595
|
+
}
|
|
586
596
|
}
|
|
587
597
|
|
|
588
598
|
// Cross-field targetType validation — merge patch over existing, then
|
package/src/http/script-runs.ts
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
countScriptRuns,
|
|
8
8
|
createScriptRun,
|
|
9
9
|
createTaskExtended,
|
|
10
|
+
ExtensionAgentAssignmentError,
|
|
10
11
|
getAgentById,
|
|
11
12
|
getDbClient,
|
|
12
13
|
getLatestScriptRunStepTaskByContextKey,
|
|
@@ -618,32 +619,40 @@ export async function handleScriptRuns(
|
|
|
618
619
|
const contextKey = `script-run:${run.id}:${parsed.body.stepKey}`;
|
|
619
620
|
let task = await getLatestScriptRunStepTaskByContextKey(contextKey);
|
|
620
621
|
if (!task) {
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
622
|
+
try {
|
|
623
|
+
task = await createTaskExtended(
|
|
624
|
+
parsed.body.template ?? parsed.body.task ?? parsed.body.stepKey,
|
|
625
|
+
{
|
|
626
|
+
agentId: parsed.body.agentId,
|
|
627
|
+
routingReason:
|
|
628
|
+
parsed.body.routingReason ?? (parsed.body.agentId ? "human_pinned" : undefined),
|
|
629
|
+
routingNote: parsed.body.routingNote,
|
|
630
|
+
routingSource: parsed.body.routingReason
|
|
631
|
+
? "declared"
|
|
632
|
+
: parsed.body.agentId
|
|
633
|
+
? "engine_default"
|
|
634
|
+
: undefined,
|
|
635
|
+
tags: parsed.body.tags,
|
|
636
|
+
priority: parsed.body.priority,
|
|
637
|
+
offeredTo: parsed.body.offerMode ? parsed.body.agentId : undefined,
|
|
638
|
+
taskType: "script-run-step",
|
|
639
|
+
source: "mcp",
|
|
640
|
+
dir: parsed.body.dir,
|
|
641
|
+
vcsRepo: parsed.body.vcsRepo,
|
|
642
|
+
model: parsed.body.model,
|
|
643
|
+
parentTaskId: parsed.body.parentTaskId,
|
|
644
|
+
requestedByUserId: parsed.body.requestedByUserId ?? run.requestedByUserId,
|
|
645
|
+
outputSchema: parsed.body.outputSchema,
|
|
646
|
+
contextKey,
|
|
647
|
+
},
|
|
648
|
+
);
|
|
649
|
+
} catch (error) {
|
|
650
|
+
if (error instanceof ExtensionAgentAssignmentError) {
|
|
651
|
+
jsonError(res, error.message, 400);
|
|
652
|
+
return true;
|
|
653
|
+
}
|
|
654
|
+
throw error;
|
|
655
|
+
}
|
|
647
656
|
}
|
|
648
657
|
|
|
649
658
|
const deadline = Date.now() + 30_000;
|