@desplega.ai/agent-swarm 2.5.4 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/{acp-adapter-ek38cwsv.js → acp-adapter-dgfakfpw.js} +9 -9
- package/dist/{actions-92mepq03.js → actions-fbqma8aa.js} +21 -21
- package/dist/{amp-adapter-9gaxg015.js → amp-adapter-en0jy6hs.js} +4 -4
- package/dist/{app-ctn8gsgx.js → app-k8a5cgpp.js} +3 -3
- package/dist/{assistant-zyr5kz47.js → assistant-a16433gr.js} +29 -25
- package/dist/{boot-reembed-dwzfttdr.js → boot-reembed-6c1sjfnr.js} +1 -1
- package/dist/{boot-reembed-w75maga2.js → boot-reembed-f4nn561n.js} +1 -1
- package/dist/{boot-scrub-sweep-e9kq43kf.js → boot-scrub-sweep-d4w9gajf.js} +4 -4
- package/dist/{boot-seeds-x28v3fhb.js → boot-seeds-9bmkspyw.js} +4 -4
- package/dist/{claude-adapter-f0afqnne.js → claude-adapter-nys1kjxa.js} +9 -9
- package/dist/{claude-managed-adapter-x81a7r65.js → claude-managed-adapter-55207973.js} +2 -2
- package/dist/{claude-managed-setup-bqzwjd9x.js → claude-managed-setup-3cm35gey.js} +10 -3
- package/dist/{claude-sdk-session-7b7whkaq.js → claude-sdk-session-nw5yd8cp.js} +11 -11
- package/dist/{cli-c61mbdds.js → cli-03gg9pd1.js} +4 -4
- package/dist/{cli-kxpfx2xr.js → cli-0z1xjzwa.js} +2 -2
- package/dist/cli-1tem6ns9.js +894 -0
- package/dist/{cli-jbnn4vxb.js → cli-24j1jeeb.js} +3 -3
- package/dist/{cli-pzatvw94.js → cli-2sjhpsns.js} +2 -2
- package/dist/{cli-dew6x255.js → cli-39aet655.js} +2 -2
- package/dist/{cli-g1cw7xtt.js → cli-39rrw8yp.js} +1 -1
- package/dist/{cli-x0f2v69c.js → cli-3aqd4jaa.js} +12 -7
- package/dist/{cli-gbfxp48k.js → cli-3fwqy71j.js} +15 -15
- package/dist/{cli-7p4jxgtt.js → cli-3gfvmzht.js} +6 -6
- package/dist/{cli-03vpq45j.js → cli-3ndmpxxv.js} +2 -2
- package/dist/{cli-asq5ydm4.js → cli-4hxrf0p4.js} +131 -50
- package/dist/{cli-mz9afxxf.js → cli-4rf0f5vg.js} +15 -3
- package/dist/{cli-k4tjsdx9.js → cli-5rj83bam.js} +272 -195
- package/dist/{cli-sbwsxvtd.js → cli-5sckpqcx.js} +2 -2
- package/dist/{cli-cz5nmdxt.js → cli-683y0b3a.js} +2 -2
- package/dist/{cli-ethgy7ha.js → cli-6y51ryva.js} +2 -2
- package/dist/{cli-n4c8e3s5.js → cli-708wdy32.js} +1 -1
- package/dist/{cli-pasyvjmm.js → cli-737pyqxr.js} +1 -1
- package/dist/{cli-gx4rm0qx.js → cli-7enjwya9.js} +1 -1
- package/dist/{cli-6x9ma5w5.js → cli-803188pb.js} +1 -1
- package/dist/{cli-hh5we352.js → cli-9abdgn5t.js} +7 -7
- package/dist/{cli-ff1tm9n9.js → cli-a2rr0fmg.js} +1 -1
- package/dist/{cli-c952c7ac.js → cli-a34pr3w1.js} +2 -2
- package/dist/{cli-s682zgqz.js → cli-abkm71vr.js} +1 -1
- package/dist/{cli-mmhj8vd7.js → cli-ag2yrnt5.js} +11 -2
- package/dist/{cli-bvpgr9ny.js → cli-aj8djyqt.js} +5 -5
- package/dist/{cli-gwhjmh8g.js → cli-art7c1zh.js} +5 -5
- package/dist/{cli-5bnsc0e1.js → cli-az25fn5f.js} +4 -4
- package/dist/{cli-9s05422w.js → cli-bqztfyve.js} +1 -1
- package/dist/{cli-bn0rewsy.js → cli-c081bj3b.js} +2 -2
- package/dist/{cli-1r5ad7j5.js → cli-c2habvsf.js} +12 -12
- package/dist/{cli-64mh9mf3.js → cli-ca6banwz.js} +69 -17
- package/dist/{cli-25n3mv5d.js → cli-cz37jqss.js} +5 -5
- package/dist/{cli-6n8axjv1.js → cli-d2dpf95n.js} +2 -2
- package/dist/{cli-gn5af0x5.js → cli-emhjcj2y.js} +11 -11
- package/dist/{cli-6a1xg1p7.js → cli-esakngh8.js} +2 -2
- package/dist/{cli-ha9rmwgq.js → cli-fb8m699c.js} +2 -2
- package/dist/{cli-4b2z6n0m.js → cli-ffvsj2qw.js} +17 -8
- package/dist/{cli-abs8ndtr.js → cli-g20jjwt4.js} +26 -26
- package/dist/{cli-jb30dzvk.js → cli-g8ka5j4j.js} +446 -164
- package/dist/{cli-19202kew.js → cli-gf95q3bm.js} +5 -5
- package/dist/{cli-eftzyh9y.js → cli-gnsb1zxa.js} +2 -2
- package/dist/{cli-nwyw3zhg.js → cli-gxee0bqa.js} +1 -1
- package/dist/{cli-e42vss7b.js → cli-hfeb68z4.js} +5 -5
- package/dist/{cli-g9vd9ece.js → cli-k21gx5ht.js} +14 -16
- package/dist/{cli-tsma7p8v.js → cli-kf4jree9.js} +2 -2
- package/dist/{cli-dabehxhp.js → cli-kt907da6.js} +1 -1
- package/dist/{cli-wx2nj19m.js → cli-m5a9m0ak.js} +6 -6
- package/dist/{cli-621ce7kw.js → cli-m70bajzr.js} +3 -3
- package/dist/{cli-9j129brq.js → cli-mtnj4fwa.js} +6 -6
- package/dist/{cli-xzfq4at3.js → cli-mvjz5an3.js} +15 -11
- package/dist/{cli-277c31dw.js → cli-n7asmce6.js} +225 -3
- package/dist/{cli-xtrz6bzs.js → cli-nn6vx2mq.js} +1 -1
- package/dist/{cli-2655k16q.js → cli-nq677csy.js} +1 -1
- package/dist/{cli-0shx9j93.js → cli-pfk92eqz.js} +2 -2
- package/dist/{cli-wghjkr0e.js → cli-sqma6vw1.js} +10 -7
- package/dist/{cli-nzfnt2gs.js → cli-tf1n0r0b.js} +1 -1
- package/dist/{cli-qqzmjxkf.js → cli-v49fkghp.js} +11 -6
- package/dist/{cli-x77gen2p.js → cli-w2656ka1.js} +7 -7
- package/dist/{cli-5kqnemen.js → cli-yvy48785.js} +1 -1
- package/dist/{cli-m4ecdxvz.js → cli-zqne1qcj.js} +7 -7
- package/dist/cli.js +17 -17
- package/dist/{codex-adapter-wch12q99.js → codex-adapter-d95q4qsn.js} +7 -7
- package/dist/{codex-login-rn7c5gxt.js → codex-login-8cg0vhf2.js} +1 -1
- package/dist/{codex-session-runner-41pfcr95.js → codex-session-runner-gdwd8h0c.js} +8 -8
- package/dist/{commands-9849dkj6.js → commands-jqmgk54b.js} +2 -2
- package/dist/{controller-ja0sd95p.js → controller-z3jq589f.js} +4 -4
- package/dist/{controllers-connect-xp05dj6x.js → controllers-connect-r2za39ef.js} +23 -23
- package/dist/{cursor-adapter-623qxqew.js → cursor-adapter-4mr4jgkw.js} +2 -2
- package/dist/{db-47vj40dy.js → db-ppv2tze7.js} +3 -3
- package/dist/{db-w759qmx0.js → db-w36wwb5g.js} +4 -2
- package/dist/{devin-adapter-4ycy4vrf.js → devin-adapter-byen2cxt.js} +1 -1
- package/dist/{dispatcher-7b8e4z2j.js → dispatcher-pv5h77vf.js} +20 -20
- package/dist/{docker-s84hjs91.js → docker-fdzyewpz.js} +2 -2
- package/dist/{dsh-adapter-2ezw3xea.js → dsh-adapter-d4pdpb3r.js} +2 -2
- package/dist/{e2b-9gkdhm3c.js → e2b-222hsajc.js} +2 -2
- package/dist/{e2b-cjtf8k1z.js → e2b-fn3zacvt.js} +2 -2
- package/dist/{easter-egg-3d-e4qv8dxj.js → easter-egg-3d-4sp4dvmc.js} +1 -1
- package/dist/{event-bus-3z78ep5n.js → event-bus-pghg8hqy.js} +1 -1
- package/dist/{execute-0xpz8950.js → execute-jev839y1.js} +1 -1
- package/dist/{external-channel-e2s028da.js → external-channel-y6nkzhnn.js} +2 -2
- package/dist/{grok-adapter-ys7gaqee.js → grok-adapter-t08b9ntm.js} +10 -10
- package/dist/handlers-eqsnmv95.js +41 -0
- package/dist/{heartbeat-w3mntghj.js → heartbeat-vn31xwdk.js} +23 -23
- package/dist/{hook-g7kvv6x9.js → hook-6d0zbvvt.js} +6 -6
- package/dist/{hook-3jjywnpb.js → hook-cpjs8q51.js} +8 -8
- package/dist/{http-tkp7d4kb.js → http-9c6medda.js} +856 -99
- package/dist/{index-xdaahthb.js → index-38t35h0n.js} +20 -20
- package/dist/{index-3n8m9ea2.js → index-g6k9tctj.js} +1 -1
- package/dist/{index-1ef1myq2.js → index-k0yt1tvq.js} +20 -20
- package/dist/{index-31ceyf42.js → index-pkma1yf8.js} +1 -1
- package/dist/{index-q7m26kvs.js → index-qhjf2htm.js} +13 -10
- package/dist/index-yqgbfgaj.js +43 -0
- package/dist/{keepalive-v4ta8wgg.js → keepalive-em0117bp.js} +4 -4
- package/dist/lead-23e7z59t.js +79 -0
- package/dist/{maintenance-84n1ex21.js → maintenance-kbme3qy9.js} +1 -1
- package/dist/{oauth-refresh-sweep-vjnnandw.js → oauth-refresh-sweep-xysj4yse.js} +3 -3
- package/dist/{onboard-sfmpe4np.js → onboard-p6q0vz97.js} +3 -3
- package/dist/{opencode-adapter-jmpst4q4.js → opencode-adapter-esv3k9yn.js} +7 -7
- package/dist/{otel-impl-v6cc0zve.js → otel-impl-54qhqh1z.js} +2 -2
- package/dist/{pi-mono-adapter-fk8fkcf6.js → pi-mono-adapter-mymbwhj0.js} +10 -10
- package/dist/{pricing-refresh-fsk1e1zc.js → pricing-refresh-yb75wv33.js} +3 -3
- package/dist/{rbac-roles-prmkdnkw.js → rbac-roles-1vp2mw4p.js} +2 -2
- package/dist/{rbac-roles-zxmxqa39.js → rbac-roles-dexhc9x8.js} +1 -1
- package/dist/render-v2-tjay5hhf.js +16 -0
- package/dist/{resume-frdtgfa0.js → resume-8ayf37g5.js} +9 -9
- package/dist/{runtime-bt5pkpm1.js → runtime-hqe5cqcd.js} +1 -1
- package/dist/{session-access-fpez4y5q.js → session-access-fdhp987s.js} +4 -4
- package/dist/session-source-backfill-qgtc86z1.js +110 -0
- package/dist/{setup-mq6m3m8q.js → setup-4rrmqcq3.js} +1 -1
- package/dist/{virtual-modules-mnfbh5mr.js → virtual-modules-52km00d6.js} +2 -2
- package/dist/{work-objects-kfahhjsq.js → work-objects-p28fgd70.js} +23 -23
- package/dist/worker-e3w7vye1.js +79 -0
- package/dist/{x-pbpezr0s.js → x-wxrzvzx8.js} +2 -2
- package/openapi.json +1274 -32
- package/package.json +1 -1
- package/src/assets/source-key.ts +95 -0
- package/src/be/db/api-keys.ts +80 -0
- package/src/be/db/session-labels.ts +28 -0
- package/src/be/db/session-moves.ts +5 -6
- package/src/be/db/session-tree.ts +153 -14
- package/src/be/db/tasks/read.ts +11 -0
- package/src/be/db/tasks/write.ts +6 -1
- package/src/be/db/workflow-runs.ts +183 -1
- package/src/be/db-retention.ts +61 -6
- package/src/be/db.ts +59 -114
- package/src/be/memory/providers/openai-embedding.ts +5 -1
- package/src/be/migrations/244_api_key_validation.sql +4 -0
- package/src/be/onboarding.ts +4 -0
- package/src/be/scripts/typecheck.ts +1 -1
- package/src/be/session-source-backfill.ts +163 -0
- package/src/be/swarm-config-guard.ts +1 -0
- package/src/comb/review-batch.ts +28 -1
- package/src/comb/templates.ts +7 -2
- package/src/commands/claude-managed-setup.ts +7 -1
- package/src/commands/credential-refresh.ts +76 -1
- package/src/commands/credential-wait.ts +2 -2
- package/src/commands/provider-credentials.ts +210 -170
- package/src/commands/runner.ts +25 -4
- package/src/extensions/apply-task-create.ts +8 -1
- package/src/extensions/contract-types.generated.ts +1 -1
- package/src/extensions/contract.ts +5 -0
- package/src/http/all-routes.ts +2 -0
- package/src/http/api-keys.ts +53 -0
- package/src/http/assets.ts +15 -0
- package/src/http/codex-oauth-accounts.ts +191 -0
- package/src/http/comb.ts +15 -2
- package/src/http/config.ts +1 -1
- package/src/http/core.ts +6 -0
- package/src/http/fleet-benchmark.ts +5 -2
- package/src/http/index.ts +11 -0
- package/src/http/integrations.ts +5 -1
- package/src/http/kv.ts +8 -3
- package/src/http/onboarding.ts +6 -1
- package/src/http/pool-keys.ts +354 -0
- package/src/http/sessions.ts +37 -12
- package/src/http/stats.ts +1 -0
- package/src/http/status.ts +26 -12
- package/src/http/tasks.ts +3 -0
- package/src/http/used-by.ts +7 -2
- package/src/http/workflows.ts +78 -9
- package/src/integrations/chat-sdk/inbound.ts +27 -0
- package/src/kv-reserved-namespaces.ts +19 -0
- package/src/providers/acp-targets.ts +1 -1
- package/src/providers/amp-adapter.ts +1 -1
- package/src/providers/claude-adapter.ts +5 -1
- package/src/providers/claude-managed-adapter.ts +16 -9
- package/src/providers/codex-adapter.ts +71 -11
- package/src/scheduler/scheduler.ts +3 -2
- package/src/scripts-runtime/swarm-sdk.ts +7 -1
- package/src/scripts-runtime/types/stdlib.d.ts +1 -1
- package/src/scripts-runtime/types/swarm-extension.d.ts +17 -8
- package/src/scripts-runtime/types/swarm-sdk.d.ts +1 -1
- package/src/slack/assistant.ts +7 -0
- package/src/slack/channel-labels.ts +74 -0
- package/src/slack/handlers.ts +8 -0
- package/src/slack/inbound-files.ts +5 -2
- package/src/slack/thread-buffer.ts +4 -0
- package/src/tasks/sibling-awareness.ts +12 -3
- package/src/telemetry-context.ts +5 -6
- package/src/telemetry-identity.ts +3 -3
- package/src/tests/api-keys-validation.test.ts +170 -0
- package/src/tests/claude-managed-adapter.test.ts +46 -0
- package/src/tests/claude-routes-e2e.test.ts +7 -2
- package/src/tests/codex-credential-pools.test.ts +120 -0
- package/src/tests/codex-gateway-route.test.ts +165 -0
- package/src/tests/codex-oauth-accounts.test.ts +357 -0
- package/src/tests/comb-review-batch.test.ts +63 -2
- package/src/tests/config-pool-keys.test.ts +533 -0
- package/src/tests/credential-check.test.ts +290 -2
- package/src/tests/credential-refresh.test.ts +172 -1
- package/src/tests/db-retention.test.ts +9 -3
- package/src/tests/db-task-read-contract.test.ts +1 -0
- package/src/tests/db-task-write-contract.test.ts +1 -3
- package/src/tests/docs-index-footprint.test.js +3 -3
- package/src/tests/extensions-pre-task.test.ts +53 -1
- package/src/tests/fixtures/extensions/record-task-create/hooks.ts +9 -0
- package/src/tests/fixtures/extensions/record-task-create/manifest.json +7 -0
- package/src/tests/fleet-benchmark.test.ts +8 -0
- package/src/tests/get-workflow-run-tool.test.ts +98 -0
- package/src/tests/integrations-http.test.ts +32 -2
- package/src/tests/internal-ai/complete-structured.test.ts +37 -1
- package/src/tests/internal-ai/credentials.test.ts +23 -0
- package/src/tests/kv-http.test.ts +32 -0
- package/src/tests/kv-tool.test.ts +25 -0
- package/src/tests/multi-runtime-registration.test.ts +12 -12
- package/src/tests/onboarding.test.ts +108 -1
- package/src/tests/openai-embedding-credentials.test.ts +35 -0
- package/src/tests/openrouter-attribution-call-sites.test.ts +2 -2
- package/src/tests/org-website.test.ts +36 -0
- package/src/tests/provider-key-probes.test.ts +181 -0
- package/src/tests/rbac-e2e-helpers.test.ts +88 -1
- package/src/tests/rbac-e2e-helpers.ts +36 -11
- package/src/tests/script-connections.test.ts +2 -0
- package/src/tests/script-runs-retention.test.ts +212 -0
- package/src/tests/sdk-allowlist.test.ts +39 -0
- package/src/tests/session-source-backfill.test.ts +429 -0
- package/src/tests/session-source-keys.test.ts +236 -0
- package/src/tests/session-source-labels.test.ts +419 -0
- package/src/tests/sessions-detail-batch.test.ts +2 -0
- package/src/tests/sessions-move.test.ts +131 -0
- package/src/tests/sessions-tree.test.ts +299 -2
- package/src/tests/slack-inbound-files.test.ts +103 -23
- package/src/tests/status.test.ts +101 -18
- package/src/tests/tasks-workflow-run-filter.test.ts +146 -0
- package/src/tests/test-net.ts +31 -0
- package/src/tests/transcription.test.ts +8 -0
- package/src/tests/used-by-route.test.ts +11 -0
- package/src/tests/workflow-cascade-cancel.test.ts +172 -0
- package/src/tests/workflow-http-v2.test.ts +131 -0
- package/src/tests/workflow-step-order.test.ts +92 -0
- package/src/tests/workflow-sub-workflow.test.ts +30 -0
- package/src/tools/get-tasks.ts +8 -0
- package/src/tools/kv/kv-read-auth.ts +4 -2
- package/src/tools/task-action.ts +1 -1
- package/src/tools/workflows/get-workflow-run.ts +14 -4
- package/src/types.ts +24 -0
- package/src/utils/company-profile.ts +51 -1
- package/src/utils/credentials.ts +5 -2
- package/src/utils/internal-ai/complete-structured.ts +5 -0
- package/src/utils/internal-ai/credentials.ts +4 -3
- package/src/utils/provider-key-probes.ts +328 -0
- package/src/utils/transcription.ts +4 -1
- package/src/workflows/executors/sub-workflow.ts +8 -1
- package/templates/ai-toolbox.manifest.json +2 -2
- package/templates/skills/researching/content.md +2 -0
- package/templates/skills/swarm-scripts/SKILL.md +1 -0
- package/templates/skills/swarm-scripts/content.md +1 -0
- package/dist/cli-7421jzsw.js +0 -84
- package/dist/cli-e45e4m6b.js +0 -413
- package/dist/cli-zw88zjba.js +0 -307
- package/dist/handlers-wy1dr6ja.js +0 -41
- package/dist/index-nmm79qgr.js +0 -43
- package/dist/lead-sawnyqxk.js +0 -79
- package/dist/render-v2-pjwxvnkw.js +0 -12
- package/dist/worker-v5e5rxj3.js +0 -79
|
@@ -1,12 +1,161 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
TERMINAL_TASK_STATUSES,
|
|
3
|
+
type WorkflowRun,
|
|
4
|
+
type WorkflowRunStatus,
|
|
5
|
+
type WorkflowRunStep,
|
|
6
|
+
type WorkflowRunStepSlim,
|
|
7
|
+
type WorkflowRunStepStatus,
|
|
8
|
+
type WorkflowRunSummary,
|
|
9
|
+
} from "../../types";
|
|
2
10
|
import { scrubSecrets } from "../../utils/secret-scrubber";
|
|
11
|
+
import { normalizeDate, normalizeDateRequired } from "../date-utils";
|
|
3
12
|
import { scrubJsonValue } from "../scrub-json";
|
|
13
|
+
import { defineWorkflowPayload, type WorkflowPayloadView } from "../workflow-replay";
|
|
4
14
|
import { getDbClient } from "./runtime";
|
|
5
15
|
|
|
6
16
|
// Parent/child links between workflow runs (migration 203): a `sub-workflow`
|
|
7
17
|
// step starts a child run that records the step in `parentStepId`, and the
|
|
8
18
|
// step's `retryCount` at that time in `parentStepAttempt` (migration 240).
|
|
9
19
|
|
|
20
|
+
export type WorkflowRunRow = {
|
|
21
|
+
id: string;
|
|
22
|
+
workflowId: string;
|
|
23
|
+
status: string;
|
|
24
|
+
triggerType: string;
|
|
25
|
+
triggerData: string | null;
|
|
26
|
+
/** Absent from list rows: see `WORKFLOW_RUN_SUMMARY_COLUMNS`. */
|
|
27
|
+
context?: string | null;
|
|
28
|
+
/** Sealed exact `context` (migration 201). Absent from list rows. */
|
|
29
|
+
context_replay?: string | null;
|
|
30
|
+
error: string | null;
|
|
31
|
+
created_by: string | null;
|
|
32
|
+
startedAt: string;
|
|
33
|
+
lastUpdatedAt: string;
|
|
34
|
+
finishedAt: string | null;
|
|
35
|
+
/** The `sub-workflow` step that started this run (migration 203). */
|
|
36
|
+
parentStepId?: string | null;
|
|
37
|
+
/** That step's attempt (`retryCount`) when it started this run (migration 240). */
|
|
38
|
+
parentStepAttempt?: number | null;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* `view` picks the `context` copy: `replay` (default) is the exact sealed value
|
|
43
|
+
* the engine resumes from; `display` is the scrubbed column for API, MCP and UI
|
|
44
|
+
* responses. See `src/be/workflow-replay.ts`.
|
|
45
|
+
*/
|
|
46
|
+
export function rowToWorkflowRun(
|
|
47
|
+
row: WorkflowRunRow,
|
|
48
|
+
view: WorkflowPayloadView = "replay",
|
|
49
|
+
): WorkflowRun {
|
|
50
|
+
const run: WorkflowRun = {
|
|
51
|
+
id: row.id,
|
|
52
|
+
workflowId: row.workflowId,
|
|
53
|
+
status: row.status as WorkflowRunStatus,
|
|
54
|
+
triggerData: row.triggerData ? JSON.parse(row.triggerData) : undefined,
|
|
55
|
+
context: undefined,
|
|
56
|
+
error: row.error ?? undefined,
|
|
57
|
+
createdBy: row.created_by ?? undefined,
|
|
58
|
+
startedAt: normalizeDateRequired(row.startedAt),
|
|
59
|
+
lastUpdatedAt: normalizeDateRequired(row.lastUpdatedAt),
|
|
60
|
+
finishedAt: normalizeDate(row.finishedAt) ?? undefined,
|
|
61
|
+
parentStepId: row.parentStepId ?? undefined,
|
|
62
|
+
parentStepAttempt: row.parentStepId ? (row.parentStepAttempt ?? 0) : undefined,
|
|
63
|
+
};
|
|
64
|
+
return defineWorkflowPayload(run, "context", row.context, row.context_replay, view, {
|
|
65
|
+
runId: row.id,
|
|
66
|
+
target: "its context",
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Everything but `context`. `triggerData` stays: it is small, and stored
|
|
71
|
+
// scripts read it off list rows (dedupe by trigger, failure reports). The
|
|
72
|
+
// parent link and attempt stay so a list row identifies a child's invocation.
|
|
73
|
+
export const WORKFLOW_RUN_SUMMARY_COLUMNS =
|
|
74
|
+
"id, workflowId, status, triggerType, triggerData, error, created_by, startedAt, lastUpdatedAt, finishedAt, parentStepId, parentStepAttempt";
|
|
75
|
+
|
|
76
|
+
export type WorkflowRunStepRow = {
|
|
77
|
+
id: string;
|
|
78
|
+
runId: string;
|
|
79
|
+
nodeId: string;
|
|
80
|
+
nodeType: string;
|
|
81
|
+
status: string;
|
|
82
|
+
/** Absent from slim rows: see `WORKFLOW_RUN_STEP_SLIM_COLUMNS`. */
|
|
83
|
+
input?: string | null;
|
|
84
|
+
output: string | null;
|
|
85
|
+
/** Sealed exact `output` (migration 201). Absent from slim rows. */
|
|
86
|
+
output_replay?: string | null;
|
|
87
|
+
error: string | null;
|
|
88
|
+
startedAt: string;
|
|
89
|
+
finishedAt: string | null;
|
|
90
|
+
retryCount: number;
|
|
91
|
+
maxRetries: number;
|
|
92
|
+
nextRetryAt: string | null;
|
|
93
|
+
idempotencyKey: string | null;
|
|
94
|
+
diagnostics: string | null;
|
|
95
|
+
nextPort: string | null;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
/** `view` picks the `output` copy, as in `rowToWorkflowRun`. */
|
|
99
|
+
export function rowToWorkflowRunStep(
|
|
100
|
+
row: WorkflowRunStepRow,
|
|
101
|
+
view: WorkflowPayloadView = "replay",
|
|
102
|
+
): WorkflowRunStep {
|
|
103
|
+
const step: WorkflowRunStep = {
|
|
104
|
+
id: row.id,
|
|
105
|
+
runId: row.runId,
|
|
106
|
+
nodeId: row.nodeId,
|
|
107
|
+
nodeType: row.nodeType,
|
|
108
|
+
status: row.status as WorkflowRunStepStatus,
|
|
109
|
+
input: row.input ? JSON.parse(row.input) : undefined,
|
|
110
|
+
output: undefined,
|
|
111
|
+
error: row.error ?? undefined,
|
|
112
|
+
startedAt: normalizeDateRequired(row.startedAt),
|
|
113
|
+
finishedAt: normalizeDate(row.finishedAt) ?? undefined,
|
|
114
|
+
retryCount: row.retryCount,
|
|
115
|
+
maxRetries: row.maxRetries,
|
|
116
|
+
nextRetryAt: normalizeDate(row.nextRetryAt) ?? undefined,
|
|
117
|
+
idempotencyKey: row.idempotencyKey ?? undefined,
|
|
118
|
+
diagnostics: row.diagnostics ?? undefined,
|
|
119
|
+
nextPort: row.nextPort ?? undefined,
|
|
120
|
+
};
|
|
121
|
+
return defineWorkflowPayload(step, "output", row.output, row.output_replay, view, {
|
|
122
|
+
runId: row.runId,
|
|
123
|
+
target: `the output of step ${row.id} (node ${row.nodeId})`,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Everything but `input` (a context snapshot per step, so loops grow it with
|
|
128
|
+
// every iteration) and `output_replay` (the display view never opens it).
|
|
129
|
+
const WORKFLOW_RUN_STEP_SLIM_COLUMNS =
|
|
130
|
+
"id, runId, nodeId, nodeType, status, output, error, startedAt, finishedAt, retryCount, maxRetries, nextRetryAt, idempotencyKey, diagnostics, nextPort";
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* `getWorkflowRun` without `context`, in the display view. Backs
|
|
134
|
+
* `GET /api/workflow-runs/{id}?fields=slim`: the large column is neither read
|
|
135
|
+
* nor parsed.
|
|
136
|
+
*/
|
|
137
|
+
export async function getWorkflowRunSummary(id: string): Promise<WorkflowRunSummary | null> {
|
|
138
|
+
const row = await getDbClient().get<WorkflowRunRow>(
|
|
139
|
+
`SELECT ${WORKFLOW_RUN_SUMMARY_COLUMNS} FROM workflow_runs WHERE id = ?`,
|
|
140
|
+
[id],
|
|
141
|
+
);
|
|
142
|
+
return row ? rowToWorkflowRun(row, "display") : null;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* `getWorkflowRunStepsByRunId` without `input`, in the display view. Same
|
|
147
|
+
* order, including the rowid tie-break for steps that share `startedAt`.
|
|
148
|
+
*/
|
|
149
|
+
export async function getWorkflowRunStepsSlimByRunId(
|
|
150
|
+
runId: string,
|
|
151
|
+
): Promise<WorkflowRunStepSlim[]> {
|
|
152
|
+
const rows = await getDbClient().query<WorkflowRunStepRow>(
|
|
153
|
+
`SELECT ${WORKFLOW_RUN_STEP_SLIM_COLUMNS} FROM workflow_run_steps WHERE runId = ? ORDER BY startedAt ASC, rowid ASC`,
|
|
154
|
+
[runId],
|
|
155
|
+
);
|
|
156
|
+
return rows.map((row) => rowToWorkflowRunStep(row, "display"));
|
|
157
|
+
}
|
|
158
|
+
|
|
10
159
|
const TERMINAL_STATUSES = new Set<WorkflowRunStatus>([
|
|
11
160
|
"completed",
|
|
12
161
|
"failed",
|
|
@@ -242,6 +391,39 @@ export async function workflowRunTreeHasOpenWork(runIds: string[]): Promise<bool
|
|
|
242
391
|
return open;
|
|
243
392
|
}
|
|
244
393
|
|
|
394
|
+
export type WorkflowRunTree = {
|
|
395
|
+
rootRunId: string;
|
|
396
|
+
/** Root first, in `getWorkflowRunTreeIds` order. List rows: no `context`. */
|
|
397
|
+
runs: WorkflowRun[];
|
|
398
|
+
hasOpenWork: boolean;
|
|
399
|
+
};
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* The run and every run below it, with whether any of them still has open
|
|
403
|
+
* work (see `workflowRunTreeHasOpenWork`). One read transaction, so `runs` and
|
|
404
|
+
* `hasOpenWork` come from the same snapshot. Null when the root is missing.
|
|
405
|
+
* Throws WorkflowRunTreeError on a cycle or a chain past the depth bound.
|
|
406
|
+
*/
|
|
407
|
+
export async function getWorkflowRunTree(rootRunId: string): Promise<WorkflowRunTree | null> {
|
|
408
|
+
return getDbClient().transaction(
|
|
409
|
+
async () => {
|
|
410
|
+
if ((await getWorkflowRunStatus(rootRunId)) === null) return null;
|
|
411
|
+
const ids = await getWorkflowRunTreeIds(rootRunId);
|
|
412
|
+
const byId = new Map<string, WorkflowRun>();
|
|
413
|
+
await forEachChunk(ids, async (chunk, placeholders) => {
|
|
414
|
+
const rows = await getDbClient().query<WorkflowRunRow>(
|
|
415
|
+
`SELECT ${WORKFLOW_RUN_SUMMARY_COLUMNS} FROM workflow_runs WHERE id IN (${placeholders})`,
|
|
416
|
+
chunk,
|
|
417
|
+
);
|
|
418
|
+
for (const row of rows) byId.set(row.id, rowToWorkflowRun(row, "display"));
|
|
419
|
+
});
|
|
420
|
+
const runs = ids.flatMap((id) => byId.get(id) ?? []);
|
|
421
|
+
return { rootRunId, runs, hasOpenWork: await workflowRunTreeHasOpenWork(ids) };
|
|
422
|
+
},
|
|
423
|
+
{ readOnly: true },
|
|
424
|
+
);
|
|
425
|
+
}
|
|
426
|
+
|
|
245
427
|
/**
|
|
246
428
|
* Cancel the pending, running and waiting steps of the given runs. The
|
|
247
429
|
* migration-140 trigger closes each cancelled step's pending approval.
|
package/src/be/db-retention.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
2
|
import type { Attributes, DbRetentionSweepMetric, SpanStatus, SwarmSpan } from "../otel";
|
|
3
3
|
import { recordDbRetentionStatement, recordDbRetentionSweep, startSpan, withSpan } from "../otel";
|
|
4
|
+
import { TERMINAL_SCRIPT_RUN_STATUSES } from "../types";
|
|
4
5
|
import { isEnvFlagEnabled } from "../utils/env-flag";
|
|
5
6
|
import { scrubSecrets } from "../utils/secret-scrubber";
|
|
6
7
|
import { getDbClient } from "./db";
|
|
@@ -23,7 +24,11 @@ const YIELD_MS = 25;
|
|
|
23
24
|
*
|
|
24
25
|
* Two policies exist:
|
|
25
26
|
* - `age`: the env value is a number of days; rows whose `timeColumn` is older
|
|
26
|
-
* than that horizon are deleted.
|
|
27
|
+
* than that horizon are deleted. Optional narrowing: `terminalStatuses`
|
|
28
|
+
* deletes only rows whose `statusColumn` is in that list, `keepWhenSet`
|
|
29
|
+
* keeps every row where that column is non-NULL, and
|
|
30
|
+
* `cutoffFormat: "sqliteDatetime"` compares against a `YYYY-MM-DD HH:MM:SS`
|
|
31
|
+
* cutoff for a column that mixes that format with ISO strings.
|
|
27
32
|
* - `keepLatest`: the env value is a count; within each `partitionBy` group,
|
|
28
33
|
* only the newest `rankBy` rows are kept. The newest row of a group is
|
|
29
34
|
* never deleted, because the smallest accepted count is 1.
|
|
@@ -65,6 +70,29 @@ export const DB_RETENTION_TABLES = [
|
|
|
65
70
|
initialBatchSize: 50,
|
|
66
71
|
maxBatchSize: 100,
|
|
67
72
|
},
|
|
73
|
+
{
|
|
74
|
+
// Every inline script run stores its full source, args and output. Only
|
|
75
|
+
// terminal runs are eligible: a running or paused durable run must survive
|
|
76
|
+
// to be resumed. Runs with an idempotencyKey are kept, so a retried launch
|
|
77
|
+
// still gets its 409 instead of executing twice. The status filter, the
|
|
78
|
+
// time range and the key exclusion all resolve on indexes
|
|
79
|
+
// (idx_script_runs_status_startedAt, idx_script_runs_idempotencyKey).
|
|
80
|
+
// Workflow runs write `datetime('now')` (space separator) while inline runs
|
|
81
|
+
// write ISO strings, hence the sqliteDatetime cutoff. script_run_journal
|
|
82
|
+
// rows go with their run through ON DELETE CASCADE on idx_srj_runId.
|
|
83
|
+
// Rows average ~30 KB and reach ~1.3 MB, so batches stay small.
|
|
84
|
+
table: "script_runs",
|
|
85
|
+
policy: "age",
|
|
86
|
+
timeColumn: "startedAt",
|
|
87
|
+
cutoffFormat: "sqliteDatetime",
|
|
88
|
+
statusColumn: "status",
|
|
89
|
+
terminalStatuses: TERMINAL_SCRIPT_RUN_STATUSES,
|
|
90
|
+
keepWhenSet: "idempotencyKey",
|
|
91
|
+
envKey: "SCRIPT_RUNS_RETENTION_DAYS",
|
|
92
|
+
metricsKey: "scriptRuns",
|
|
93
|
+
initialBatchSize: 100,
|
|
94
|
+
maxBatchSize: 500,
|
|
95
|
+
},
|
|
68
96
|
] as const satisfies ReadonlyArray<
|
|
69
97
|
{
|
|
70
98
|
table: string;
|
|
@@ -73,7 +101,14 @@ export const DB_RETENTION_TABLES = [
|
|
|
73
101
|
initialBatchSize?: number;
|
|
74
102
|
maxBatchSize?: number;
|
|
75
103
|
} & (
|
|
76
|
-
| {
|
|
104
|
+
| {
|
|
105
|
+
policy: "age";
|
|
106
|
+
timeColumn: string;
|
|
107
|
+
cutoffFormat?: "sqliteDatetime";
|
|
108
|
+
statusColumn?: string;
|
|
109
|
+
terminalStatuses?: readonly string[];
|
|
110
|
+
keepWhenSet?: string;
|
|
111
|
+
}
|
|
77
112
|
| { policy: "keepLatest"; partitionBy: string; rankBy: string }
|
|
78
113
|
)
|
|
79
114
|
>;
|
|
@@ -326,13 +361,33 @@ export function sweepStatements(table: RetentionTable, value: number, now: Date)
|
|
|
326
361
|
params: [value],
|
|
327
362
|
};
|
|
328
363
|
}
|
|
329
|
-
const
|
|
364
|
+
const iso = new Date(now.getTime() - value * DAY_MS).toISOString();
|
|
365
|
+
// Text comparison of the two formats: ' ' sorts before 'T', so against a
|
|
366
|
+
// space-separated cutoff an ISO row on the cutoff's own date is kept, never
|
|
367
|
+
// deleted early. It is swept on the next day's ticks instead.
|
|
368
|
+
const cutoff = "cutoffFormat" in table ? `${iso.slice(0, 10)} ${iso.slice(11, 19)}` : iso;
|
|
369
|
+
const conditions: string[] = [];
|
|
370
|
+
const params: Array<string | number> = [];
|
|
371
|
+
if ("terminalStatuses" in table) {
|
|
372
|
+
conditions.push(
|
|
373
|
+
`${table.statusColumn} IN (${table.terminalStatuses.map(() => "?").join(", ")})`,
|
|
374
|
+
);
|
|
375
|
+
params.push(...table.terminalStatuses);
|
|
376
|
+
}
|
|
377
|
+
conditions.push(`${table.timeColumn} < ?`);
|
|
378
|
+
params.push(cutoff);
|
|
379
|
+
if ("keepWhenSet" in table) {
|
|
380
|
+
conditions.push(
|
|
381
|
+
`rowid NOT IN (SELECT rowid FROM ${table.table} WHERE ${table.keepWhenSet} IS NOT NULL)`,
|
|
382
|
+
);
|
|
383
|
+
}
|
|
384
|
+
const where = conditions.join(" AND ");
|
|
330
385
|
return {
|
|
331
|
-
backlogSql: `SELECT COUNT(*) AS n FROM ${table.table} WHERE ${
|
|
386
|
+
backlogSql: `SELECT COUNT(*) AS n FROM ${table.table} WHERE ${where}`,
|
|
332
387
|
deleteSql: `DELETE FROM ${table.table} WHERE rowid IN (
|
|
333
|
-
SELECT rowid FROM ${table.table} WHERE ${
|
|
388
|
+
SELECT rowid FROM ${table.table} WHERE ${where} ORDER BY ${table.timeColumn} LIMIT ?
|
|
334
389
|
)`,
|
|
335
|
-
params
|
|
390
|
+
params,
|
|
336
391
|
};
|
|
337
392
|
}
|
|
338
393
|
|
package/src/be/db.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Database } from "bun:sqlite";
|
|
2
2
|
import pkg from "../../package.json";
|
|
3
|
-
import { defaultAssetKey, normalizeAssetKey
|
|
3
|
+
import { defaultAssetKey, normalizeAssetKey } from "../assets/key";
|
|
4
|
+
import { sessionSourceKey, sessionThreadGroupKey } from "../assets/source-key";
|
|
4
5
|
import {
|
|
5
6
|
generateDefaultClaudeMd,
|
|
6
7
|
generateDefaultIdentityMd,
|
|
@@ -207,6 +208,7 @@ import {
|
|
|
207
208
|
sessionCountQuery,
|
|
208
209
|
sessionPageFields,
|
|
209
210
|
sessionSummaryQuery,
|
|
211
|
+
threadRootsSql,
|
|
210
212
|
} from "./db/session-tree";
|
|
211
213
|
import { searchSkillRowsRanked } from "./db/skill-search";
|
|
212
214
|
import { classifyTaskHumanFree, reclassifyTaskHumanFree } from "./db/tasks/human-free";
|
|
@@ -227,6 +229,11 @@ import {
|
|
|
227
229
|
emitChildRunFinished,
|
|
228
230
|
getRunLineageBlock,
|
|
229
231
|
getStepExecutionBlock,
|
|
232
|
+
rowToWorkflowRun,
|
|
233
|
+
rowToWorkflowRunStep,
|
|
234
|
+
WORKFLOW_RUN_SUMMARY_COLUMNS,
|
|
235
|
+
type WorkflowRunRow,
|
|
236
|
+
type WorkflowRunStepRow,
|
|
230
237
|
WorkflowStepNotExecutableError,
|
|
231
238
|
wakeParentsOfDeletedRuns,
|
|
232
239
|
} from "./db/workflow-runs";
|
|
@@ -242,7 +249,7 @@ import {
|
|
|
242
249
|
} from "./swarm-config-guard";
|
|
243
250
|
import { emitTaskStarted } from "./task-lifecycle-events";
|
|
244
251
|
import { publishWorkerConfigNudge, publishWorkerNudge } from "./worker-nudges";
|
|
245
|
-
import {
|
|
252
|
+
import type { WorkflowPayloadView } from "./workflow-replay";
|
|
246
253
|
|
|
247
254
|
export {
|
|
248
255
|
refreshActiveSessionOnActivity,
|
|
@@ -285,7 +292,7 @@ export {
|
|
|
285
292
|
updateAgentStatus,
|
|
286
293
|
updateAgentStatusFromCapacity,
|
|
287
294
|
} from "./db/agents";
|
|
288
|
-
export { recordKeySeatMismatch } from "./db/api-keys";
|
|
295
|
+
export { recordKeySeatMismatch, recordKeyValidation } from "./db/api-keys";
|
|
289
296
|
export {
|
|
290
297
|
type ApprovalRequestSummary,
|
|
291
298
|
type ApprovalVote,
|
|
@@ -2497,6 +2504,8 @@ export async function createTaskExtended(
|
|
|
2497
2504
|
);
|
|
2498
2505
|
}
|
|
2499
2506
|
let requestedByUserIdInherited = false;
|
|
2507
|
+
// The parent key is read again inside the INSERT transaction (see below).
|
|
2508
|
+
let keyInherited = false;
|
|
2500
2509
|
// True only when `options.routingAffinity` ends up populated purely via
|
|
2501
2510
|
// the plain parent-fallback inherit below (child declared no affinity of
|
|
2502
2511
|
// its own, the parent wasn't lead-only, AND the parent's affinity actually
|
|
@@ -2661,6 +2670,7 @@ export async function createTaskExtended(
|
|
|
2661
2670
|
}
|
|
2662
2671
|
if (parent.key && !options.key) {
|
|
2663
2672
|
options.key = parent.key;
|
|
2673
|
+
keyInherited = true;
|
|
2664
2674
|
}
|
|
2665
2675
|
if (parent.contextKey && !options.contextKey) {
|
|
2666
2676
|
options.contextKey = parent.contextKey;
|
|
@@ -2850,7 +2860,18 @@ export async function createTaskExtended(
|
|
|
2850
2860
|
}
|
|
2851
2861
|
|
|
2852
2862
|
const auditUserId = getCurrentRequestUserId() ?? null;
|
|
2853
|
-
const assetKey = normalizeAssetKey(
|
|
2863
|
+
const assetKey = normalizeAssetKey(
|
|
2864
|
+
options?.key ??
|
|
2865
|
+
(options?.parentTaskId
|
|
2866
|
+
? null
|
|
2867
|
+
: sessionSourceKey({
|
|
2868
|
+
id,
|
|
2869
|
+
slackChannelId: options?.slackChannelId,
|
|
2870
|
+
slackThreadTs: options?.slackThreadTs,
|
|
2871
|
+
contextKey: options?.contextKey,
|
|
2872
|
+
})) ??
|
|
2873
|
+
defaultAssetKey("task", id),
|
|
2874
|
+
);
|
|
2854
2875
|
// The Linear tracker dedup guard and the INSERT commit as one unit. Two
|
|
2855
2876
|
// distinct webhook deliveries for the same issue are dispatched
|
|
2856
2877
|
// fire-and-forget (src/linear/webhook.ts), nothing serializes them, and
|
|
@@ -2875,6 +2896,19 @@ export async function createTaskExtended(
|
|
|
2875
2896
|
: await findExistingLinearTrackerContextWork(options?.contextKey);
|
|
2876
2897
|
if (existingTrackerWork) return { existing: existingTrackerWork };
|
|
2877
2898
|
|
|
2899
|
+
// A chain move (a user move, the source key backfill) can commit between
|
|
2900
|
+
// the parent read above and this write lock. Its chain query did not see
|
|
2901
|
+
// this child, so take the parent's key as it is now.
|
|
2902
|
+
const parentKey =
|
|
2903
|
+
keyInherited && options?.parentTaskId
|
|
2904
|
+
? (
|
|
2905
|
+
await getDbClient().get<{ key: string }>('SELECT "key" FROM agent_tasks WHERE id = ?', [
|
|
2906
|
+
options.parentTaskId,
|
|
2907
|
+
])
|
|
2908
|
+
)?.key
|
|
2909
|
+
: undefined;
|
|
2910
|
+
const insertKey = parentKey ? normalizeAssetKey(parentKey) : assetKey;
|
|
2911
|
+
|
|
2878
2912
|
let routingDecisionSnapshot: RoutingDecisionSnapshot | null = null;
|
|
2879
2913
|
try {
|
|
2880
2914
|
routingDecisionSnapshot = await captureRoutingDecisionSnapshot(options);
|
|
@@ -2905,7 +2939,7 @@ export async function createTaskExtended(
|
|
|
2905
2939
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING *`,
|
|
2906
2940
|
[
|
|
2907
2941
|
id,
|
|
2908
|
-
|
|
2942
|
+
insertKey,
|
|
2909
2943
|
options?.agentId ?? null,
|
|
2910
2944
|
options?.creatorAgentId ?? null,
|
|
2911
2945
|
// Free text from every task source; scrub at the single INSERT.
|
|
@@ -7859,53 +7893,6 @@ export async function getWorkflowsByScheduleId(scheduleId: string): Promise<Work
|
|
|
7859
7893
|
// Workflow Run CRUD
|
|
7860
7894
|
// ============================================================================
|
|
7861
7895
|
|
|
7862
|
-
type WorkflowRunRow = {
|
|
7863
|
-
id: string;
|
|
7864
|
-
workflowId: string;
|
|
7865
|
-
status: string;
|
|
7866
|
-
triggerType: string;
|
|
7867
|
-
triggerData: string | null;
|
|
7868
|
-
/** Absent from list rows: see `WORKFLOW_RUN_SUMMARY_COLUMNS`. */
|
|
7869
|
-
context?: string | null;
|
|
7870
|
-
/** Sealed exact `context` (migration 201). Absent from list rows. */
|
|
7871
|
-
context_replay?: string | null;
|
|
7872
|
-
error: string | null;
|
|
7873
|
-
created_by: string | null;
|
|
7874
|
-
startedAt: string;
|
|
7875
|
-
lastUpdatedAt: string;
|
|
7876
|
-
finishedAt: string | null;
|
|
7877
|
-
/** The `sub-workflow` step that started this run (migration 203). */
|
|
7878
|
-
parentStepId?: string | null;
|
|
7879
|
-
/** That step's attempt (`retryCount`) when it started this run (migration 240). */
|
|
7880
|
-
parentStepAttempt?: number | null;
|
|
7881
|
-
};
|
|
7882
|
-
|
|
7883
|
-
/**
|
|
7884
|
-
* `view` picks the `context` copy: `replay` (default) is the exact sealed value
|
|
7885
|
-
* the engine resumes from; `display` is the scrubbed column for API, MCP and UI
|
|
7886
|
-
* responses. See `src/be/workflow-replay.ts`.
|
|
7887
|
-
*/
|
|
7888
|
-
function rowToWorkflowRun(row: WorkflowRunRow, view: WorkflowPayloadView = "replay"): WorkflowRun {
|
|
7889
|
-
const run: WorkflowRun = {
|
|
7890
|
-
id: row.id,
|
|
7891
|
-
workflowId: row.workflowId,
|
|
7892
|
-
status: row.status as WorkflowRunStatus,
|
|
7893
|
-
triggerData: row.triggerData ? JSON.parse(row.triggerData) : undefined,
|
|
7894
|
-
context: undefined,
|
|
7895
|
-
error: row.error ?? undefined,
|
|
7896
|
-
createdBy: row.created_by ?? undefined,
|
|
7897
|
-
startedAt: normalizeDateRequired(row.startedAt),
|
|
7898
|
-
lastUpdatedAt: normalizeDateRequired(row.lastUpdatedAt),
|
|
7899
|
-
finishedAt: normalizeDate(row.finishedAt) ?? undefined,
|
|
7900
|
-
parentStepId: row.parentStepId ?? undefined,
|
|
7901
|
-
parentStepAttempt: row.parentStepId ? (row.parentStepAttempt ?? 0) : undefined,
|
|
7902
|
-
};
|
|
7903
|
-
return defineWorkflowPayload(run, "context", row.context, row.context_replay, view, {
|
|
7904
|
-
runId: row.id,
|
|
7905
|
-
target: "its context",
|
|
7906
|
-
});
|
|
7907
|
-
}
|
|
7908
|
-
|
|
7909
7896
|
export async function createWorkflowRun(data: {
|
|
7910
7897
|
id: string;
|
|
7911
7898
|
workflowId: string;
|
|
@@ -8031,12 +8018,6 @@ export type WorkflowRunListOptions = {
|
|
|
8031
8018
|
includeContext?: boolean;
|
|
8032
8019
|
};
|
|
8033
8020
|
|
|
8034
|
-
// Everything but `context`. `triggerData` stays: it is small, and stored
|
|
8035
|
-
// scripts read it off list rows (dedupe by trigger, failure reports). The
|
|
8036
|
-
// parent link and attempt stay so a list row identifies a child's invocation.
|
|
8037
|
-
const WORKFLOW_RUN_SUMMARY_COLUMNS =
|
|
8038
|
-
"id, workflowId, status, triggerType, triggerData, error, created_by, startedAt, lastUpdatedAt, finishedAt, parentStepId, parentStepAttempt";
|
|
8039
|
-
|
|
8040
8021
|
export type WorkflowRunPage = {
|
|
8041
8022
|
runs: WorkflowRun[];
|
|
8042
8023
|
page: {
|
|
@@ -8121,56 +8102,6 @@ export async function listWorkflowRunsPage(
|
|
|
8121
8102
|
// Workflow Run Step CRUD
|
|
8122
8103
|
// ============================================================================
|
|
8123
8104
|
|
|
8124
|
-
type WorkflowRunStepRow = {
|
|
8125
|
-
id: string;
|
|
8126
|
-
runId: string;
|
|
8127
|
-
nodeId: string;
|
|
8128
|
-
nodeType: string;
|
|
8129
|
-
status: string;
|
|
8130
|
-
input: string | null;
|
|
8131
|
-
output: string | null;
|
|
8132
|
-
/** Sealed exact `output` (migration 201). */
|
|
8133
|
-
output_replay: string | null;
|
|
8134
|
-
error: string | null;
|
|
8135
|
-
startedAt: string;
|
|
8136
|
-
finishedAt: string | null;
|
|
8137
|
-
retryCount: number;
|
|
8138
|
-
maxRetries: number;
|
|
8139
|
-
nextRetryAt: string | null;
|
|
8140
|
-
idempotencyKey: string | null;
|
|
8141
|
-
diagnostics: string | null;
|
|
8142
|
-
nextPort: string | null;
|
|
8143
|
-
};
|
|
8144
|
-
|
|
8145
|
-
/** `view` picks the `output` copy, as in `rowToWorkflowRun`. */
|
|
8146
|
-
function rowToWorkflowRunStep(
|
|
8147
|
-
row: WorkflowRunStepRow,
|
|
8148
|
-
view: WorkflowPayloadView = "replay",
|
|
8149
|
-
): WorkflowRunStep {
|
|
8150
|
-
const step: WorkflowRunStep = {
|
|
8151
|
-
id: row.id,
|
|
8152
|
-
runId: row.runId,
|
|
8153
|
-
nodeId: row.nodeId,
|
|
8154
|
-
nodeType: row.nodeType,
|
|
8155
|
-
status: row.status as WorkflowRunStepStatus,
|
|
8156
|
-
input: row.input ? JSON.parse(row.input) : undefined,
|
|
8157
|
-
output: undefined,
|
|
8158
|
-
error: row.error ?? undefined,
|
|
8159
|
-
startedAt: normalizeDateRequired(row.startedAt),
|
|
8160
|
-
finishedAt: normalizeDate(row.finishedAt) ?? undefined,
|
|
8161
|
-
retryCount: row.retryCount,
|
|
8162
|
-
maxRetries: row.maxRetries,
|
|
8163
|
-
nextRetryAt: normalizeDate(row.nextRetryAt) ?? undefined,
|
|
8164
|
-
idempotencyKey: row.idempotencyKey ?? undefined,
|
|
8165
|
-
diagnostics: row.diagnostics ?? undefined,
|
|
8166
|
-
nextPort: row.nextPort ?? undefined,
|
|
8167
|
-
};
|
|
8168
|
-
return defineWorkflowPayload(step, "output", row.output, row.output_replay, view, {
|
|
8169
|
-
runId: row.runId,
|
|
8170
|
-
target: `the output of step ${row.id} (node ${row.nodeId})`,
|
|
8171
|
-
});
|
|
8172
|
-
}
|
|
8173
|
-
|
|
8174
8105
|
export async function createWorkflowRunStep(data: {
|
|
8175
8106
|
id: string;
|
|
8176
8107
|
runId: string;
|
|
@@ -8284,7 +8215,7 @@ export async function getWorkflowRunStepsByRunId(
|
|
|
8284
8215
|
view: WorkflowPayloadView = "replay",
|
|
8285
8216
|
): Promise<WorkflowRunStep[]> {
|
|
8286
8217
|
const rows = await getDbClient().query<WorkflowRunStepRow>(
|
|
8287
|
-
"SELECT * FROM workflow_run_steps WHERE runId = ? ORDER BY startedAt ASC",
|
|
8218
|
+
"SELECT * FROM workflow_run_steps WHERE runId = ? ORDER BY startedAt ASC, rowid ASC",
|
|
8288
8219
|
[runId],
|
|
8289
8220
|
);
|
|
8290
8221
|
return rows.map((row) => rowToWorkflowRunStep(row, view));
|
|
@@ -8400,7 +8331,7 @@ export async function getLatestStepForNode(
|
|
|
8400
8331
|
nodeId: string,
|
|
8401
8332
|
): Promise<WorkflowRunStep | null> {
|
|
8402
8333
|
const row = await getDbClient().get<WorkflowRunStepRow>(
|
|
8403
|
-
"SELECT * FROM workflow_run_steps WHERE runId = ? AND nodeId = ? ORDER BY startedAt DESC LIMIT 1",
|
|
8334
|
+
"SELECT * FROM workflow_run_steps WHERE runId = ? AND nodeId = ? ORDER BY startedAt DESC, rowid DESC LIMIT 1",
|
|
8404
8335
|
[runId, nodeId],
|
|
8405
8336
|
);
|
|
8406
8337
|
return row ? rowToWorkflowRunStep(row) : null;
|
|
@@ -11742,6 +11673,8 @@ export interface ApiKeyStatus {
|
|
|
11742
11673
|
/** Auth failures in a row since the last success or clear. */
|
|
11743
11674
|
consecutiveAuthFailures: number;
|
|
11744
11675
|
lastAuthFailureAt: string | null;
|
|
11676
|
+
lastValidationError: string | null;
|
|
11677
|
+
lastValidationFailedAt: string | null;
|
|
11745
11678
|
createdAt: string;
|
|
11746
11679
|
updatedAt: string;
|
|
11747
11680
|
}
|
|
@@ -11767,6 +11700,7 @@ function rowToApiKeyStatus(row: ApiKeyStatusRow): ApiKeyStatus {
|
|
|
11767
11700
|
|
|
11768
11701
|
export interface AvailableKeyIndicesResult {
|
|
11769
11702
|
availableIndices: number[];
|
|
11703
|
+
rejectedIndices: number[];
|
|
11770
11704
|
/** Indices excluded only by an active model-scoped window block (not key-wide). */
|
|
11771
11705
|
modelBlockedIndices: number[];
|
|
11772
11706
|
/** ISO of the earliest resetsAt among modelBlockedIndices, or null when none. */
|
|
@@ -11815,8 +11749,9 @@ export async function getAvailableKeyIndices(
|
|
|
11815
11749
|
rateLimitWindows: string | null;
|
|
11816
11750
|
plan: string | null;
|
|
11817
11751
|
authFailureSeq: number;
|
|
11752
|
+
lastValidationFailedAt: string | null;
|
|
11818
11753
|
}>(
|
|
11819
|
-
`SELECT keyIndex, status, rateLimitWindows, plan, authFailureSeq FROM api_key_status
|
|
11754
|
+
`SELECT keyIndex, status, rateLimitWindows, plan, authFailureSeq, lastValidationFailedAt FROM api_key_status
|
|
11820
11755
|
WHERE keyType = ? AND scope = ? AND scopeId = ?`,
|
|
11821
11756
|
[keyType, scope, effectiveScopeId],
|
|
11822
11757
|
);
|
|
@@ -11824,6 +11759,12 @@ export async function getAvailableKeyIndices(
|
|
|
11824
11759
|
const blockedIndices = new Set(
|
|
11825
11760
|
rows.filter((r) => r.status === "rate_limited").map((r) => r.keyIndex),
|
|
11826
11761
|
);
|
|
11762
|
+
const rejectedIndices = [
|
|
11763
|
+
...new Set(
|
|
11764
|
+
rows.filter((row) => row.lastValidationFailedAt !== null).map((row) => row.keyIndex),
|
|
11765
|
+
),
|
|
11766
|
+
];
|
|
11767
|
+
for (const index of rejectedIndices) blockedIndices.add(index);
|
|
11827
11768
|
|
|
11828
11769
|
const modelBlockedIndices: number[] = [];
|
|
11829
11770
|
let earliestModelResetsAtSec: number | undefined;
|
|
@@ -11866,6 +11807,7 @@ export async function getAvailableKeyIndices(
|
|
|
11866
11807
|
|
|
11867
11808
|
return {
|
|
11868
11809
|
availableIndices,
|
|
11810
|
+
rejectedIndices,
|
|
11869
11811
|
modelBlockedIndices,
|
|
11870
11812
|
seatBlockedIndices,
|
|
11871
11813
|
earliestModelResetAt:
|
|
@@ -12997,6 +12939,7 @@ export async function listRecentSessions(
|
|
|
12997
12939
|
opts: ListRecentSessionsOpts = {},
|
|
12998
12940
|
): Promise<SessionListItem[] | SessionListItemSummary[]> {
|
|
12999
12941
|
const query = sessionSummaryQuery(opts);
|
|
12942
|
+
const threadRoots = threadRootsSql();
|
|
13000
12943
|
const rootRows = await getDbClient().query<
|
|
13001
12944
|
AgentTaskRow & {
|
|
13002
12945
|
__chainCount: number;
|
|
@@ -13005,15 +12948,17 @@ export async function listRecentSessions(
|
|
|
13005
12948
|
__readState: SessionReadState | null;
|
|
13006
12949
|
__snoozeUntil: string | null;
|
|
13007
12950
|
__needsYou: number;
|
|
12951
|
+
__threadRoots: number;
|
|
13008
12952
|
}
|
|
13009
12953
|
>(
|
|
13010
12954
|
`SELECT r.*, a.chainCount AS __chainCount, a.lastActivityAt AS __lastActivityAt,
|
|
13011
12955
|
a.latestStatus AS __latestStatus, a.readState AS __readState,
|
|
13012
|
-
a.snoozeUntil AS __snoozeUntil, a.needsYou AS __needsYou
|
|
12956
|
+
a.snoozeUntil AS __snoozeUntil, a.needsYou AS __needsYou,
|
|
12957
|
+
${threadRoots.sql} AS __threadRoots
|
|
13013
12958
|
FROM (${query.sql}) a CROSS JOIN agent_tasks r ON r.id = a.root_id
|
|
13014
12959
|
ORDER BY ${opts.triage === "later" ? "a.snoozeUntil ASC," : ""}
|
|
13015
12960
|
a.lastActivityAt DESC, r.createdAt DESC, r.id DESC LIMIT ? OFFSET ?`,
|
|
13016
|
-
[...query.params, opts.limit ?? 25, opts.offset ?? 0],
|
|
12961
|
+
[...threadRoots.params, ...query.params, opts.limit ?? 25, opts.offset ?? 0],
|
|
13017
12962
|
);
|
|
13018
12963
|
const fields = await sessionPageFields(
|
|
13019
12964
|
rootRows.map((row) => ({
|
|
@@ -13029,7 +12974,7 @@ export async function listRecentSessions(
|
|
|
13029
12974
|
...(row.__readState ? { readState: row.__readState } : {}),
|
|
13030
12975
|
snoozeUntil: row.__snoozeUntil,
|
|
13031
12976
|
needsYou: !!row.__needsYou,
|
|
13032
|
-
groupKey:
|
|
12977
|
+
groupKey: sessionThreadGroupKey(row.key, row.__threadRoots),
|
|
13033
12978
|
...fields.get(row.id),
|
|
13034
12979
|
}));
|
|
13035
12980
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import OpenAI from "openai";
|
|
2
|
+
import { selectCredential } from "../../../utils/credentials";
|
|
2
3
|
import { getOpenRouterAttributionHeaders } from "../../../utils/openrouter-base-url";
|
|
3
4
|
import { DEFAULT_EMBEDDING_MODEL, EMBEDDING_DIMENSIONS } from "../constants";
|
|
4
5
|
import type { EmbeddingProvider } from "../types";
|
|
@@ -46,7 +47,10 @@ export class OpenAIEmbeddingProvider implements EmbeddingProvider {
|
|
|
46
47
|
private readonly apiKey: string | undefined;
|
|
47
48
|
|
|
48
49
|
constructor(config?: OpenAIEmbeddingConfig) {
|
|
49
|
-
|
|
50
|
+
const apiKey = config?.apiKey ?? process.env.EMBEDDING_API_KEY ?? process.env.OPENAI_API_KEY;
|
|
51
|
+
this.apiKey = apiKey
|
|
52
|
+
? selectCredential(apiKey, undefined, "EMBEDDING_API_KEY").selected
|
|
53
|
+
: undefined;
|
|
50
54
|
|
|
51
55
|
this.model = config?.model ?? process.env.EMBEDDING_MODEL ?? "text-embedding-3-small";
|
|
52
56
|
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
-- Last provider rejection of a pooled key by a worker live test (HTTP 401).
|
|
2
|
+
-- A key with lastValidationFailedAt set is left out of /api/keys/available.
|
|
3
|
+
ALTER TABLE api_key_status ADD COLUMN lastValidationError TEXT;
|
|
4
|
+
ALTER TABLE api_key_status ADD COLUMN lastValidationFailedAt TEXT;
|
package/src/be/onboarding.ts
CHANGED
|
@@ -130,6 +130,10 @@ export const OnboardingSignalsSchema = z.object({
|
|
|
130
130
|
state: z.enum(["unverified", "configured", "verified"]),
|
|
131
131
|
workers: z.number().int().nonnegative(),
|
|
132
132
|
verifiedWorkers: z.number().int().nonnegative(),
|
|
133
|
+
/** The latest live test failed: its error. */
|
|
134
|
+
error: z.string().nullable(),
|
|
135
|
+
/** The latest live test passed, but some pool keys failed: names them. */
|
|
136
|
+
warning: z.string().nullable(),
|
|
133
137
|
}),
|
|
134
138
|
),
|
|
135
139
|
embeddings: z.object({
|
|
@@ -250,7 +250,7 @@ export interface SwarmSdk {
|
|
|
250
250
|
workflow_list(args?: { enabled?: boolean; includeFull?: boolean; consecutiveErrorsMin?: number; lastRunStatus?: "running" | "waiting" | "completed" | "failed" | "skipped" | "cancelled" }): Promise<unknown>;
|
|
251
251
|
workflow_get(args: { id: string }): Promise<unknown>;
|
|
252
252
|
workflow_listRuns(args: { workflowId: string; status?: "running" | "waiting" | "completed" | "failed" | "skipped" | "cancelled"; limit?: number; offset?: number }): Promise<unknown>;
|
|
253
|
-
workflow_getRun(args: { id: string }): Promise<unknown>;
|
|
253
|
+
workflow_getRun(args: { id: string; fields?: "full" | "slim" }): Promise<unknown>;
|
|
254
254
|
// --- prompt templates ---
|
|
255
255
|
prompt_list(args?: { eventType?: string; scope?: "global" | "agent" | "repo"; scopeId?: string; isDefault?: boolean }): Promise<unknown>;
|
|
256
256
|
prompt_get(args: { id: string }): Promise<unknown>;
|