@desplega.ai/agent-swarm 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{acp-adapter-sj2g0m84.js → acp-adapter-kr4abrsk.js} +10 -10
- package/dist/{actions-fegk44gr.js → actions-eh4g6w2r.js} +27 -27
- package/dist/{amp-adapter-ad8ft89x.js → amp-adapter-7h7wdt8q.js} +5 -5
- package/dist/{app-kfvgbvzy.js → app-7k8s9w5p.js} +9 -9
- package/dist/{assistant-z7zdy9ja.js → assistant-wswt2y7p.js} +28 -28
- package/dist/{boot-reembed-j8xa181e.js → boot-reembed-9apm9qw6.js} +7 -7
- package/dist/{boot-reembed-5c2z0mbe.js → boot-reembed-whzbdthj.js} +7 -7
- package/dist/{boot-scrub-sweep-hnkn3rgt.js → boot-scrub-sweep-ndzmvmgt.js} +8 -8
- package/dist/{boot-seeds-kwjq4d2z.js → boot-seeds-e9sta0yt.js} +13 -11
- package/dist/{claude-adapter-9vmvpvgd.js → claude-adapter-4mqd3r6r.js} +11 -11
- package/dist/{claude-managed-adapter-k9qae05j.js → claude-managed-adapter-9wswb52m.js} +3 -3
- package/dist/{claude-managed-setup-q54zx5q2.js → claude-managed-setup-tpe1effx.js} +4 -4
- package/dist/{claude-sdk-session-9jye6cxr.js → claude-sdk-session-q49ghmh9.js} +10340 -10197
- package/dist/{cli-ke896fcf.js → cli-01xvj1rq.js} +10 -10
- package/dist/{cli-tr4sxp68.js → cli-035vekcw.js} +2 -2
- package/dist/{cli-z5jjpaxw.js → cli-0pd8hzhc.js} +3 -3
- package/dist/{cli-n5vwg842.js → cli-0veb92a3.js} +6 -1
- package/dist/{cli-wayw93nt.js → cli-10jesr25.js} +3 -3
- package/dist/{cli-se8w6wny.js → cli-1y7yywmj.js} +1 -1
- package/dist/{cli-v1yb0rqb.js → cli-20krdhz5.js} +47 -19
- package/dist/{cli-d5f9ed5c.js → cli-29rja109.js} +39 -20
- package/dist/{cli-apx87jne.js → cli-2pwa0q4d.js} +15 -4
- package/dist/{cli-ee9gtz70.js → cli-4mengw46.js} +348 -25
- package/dist/{cli-03gedhng.js → cli-5twdke5x.js} +6 -6
- package/dist/{cli-6razj6t4.js → cli-711vwbxn.js} +2 -2
- package/dist/{cli-s2sp957d.js → cli-78vv7446.js} +1 -1
- package/dist/{cli-3fbmmawm.js → cli-7n6s5man.js} +4 -4
- package/dist/{cli-hnhtk6cj.js → cli-8wwxw8a3.js} +1 -1
- package/dist/{cli-hnmhtzvh.js → cli-9jzdj90z.js} +2 -0
- package/dist/{cli-skxvvpap.js → cli-cvv9k0hh.js} +28 -25
- package/dist/{cli-74ahz61x.js → cli-d9286m9h.js} +2 -2
- package/dist/{cli-4jxm2qtz.js → cli-gxzkg1de.js} +28 -16
- package/dist/{cli-zkpks3r3.js → cli-h1arqn31.js} +1 -1
- package/dist/{cli-r62a35ea.js → cli-hqr88ew6.js} +1 -1
- package/dist/{cli-ra0xzggj.js → cli-j67mh2vg.js} +2 -2
- package/dist/{cli-29jc5q7w.js → cli-ksphgh48.js} +2 -2
- package/dist/{cli-eejsgvm4.js → cli-mgz4nbgg.js} +4 -4
- package/dist/{cli-vatyhshy.js → cli-mz5as5ha.js} +4 -4
- package/dist/{cli-f7kr7svz.js → cli-n8ndw8j7.js} +2 -2
- package/dist/{cli-1e4b6bzh.js → cli-naqzr17j.js} +7 -7
- package/dist/{cli-q0ke1yww.js → cli-nm1p8jfr.js} +6 -6
- package/dist/{cli-m50t4309.js → cli-p3zbc1rw.js} +5 -5
- package/dist/{cli-cgtksawx.js → cli-pe05f8yk.js} +23 -3
- package/dist/{cli-vdach5a9.js → cli-qmzsbkvy.js} +4 -4
- package/dist/{cli-n3hzg89b.js → cli-qnm4e9e4.js} +57 -579
- package/dist/{cli-s4setq91.js → cli-qqy4q48b.js} +3 -3
- package/dist/{cli-k382tshb.js → cli-r35rw5de.js} +8 -7
- package/dist/{cli-k3dp5tpf.js → cli-r610p9bs.js} +2 -2
- package/dist/{cli-e6mpjhed.js → cli-rc6pf5kv.js} +1 -1
- package/dist/{cli-3dj8k4vy.js → cli-rdxm77k3.js} +1 -1
- package/dist/{cli-tqaazemc.js → cli-s69nfyeh.js} +1 -1
- package/dist/{cli-6jyj59nb.js → cli-sawv63e8.js} +2 -2
- package/dist/{cli-q50942rs.js → cli-sg19gbf0.js} +1 -1
- package/dist/{cli-p1x85d2t.js → cli-shk7r5cs.js} +12 -9
- package/dist/{cli-przpmdgr.js → cli-sksetb2q.js} +11 -1
- package/dist/{cli-d2qr7aj4.js → cli-t02z59f2.js} +1 -1
- package/dist/{cli-j0pfptdp.js → cli-v7g551te.js} +39 -43
- package/dist/{cli-bzszjf2p.js → cli-vgqzp4em.js} +519 -126
- package/dist/cli-wfnemr1t.js +86 -0
- package/dist/{cli-ne236afy.js → cli-wms6x9db.js} +8 -8
- package/dist/{cli-5hkm7whv.js → cli-x1r545x8.js} +1 -1
- package/dist/{cli-kt6zqg0e.js → cli-xczsgshn.js} +2 -2
- package/dist/{cli-ef1hg1wv.js → cli-y3167wt1.js} +430 -238
- package/dist/{cli-tsde1hdr.js → cli-yjmqpxt3.js} +12 -11
- package/dist/{cli-9nfx44eh.js → cli-z780ec7c.js} +1 -1
- package/dist/{cli-r2pnvj8g.js → cli-zm9t2vde.js} +2 -2
- package/dist/cli.js +24 -23
- package/dist/{codex-adapter-y81sssk0.js → codex-adapter-4z16v9n0.js} +8 -8
- package/dist/{codex-login-f2rcfpp9.js → codex-login-pkepy2j1.js} +2 -2
- package/dist/{codex-session-runner-8vx4j6a9.js → codex-session-runner-rvms9y2x.js} +8 -8
- package/dist/{commands-kcpvpcrr.js → commands-2a9rtyat.js} +8 -8
- package/dist/{controller-ywrmt6yd.js → controller-3k3madbn.js} +4 -4
- package/dist/{controllers-connect-kwpvj3rf.js → controllers-connect-v9yn8zb9.js} +30 -30
- package/dist/{cursor-adapter-cehv1qns.js → cursor-adapter-98gbhyzn.js} +4 -4
- package/dist/{db-ww5d7jq3.js → db-eywwsnjw.js} +9 -7
- package/dist/{db-k8ankj20.js → db-ncypk9np.js} +9 -9
- package/dist/{devin-adapter-x1022qb1.js → devin-adapter-zeq8b9gz.js} +2 -2
- package/dist/{dispatcher-e8gq5m68.js → dispatcher-w94kc575.js} +26 -26
- package/dist/{docker-ff6y1190.js → docker-b1e4cqab.js} +2 -2
- package/dist/{dsh-adapter-3epjm3af.js → dsh-adapter-47ztej36.js} +4 -4
- package/dist/{e2b-8ygwhg0f.js → e2b-bbt8ey45.js} +3 -3
- package/dist/{e2b-swhnpw3n.js → e2b-dp804sen.js} +3 -3
- package/dist/{event-bus-6axnngdj.js → event-bus-j2jdg3qb.js} +2 -2
- package/dist/{external-channel-h3359cwn.js → external-channel-zrkg8ar9.js} +8 -8
- package/dist/{grok-adapter-0d4x7gkx.js → grok-adapter-s9ncp9vy.js} +11 -11
- package/dist/{handlers-an1gzv21.js → handlers-kqftvjgw.js} +29 -29
- package/dist/{heartbeat-6dakeb61.js → heartbeat-9q96p8v9.js} +31 -31
- package/dist/{hook-6v6cv6d5.js → hook-56qe10dp.js} +7 -7
- package/dist/{hook-sa5xye4y.js → hook-yy6rw42k.js} +8 -8
- package/dist/{http-r7f3eky0.js → http-r2dnb8f8.js} +963 -75
- package/dist/{index-7ep4gzdk.js → index-3gtmkhc6.js} +26 -26
- package/dist/{index-v1v6n7hg.js → index-3k5hkz7r.js} +31 -31
- package/dist/{index-rmpw9a8q.js → index-5e101240.js} +26 -26
- package/dist/{index-s2wxgzr0.js → index-5wyfjc2c.js} +392 -15
- package/dist/{index-zk96aq1k.js → index-vxvhxxbn.js} +7 -7
- package/dist/{keepalive-be9vwy5h.js → keepalive-psj6j54c.js} +9 -9
- package/dist/{lead-0861cwrf.js → lead-hajz4688.js} +43 -43
- package/dist/{maintenance-mzzgc8bn.js → maintenance-ps02sb5m.js} +7 -7
- package/dist/{node-mn1zs7an.js → node-mwb5r697.js} +1 -1
- package/dist/{oauth-refresh-sweep-cpdnfwdj.js → oauth-refresh-sweep-0284h93b.js} +8 -8
- package/dist/{onboard-fh8qs4r3.js → onboard-rbf6bg01.js} +2 -2
- package/dist/{opencode-adapter-nm42wwj8.js → opencode-adapter-13dcatgq.js} +8 -8
- package/dist/{otel-impl-x6c1c9g4.js → otel-impl-zvqcbhp7.js} +2 -2
- package/dist/{pi-mono-adapter-9n2ya240.js → pi-mono-adapter-xxy47q4v.js} +7 -7
- package/dist/{pricing-refresh-qb0rv0ap.js → pricing-refresh-kefx9v3t.js} +9 -9
- package/dist/{rbac-roles-1gzdwbh1.js → rbac-roles-3m36rxm2.js} +8 -8
- package/dist/{rbac-roles-3mraey52.js → rbac-roles-z76xv0hq.js} +9 -9
- package/dist/{render-v2-agpa45zz.js → render-v2-wz73bs25.js} +9 -9
- package/dist/{resume-x0ydy9s9.js → resume-k4wzge8w.js} +14 -14
- package/dist/{setup-as9ga0d6.js → setup-2fp2pypp.js} +2 -2
- package/dist/{work-objects-y5g1y7ev.js → work-objects-6k0ywz3m.js} +29 -29
- package/dist/{worker-860c3e38.js → worker-5v3rxsdh.js} +43 -43
- package/dist/{x-m477fr0h.js → x-4mtdeym1.js} +2 -2
- package/openapi.json +777 -6
- package/package.json +8 -7
- package/src/agentmail/handlers.ts +21 -11
- package/src/apps/definition.ts +16 -0
- package/src/azure-devops/api.ts +52 -2
- package/src/azure-devops/boards-sync.ts +50 -20
- package/src/azure-devops/index.ts +1 -0
- package/src/azure-devops/outbound.ts +95 -0
- package/src/be/boot-seeds.ts +11 -0
- package/src/be/db/model-profiles.ts +315 -0
- package/src/be/db/tasks/read.ts +7 -0
- package/src/be/db.ts +53 -47
- package/src/be/migrations/219_relabel_pi_context_compactions.sql +22 -0
- package/src/be/migrations/220_model_profiles.sql +137 -0
- package/src/be/model-tier-resolution.ts +145 -11
- package/src/be/model-validation.ts +8 -1
- package/src/be/scripts/typecheck.ts +12 -0
- package/src/be/secret-registry.ts +6 -2
- package/src/be/seed-model-profiles.ts +41 -0
- package/src/be/seed-scripts/catalog/heartbeat-jev-gate.ts +368 -0
- package/src/be/seed-scripts/index.ts +9 -0
- package/src/be/swarm-config-guard.ts +1 -0
- package/src/commands/runner.ts +3 -17
- package/src/extensions/catalog.generated.json +17 -0
- package/src/extensions/contract-types.generated.ts +1 -1
- package/src/extensions/loader.ts +10 -5
- package/src/heartbeat/heartbeat.ts +2 -1
- package/src/http/agents.ts +49 -2
- package/src/http/all-routes.ts +1 -0
- package/src/http/apps.ts +30 -3
- package/src/http/config.ts +26 -2
- package/src/http/core.ts +4 -0
- package/src/http/index.ts +11 -1
- package/src/http/model-profiles.ts +303 -0
- package/src/http/pages-public.ts +11 -5
- package/src/http/poll.ts +16 -5
- package/src/http/script-runs.ts +1 -1
- package/src/http/scripts.ts +25 -2
- package/src/http/tasks.ts +18 -0
- package/src/http/webhooks.ts +1 -2
- package/src/pages/feedback-overlay.ts +65 -6
- package/src/providers/acp-targets.ts +1 -1
- package/src/providers/amp-adapter.ts +1 -1
- package/src/providers/dsh-adapter.ts +1 -1
- package/src/providers/pi-mono-extension.ts +17 -3
- package/src/providers/reasoning-effort.ts +10 -0
- package/src/rbac/legacy-policy.ts +8 -0
- package/src/rbac/permissions.ts +5 -0
- package/src/scheduler/deferred-task-waits.ts +18 -5
- package/src/scheduler/scheduler.ts +7 -1
- package/src/scripts-runtime/executors/quickjs-prelude.ts +75 -6
- package/src/scripts-runtime/executors/quickjs-runner.ts +85 -8
- package/src/scripts-runtime/executors/quickjs-web-shims.ts +1041 -0
- package/src/scripts-runtime/extract-args-schema.ts +9 -8
- package/src/scripts-runtime/quickjs-compat.ts +464 -0
- package/src/scripts-runtime/sdk-allowlist.ts +4 -0
- package/src/scripts-runtime/types/stdlib.d.ts +12 -0
- package/src/scripts-runtime/types/swarm-extension.d.ts +3 -0
- package/src/scripts-runtime/types/swarm-sdk.d.ts +12 -0
- package/src/server.ts +10 -1
- package/src/slack/blocks.ts +4 -2
- package/src/slack/reaction-shortcode.ts +14 -0
- package/src/slack/render-v2.ts +12 -7
- package/src/slack/responses.ts +5 -4
- package/src/slack/thread-buffer.ts +41 -22
- package/src/tasks/worker-follow-up.ts +11 -2
- package/src/tests/agentmail-filters.test.ts +47 -0
- package/src/tests/apps-spike2.test.ts +35 -0
- package/src/tests/azure-devops-outbound.test.ts +420 -0
- package/src/tests/db-task-read-contract.test.ts +2 -0
- package/src/tests/db-task-write-contract.test.ts +1 -0
- package/src/tests/deferred-task-wake.test.ts +78 -1
- package/src/tests/docs-index-footprint.test.js +2 -2
- package/src/tests/dsh-adapter.test.ts +1 -1
- package/src/tests/env-flag.test.ts +1 -0
- package/src/tests/extensions-catalog.test.ts +1 -0
- package/src/tests/extensions-example-comms-adherence-gate.test.ts +413 -0
- package/src/tests/extensions-loader.test.ts +15 -0
- package/src/tests/heartbeat-jev-gate-credentials.test.ts +168 -0
- package/src/tests/heartbeat-jev-gate.test.ts +385 -0
- package/src/tests/model-profile-backcompat.test.ts +327 -0
- package/src/tests/model-profile-providers.test.ts +302 -0
- package/src/tests/model-profile-runtime-secrets.test.ts +223 -0
- package/src/tests/model-profiles-api.test.ts +614 -0
- package/src/tests/pages-feedback-keys.test.ts +121 -0
- package/src/tests/pages-feedback.test.ts +3 -0
- package/src/tests/pi-context-compaction.test.ts +193 -0
- package/src/tests/profile-claim.test.ts +269 -0
- package/src/tests/quickjs-compat.test.ts +213 -0
- package/src/tests/rbac-engine.test.ts +21 -0
- package/src/tests/script-connections.test.ts +4 -0
- package/src/tests/script-executor-conformance.test.ts +252 -1
- package/src/tests/script-executor-quickjs.test.ts +96 -0
- package/src/tests/script-runs-http.test.ts +89 -0
- package/src/tests/scripts-http.test.ts +16 -0
- package/src/tests/secret-scrubber.test.ts +10 -0
- package/src/tests/seed-model-profiles.test.ts +174 -0
- package/src/tests/seed-scripts.test.ts +2 -2
- package/src/tests/skill-invocations.test.ts +4 -2
- package/src/tests/slack-blocks.test.ts +36 -0
- package/src/tests/slack-inline-output.test.ts +14 -0
- package/src/tests/slack-reaction-config.test.ts +20 -0
- package/src/tests/slack-render-v2.test.ts +37 -0
- package/src/tests/slack-thread-buffer.test.ts +98 -1
- package/src/tests/workflow-llm.test.ts +61 -0
- package/src/tools/model-profiles/list-model-profiles.ts +54 -0
- package/src/tools/request-human-input.ts +11 -3
- package/src/tools/script-apis.ts +5 -1
- package/src/tools/script-upsert.ts +4 -1
- package/src/tools/send-task.ts +24 -1
- package/src/tools/system-one-decide.ts +104 -0
- package/src/tools/task-action.ts +22 -1
- package/src/tools/tool-config.ts +6 -0
- package/src/types.ts +36 -1
- package/src/utils/secret-scrubber.ts +19 -12
- package/src/utils/vcs-repo-match.ts +29 -0
- package/src/workflows/executors/raw-llm.ts +8 -3
- package/src/workflows/executors/system-one-decision.ts +21 -11
- package/src/workflows/executors/workflow-llm.ts +22 -0
- package/src/workflows/index.ts +21 -12
- package/templates/extensions/README.md +1 -0
- package/templates/extensions/comms-adherence-gate/README.md +88 -0
- package/templates/extensions/comms-adherence-gate/hooks.ts +489 -0
- package/templates/extensions/comms-adherence-gate/manifest.json +8 -0
- package/dist/cli-5t4f29ez.js +0 -33
package/src/slack/render-v2.ts
CHANGED
|
@@ -51,7 +51,7 @@ import {
|
|
|
51
51
|
splitSlackSectionText,
|
|
52
52
|
} from "./blocks";
|
|
53
53
|
import { buildAskClosure, type ClosureState, closureState } from "./closure";
|
|
54
|
-
import { reactionName, type SlackReactionEvent } from "./reaction-shortcode";
|
|
54
|
+
import { completedStatusIcon, reactionName, type SlackReactionEvent } from "./reaction-shortcode";
|
|
55
55
|
import { getAgentDisplayName, getAgentEmoji } from "./responses";
|
|
56
56
|
import {
|
|
57
57
|
_resetSlackSessionStatusForTests,
|
|
@@ -346,7 +346,7 @@ async function taskStateGlyph(
|
|
|
346
346
|
case "paused":
|
|
347
347
|
return "⏸️";
|
|
348
348
|
case "completed":
|
|
349
|
-
return
|
|
349
|
+
return completedStatusIcon();
|
|
350
350
|
case "failed":
|
|
351
351
|
return "❌";
|
|
352
352
|
case "cancelled":
|
|
@@ -496,7 +496,9 @@ function threadHeaderText(tasks: AgentTask[], now: Date, duration: string): stri
|
|
|
496
496
|
// A parked deferral is stored `completed`, but the ask is not answered yet.
|
|
497
497
|
if (tasks.some((task) => isAwaitingWake(task, tasks))) return `🧵 ⏳ waiting — ${duration}`;
|
|
498
498
|
const hasFailure = tasks.some((task) => task.status === "failed");
|
|
499
|
-
return hasFailure
|
|
499
|
+
return hasFailure
|
|
500
|
+
? `🧵 ❌ done with failures — ${duration}`
|
|
501
|
+
: `🧵 ${completedStatusIcon()} done — ${duration}`;
|
|
500
502
|
}
|
|
501
503
|
|
|
502
504
|
export async function renderThreadTree(
|
|
@@ -909,7 +911,7 @@ async function outcomeContent(task: AgentTask, slackReplySent: boolean): Promise
|
|
|
909
911
|
const agentName = task.agentId
|
|
910
912
|
? ((await getAgentById(task.agentId))?.name ?? "Agent")
|
|
911
913
|
: "Agent";
|
|
912
|
-
return
|
|
914
|
+
return `${completedStatusIcon()} ${agentName} completed`;
|
|
913
915
|
}
|
|
914
916
|
// A deferral is `completed` in the database but unfinished to a human: the
|
|
915
917
|
// work resumes in a wake-up task. Rendering it ✅ told the thread the ask
|
|
@@ -917,7 +919,10 @@ async function outcomeContent(task: AgentTask, slackReplySent: boolean): Promise
|
|
|
917
919
|
if (isDeferred) {
|
|
918
920
|
return withStatusLead("⏳", outcomeText(slackTaskOutput(task), "Deferred."));
|
|
919
921
|
}
|
|
920
|
-
return withStatusLead(
|
|
922
|
+
return withStatusLead(
|
|
923
|
+
completedStatusIcon(),
|
|
924
|
+
outcomeText(slackTaskOutput(task), "Task completed."),
|
|
925
|
+
);
|
|
921
926
|
}
|
|
922
927
|
|
|
923
928
|
async function agentDisplayNameFor(task: AgentTask): Promise<string> {
|
|
@@ -947,7 +952,7 @@ export async function childOutcomeContent(
|
|
|
947
952
|
);
|
|
948
953
|
}
|
|
949
954
|
if (slackReplySent && !isDeferredTask(task)) {
|
|
950
|
-
return `↳
|
|
955
|
+
return `↳ ${completedStatusIcon()} ${agentName} completed`;
|
|
951
956
|
}
|
|
952
957
|
if (isDeferredTask(task)) {
|
|
953
958
|
return withStatusLead(
|
|
@@ -956,7 +961,7 @@ export async function childOutcomeContent(
|
|
|
956
961
|
);
|
|
957
962
|
}
|
|
958
963
|
return withStatusLead(
|
|
959
|
-
`↳
|
|
964
|
+
`↳ ${completedStatusIcon()} ${agentName} — result:`,
|
|
960
965
|
outcomeText(slackTaskOutput(task), "Task completed."),
|
|
961
966
|
);
|
|
962
967
|
}
|
package/src/slack/responses.ts
CHANGED
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
markdownToSlack,
|
|
16
16
|
splitSlackSectionText,
|
|
17
17
|
} from "./blocks";
|
|
18
|
+
import { completedStatusIcon } from "./reaction-shortcode";
|
|
18
19
|
import { getSlackOutputAttachments } from "./task-attachments";
|
|
19
20
|
import { slackTaskOutput } from "./task-output";
|
|
20
21
|
|
|
@@ -67,7 +68,7 @@ export function formatInlineCompletionOutputChunks(opts: {
|
|
|
67
68
|
taskId: string;
|
|
68
69
|
output: string;
|
|
69
70
|
}): string[] {
|
|
70
|
-
const header =
|
|
71
|
+
const header = `${completedStatusIcon()} *${opts.agentName}* completed with output (${getTaskLink(opts.taskId)}):\n\n`;
|
|
71
72
|
const slackOutput = markdownToSlack(opts.output.trim());
|
|
72
73
|
return splitSlackSectionText(header + slackOutput);
|
|
73
74
|
}
|
|
@@ -169,7 +170,7 @@ export async function sendTaskResponse(task: AgentTask): Promise<boolean> {
|
|
|
169
170
|
thread_ts: task.slackThreadTs,
|
|
170
171
|
text:
|
|
171
172
|
task.slackReplySent || i > 0
|
|
172
|
-
?
|
|
173
|
+
? `${completedStatusIcon()} ${agentName} completed${i > 0 ? ` (continued ${i + 1}/${blockBatches.length})` : ""}`
|
|
173
174
|
: body,
|
|
174
175
|
username: getAgentDisplayName(agent),
|
|
175
176
|
icon_emoji: getAgentEmoji(agent),
|
|
@@ -324,7 +325,7 @@ export async function updateToFinal(
|
|
|
324
325
|
};
|
|
325
326
|
completionBlockBatches = buildCompletedBlockBatches(completionOpts);
|
|
326
327
|
blocks = completionBlockBatches[0] ?? buildCompletedBlocks(completionOpts);
|
|
327
|
-
text = task.slackReplySent ?
|
|
328
|
+
text = task.slackReplySent ? `${completedStatusIcon()} ${agentName} completed` : body;
|
|
328
329
|
} else if (task.status === "cancelled") {
|
|
329
330
|
blocks = buildCancelledBlocks({ agentName, taskId: task.id });
|
|
330
331
|
text = "Task cancelled";
|
|
@@ -351,7 +352,7 @@ export async function updateToFinal(
|
|
|
351
352
|
await sendWithPersona(app.client, {
|
|
352
353
|
channel: task.slackChannelId,
|
|
353
354
|
thread_ts: task.slackThreadTs ?? messageTs,
|
|
354
|
-
text:
|
|
355
|
+
text: `${completedStatusIcon()} ${agentName} completed (continued ${i + 1}/${completionBlockBatches.length})`,
|
|
355
356
|
username: getAgentDisplayName(agent),
|
|
356
357
|
icon_emoji: getAgentEmoji(agent),
|
|
357
358
|
blocks: completionBlockBatches[i],
|
|
@@ -7,6 +7,9 @@ import {
|
|
|
7
7
|
import { findUserByExternalId } from "../be/users";
|
|
8
8
|
import { createAdditiveBuffer } from "../tasks/additive-buffer";
|
|
9
9
|
import { slackContextKey } from "../tasks/context-key";
|
|
10
|
+
import { TaskCreationBlockedError } from "../tasks/errors";
|
|
11
|
+
import { scrubSecrets } from "../utils/secret-scrubber";
|
|
12
|
+
import { ackSlackMessage } from "./ack";
|
|
10
13
|
import { getSlackApp } from "./app";
|
|
11
14
|
import { buildBufferFlushBlocks } from "./blocks";
|
|
12
15
|
import { resolveSlackUserId, rewriteSlackMentions } from "./enrich";
|
|
@@ -286,28 +289,44 @@ async function slackFlush(
|
|
|
286
289
|
const dependsOn = !immediate && latestActiveTask ? [latestActiveTask.id] : undefined;
|
|
287
290
|
|
|
288
291
|
const mostRecentTask = await getMostRecentTaskInThread(channelId, threadTs);
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
292
|
+
let created: Awaited<ReturnType<typeof createSlackTaskWithFiles>>;
|
|
293
|
+
try {
|
|
294
|
+
created = await createSlackTaskWithFiles(
|
|
295
|
+
fullDescription,
|
|
296
|
+
{
|
|
297
|
+
agentId: lead?.id,
|
|
298
|
+
routingReason:
|
|
299
|
+
lead && mostRecentTask?.agentId === lead.id ? "continuity" : lead ? "skill" : undefined,
|
|
300
|
+
routingSource: lead ? "engine_default" : undefined,
|
|
301
|
+
source: "slack",
|
|
302
|
+
slackChannelId: channelId,
|
|
303
|
+
slackThreadTs: threadTs,
|
|
304
|
+
slackTriggerMessageTs: items.at(-1)!.ts,
|
|
305
|
+
slackUserId: originalRequesterId,
|
|
306
|
+
requestedByUserId,
|
|
307
|
+
dependsOn,
|
|
308
|
+
parentTaskId: mostRecentTask?.id,
|
|
309
|
+
// A human message in the thread: never answer it in the latest thread
|
|
310
|
+
// task's structured-output contract.
|
|
311
|
+
inheritParentOutputSchema: false,
|
|
312
|
+
contextKey: slackContextKey({ channelId, threadTs }),
|
|
313
|
+
},
|
|
314
|
+
inbound,
|
|
315
|
+
);
|
|
316
|
+
} catch (error) {
|
|
317
|
+
if (!(error instanceof TaskCreationBlockedError)) throw error;
|
|
318
|
+
|
|
319
|
+
console.info(
|
|
320
|
+
"[Slack] pre.task.create blocked:",
|
|
321
|
+
error.extension.name,
|
|
322
|
+
scrubSecrets(error.reason),
|
|
323
|
+
);
|
|
324
|
+
if (app) {
|
|
325
|
+
await ackSlackMessage(app.client, channelId, items.at(-1)!.ts, "no_entry");
|
|
326
|
+
}
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
const { task, unattached } = created;
|
|
311
330
|
|
|
312
331
|
if (app) await notifySlackFileFailures(app.client, channelId, threadTs, unattached);
|
|
313
332
|
|
|
@@ -179,13 +179,22 @@ export function getNextResumeGeneration(parent: Pick<AgentTask, "tags">): number
|
|
|
179
179
|
export function inheritedModelSelection(
|
|
180
180
|
parent: Pick<
|
|
181
181
|
AgentTask,
|
|
182
|
-
|
|
182
|
+
| "model"
|
|
183
|
+
| "modelTier"
|
|
184
|
+
| "modelSource"
|
|
185
|
+
| "modelAlias"
|
|
186
|
+
| "resolvedModel"
|
|
187
|
+
| "claudeSessionId"
|
|
188
|
+
| "profile"
|
|
183
189
|
>,
|
|
184
|
-
): Pick<CreateTaskOptions, "model" | "modelTier"> {
|
|
190
|
+
): Pick<CreateTaskOptions, "model" | "modelTier" | "profile"> {
|
|
185
191
|
const model = taskModelPin(parent);
|
|
192
|
+
// The task's own profile travels like its tier; an agent-default profile
|
|
193
|
+
// (`resolvedProfile` only) is re-resolved by the child's claim.
|
|
186
194
|
return {
|
|
187
195
|
...(model ? { model } : {}),
|
|
188
196
|
...(parent.modelTier ? { modelTier: parent.modelTier } : {}),
|
|
197
|
+
...(parent.profile ? { profile: parent.profile } : {}),
|
|
189
198
|
};
|
|
190
199
|
}
|
|
191
200
|
|
|
@@ -38,6 +38,27 @@ describe("isInboxAllowed", () => {
|
|
|
38
38
|
test("rejects empty inbox id", () => {
|
|
39
39
|
expect(isInboxAllowed("", "x.dev")).toBe(false);
|
|
40
40
|
});
|
|
41
|
+
|
|
42
|
+
test("allows exact full-address match", () => {
|
|
43
|
+
expect(isInboxAllowed("bot@x.dev", "bot@x.dev")).toBe(true);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test("rejects another address on the same domain as a full-address entry", () => {
|
|
47
|
+
expect(isInboxAllowed("other@x.dev", "bot@x.dev")).toBe(false);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test("full-address match is case-insensitive", () => {
|
|
51
|
+
expect(isInboxAllowed("Bot@X.Dev", "bot@x.dev")).toBe(true);
|
|
52
|
+
expect(isInboxAllowed("bot@x.dev", "BOT@X.DEV")).toBe(true);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test("supports mixed address and domain entries", () => {
|
|
56
|
+
const filter = "bot@x.dev, y.xyz";
|
|
57
|
+
expect(isInboxAllowed("bot@x.dev", filter)).toBe(true);
|
|
58
|
+
expect(isInboxAllowed("anyone@y.xyz", filter)).toBe(true);
|
|
59
|
+
expect(isInboxAllowed("other@x.dev", filter)).toBe(false);
|
|
60
|
+
expect(isInboxAllowed("bot@evil.com", filter)).toBe(false);
|
|
61
|
+
});
|
|
41
62
|
});
|
|
42
63
|
|
|
43
64
|
describe("isSenderAllowed", () => {
|
|
@@ -94,4 +115,30 @@ describe("isSenderAllowed", () => {
|
|
|
94
115
|
test("rejects non-matching domain in 'Name <email>' format", () => {
|
|
95
116
|
expect(isSenderAllowed("Taras <t@evil.com>", "desplega.ai")).toBe(false);
|
|
96
117
|
});
|
|
118
|
+
|
|
119
|
+
test("allows exact full-address match", () => {
|
|
120
|
+
expect(isSenderAllowed("alice@a.com", "alice@a.com")).toBe(true);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
test("rejects another address on the same domain as a full-address entry", () => {
|
|
124
|
+
expect(isSenderAllowed("bob@a.com", "alice@a.com")).toBe(false);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
test("full-address match is case-insensitive", () => {
|
|
128
|
+
expect(isSenderAllowed("Alice@A.com", "alice@a.com")).toBe(true);
|
|
129
|
+
expect(isSenderAllowed("alice@a.com", "ALICE@A.COM")).toBe(true);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
test("matches a full address in 'Name <email>' format", () => {
|
|
133
|
+
expect(isSenderAllowed("Taras <t@gmail.com>", "t@gmail.com")).toBe(true);
|
|
134
|
+
expect(isSenderAllowed(["Someone <s@gmail.com>"], "t@gmail.com")).toBe(false);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
test("supports mixed address and domain entries", () => {
|
|
138
|
+
const filter = "t@gmail.com, desplega.ai";
|
|
139
|
+
expect(isSenderAllowed("t@gmail.com", filter)).toBe(true);
|
|
140
|
+
expect(isSenderAllowed("anyone@desplega.ai", filter)).toBe(true);
|
|
141
|
+
expect(isSenderAllowed("other@gmail.com", filter)).toBe(false);
|
|
142
|
+
expect(isSenderAllowed(["other@gmail.com", "Taras <t@gmail.com>"], filter)).toBe(true);
|
|
143
|
+
});
|
|
97
144
|
});
|
|
@@ -987,6 +987,41 @@ describe("custom app actions", () => {
|
|
|
987
987
|
);
|
|
988
988
|
});
|
|
989
989
|
|
|
990
|
+
test("app writes return quickjs compat warnings for wired scripts without rejecting", async () => {
|
|
991
|
+
const saved = await upsertScriptByName({
|
|
992
|
+
name: `app_compat_${crypto.randomUUID().replaceAll("-", "")}`,
|
|
993
|
+
scope: "agent",
|
|
994
|
+
scopeId: AGENT_ID,
|
|
995
|
+
source: "export default async () => Buffer.from('aGk=', 'base64').toString();",
|
|
996
|
+
description: "Apps compat warning fixture",
|
|
997
|
+
intent: "Exercise quickjs compat warnings on app writes",
|
|
998
|
+
signatureJson: JSON.stringify({ args: { type: "object" }, result: { type: "string" } }),
|
|
999
|
+
agentId: AGENT_ID,
|
|
1000
|
+
typeChecked: true,
|
|
1001
|
+
});
|
|
1002
|
+
const created = await request<{ app: { id: string }; warnings?: string[] }>("/api/apps", {
|
|
1003
|
+
method: "POST",
|
|
1004
|
+
body: JSON.stringify({
|
|
1005
|
+
name: "Compat",
|
|
1006
|
+
definition: {
|
|
1007
|
+
...baseDefinition,
|
|
1008
|
+
actions: { decode: { kind: "script", scriptId: saved.script.id } },
|
|
1009
|
+
},
|
|
1010
|
+
}),
|
|
1011
|
+
});
|
|
1012
|
+
expect(created.status).toBe(201);
|
|
1013
|
+
expect(created.body.warnings).toEqual([
|
|
1014
|
+
"actions.decode: quickjs compat (unsupportedGlobal, line 1): Buffer is not available under quickjs",
|
|
1015
|
+
]);
|
|
1016
|
+
|
|
1017
|
+
const cleared = await request<{ warnings?: string[] }>(`/api/apps/${created.body.app.id}`, {
|
|
1018
|
+
method: "PUT",
|
|
1019
|
+
body: JSON.stringify({ definition: baseDefinition }),
|
|
1020
|
+
});
|
|
1021
|
+
expect(cleared.status).toBe(200);
|
|
1022
|
+
expect(cleared.body.warnings).toBeUndefined();
|
|
1023
|
+
});
|
|
1024
|
+
|
|
990
1025
|
test("agent writers cannot wire another agent's script; operator wiring is grandfathered", async () => {
|
|
991
1026
|
const OTHER_AGENT_ID = crypto.randomUUID();
|
|
992
1027
|
const foreign = await upsertScriptByName({
|