@desplega.ai/agent-swarm 2.5.1 → 2.5.3
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-7g6bpwbk.js → acp-adapter-gr2fcyyj.js} +3 -3
- package/dist/{actions-w8xd014k.js → actions-wv766ghf.js} +18 -18
- package/dist/{amp-adapter-z4qvhacj.js → amp-adapter-speb906t.js} +3 -3
- package/dist/{app-nndcvftq.js → app-ag29n14f.js} +3 -3
- package/dist/{assistant-hjmqyscx.js → assistant-3x8bz3eg.js} +19 -19
- package/dist/{boot-reembed-r8rhf9h8.js → boot-reembed-5b3d2eea.js} +1 -1
- package/dist/{boot-reembed-vwcj2y5d.js → boot-reembed-jsmfrpzf.js} +1 -1
- package/dist/{boot-scrub-sweep-fr9q36ne.js → boot-scrub-sweep-s73gerxy.js} +2 -2
- package/dist/{boot-seeds-9jqwnegq.js → boot-seeds-5zmax2wr.js} +4 -4
- package/dist/{claude-adapter-2f8b85df.js → claude-adapter-hswdnm3t.js} +3 -3
- package/dist/{claude-managed-adapter-gt6pa35d.js → claude-managed-adapter-cvbzj1h6.js} +2 -2
- package/dist/{claude-managed-setup-c3hp36ph.js → claude-managed-setup-3k8m3hhc.js} +2 -2
- package/dist/{claude-sdk-session-05kat8rv.js → claude-sdk-session-fkbyk66x.js} +6905 -6855
- package/dist/{cli-y3zyaee5.js → cli-1fc85mvt.js} +5 -5
- package/dist/{cli-v2g93k4v.js → cli-210xd1f4.js} +4 -4
- package/dist/{cli-sn1vbtg2.js → cli-25j01dpj.js} +3 -3
- package/dist/{cli-n6kmqngw.js → cli-35gtk1p6.js} +1 -1
- package/dist/{cli-r9fy0ddc.js → cli-3ngdk5te.js} +1 -1
- package/dist/{cli-z63a269p.js → cli-40jsnrjm.js} +2 -2
- package/dist/{cli-ba8ne1es.js → cli-4rvr7zfx.js} +10 -0
- package/dist/{cli-qfm6my86.js → cli-502eqv93.js} +1 -1
- package/dist/{cli-xd4eqtr2.js → cli-6n8axjv1.js} +1 -1
- package/dist/{cli-hadv0725.js → cli-8s5wx3nz.js} +2 -2
- package/dist/{cli-z2dv25mf.js → cli-99xgw26r.js} +116 -5
- package/dist/{cli-jpxsrxcb.js → cli-9s05422w.js} +1 -1
- package/dist/{cli-kcsd1ez8.js → cli-aecdcr77.js} +343 -325
- package/dist/{cli-w9etnnxk.js → cli-bf8ma3f1.js} +120 -50
- package/dist/{cli-e9btqw4y.js → cli-ce0ak9ms.js} +18 -13
- package/dist/{cli-nj12tn2c.js → cli-e2ar4bp1.js} +71 -20
- package/dist/{cli-gwcf52gz.js → cli-e8jyq7d8.js} +11 -7
- package/dist/{cli-rngde2jv.js → cli-egjggehm.js} +1 -1
- package/dist/{cli-qfjm4gvt.js → cli-ewm3pwbq.js} +2 -2
- package/dist/{cli-9mm5ade4.js → cli-fsdprwrg.js} +1 -1
- package/dist/{cli-3ee5z0q0.js → cli-fzcn3d87.js} +1 -1
- package/dist/{cli-eym3jg7w.js → cli-gpsz7g88.js} +6 -6
- package/dist/{cli-cwyafhw1.js → cli-hcpj2xqz.js} +1 -1
- package/dist/{cli-j1eghqfm.js → cli-hhxjnjzs.js} +1 -1
- package/dist/{cli-pyy4c9ch.js → cli-jjznk3f7.js} +881 -232
- package/dist/{cli-q9pzwxvk.js → cli-kxawjrxg.js} +222 -37
- package/dist/{cli-rh5smt8t.js → cli-mz9afxxf.js} +14 -3
- package/dist/{cli-mz2ygeyq.js → cli-n45mr7zh.js} +2 -2
- package/dist/{cli-fes51ya2.js → cli-p0rzjkya.js} +2 -2
- package/dist/{cli-3qv8naq8.js → cli-p6zs9h4y.js} +827 -107
- package/dist/{cli-mq7kg1b1.js → cli-p9rafdrb.js} +41 -6
- package/dist/{cli-cah5detr.js → cli-qgf8emrn.js} +5 -5
- package/dist/{cli-2wba1ac9.js → cli-qzme5949.js} +26 -26
- package/dist/{cli-znvq0k3t.js → cli-s49hpn37.js} +2 -2
- package/dist/{cli-xc65x52q.js → cli-s53vp709.js} +1 -1
- package/dist/{cli-0jy85zz4.js → cli-svrj71pg.js} +18 -16
- package/dist/{cli-1dp1zj04.js → cli-vwn81jbs.js} +3 -3
- package/dist/{cli-ajqhxtnz.js → cli-w1zwctj2.js} +1 -1
- package/dist/{cli-22rmcjkg.js → cli-y561h5a9.js} +7 -6
- package/dist/{cli-2jg61wab.js → cli-z21whrrg.js} +15 -15
- package/dist/cli.js +19 -18
- package/dist/{codex-adapter-p4ptwar5.js → codex-adapter-n8ka66vf.js} +2 -2
- package/dist/{codex-session-runner-964c7dnk.js → codex-session-runner-fsebts37.js} +1 -1
- package/dist/{commands-mbvhbrcy.js → commands-t04rt2pc.js} +2 -2
- package/dist/{controller-gnq1k1mf.js → controller-rgna04r2.js} +2 -2
- package/dist/{controllers-connect-6dkm71tg.js → controllers-connect-bqm1rtft.js} +20 -20
- package/dist/{cursor-adapter-yq5xgfcg.js → cursor-adapter-swr2k95y.js} +1 -1
- package/dist/{db-88wcymxt.js → db-24ck09xy.js} +1 -1
- package/dist/{db-af9y8nkf.js → db-a4m7haw0.js} +3 -3
- package/dist/{dispatcher-606ydx5v.js → dispatcher-nwf9ss0q.js} +17 -17
- package/dist/{dsh-adapter-tvhc4k2d.js → dsh-adapter-kpdnhjst.js} +2 -2
- package/dist/{external-channel-y8q5z8z2.js → external-channel-j1e2p34p.js} +2 -2
- package/dist/{grok-adapter-39aha52t.js → grok-adapter-qtjz0whk.js} +4 -4
- package/dist/{handlers-rk9dqech.js → handlers-19xr6dnz.js} +20 -20
- package/dist/{heartbeat-jfe9zfjr.js → heartbeat-vtesda5d.js} +20 -20
- package/dist/{hook-b1mez1m8.js → hook-8wqaszda.js} +1 -1
- package/dist/{hook-ffevzed1.js → hook-b2hhj6rd.js} +1 -1
- package/dist/{http-d5b44f1r.js → http-mxcqhhcs.js} +3020 -2463
- package/dist/{index-5en23s5z.js → index-1r8wfpzs.js} +1 -1
- package/dist/{index-cbxkmfc5.js → index-9pn1pfkf.js} +1 -1
- package/dist/{index-mj46rk9k.js → index-kw2mmat7.js} +20 -20
- package/dist/{index-nq8hhv28.js → index-n1vbvta9.js} +19 -10
- package/dist/{index-6srwvd5b.js → index-p4xfz37z.js} +1 -1
- package/dist/{index-d2qeem97.js → index-spwxjgay.js} +17 -17
- package/dist/{index-4q3vj1yr.js → index-srg522m6.js} +17 -17
- package/dist/{keepalive-wsby1btq.js → keepalive-dsd8rtgx.js} +3 -3
- package/dist/{lead-paa141j6.js → lead-zyerk1x2.js} +30 -30
- package/dist/{maintenance-vnshcyv9.js → maintenance-jqmqwnet.js} +1 -1
- package/dist/{oauth-refresh-sweep-d6sqn5np.js → oauth-refresh-sweep-q2c3bnvm.js} +2 -2
- package/dist/{onboard-nr4xhpzy.js → onboard-w4yd7s8t.js} +2 -2
- package/dist/{opencode-adapter-e56086m1.js → opencode-adapter-9tppvxar.js} +2 -2
- package/dist/{otel-impl-ytn9wyv9.js → otel-impl-f36byzbd.js} +1 -1
- package/dist/{pricing-refresh-pva04zee.js → pricing-refresh-xvak5p4v.js} +3 -3
- package/dist/{rbac-roles-7rc1q3g4.js → rbac-roles-682k2mt5.js} +12 -2
- package/dist/rbac-roles-gfn30fb8.js +26 -0
- package/dist/{render-v2-pyfsg9zk.js → render-v2-hgytz8nn.js} +3 -3
- package/dist/{resume-phj8xhqr.js → resume-2dvq348g.js} +7 -5
- package/dist/{session-access-jzx2z9h5.js → session-access-7f21ekq9.js} +4 -4
- package/dist/{setup-rfg0f0sg.js → setup-epffxtbk.js} +1 -1
- package/dist/{work-objects-44z2yjg8.js → work-objects-xz1a5rqk.js} +20 -20
- package/dist/{worker-zsfqzxhm.js → worker-42t8emjt.js} +30 -30
- package/openapi.json +3163 -2114
- package/package.json +7 -6
- package/src/be/approval-config-question.ts +389 -0
- package/src/be/automation-preflight.ts +37 -3
- package/src/be/db/session-tree.ts +158 -13
- package/src/be/db/session-visibility.ts +31 -0
- package/src/be/db/skill-search.ts +87 -0
- package/src/be/db/tasks/read.ts +47 -7
- package/src/be/db/workflow-runs.ts +54 -7
- package/src/be/db.ts +27 -5
- package/src/be/migrations/233_script_run_session_tokens.sql +8 -0
- package/src/be/migrations/235_chat_relay_defer.sql +86 -0
- package/src/be/migrations/237_session_inbox_cleanup.sql +75 -0
- 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/rbac-roles.ts +138 -0
- package/src/be/scripts/run-credential.ts +64 -0
- package/src/be/scripts/run-saved.ts +3 -2
- package/src/be/swarm-config-guard.ts +23 -0
- package/src/be/users.ts +28 -4
- package/src/extensions/catalog.generated.json +4 -4
- package/src/http/all-routes.ts +1 -0
- package/src/http/approval-requests.ts +254 -43
- package/src/http/config.ts +22 -37
- package/src/http/index.ts +2 -0
- package/src/http/schedules.ts +26 -0
- package/src/http/scripts.ts +2 -2
- package/src/http/sessions.ts +118 -2
- package/src/http/user-roles.ts +173 -0
- package/src/http/users.ts +9 -0
- package/src/http/x.ts +2 -2
- package/src/integrations/chat-sdk/commands.ts +14 -1
- package/src/integrations/chat-sdk/inbound.ts +42 -6
- package/src/integrations/chat-sdk/outbound.ts +35 -10
- package/src/integrations/chat-sdk/reactions.ts +107 -0
- package/src/integrations/chat-sdk/relay.ts +75 -7
- package/src/integrations/chat-sdk/thread-buffer.ts +2 -0
- package/src/integrations/reaction-events.ts +20 -0
- package/src/providers/amp-adapter.ts +1 -1
- package/src/providers/dsh-adapter.ts +1 -1
- package/src/rbac/legacy-policy.ts +3 -0
- package/src/rbac/permissions.ts +8 -0
- package/src/scheduler/schedule-task.ts +44 -3
- package/src/scheduler/scheduler.ts +4 -4
- package/src/scripts-runtime/loader.ts +8 -1
- package/src/slack/ack.ts +2 -1
- package/src/slack/reaction-shortcode.ts +4 -7
- package/src/tasks/worker-follow-up.ts +30 -0
- package/src/tests/approval-config-question.test.ts +702 -0
- package/src/tests/approval-requests-notifications.test.ts +189 -0
- package/src/tests/chat-sdk-commands.test.ts +12 -0
- package/src/tests/chat-sdk-inbound.test.ts +183 -9
- package/src/tests/chat-sdk-outbound-files.test.ts +9 -2
- package/src/tests/chat-sdk-relay.test.ts +361 -10
- package/src/tests/chat-sdk-tools.test.ts +104 -0
- package/src/tests/docs-index-footprint.test.js +3 -3
- 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/http-users.test.ts +38 -0
- package/src/tests/list-endpoint-slimming.test.ts +33 -0
- package/src/tests/migration-237-session-inbox-cleanup.test.ts +136 -0
- package/src/tests/migration-240-workflow-run-parent-step-attempt.test.ts +55 -0
- package/src/tests/rbac-engine.test.ts +2 -0
- package/src/tests/rbac-roles.test.ts +10 -0
- package/src/tests/schedule-http-triage-tools.test.ts +44 -0
- package/src/tests/script-api-credential.test.ts +173 -0
- package/src/tests/script-connections.test.ts +10 -0
- package/src/tests/script-run-config-scope.test.ts +120 -0
- package/src/tests/script-run-token.test.ts +75 -0
- package/src/tests/scripts-http.test.ts +19 -0
- package/src/tests/sessions-tree.test.ts +410 -1
- package/src/tests/skill-search-long-content.test.ts +193 -0
- package/src/tests/skill-search.test.ts +173 -22
- package/src/tests/user-roles-http.test.ts +204 -0
- package/src/tests/workflow-hitl-dynamic-questions.test.ts +1 -1
- package/src/tests/workflow-sub-workflow.test.ts +538 -1
- package/src/tools/chat-reply.ts +2 -0
- package/src/tools/request-human-input.ts +27 -5
- package/src/tools/schedules/patch-schedule.ts +18 -0
- package/src/tools/schedules/update-schedule.ts +18 -0
- package/src/types.ts +23 -0
- package/src/workflows/approval-notifications.ts +221 -0
- package/src/workflows/checkpoint.ts +69 -35
- package/src/workflows/engine.ts +234 -87
- package/src/workflows/executors/base.ts +6 -0
- package/src/workflows/executors/human-in-the-loop.ts +36 -173
- package/src/workflows/executors/sub-workflow.ts +45 -16
- package/src/workflows/executors/swarm-script.ts +2 -2
- package/src/workflows/recovery.ts +63 -1
- package/src/workflows/resume.ts +60 -21
- package/src/workflows/retry-poller.ts +25 -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/schedules/weekly-harness-upgrade-check/content.md +1 -1
- package/templates/skills/code-quality/content.md +1 -1
- package/templates/skills/turso-interaction/content.md +1 -1
- package/templates/skills/workflow-iterate/content.md +8 -1
- package/dist/rbac-roles-ff1ffmec.js +0 -12
- /package/dist/{cli-stxdrqs2.js → cli-avdvgcay.js} +0 -0
- /package/dist/{pi-mono-adapter-tzps8f38.js → pi-mono-adapter-tqsge1ay.js} +0 -0
|
@@ -61,6 +61,9 @@ export function startRetryPoller(registry: ExecutorRegistry, intervalMs = 5000):
|
|
|
61
61
|
// the run were read outside it: a cancel committing in between must
|
|
62
62
|
// win, or the step re-executes inside a cancelled run.
|
|
63
63
|
if (!(await claimRetry(run.id, step.id))) continue;
|
|
64
|
+
// Every result write below is fenced on this claim, so a cancel or
|
|
65
|
+
// another settle that commits while the executor runs wins.
|
|
66
|
+
const fence = { retryCount: step.retryCount };
|
|
64
67
|
|
|
65
68
|
// Rebuild `run.id`, upstream outputs and the node's declared-inputs
|
|
66
69
|
// context the way the original execution saw them. Without the alias
|
|
@@ -78,6 +81,8 @@ export function startRetryPoller(registry: ExecutorRegistry, intervalMs = 5000):
|
|
|
78
81
|
step.id,
|
|
79
82
|
scriptBodyInterpolationError(node.id, scriptBodyUnresolved),
|
|
80
83
|
step.retryCount,
|
|
84
|
+
undefined,
|
|
85
|
+
{ fence },
|
|
81
86
|
);
|
|
82
87
|
continue;
|
|
83
88
|
}
|
|
@@ -104,14 +109,17 @@ export function startRetryPoller(registry: ExecutorRegistry, intervalMs = 5000):
|
|
|
104
109
|
if (result.status === "failed") {
|
|
105
110
|
// Re-failed — use the EXISTING retryCount from the step.
|
|
106
111
|
// checkpointStepFailure handles marking run as failed if no retries left,
|
|
107
|
-
// or setting nextRetryAt for the next poll cycle.
|
|
108
|
-
|
|
112
|
+
// or setting nextRetryAt for the next poll cycle. A non-retryable
|
|
113
|
+
// failure skips the policy, as in executeStep.
|
|
114
|
+
const retryPolicy =
|
|
115
|
+
result.retryable === false ? undefined : node.retry || executor.retryPolicy;
|
|
109
116
|
await checkpointStepFailure(
|
|
110
117
|
run.id,
|
|
111
118
|
step.id,
|
|
112
119
|
result.error || "Retry failed",
|
|
113
120
|
step.retryCount,
|
|
114
121
|
retryPolicy,
|
|
122
|
+
{ fence, output: result.output },
|
|
115
123
|
);
|
|
116
124
|
} else if (
|
|
117
125
|
"async" in result &&
|
|
@@ -150,6 +158,12 @@ export function startRetryPoller(registry: ExecutorRegistry, intervalMs = 5000):
|
|
|
150
158
|
// find the parent already advanced.
|
|
151
159
|
const waiting = await checkpointStepWaiting(run.id, step.id, ctx);
|
|
152
160
|
if (!waiting) continue;
|
|
161
|
+
// As in executeStep: a child run that finished before the step
|
|
162
|
+
// parked had its event dropped.
|
|
163
|
+
if ((result as AsyncExecutorResult).waitFor === "workflow.child.finished") {
|
|
164
|
+
const { resumeFromChildRun } = await import("./resume");
|
|
165
|
+
await resumeFromChildRun(step.id, registry);
|
|
166
|
+
}
|
|
153
167
|
} else {
|
|
154
168
|
// Success! Re-run validation if configured before checkpointing.
|
|
155
169
|
let validationPassed: boolean | undefined;
|
|
@@ -168,6 +182,8 @@ export function startRetryPoller(registry: ExecutorRegistry, intervalMs = 5000):
|
|
|
168
182
|
step.id,
|
|
169
183
|
"Validation failed (mustPass)",
|
|
170
184
|
step.retryCount,
|
|
185
|
+
undefined,
|
|
186
|
+
{ fence },
|
|
171
187
|
);
|
|
172
188
|
continue;
|
|
173
189
|
}
|
|
@@ -186,6 +202,7 @@ export function startRetryPoller(registry: ExecutorRegistry, intervalMs = 5000):
|
|
|
186
202
|
"Validation failed, retrying",
|
|
187
203
|
step.retryCount,
|
|
188
204
|
retryPolicy,
|
|
205
|
+
{ fence },
|
|
189
206
|
);
|
|
190
207
|
continue;
|
|
191
208
|
}
|
|
@@ -197,7 +214,9 @@ export function startRetryPoller(registry: ExecutorRegistry, intervalMs = 5000):
|
|
|
197
214
|
result.nextPort = resolveValidationPort(node.next, result.nextPort, validationPassed);
|
|
198
215
|
|
|
199
216
|
// Validation passed (or no validation) — checkpoint and continue
|
|
200
|
-
await checkpointStep(run.id, step.id, step.nodeId, result, ctx)
|
|
217
|
+
if (!(await checkpointStep(run.id, step.id, step.nodeId, result, ctx, { fence }))) {
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
201
220
|
|
|
202
221
|
const port = result.nextPort || "default";
|
|
203
222
|
const successors = getSuccessors(workflow.definition, step.nodeId, port);
|
|
@@ -219,7 +238,9 @@ export function startRetryPoller(registry: ExecutorRegistry, intervalMs = 5000):
|
|
|
219
238
|
if (await failRunOnUnreadableReplay(run.id, err, step.id)) continue;
|
|
220
239
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
221
240
|
const retryPolicy = node.retry || executor.retryPolicy;
|
|
222
|
-
await checkpointStepFailure(run.id, step.id, errorMsg, step.retryCount, retryPolicy
|
|
241
|
+
await checkpointStepFailure(run.id, step.id, errorMsg, step.retryCount, retryPolicy, {
|
|
242
|
+
fence,
|
|
243
|
+
});
|
|
223
244
|
}
|
|
224
245
|
} catch (err) {
|
|
225
246
|
if (await failRunOnUnreadableReplay(step.runId, err, step.id)) continue;
|
|
@@ -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`).
|