@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
|
@@ -936,15 +936,29 @@ export class SystemOneDecisionExecutor extends BaseExecutor<
|
|
|
936
936
|
}
|
|
937
937
|
}
|
|
938
938
|
|
|
939
|
+
const decided = await this.decide(config);
|
|
940
|
+
if (!decided.ok) return { status: "failed", error: decided.error };
|
|
941
|
+
if (!config.humanReview) return { status: "success", output: decided.output };
|
|
942
|
+
return this.reviewDecision(config, config.humanReview, decided.output, context, meta);
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
/**
|
|
946
|
+
* One decision from the provider, without the review band: resolve the target,
|
|
947
|
+
* send, validate. The credential stays in this process; the `system-one-decide`
|
|
948
|
+
* tool calls this so scripts get Jev answers without holding a key.
|
|
949
|
+
*/
|
|
950
|
+
async decide(
|
|
951
|
+
config: SystemOneDecisionConfig,
|
|
952
|
+
): Promise<{ ok: true; output: SystemOneDecisionOutput } | { ok: false; error: string }> {
|
|
939
953
|
const providerId = config.provider;
|
|
940
954
|
const provider: SystemOneProvider = SYSTEM_ONE_PROVIDERS[providerId];
|
|
941
955
|
|
|
942
956
|
// A provider with no default sends no `model` when the node sets none.
|
|
943
957
|
const model = config.model ?? provider.defaultModel;
|
|
944
958
|
const modelProblem = systemOneModelProblem(providerId, model);
|
|
945
|
-
if (modelProblem) return {
|
|
959
|
+
if (modelProblem) return { ok: false, error: modelProblem };
|
|
946
960
|
const target = await this.resolveTarget(providerId, model);
|
|
947
|
-
if (!target.ok) return {
|
|
961
|
+
if (!target.ok) return { ok: false, error: target.error };
|
|
948
962
|
const { apiKey, endpoint } = target;
|
|
949
963
|
|
|
950
964
|
const wire = provider.wire ?? SYSTEM_ONE_WIRE;
|
|
@@ -959,13 +973,13 @@ export class SystemOneDecisionExecutor extends BaseExecutor<
|
|
|
959
973
|
config.timeoutMs,
|
|
960
974
|
config.maxRetries,
|
|
961
975
|
);
|
|
962
|
-
if (!sent.ok) return {
|
|
976
|
+
if (!sent.ok) return { ok: false, error: scrub(sent.error) };
|
|
963
977
|
|
|
964
978
|
let parsed: unknown;
|
|
965
979
|
try {
|
|
966
980
|
parsed = JSON.parse(sent.text);
|
|
967
981
|
} catch {
|
|
968
|
-
return {
|
|
982
|
+
return { ok: false, error: "SystemOne response was not valid JSON" };
|
|
969
983
|
}
|
|
970
984
|
let decision: SystemOneDecisionOutput;
|
|
971
985
|
try {
|
|
@@ -978,18 +992,14 @@ export class SystemOneDecisionExecutor extends BaseExecutor<
|
|
|
978
992
|
} catch (err) {
|
|
979
993
|
if (err instanceof SystemOneWireError) {
|
|
980
994
|
const withId = sent.requestId ? `${err.message} [request ${sent.requestId}]` : err.message;
|
|
981
|
-
return {
|
|
995
|
+
return { ok: false, error: scrub(withId) };
|
|
982
996
|
}
|
|
983
997
|
if (err instanceof SystemOneContractError) {
|
|
984
|
-
return {
|
|
985
|
-
status: "failed",
|
|
986
|
-
error: scrub(`SystemOne response failed validation: ${err.message}`),
|
|
987
|
-
};
|
|
998
|
+
return { ok: false, error: scrub(`SystemOne response failed validation: ${err.message}`) };
|
|
988
999
|
}
|
|
989
1000
|
throw err;
|
|
990
1001
|
}
|
|
991
|
-
|
|
992
|
-
return this.reviewDecision(config, config.humanReview, decision, context, meta);
|
|
1002
|
+
return { ok: true, output: decision };
|
|
993
1003
|
}
|
|
994
1004
|
|
|
995
1005
|
/**
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { providerBaseUrl, providerForModel, providerKeyVars } from "@desplega/model-catalog";
|
|
2
|
+
import { selectCredential } from "../../utils/credentials";
|
|
1
3
|
import { DEFAULT_MODEL, resolveCredential } from "../../utils/internal-ai/credentials";
|
|
2
4
|
import {
|
|
3
5
|
getOpenRouterAttributionHeaders,
|
|
@@ -25,6 +27,26 @@ export async function resolveWorkflowLlmConfig(
|
|
|
25
27
|
env: NodeJS.ProcessEnv = process.env,
|
|
26
28
|
options: { requireKind?: "openrouter" | "openai" } = {},
|
|
27
29
|
): Promise<WorkflowLlmConfig> {
|
|
30
|
+
// `nan/<model>`, `helmcode/<model>`, ...: an OpenAI-compatible registry
|
|
31
|
+
// provider is reached with its own key and base URL, never through OpenRouter.
|
|
32
|
+
const registryProvider = options.requireKind ? undefined : providerForModel(requestedModel);
|
|
33
|
+
if (registryProvider && requestedModel) {
|
|
34
|
+
const keyVar = providerKeyVars(registryProvider).find((name) => env[name]?.trim());
|
|
35
|
+
if (!keyVar) {
|
|
36
|
+
throw new Error(
|
|
37
|
+
`No ${registryProvider.displayName} credential found for ${requestedModel}. Set ${registryProvider.envVar}.`,
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
// The API hydrates global config verbatim, so a key may still be a
|
|
42
|
+
// comma pool; workflow calls skip the runner's per-task pick.
|
|
43
|
+
apiKey: selectCredential(env[keyVar] as string, undefined, keyVar).selected,
|
|
44
|
+
baseURL: providerBaseUrl(registryProvider, env),
|
|
45
|
+
headers: {},
|
|
46
|
+
model: requestedModel.slice(registryProvider.id.length + 1),
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
28
50
|
const credential = await resolveCredential({ env });
|
|
29
51
|
if (!credential) {
|
|
30
52
|
throw new Error("No workflow LLM credential found. Set OPENROUTER_API_KEY or OPENAI_API_KEY.");
|
package/src/workflows/index.ts
CHANGED
|
@@ -37,7 +37,14 @@ import { startWaitPoller } from "./wait-poller";
|
|
|
37
37
|
// ─── Module-level singleton ────────────────────────────────
|
|
38
38
|
|
|
39
39
|
let _registry: ExecutorRegistry | null = null;
|
|
40
|
-
let
|
|
40
|
+
let eventTriggerListenersInitialized = false;
|
|
41
|
+
|
|
42
|
+
/** Bus events that start workflows with a matching `event` trigger. Keep in sync with `TriggerConfigSchema`. */
|
|
43
|
+
const EVENT_TRIGGER_NAMES = [
|
|
44
|
+
"slack.message",
|
|
45
|
+
"azure-devops.work_item.assigned",
|
|
46
|
+
"azure-devops.work_item.commented",
|
|
47
|
+
] as const;
|
|
41
48
|
|
|
42
49
|
function createBuiltInExecutorRegistry(): ExecutorRegistry {
|
|
43
50
|
return createExecutorRegistry({
|
|
@@ -57,18 +64,20 @@ export function getExecutorRegistry(): ExecutorRegistry {
|
|
|
57
64
|
return _registry;
|
|
58
65
|
}
|
|
59
66
|
|
|
60
|
-
function
|
|
61
|
-
if (
|
|
67
|
+
function setupEventTriggerListeners(): void {
|
|
68
|
+
if (eventTriggerListenersInitialized) return;
|
|
62
69
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
70
|
+
for (const eventName of EVENT_TRIGGER_NAMES) {
|
|
71
|
+
workflowEventBus.on(eventName, (payload) => {
|
|
72
|
+
void handleEventTrigger(eventName, payload, getExecutorRegistry()).catch((err) => {
|
|
73
|
+
console.error(
|
|
74
|
+
`[workflows] Failed to dispatch ${eventName} event trigger:`,
|
|
75
|
+
scrubSecrets(err instanceof Error ? err.message : String(err)),
|
|
76
|
+
);
|
|
77
|
+
});
|
|
69
78
|
});
|
|
70
|
-
}
|
|
71
|
-
|
|
79
|
+
}
|
|
80
|
+
eventTriggerListenersInitialized = true;
|
|
72
81
|
}
|
|
73
82
|
|
|
74
83
|
/**
|
|
@@ -84,7 +93,7 @@ export async function initWorkflows(): Promise<void> {
|
|
|
84
93
|
|
|
85
94
|
// 2. Wire up resume listener (task.completed / task.failed / task.cancelled)
|
|
86
95
|
setupWorkflowResumeListener(workflowEventBus, _registry);
|
|
87
|
-
|
|
96
|
+
setupEventTriggerListeners();
|
|
88
97
|
|
|
89
98
|
// 3. Recover incomplete runs (running + waiting) from previous lifecycle
|
|
90
99
|
recoverIncompleteRuns(_registry).catch((err) => {
|
|
@@ -14,6 +14,7 @@ Each folder holds exactly one `manifest.yaml`, `manifest.yml`, or `manifest.json
|
|
|
14
14
|
| `notify-on-complete` | `post.task.completed`, `post.task.failed` | Posts a summary to a Slack channel |
|
|
15
15
|
| `deploy-awareness` | `pre.task.create` | Appends a one-line note to new tasks while a Dokploy deploy is in progress |
|
|
16
16
|
| `slack-reply-gate` | `pre.task.create` | Asks Jev (TypeSafe or OpenRouter) whether Lead should answer a Slack thread follow-up; in `enforce` drops the wake-up and marks the message :mute: |
|
|
17
|
+
| `comms-adherence-gate` | `pre.tool.call` | Asks Jev (TypeSafe or OpenRouter) whether a Slack task's final output matches the requester's comms preferences and latest request; in `enforce` blocks a non-adherent completion once per task |
|
|
17
18
|
| `require-verification-note` | `pre.tool.call` | Refuses completion without a `Verified:` line in the output |
|
|
18
19
|
| `task-digest` | `post.task.completed`, `post.task.failed` | Ships the `task-digest-collect` script, the `task-digest-daily` schedule (09:00 UTC), the `task-digest-report` workflow, and the `task-digest-guide` skill |
|
|
19
20
|
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# comms-adherence-gate
|
|
2
|
+
|
|
3
|
+
Agents finish Slack tasks with a final message a human reads. This extension judges that message against the requester's stated comms preferences and their latest request with Jev, [TypeSafe](https://typesafe.ai)'s decision model. In `enforce` it blocks a non-adherent completion once and tells the agent what to fix.
|
|
4
|
+
|
|
5
|
+
Install config:
|
|
6
|
+
|
|
7
|
+
```json
|
|
8
|
+
{ "mode": "shadow" }
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Start in `shadow`, read the verdicts in KV for a few days, then switch to `enforce`.
|
|
12
|
+
|
|
13
|
+
## Modes
|
|
14
|
+
|
|
15
|
+
| `mode` | Effect |
|
|
16
|
+
|---|---|
|
|
17
|
+
| `off` | Never evaluates. |
|
|
18
|
+
| `shadow` (default) | Asks Jev and records the verdict. Never blocks. |
|
|
19
|
+
| `enforce` | Asks Jev, records the verdict, and blocks the `store-progress` call when a dimension fails, at most `maxBlocksPerTask` times per task. The agent gets the failed dimensions, their scores and the stated preference, and calls again. |
|
|
20
|
+
|
|
21
|
+
## What it gates
|
|
22
|
+
|
|
23
|
+
Hook: `pre.tool.call` on `store-progress` with `status: "completed"` and a non-empty `output`. Calls made by extensions are skipped. The task must:
|
|
24
|
+
|
|
25
|
+
1. Come from Slack (`source = "slack"` or a `slackThreadTs`). Other tasks pass without a Jev call.
|
|
26
|
+
2. Have a requester with stated preferences in `users.metadata.comms`. A requester with none passes. Set them with the `manage-user` tool.
|
|
27
|
+
|
|
28
|
+
## Decision
|
|
29
|
+
|
|
30
|
+
The gate reads the task row and asks one `noul` question per stated preference it knows: `verbosity`, `tone`, `language`. Any other key in `comms` is context only.
|
|
31
|
+
|
|
32
|
+
Jev sees the preferences, the requester's latest message (the text after the last `</thread_context>`, from the last 1,400 characters of the task), the first 400 characters of the task, and the output. Its judging rules say an explicit request outranks the stored default. A requested long answer, story or document is not a verbosity failure.
|
|
33
|
+
|
|
34
|
+
A dimension fails when its score is below `threshold`. Any failed dimension fails the output.
|
|
35
|
+
|
|
36
|
+
## Jev transport
|
|
37
|
+
|
|
38
|
+
Jev is reachable two ways. Both take the same request and return the same answer.
|
|
39
|
+
|
|
40
|
+
| `provider` | Endpoint | Key | Model |
|
|
41
|
+
|---|---|---|---|
|
|
42
|
+
| `typesafe` | `POST https://api.typesafe.ai/v1/systemone` | `TYPESAFE_API_KEY` (or `apiKey`) | `model`, default `jev-latest` |
|
|
43
|
+
| `openrouter` | `POST https://openrouter.ai/api/alpha/decisions` | `OPENROUTER_API_KEY` | `openrouterModel`, default `typesafe/jev-1.13` |
|
|
44
|
+
| `auto` (default) | TypeSafe when its key is set, else OpenRouter | | |
|
|
45
|
+
|
|
46
|
+
Each verdict row records the `provider` that answered.
|
|
47
|
+
|
|
48
|
+
## Fail open
|
|
49
|
+
|
|
50
|
+
Every error lets the completion through: a missing key, an HTTP error, a network error, a timeout, a malformed answer, or a failed DB read. The handler never throws, so an outage cannot auto-disable the extension. Each failure bumps the `error` counter and overwrites `error:last`.
|
|
51
|
+
|
|
52
|
+
## Self-test
|
|
53
|
+
|
|
54
|
+
Call `store-progress` with a non-completed status and `progress` starting with `comms-gate-selftest`. The gate judges a known-bad and a known-good message against a terse preference and writes `selftest:last`: both scores, the threshold, the provider, and `controlsOk` (bad below threshold and good at or above it). Real traffic is untouched.
|
|
55
|
+
|
|
56
|
+
## Config
|
|
57
|
+
|
|
58
|
+
| Key | Default | Notes |
|
|
59
|
+
|---|---|---|
|
|
60
|
+
| `mode` | `shadow` | `off`, `shadow`, or `enforce` |
|
|
61
|
+
| `threshold` | `0.5` | A dimension scoring below this fails |
|
|
62
|
+
| `timeoutMs` | `4000` | Jev call timeout, capped at 4000 (the dispatcher cancels a handler at 5 s) |
|
|
63
|
+
| `maxBlocksPerTask` | `1` | Blocks per task in `enforce`; after that the gate records and lets it through |
|
|
64
|
+
| `provider` | `auto` | `auto`, `typesafe`, or `openrouter`. See [Jev transport](#jev-transport) |
|
|
65
|
+
| `model` | `jev-latest` | Jev model on TypeSafe |
|
|
66
|
+
| `openrouterModel` | `typesafe/jev-1.13` | Jev model on OpenRouter |
|
|
67
|
+
| `apiKey` | unset | Explicit TypeSafe key. Prefer the `TYPESAFE_API_KEY` secret below |
|
|
68
|
+
|
|
69
|
+
## Required secrets
|
|
70
|
+
|
|
71
|
+
Read from the API process environment at call time. Set them as global `swarm_config` secrets: the API process loads global rows into `process.env` at boot and on config reload.
|
|
72
|
+
|
|
73
|
+
- `TYPESAFE_API_KEY` or `OPENROUTER_API_KEY`: one is required for any evaluation. Under `auto`, TypeSafe wins when both are set. Without either, every completion fails open.
|
|
74
|
+
|
|
75
|
+
No key is written to config, KV, logs, or a block reason.
|
|
76
|
+
|
|
77
|
+
## KV namespace `ext:comms-gate`
|
|
78
|
+
|
|
79
|
+
- `last`: the latest verdict, with `scores`, `failed`, `provider`, `latencyMs`, the preferences, a 400-character `askPreview` and a 700-character `outputPreview`.
|
|
80
|
+
- `fail:<epochMs>:<taskId>`: one row per failed verdict, with `action` (`blocked` or `recorded`).
|
|
81
|
+
- `blocks:<taskId>`: blocks issued for that task.
|
|
82
|
+
- `error:last`, `selftest:last`.
|
|
83
|
+
- Counters: `total`, `pass`, `fail`, `blocked`, `escaped` (enforce, block budget spent), `shadowed`, `error`, and `dim:<dimension>`.
|
|
84
|
+
|
|
85
|
+
## Known limits
|
|
86
|
+
|
|
87
|
+
- Only the last 1,400 characters of the task are searched for the latest request. A very long final message is cut to its last 900 characters.
|
|
88
|
+
- The block budget is per task, so a retried completion after a block goes through once the budget is spent, even if it still fails.
|