@desplega.ai/agent-swarm 2.5.2 → 2.5.4
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-csh0zz3b.js → acp-adapter-ek38cwsv.js} +5 -5
- package/dist/{actions-6xx079d1.js → actions-92mepq03.js} +19 -19
- package/dist/{amp-adapter-8n6f0hz8.js → amp-adapter-9gaxg015.js} +4 -4
- package/dist/{app-ft6r6v23.js → app-ctn8gsgx.js} +3 -3
- package/dist/{assistant-vt2cyfzd.js → assistant-zyr5kz47.js} +21 -21
- package/dist/{boot-reembed-tgrfy27t.js → boot-reembed-dwzfttdr.js} +1 -1
- package/dist/{boot-reembed-r3e808ky.js → boot-reembed-w75maga2.js} +1 -1
- package/dist/{boot-scrub-sweep-masdwxkm.js → boot-scrub-sweep-e9kq43kf.js} +3 -3
- package/dist/{boot-seeds-0n82qc7q.js → boot-seeds-x28v3fhb.js} +4 -4
- package/dist/{claude-adapter-s7j47k06.js → claude-adapter-f0afqnne.js} +5 -5
- package/dist/{claude-managed-adapter-gt6pa35d.js → claude-managed-adapter-x81a7r65.js} +3 -3
- package/dist/{claude-managed-setup-3hzrgrnk.js → claude-managed-setup-bqzwjd9x.js} +2 -2
- package/dist/{claude-sdk-session-0zrdzyx2.js → claude-sdk-session-7b7whkaq.js} +9307 -9254
- package/dist/{cli-a5ebc95n.js → cli-0shx9j93.js} +16 -13
- package/dist/{cli-4qkg9968.js → cli-19202kew.js} +4 -4
- package/dist/{cli-cyfgxnvb.js → cli-1r5ad7j5.js} +19 -17
- package/dist/{cli-jmcjbjkt.js → cli-25n3mv5d.js} +9 -7
- package/dist/cli-2655k16q.js +140 -0
- package/dist/{cli-wadzapwb.js → cli-277c31dw.js} +1 -1
- package/dist/{cli-n6kmqngw.js → cli-4b2z6n0m.js} +29 -7
- package/dist/{cli-7ccf7akh.js → cli-5bnsc0e1.js} +9 -7
- package/dist/{cli-qrjv7t18.js → cli-621ce7kw.js} +1 -1
- package/dist/{cli-xzge46sj.js → cli-64mh9mf3.js} +20 -6
- package/dist/{cli-b6tnjdvx.js → cli-6a1xg1p7.js} +6 -2
- package/dist/{cli-ydg6ep51.js → cli-6n8axjv1.js} +1 -1
- package/dist/{cli-e91vshb1.js → cli-7p4jxgtt.js} +5 -5
- package/dist/{cli-f3pfn4x3.js → cli-9j129brq.js} +9 -3
- package/dist/{cli-xw9q173h.js → cli-9s05422w.js} +1 -1
- package/dist/{cli-15xd0j7w.js → cli-abs8ndtr.js} +12 -12
- package/dist/{cli-h5d22pfs.js → cli-asq5ydm4.js} +219 -309
- package/dist/{cli-w9etnnxk.js → cli-bf8ma3f1.js} +120 -50
- package/dist/{cli-0vgy5wfw.js → cli-bvpgr9ny.js} +2 -2
- package/dist/{cli-3e9vs93g.js → cli-c61mbdds.js} +4 -4
- package/dist/{cli-e5bxnjps.js → cli-c952c7ac.js} +5 -5
- package/dist/{cli-p859hcc8.js → cli-dew6x255.js} +1 -1
- package/dist/{cli-tx00c0h6.js → cli-dqygeeh5.js} +17 -1
- package/dist/{cli-gpv60278.js → cli-e42vss7b.js} +2 -2
- package/dist/{cli-b63d08rs.js → cli-eftzyh9y.js} +1 -1
- package/dist/{cli-fgn7yg5r.js → cli-ethgy7ha.js} +1 -1
- package/dist/{cli-86zpfje5.js → cli-ff1tm9n9.js} +1 -1
- package/dist/{cli-cf63dnxj.js → cli-g9vd9ece.js} +37 -12
- package/dist/{cli-j3dwd4kz.js → cli-gbfxp48k.js} +770 -126
- package/dist/{cli-n2918sed.js → cli-gn5af0x5.js} +21 -21
- package/dist/{cli-d172svcw.js → cli-gwhjmh8g.js} +4 -4
- package/dist/{cli-stxdrqs2.js → cli-hh5we352.js} +24 -5
- package/dist/{cli-0ebey9hg.js → cli-j6rktc91.js} +1 -1
- package/dist/{cli-csh8fnbg.js → cli-jb30dzvk.js} +473 -58
- package/dist/{cli-vaxw3bg0.js → cli-jbnn4vxb.js} +3 -3
- package/dist/{cli-ha22h0eg.js → cli-k4tjsdx9.js} +45 -40
- package/dist/{cli-f55b6ky0.js → cli-m4ecdxvz.js} +40 -13
- package/dist/{cli-mntxtkhn.js → cli-mz9afxxf.js} +2 -1
- package/dist/{cli-ta8bd7th.js → cli-pzatvw94.js} +1 -1
- package/dist/{cli-3qv8naq8.js → cli-rr7zx19j.js} +1591 -353
- package/dist/{cli-b1tv22wr.js → cli-s682zgqz.js} +1 -1
- package/dist/{cli-bpbcpd8b.js → cli-sbwsxvtd.js} +1 -1
- package/dist/{cli-0xk505e8.js → cli-sxgtf5fw.js} +1 -1
- package/dist/{cli-6y43v79h.js → cli-tsma7p8v.js} +1 -1
- package/dist/{cli-edtm741g.js → cli-wghjkr0e.js} +5 -5
- package/dist/{cli-aaqnqpyw.js → cli-wx2nj19m.js} +30 -11
- package/dist/{cli-y3dzwyy3.js → cli-x0f2v69c.js} +180 -60
- package/dist/{cli-kcnwn85g.js → cli-xtrz6bzs.js} +7 -7
- package/dist/{cli-14v1sxxy.js → cli-xzfq4at3.js} +7 -7
- package/dist/cli.js +20 -20
- package/dist/{codex-adapter-brh2tcxr.js → codex-adapter-wch12q99.js} +3 -3
- package/dist/{codex-hook-e2he7aca.js → codex-hook-brpnycn5.js} +1 -1
- package/dist/{codex-session-runner-0dnj21qs.js → codex-session-runner-41pfcr95.js} +2 -2
- package/dist/{commands-1cnvseqx.js → commands-9849dkj6.js} +2 -2
- package/dist/{controller-kc7s9xhn.js → controller-ja0sd95p.js} +29 -3
- package/dist/{controllers-connect-jzx7t4bn.js → controllers-connect-xp05dj6x.js} +20 -20
- package/dist/{cursor-adapter-qpwp5sfg.js → cursor-adapter-623qxqew.js} +1 -2
- package/dist/{db-4vq88jkc.js → db-47vj40dy.js} +3 -3
- package/dist/{db-v5ytg5pw.js → db-w759qmx0.js} +2 -2
- package/dist/{dispatcher-trtcrqae.js → dispatcher-7b8e4z2j.js} +17 -17
- package/dist/{docker-nfy2gc8t.js → docker-s84hjs91.js} +2 -2
- package/dist/{dsh-adapter-f20xwn1z.js → dsh-adapter-2ezw3xea.js} +2 -3
- package/dist/{external-channel-n9bymc2n.js → external-channel-e2s028da.js} +2 -2
- package/dist/{grok-adapter-4kpmmnr6.js → grok-adapter-ys7gaqee.js} +6 -6
- package/dist/{handlers-ptsraqa8.js → handlers-wy1dr6ja.js} +20 -20
- package/dist/{heartbeat-60qtxtjf.js → heartbeat-w3mntghj.js} +20 -20
- package/dist/{hook-rbktcstg.js → hook-3jjywnpb.js} +9 -3
- package/dist/{hook-ngtvh729.js → hook-g7kvv6x9.js} +3 -2
- package/dist/{http-z8xy72gp.js → http-tkp7d4kb.js} +2984 -2483
- package/dist/{index-r70q0xjx.js → index-1ef1myq2.js} +17 -17
- package/dist/{index-a9nsavhx.js → index-3n8m9ea2.js} +1 -1
- package/dist/{index-cbxkmfc5.js → index-9pn1pfkf.js} +1 -1
- package/dist/{index-s193j5fh.js → index-nmm79qgr.js} +20 -20
- package/dist/{index-6srwvd5b.js → index-p4xfz37z.js} +1 -1
- package/dist/{index-06qkmcx6.js → index-q7m26kvs.js} +33 -23
- package/dist/{index-1cscynkb.js → index-xdaahthb.js} +17 -17
- package/dist/{keepalive-a9srcphj.js → keepalive-v4ta8wgg.js} +3 -3
- package/dist/{lead-3devneqy.js → lead-sawnyqxk.js} +35 -35
- package/dist/{maintenance-0er16h2y.js → maintenance-84n1ex21.js} +1 -1
- package/dist/{oauth-refresh-sweep-b65jcyw2.js → oauth-refresh-sweep-vjnnandw.js} +2 -2
- package/dist/{onboard-wqywz5py.js → onboard-sfmpe4np.js} +2 -2
- package/dist/{opencode-adapter-e56086m1.js → opencode-adapter-jmpst4q4.js} +4 -4
- package/dist/{otel-impl-vk32s884.js → otel-impl-v6cc0zve.js} +1 -1
- package/dist/{pi-mono-adapter-tzps8f38.js → pi-mono-adapter-fk8fkcf6.js} +33 -6
- package/dist/{pricing-refresh-t27anv4z.js → pricing-refresh-fsk1e1zc.js} +3 -3
- package/dist/{rbac-roles-452665y0.js → rbac-roles-prmkdnkw.js} +2 -2
- package/dist/{rbac-roles-bmcgsxjn.js → rbac-roles-zxmxqa39.js} +1 -1
- package/dist/{render-v2-0ttsk2j4.js → render-v2-pjwxvnkw.js} +3 -3
- package/dist/{resume-a26kvr41.js → resume-frdtgfa0.js} +9 -5
- package/dist/{session-access-dgh9nkwk.js → session-access-fpez4y5q.js} +4 -4
- package/dist/{setup-9vc9qahm.js → setup-mq6m3m8q.js} +1 -1
- package/dist/{work-objects-vn8nkxwn.js → work-objects-kfahhjsq.js} +20 -20
- package/dist/{worker-1x1h8vnm.js → worker-v5e5rxj3.js} +35 -35
- package/openapi.json +3078 -2424
- package/package.json +7 -7
- package/plugin/commands/create-pr.md +2 -2
- package/plugin/pi-skills/create-pr/SKILL.md +2 -2
- package/src/be/approval-config-question.ts +389 -0
- package/src/be/automation-preflight.ts +37 -3
- package/src/be/db/approvals.ts +48 -0
- package/src/be/db/session-tree.ts +73 -15
- package/src/be/db/skill-search.ts +87 -0
- package/src/be/db/tasks/read.ts +29 -2
- package/src/be/db/tasks/write.ts +15 -5
- package/src/be/db/workflow-idempotency.ts +183 -0
- package/src/be/db/workflow-runs.ts +366 -8
- package/src/be/db.ts +92 -22
- package/src/be/migrations/239_skills_fts.sql +40 -0
- package/src/be/migrations/240_workflow_run_parent_step_attempt.sql +37 -0
- package/src/be/migrations/241_skills_meta_fts.sql +39 -0
- package/src/be/migrations/242_workflow_idempotency_keys.sql +40 -0
- package/src/be/sealed-json.ts +1 -0
- package/src/be/seed-scripts/catalog/compound-insights.inline.ts +2 -2
- package/src/be/seed-scripts/catalog/compound-insights.ts +2 -2
- package/src/be/seed-skills/bundled-files.generated.json +1 -1
- package/src/be/sql-connections.ts +7 -1
- package/src/be/swarm-config-guard.ts +25 -0
- package/src/be/users.ts +9 -0
- package/src/commands/controller.ts +33 -0
- package/src/commands/runner.ts +6 -2
- package/src/extensions/apply-task-create.ts +57 -11
- package/src/extensions/catalog.generated.json +4 -4
- package/src/extensions/contract-types.generated.ts +1 -1
- package/src/extensions/contract.ts +25 -0
- package/src/extensions/dispatcher.ts +4 -0
- package/src/extensions/post-bridge.ts +8 -0
- package/src/hooks/context-bound-note.ts +196 -0
- package/src/hooks/hook.ts +12 -0
- package/src/http/approval-requests.ts +209 -38
- package/src/http/config.ts +70 -53
- package/src/http/idempotency-key.ts +117 -0
- package/src/http/sessions.ts +27 -2
- package/src/http/workflows.ts +129 -24
- package/src/integrations/chat-sdk/inbound.ts +5 -4
- package/src/prompts/context-bound-note.ts +48 -0
- package/src/prompts/session-templates.ts +25 -1
- package/src/providers/amp-adapter.ts +30 -8
- package/src/providers/claude-adapter.ts +29 -4
- package/src/providers/claude-managed-adapter.ts +26 -0
- package/src/providers/claude-sdk-session.ts +10 -0
- package/src/providers/claude-session-events.ts +13 -0
- package/src/providers/codex-adapter.ts +12 -0
- package/src/providers/dsh-adapter.ts +1 -1
- package/src/providers/opencode-adapter.ts +18 -0
- package/src/providers/pi-mono-adapter.ts +35 -0
- package/src/runtime-providers/docker.ts +6 -2
- package/src/scripts-runtime/types/swarm-extension.d.ts +35 -0
- package/src/tests/amp-adapter.test.ts +97 -0
- package/src/tests/approval-config-question.test.ts +702 -0
- package/src/tests/approval-requests.test.ts +10 -7
- package/src/tests/approval-sweeps.test.ts +12 -6
- package/src/tests/base-prompt.test.ts +1 -1
- package/src/tests/chat-sdk-inbound.test.ts +20 -0
- package/src/tests/claude-hook-output.test.ts +79 -1
- package/src/tests/claude-managed-adapter.test.ts +275 -0
- package/src/tests/codex-adapter.test.ts +163 -0
- package/src/tests/context-bound-note.test.ts +269 -0
- package/src/tests/dsh-adapter.test.ts +1 -1
- package/src/tests/entrypoint-gitlab-credential-helper.test.ts +273 -0
- package/src/tests/extensions-example-comms-adherence-gate.test.ts +321 -12
- package/src/tests/extensions-pre-task.test.ts +273 -0
- package/src/tests/fetch-resolved-env.test.ts +17 -0
- package/src/tests/fixtures/extensions/record-blocks/hooks.ts +10 -0
- package/src/tests/fixtures/extensions/record-blocks/manifest.json +7 -0
- package/src/tests/fixtures/fake-amp.ts +22 -8
- package/src/tests/fixtures/held-template-render.ts +42 -0
- package/src/tests/fixtures/workflow-idempotency-trigger.child.ts +73 -0
- package/src/tests/list-endpoint-slimming.test.ts +33 -0
- package/src/tests/migration-240-workflow-run-parent-step-attempt.test.ts +55 -0
- package/src/tests/openapi-route-headers.test.ts +2 -0
- package/src/tests/opencode-adapter.test.ts +134 -0
- package/src/tests/pi-mono-adapter.test.ts +105 -0
- package/src/tests/prompt-template-session.test.ts +4 -3
- package/src/tests/runner-push-delivery.test.ts +25 -0
- package/src/tests/runtime-controller-liveness.test.ts +133 -0
- package/src/tests/runtime-providers-docker.test.ts +2 -0
- package/src/tests/script-connections.test.ts +9 -0
- package/src/tests/script-run-config-scope.test.ts +91 -3
- package/src/tests/session-logs.test.ts +70 -0
- package/src/tests/sessions-tree.test.ts +205 -1
- package/src/tests/skill-search-long-content.test.ts +193 -0
- package/src/tests/skill-search.test.ts +173 -22
- package/src/tests/workflow-cascade-cancel.test.ts +1169 -0
- package/src/tests/workflow-foreach-supersede-retry.test.ts +488 -0
- package/src/tests/workflow-idempotency.test.ts +915 -0
- package/src/tests/workflow-sub-workflow.test.ts +538 -1
- package/src/tools/request-human-input.ts +27 -5
- package/src/tools/workflows/cancel-workflow-run.ts +1 -1
- package/src/types.ts +7 -0
- package/src/utils/context-window.ts +8 -6
- package/src/workflows/approval-cancel.ts +15 -13
- package/src/workflows/approval-notifications.ts +15 -4
- package/src/workflows/checkpoint.ts +72 -35
- package/src/workflows/engine.ts +262 -93
- package/src/workflows/executors/base.ts +6 -0
- package/src/workflows/executors/foreach.ts +2 -2
- package/src/workflows/executors/human-in-the-loop.ts +25 -1
- package/src/workflows/executors/sub-workflow.ts +45 -16
- package/src/workflows/foreach-driver.ts +10 -3
- package/src/workflows/foreach-join.ts +29 -6
- package/src/workflows/recovery.ts +68 -1
- package/src/workflows/resume.ts +228 -77
- package/src/workflows/retry-poller.ts +74 -7
- package/src/workflows/task-step-routing.ts +29 -7
- package/templates/ai-toolbox.manifest.json +8 -8
- package/templates/community/open-prompt-showreel/PLAYBOOK.md +5 -6
- package/templates/community/open-prompt-showreel/lead-prompt.md +1 -1
- package/templates/community/open-prompt-showreel/skills/open-prompt-showreel/SKILL.md +4 -4
- package/templates/extensions/comms-adherence-gate/README.md +38 -13
- package/templates/extensions/comms-adherence-gate/hooks.ts +272 -69
- package/templates/extensions/comms-adherence-gate/manifest.json +2 -2
- package/templates/skills/agent-browser/SKILL.md +4 -4
- package/templates/skills/agent-browser/content.md +4 -4
- package/templates/skills/code-quality/config.json +1 -1
- package/templates/skills/code-quality/content.md +2 -2
- package/templates/skills/delegate-work/content.md +29 -25
- package/templates/skills/delegate-work/files/scripts/codex-exec.sh +5 -5
- package/templates/skills/qa/content.md +2 -2
- package/templates/skills/researching/content.md +2 -0
- package/templates/skills/turso-interaction/content.md +1 -1
- package/templates/skills/work-on-task/SKILL.md +1 -1
- package/templates/skills/work-on-task/content.md +1 -1
- package/templates/skills/workflow-iterate/content.md +8 -1
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
updateWorkflowRun,
|
|
8
8
|
updateWorkflowRunStep,
|
|
9
9
|
} from "../be/db";
|
|
10
|
+
import { getRunLineageBlock } from "../be/db/workflow-runs";
|
|
10
11
|
import {
|
|
11
12
|
type RetryPolicy,
|
|
12
13
|
RetryPolicySchema,
|
|
@@ -18,6 +19,7 @@ import {
|
|
|
18
19
|
import { checkpointStep, checkpointStepFailure } from "./checkpoint";
|
|
19
20
|
import { getSuccessors } from "./definition";
|
|
20
21
|
import type { ExecutorRegistry } from "./executors/registry";
|
|
22
|
+
import { resolveStepNode } from "./foreach-body";
|
|
21
23
|
import { type ForeachSettleResult, resolveForeachParent, settleForeachChild } from "./foreach-join";
|
|
22
24
|
|
|
23
25
|
export interface TaskStepRoutingResult {
|
|
@@ -146,6 +148,7 @@ export async function checkpointPortStepAndResolveSuccessors(
|
|
|
146
148
|
if (!run || (run.status !== "waiting" && run.status !== "running")) {
|
|
147
149
|
return UNCLAIMED;
|
|
148
150
|
}
|
|
151
|
+
if (await getRunLineageBlock(runId)) return UNCLAIMED;
|
|
149
152
|
// A foreach child's port (approval rejected/timeout, wait event/timeout)
|
|
150
153
|
// is stored for display and still counts as a completed item: the body's
|
|
151
154
|
// ports do not route, the item's consumer reads the output.
|
|
@@ -182,8 +185,8 @@ export async function checkpointPortStepAndResolveSuccessors(
|
|
|
182
185
|
* or the failed task is no longer the one bound to the step (it was
|
|
183
186
|
* already retried and a fresh task now owns it); the caller stops.
|
|
184
187
|
* - `not-eligible`: no retry applies (no `node.retry` and not a supersede
|
|
185
|
-
* cut, not an agent-task node,
|
|
186
|
-
* `onNodeFailure` as before.
|
|
188
|
+
* cut, not an agent-task node, retries exhausted, or a foreach child whose
|
|
189
|
+
* run is no longer live); the caller applies `onNodeFailure` as before.
|
|
187
190
|
*/
|
|
188
191
|
export type TaskStepRetryOutcome = "scheduled" | "not-claimed" | "not-eligible";
|
|
189
192
|
|
|
@@ -217,8 +220,13 @@ export const SUPERSEDE_RETRY_POLICY: RetryPolicy = {
|
|
|
217
220
|
* only after ownership is confirmed: a stale caller's step snapshot may carry
|
|
218
221
|
* the counter of a newer attempt.
|
|
219
222
|
*
|
|
220
|
-
*
|
|
221
|
-
*
|
|
223
|
+
* A foreach child (`<foreachId>#<key>`) with an `agent-task` body takes the
|
|
224
|
+
* parent foreach node's `retry`, else the same supersede default. While queued
|
|
225
|
+
* it does not settle, so the join stays open; the retry poller re-queues it and
|
|
226
|
+
* the foreach driver starts it under the fan-out's concurrency cap. A child is
|
|
227
|
+
* queued only while its run is live. Exhausted retries, a run that is no longer
|
|
228
|
+
* live and every other failure settle the item through `onNodeFailure` and the
|
|
229
|
+
* fan-out's failure policy as before.
|
|
222
230
|
*/
|
|
223
231
|
export async function scheduleTaskStepRetry(
|
|
224
232
|
def: WorkflowDefinition,
|
|
@@ -227,11 +235,14 @@ export async function scheduleTaskStepRetry(
|
|
|
227
235
|
taskId: string,
|
|
228
236
|
reason: string,
|
|
229
237
|
): Promise<TaskStepRetryOutcome> {
|
|
230
|
-
const
|
|
238
|
+
const foreachParent = resolveForeachParent(def, step.nodeId);
|
|
239
|
+
const node = resolveStepNode(def, step.nodeId);
|
|
231
240
|
if (!node || node.type !== "agent-task") return "not-eligible";
|
|
241
|
+
// A synthesized child node carries only the body; its retry lives on the parent.
|
|
242
|
+
const declaredRetry = foreachParent ? foreachParent.retry : node.retry;
|
|
232
243
|
let policy: RetryPolicy;
|
|
233
|
-
if (
|
|
234
|
-
const parsed = RetryPolicySchema.safeParse(
|
|
244
|
+
if (declaredRetry) {
|
|
245
|
+
const parsed = RetryPolicySchema.safeParse(declaredRetry);
|
|
235
246
|
if (!parsed.success) return "not-eligible";
|
|
236
247
|
policy = parsed.data;
|
|
237
248
|
} else {
|
|
@@ -243,6 +254,15 @@ export async function scheduleTaskStepRetry(
|
|
|
243
254
|
const current = await getWorkflowRunStep(step.id);
|
|
244
255
|
if (!current || current.status !== "waiting") return "not-claimed";
|
|
245
256
|
if (!(await isTaskBoundToStep(taskId, step.id))) return "not-claimed";
|
|
257
|
+
// A sibling's fail-fast or another branch can fail the run while the
|
|
258
|
+
// caller awaited. A child queued then would revive the failed run, so the
|
|
259
|
+
// caller records the cut as the item's late outcome instead.
|
|
260
|
+
if (foreachParent) {
|
|
261
|
+
const run = await getWorkflowRun(runId);
|
|
262
|
+
if (run?.status !== "running" && run?.status !== "waiting") return "not-eligible";
|
|
263
|
+
}
|
|
264
|
+
// No retry is queued inside a cancelled tree.
|
|
265
|
+
if (await getRunLineageBlock(runId)) return "not-claimed";
|
|
246
266
|
if (current.retryCount >= policy.maxRetries) return "not-eligible";
|
|
247
267
|
await detachTaskFromWorkflowRunStep(taskId);
|
|
248
268
|
await checkpointStepFailure(runId, step.id, reason, current.retryCount, policy);
|
|
@@ -280,6 +300,8 @@ export async function completeTaskStepAndResolveSuccessors(
|
|
|
280
300
|
if (!current || current.status !== "waiting") return UNCLAIMED;
|
|
281
301
|
// A stale snapshot of a step since redriven to a fresh task must not route it.
|
|
282
302
|
if (ownerTaskId && !(await isTaskBoundToStep(ownerTaskId, step.id))) return UNCLAIMED;
|
|
303
|
+
// A late result inside a cancelled tree routes nothing and revives nothing.
|
|
304
|
+
if (await getRunLineageBlock(runId)) return UNCLAIMED;
|
|
283
305
|
|
|
284
306
|
if (resolveForeachParent(def, step.nodeId)) {
|
|
285
307
|
// onNodeFailure:"continue" completions persist the failure reason as
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"commit": "
|
|
2
|
+
"commit": "5a577022800d9b43ccec5376bfe16c7b24eecd91",
|
|
3
3
|
"excludedSkills": [
|
|
4
4
|
"feedback"
|
|
5
5
|
],
|
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
"templates/skills/comms/files/references/ste-rules.md": "6ee2ed5d6b49d2e421b6c9d22310a5b589fa1abe81de06213e4b89fbc1271e74",
|
|
17
17
|
"templates/skills/comms/files/references/visual-shapes.md": "5e07e9250ad4cdc877af2281525185f0de89c01f90a077463a2c77c8a7b998e8",
|
|
18
18
|
"templates/skills/delegate-work/config.json": "5b1542eb0be359bb8a17117fcf87917e0607d1b939ae3266ddffacdc14efb281",
|
|
19
|
-
"templates/skills/delegate-work/content.md": "
|
|
20
|
-
"templates/skills/delegate-work/files/scripts/codex-exec.sh": "
|
|
19
|
+
"templates/skills/delegate-work/content.md": "1046da2fbc5603c4deed92beb6d98c443df21e87cf83a7d7b3ce58dbc9d01997",
|
|
20
|
+
"templates/skills/delegate-work/files/scripts/codex-exec.sh": "4524a224fe0657d2e2872d97a70357c4a3ce84d2241bd935ed83febf1fac6e37",
|
|
21
21
|
"templates/skills/design-docs/config.json": "b78141763541486d67a105e8fa23873c9ffee6a682832c4b7266b8320d55ead7",
|
|
22
22
|
"templates/skills/design-docs/content.md": "fc860031defbd192c1b8e1c024b46509d78dcf10dbf1b8c93cc4d62396153a52",
|
|
23
23
|
"templates/skills/design-docs/files/template.md": "72d2cdcf8713c08d525d69892c5fed536107594522e3e799ae44f51df7015a36",
|
|
@@ -38,13 +38,13 @@
|
|
|
38
38
|
"templates/skills/planning/content.md": "543476e4dbb5c352acd686c3929c45cc992d7d033d06f7e9a30f78009b50785c",
|
|
39
39
|
"templates/skills/planning/files/template.md": "c0d5ca63c07934546172322bbd1180e2901dc45e27b5d12ce3dc5c30f5e7a98b",
|
|
40
40
|
"templates/skills/qa/config.json": "c53b01558a691c0343b6270a548e6172af86600a429b789eedb518849f7ea741",
|
|
41
|
-
"templates/skills/qa/content.md": "
|
|
41
|
+
"templates/skills/qa/content.md": "46b1147d8af1a292d9bcd7091385d2c98264f10bb5afe4fb61073113eb0a6574",
|
|
42
42
|
"templates/skills/qa/files/template.md": "1902e4bb65b19ab58c54ab5b63693ad09adecee69079c2c70f58d7d6c6597896",
|
|
43
43
|
"templates/skills/questioning/config.json": "da2d5fe6dfdac1915a0476a7063e62ebeb996b84caff07cc7527a55477656a80",
|
|
44
44
|
"templates/skills/questioning/content.md": "66f826a191e96d0041adca00a7c197a458de8e6fe164c1599b8708098d4109ab",
|
|
45
45
|
"templates/skills/questioning/files/template.md": "a68d72801a0b10380bf773b07e9d2f8d4c5b4b1c609ce954289e38ffc78cc223",
|
|
46
46
|
"templates/skills/researching/config.json": "921c062cab599097cc7677d2dad4797ec4d5844d2ee73607fbd417ec3fa487d4",
|
|
47
|
-
"templates/skills/researching/content.md": "
|
|
47
|
+
"templates/skills/researching/content.md": "92951142327415696726a2577defdb2379cc611cdafa9cdac92431a5e60f618f",
|
|
48
48
|
"templates/skills/researching/files/template.md": "411d34b98c610ef715edf0f24d09f828ba2d0f65f688e7879ef33a9453007949",
|
|
49
49
|
"templates/skills/reviewing/config.json": "09c40bb098e9738c60a848995fa9fe7568d158631a089c76ba02c0dd955445c7",
|
|
50
50
|
"templates/skills/reviewing/content.md": "1bbc285c9ad8cc3312f1ee2c558b04ffee914ffb6f5c63fb3741fcbb3e300fa5",
|
|
@@ -130,13 +130,13 @@
|
|
|
130
130
|
},
|
|
131
131
|
{
|
|
132
132
|
"from": "`${CLAUDE_PLUGIN_ROOT}/skills/delegate-work/scripts/codex-exec.sh`",
|
|
133
|
-
"line":
|
|
133
|
+
"line": 70,
|
|
134
134
|
"skill": "delegate-work",
|
|
135
135
|
"to": "`bash ~/.claude/skills/delegate-work/scripts/codex-exec.sh`"
|
|
136
136
|
},
|
|
137
137
|
{
|
|
138
138
|
"from": "| codex-exec.sh -m",
|
|
139
|
-
"line":
|
|
139
|
+
"line": 73,
|
|
140
140
|
"skill": "delegate-work",
|
|
141
141
|
"to": "| bash ~/.claude/skills/delegate-work/scripts/codex-exec.sh -m"
|
|
142
142
|
},
|
|
@@ -178,7 +178,7 @@
|
|
|
178
178
|
},
|
|
179
179
|
{
|
|
180
180
|
"from": "cc-plugin/base/skills/researching/template.md",
|
|
181
|
-
"line":
|
|
181
|
+
"line": 115,
|
|
182
182
|
"skill": "researching",
|
|
183
183
|
"to": "template.md"
|
|
184
184
|
},
|
|
@@ -10,7 +10,7 @@ The worked example is a 24-second video about a model checker finding race condi
|
|
|
10
10
|
- Every number on screen read live from a named source into `facts.json` right before render.
|
|
11
11
|
- The real Agent Swarm logo on the end card, never a redrawn one.
|
|
12
12
|
- A contact sheet per critique round, so a reviewer sees what the maker saw.
|
|
13
|
-
- Delivery to agent-fs, with a
|
|
13
|
+
- Delivery to agent-fs, with a live app link per mp4.
|
|
14
14
|
|
|
15
15
|
## Template Files
|
|
16
16
|
|
|
@@ -120,10 +120,9 @@ The maker uploads to `thoughts/<maker-agent-id>/videos/<slug>/` (versions as `<s
|
|
|
120
120
|
|
|
121
121
|
```bash
|
|
122
122
|
agent-fs --org <ORG_ID> --drive <DRIVE_ID> write thoughts/<maker-agent-id>/videos/<slug>/<file>.mp4 --file ./out/<file>.mp4
|
|
123
|
-
agent-fs --org <ORG_ID> --drive <DRIVE_ID> share-create thoughts/<maker-agent-id>/videos/<slug>/<file>.mp4 --expires-in 604800 --json
|
|
124
123
|
```
|
|
125
124
|
|
|
126
|
-
|
|
125
|
+
Hand each mp4 over as its live app link `${AGENT_FS_LIVE_URL}/file/~/<org>/<drive>/<path>`: the viewer plays it for a signed-in teammate. Make a `share-create` link only when the brief asks for one or names a reader without an agent-fs login (a customer, a public post, an outsider). Then run `agent-fs --org <ORG_ID> --drive <DRIVE_ID> share-create <path> --expires-in 604800 --json`; the share page plays with no login for 7 days at most. Report the url, the share id and the expiry. A raw signed URL downloads instead of playing, and agent-fs serves a raw `.mp4` as `application/octet-stream`. For a byte-exact copy back down: `agent-fs download <path> -o <file>` (`cat` is paginated and truncates).
|
|
127
126
|
|
|
128
127
|
**Not run** for this playbook: `write` to a real drive and `share-create` (it publishes a public link). `write --file`, `share-create` and `signed-url --inline` flags were checked against the CLI's `--help`. `agent-fs download` was run on the example master.
|
|
129
128
|
|
|
@@ -261,8 +260,8 @@ The maker's output is not the finish line. Before relaying, the Lead:
|
|
|
261
260
|
2. Opens the end-card frame: real logo, credit line present and readable.
|
|
262
261
|
3. Checks every on-screen number against the maker's `facts.json`, and `facts.json` against the source named in the brief.
|
|
263
262
|
4. Checks the ffprobe line: `h264`, `yuv420p`, exactly 1920x1080 (and 1080x1350), duration as briefed, and an `aac` audio stream when the video has music. (`ffprobe` after `setup.sh`, or `ffmpeg -i` stderr in the stock image.)
|
|
264
|
-
5. Plays the
|
|
265
|
-
6. Relays the
|
|
263
|
+
5. Plays the live app link once.
|
|
264
|
+
6. Relays the live app link (plus the share url, id and expiry when one was asked for) and the `attribution` string from `music/current.json` (source link, license link, changes made). The maker never posts.
|
|
266
265
|
|
|
267
266
|
## Known Gotchas
|
|
268
267
|
|
|
@@ -274,7 +273,7 @@ The maker's output is not the finish line. Before relaying, the Lead:
|
|
|
274
273
|
| PNG frames encoded by libx264 without `-pix_fmt` | The output is `yuv444p` (High 4:4:4 Predictive), which many players and uploaders reject | Always `-pix_fmt yuv420p`. `render.mjs` does. |
|
|
275
274
|
| Full-range `yuvj420p` | Rendered by `remotion render` by default | `--color-space=bt709`, or `x-reencode.sh` |
|
|
276
275
|
| Remotion's bundled ffmpeg is stripped | no `fps`, `tile`, `hstack` filters | Use the image's `/usr/local/bin/ffmpeg` for analysis and sheets |
|
|
277
|
-
| agent-fs serves `.mp4` as `application/octet-stream` | A content-type check on a raw link fails |
|
|
276
|
+
| agent-fs serves `.mp4` as `application/octet-stream` | A content-type check on a raw link fails | Open it through the live app link, or a `share-create` link for a reader without a login. Do not retry the content-type check. |
|
|
278
277
|
| Signed URLs expire and default to attachment | A `<video src>` pointing at one downloads instead of playing, and stops working at the expiry | Never use one as a public URL. For a page, commit a compressed copy. `signed-url --inline` renders in a browser tab but still expires. |
|
|
279
278
|
| `agent-fs cat` truncates | Pagination hides the tail of a big file | `agent-fs download <path> -o <file>` for exact bytes |
|
|
280
279
|
| Container restart mid-render | The scratch dir is the only thing that survives | Keep it in `/workspace/personal/<slug>`, name it in the first progress note, upload an interim cut before the long render |
|
|
@@ -65,7 +65,7 @@ How:
|
|
|
65
65
|
|
|
66
66
|
Deliver to agent-fs thoughts/<maker-agent-id>/videos/<slug>/: <1920x1080 [+ 1080x1350]> h264 yuv420p mp4,
|
|
67
67
|
contact sheet(s), facts.json, reel.html.
|
|
68
|
-
Output: agent-fs paths, a
|
|
68
|
+
Output: agent-fs paths, a live app link per mp4, ffprobe line, model you ran on,
|
|
69
69
|
each on-screen number with its source, music attribution (source link, license link, changes made).
|
|
70
70
|
```
|
|
71
71
|
|
|
@@ -54,7 +54,7 @@ How:
|
|
|
54
54
|
|
|
55
55
|
Deliver to agent-fs thoughts/<maker-agent-id>/videos/<slug>/: <1920x1080 [+ 1080x1350]> h264 yuv420p mp4,
|
|
56
56
|
contact sheet(s), facts.json.
|
|
57
|
-
Output: agent-fs paths, a
|
|
57
|
+
Output: agent-fs paths, a live app link per mp4, ffprobe line, model you ran on, each on-screen number with its source, music attribution (source link, license link, changes made).
|
|
58
58
|
```
|
|
59
59
|
|
|
60
60
|
Optional style add-on for a calmer, premium cut (the "Apple framework"): 3 colors, one background, one font, no HUD/grain; one shot = one idea; ~3 s open and ~2 s held end; key object centered; eased, overlapping keyframes; no hard cuts; ~108 BPM, phrase hit on the first product frame; no SFX. Add it only when asked.
|
|
@@ -127,9 +127,9 @@ window.frame = (f) => { render(f); return cv.toDataURL("image/png"); };
|
|
|
127
127
|
|
|
128
128
|
- agent-fs: `thoughts/<maker-agent-id>/videos/<slug>/` (versions as `<slug>-v2`, `<slug>-v3`, never overwrite an earlier version).
|
|
129
129
|
- Upload the mp4s, contact sheet(s), `facts.json`, the music json, and `reel.html` so the next round can start from it. After every non-trivial write, `agent-fs stat <path> --json` and check the size.
|
|
130
|
-
-
|
|
131
|
-
- agent-fs serves a raw `.mp4` download as `application/octet-stream`; the share page
|
|
132
|
-
- Output (short): paths, share
|
|
130
|
+
- Link for each final mp4: the live app link `${AGENT_FS_LIVE_URL}/file/~/<org>/<drive>/<path>`; its viewer plays the mp4 for a signed-in teammate. Make a `share-create` link only when the brief asks for one or names a reader without an agent-fs login (a customer, a public post, an outsider). Then run `agent-fs --org <ORG_ID> --drive <DRIVE_ID> share-create <path> --expires-in 604800 --json`; the share page plays the video with no login for 7 days at most. Report the url, the share id and the expiry. Do not hand out raw signed URLs for video.
|
|
131
|
+
- agent-fs serves a raw `.mp4` download as `application/octet-stream`; the viewer and the share page play it. Mention it rather than retrying.
|
|
132
|
+
- Output (short): paths, live app links (plus share url, id and expiry only if asked), ffprobe line, model, each on-screen fact with its source and SHA/run id, music attribution (source link, license link, changes made), logo source. No Slack post by the maker; Lead relays.
|
|
133
133
|
|
|
134
134
|
## Feedback rounds (v2, v3)
|
|
135
135
|
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
# comms-adherence-gate
|
|
2
2
|
|
|
3
|
-
Agents finish Slack tasks with a final message a human reads. This extension judges that
|
|
3
|
+
Agents finish Slack tasks with a final message a human reads, and post to Slack threads directly. This extension judges that text with Jev, [TypeSafe](https://typesafe.ai)'s decision model: against the requester's stated comms preferences and their latest request, and against a plain-language rubric that applies to every message. In `enforce` it blocks a failing call once and tells the agent what to fix. A `plain` failure points the agent at the `comms` skill.
|
|
4
4
|
|
|
5
5
|
Install config:
|
|
6
6
|
|
|
7
7
|
```json
|
|
8
|
-
{ "mode": "shadow" }
|
|
8
|
+
{ "mode": "shadow", "plainMode": "shadow" }
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
Start in `shadow`, read the verdicts in KV for a few days, then switch to `enforce`.
|
|
11
|
+
Start in `shadow`, read the verdicts in KV for a few days, then switch to `enforce`. `mode` and `plainMode` move independently.
|
|
12
12
|
|
|
13
13
|
## Modes
|
|
14
14
|
|
|
@@ -16,14 +16,22 @@ Start in `shadow`, read the verdicts in KV for a few days, then switch to `enfor
|
|
|
16
16
|
|---|---|
|
|
17
17
|
| `off` | Never evaluates. |
|
|
18
18
|
| `shadow` (default) | Asks Jev and records the verdict. Never blocks. |
|
|
19
|
-
| `enforce` | Asks Jev, records the verdict, and blocks the
|
|
19
|
+
| `enforce` | Asks Jev, records the verdict, and blocks the 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
|
+
`mode` governs the preference dimensions (`verbosity`, `tone`, `language`). `plainMode` governs `plain`. A dimension blocks only when its own mode is `enforce`. Both share one block budget per task, across every gated tool.
|
|
20
22
|
|
|
21
23
|
## What it gates
|
|
22
24
|
|
|
23
|
-
Hook: `pre.tool.call` on
|
|
25
|
+
Hook: `pre.tool.call` on:
|
|
26
|
+
|
|
27
|
+
- `store-progress` with `status: "completed"` and a non-empty `output`, on a task that came from Slack (`source = "slack"` or a `slackThreadTs`). Other completions pass without a Jev call.
|
|
28
|
+
- `slack-reply`, `slack-post`, `slack-start-thread` and `slack-update`, judged on `message`. The task is `slack-reply`'s `taskId`, else the caller's task context. A post with no task context passes.
|
|
29
|
+
|
|
30
|
+
Only a call the tool itself accepts is judged, so no other task's text reaches Jev. For `store-progress` the caller must own the task, the task must be unassigned, or the caller must be a lead. For the Slack tools the caller must be the task's assignee or its creator, the rule `slack-reply` enforces. Calls made by extensions are skipped. Script SDK calls never reach `pre.tool.call` (the tool registrar dispatches it only for MCP calls); if one ever does, the gate records it and never blocks.
|
|
24
31
|
|
|
25
|
-
|
|
26
|
-
|
|
32
|
+
A requester with no stated preferences in `users.metadata.comms` still gets the `plain` question. Set preferences with the `manage-user` tool.
|
|
33
|
+
|
|
34
|
+
In a thread with several people, the requester is the person whose message created the task. A message that @-mentions someone (`<@U123>`) is judged against the first mentioned person with stored preferences, resolved through their Slack identity in `user_external_ids`; otherwise against the requester. The lookup is one SQL read, only when the text mentions someone and `mode` is not `off`. The verdict records who was judged in `judgedFor` (`mention` or `requester`) and `audience`.
|
|
27
35
|
|
|
28
36
|
## Decision
|
|
29
37
|
|
|
@@ -31,7 +39,21 @@ The gate reads the task row and asks one `noul` question per stated preference i
|
|
|
31
39
|
|
|
32
40
|
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
41
|
|
|
34
|
-
|
|
42
|
+
It also asks `plain` on every message unless `plainMode` is `off` or the text is a JSON document (structured output for an `outputSchema`). The rubric comes from the `comms` skill: everyday words, one idea per sentence, every number with a unit and a direction word, and no unexplained shorthand (n, median, p90, cohort, baseline, delta, `±X at 95%`). Code identifiers, task ids, PR numbers, links and versions are fine. Jev gets contrasting plain and dense examples in `plain_language_rules`; a prose-only rubric scored plain rewrites as low as the dense originals.
|
|
43
|
+
|
|
44
|
+
All questions go in one Jev request. A preference dimension fails below `threshold`; `plain` fails below `plainThreshold`. Any failed dimension fails the message.
|
|
45
|
+
|
|
46
|
+
### Calibration of `plain`
|
|
47
|
+
|
|
48
|
+
Jev scores dense status text near 0 and plain text near 0.8 to 0.9, with a wide middle of compact but readable engineering notes. Measured on 400 Slack-facing completions over 7 days, at `threshold` 0.75 for preferences:
|
|
49
|
+
|
|
50
|
+
| `plainThreshold` | Newly blocked (of messages that pass today) |
|
|
51
|
+
|---|---|
|
|
52
|
+
| 0.75 | 81% to 83% |
|
|
53
|
+
| 0.3 | 9% to 13% |
|
|
54
|
+
| 0.25 (default) | 5% to 7% |
|
|
55
|
+
|
|
56
|
+
At 0.25 the dense prompt-cache-monitor post scores about 0.15 and its plain rewrite about 0.81.
|
|
35
57
|
|
|
36
58
|
## Jev transport
|
|
37
59
|
|
|
@@ -51,14 +73,16 @@ Every error lets the completion through: a missing key, an HTTP error, a network
|
|
|
51
73
|
|
|
52
74
|
## Self-test
|
|
53
75
|
|
|
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`:
|
|
76
|
+
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 a dense and a plain message on `plain`, then writes `selftest:last`: every score, both thresholds, the provider, `controlsOk` (verbosity: bad below `threshold`, good at or above it) and `plainControlsOk` (the same against `plainThreshold`). Real traffic is untouched.
|
|
55
77
|
|
|
56
78
|
## Config
|
|
57
79
|
|
|
58
80
|
| Key | Default | Notes |
|
|
59
81
|
|---|---|---|
|
|
60
82
|
| `mode` | `shadow` | `off`, `shadow`, or `enforce` |
|
|
61
|
-
| `threshold` | `0.5` | A dimension scoring below this fails |
|
|
83
|
+
| `threshold` | `0.5` | A preference dimension scoring below this fails |
|
|
84
|
+
| `plainMode` | `shadow` | `off`, `shadow`, or `enforce` for the `plain` dimension |
|
|
85
|
+
| `plainThreshold` | `0.25` | `plain` scoring below this fails. See [Calibration](#calibration-of-plain) |
|
|
62
86
|
| `timeoutMs` | `4000` | Jev call timeout, capped at 4000 (the dispatcher cancels a handler at 5 s) |
|
|
63
87
|
| `maxBlocksPerTask` | `1` | Blocks per task in `enforce`; after that the gate records and lets it through |
|
|
64
88
|
| `provider` | `auto` | `auto`, `typesafe`, or `openrouter`. See [Jev transport](#jev-transport) |
|
|
@@ -76,13 +100,14 @@ No key is written to config, KV, logs, or a block reason.
|
|
|
76
100
|
|
|
77
101
|
## KV namespace `ext:comms-gate`
|
|
78
102
|
|
|
79
|
-
- `last`: the latest verdict, with `scores`, `failed`, `provider`, `latencyMs`, the preferences, a 400-character `askPreview` and a 700-character `outputPreview`.
|
|
103
|
+
- `last`: the latest verdict, with `tool`, `scores`, `failed`, `mode`, `plainMode`, `provider`, `latencyMs`, the preferences, a 400-character `askPreview` and a 700-character `outputPreview`.
|
|
80
104
|
- `fail:<epochMs>:<taskId>`: one row per failed verdict, with `action` (`blocked` or `recorded`).
|
|
81
105
|
- `blocks:<taskId>`: blocks issued for that task.
|
|
82
106
|
- `error:last`, `selftest:last`.
|
|
83
|
-
- Counters: `total`, `pass`, `fail`, `blocked`, `escaped` (
|
|
107
|
+
- Counters: `total`, `pass`, `fail`, `blocked`, `escaped` (a blocking dimension failed, block budget spent), `shadowed` (only non-blocking dimensions failed), `error`, and `dim:<dimension>`.
|
|
84
108
|
|
|
85
109
|
## Known limits
|
|
86
110
|
|
|
87
111
|
- 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
|
|
112
|
+
- The block budget is per task, so a retried call after a block goes through once the budget is spent, even if it still fails.
|
|
113
|
+
- Each gated Slack post waits for one Jev call (about 0.2 s measured, capped by `timeoutMs`).
|