@desplega.ai/agent-swarm 1.157.0 → 1.158.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{acp-adapter-qhqrk4q0.js → acp-adapter-xaq4n6pc.js} +1 -1
- package/dist/{actions-9rrg0zxm.js → actions-p9hz5yat.js} +15 -15
- package/dist/{app-enysy1zd.js → app-g26wdcz6.js} +4 -4
- package/dist/{artifact-td4q4dch.js → artifact-3v5t9jtg.js} +1 -1
- package/dist/{assistant-zegamy48.js → assistant-wydfqkb3.js} +16 -16
- package/dist/{boot-reembed-r6shx5qx.js → boot-reembed-g5ckg9yb.js} +6 -6
- package/dist/{boot-reembed-zfxb937k.js → boot-reembed-se8zmdcp.js} +5 -5
- package/dist/{boot-scrub-logs-k5y0se6r.js → boot-scrub-logs-wfxqan61.js} +3 -3
- package/dist/{seed-pricing-vqxa06qf.js → boot-seeds-s3q01xj3.js} +10 -10
- package/dist/{claude-adapter-5yc7hxwv.js → claude-adapter-tv2x4f36.js} +7 -7
- package/dist/{claude-managed-adapter-ea2aga6w.js → claude-managed-adapter-41b2s7fd.js} +2 -2
- package/dist/{claude-sdk-session-j4ywn3xf.js → claude-sdk-session-t442qz6f.js} +8 -8
- package/dist/{cli-e781c6f9.js → cli-0xjkj1j7.js} +1 -0
- package/dist/{cli-58rgvkt4.js → cli-1hkrch3k.js} +2 -2
- package/dist/{cli-yscm6cv8.js → cli-20cj37vt.js} +1866 -9
- package/dist/{cli-h0wdadsx.js → cli-4198qc9v.js} +7 -3
- package/dist/{cli-awwphhcs.js → cli-47m6dkd3.js} +362 -126
- package/dist/{cli-wb2zvbfg.js → cli-53sz8gkz.js} +1 -1
- package/dist/{cli-005pze6s.js → cli-5d10vd2m.js} +1 -1
- package/dist/{cli-v53rbqp3.js → cli-65hjmev5.js} +57 -17
- package/dist/{cli-scwq51gn.js → cli-68w09sjw.js} +7 -1
- package/dist/{cli-s98skd5r.js → cli-7gtvvpbh.js} +4 -4
- package/dist/{cli-y3nxwx0t.js → cli-7s3syk22.js} +18 -11
- package/dist/{cli-74h6f2bp.js → cli-8qmme6vd.js} +2 -2
- package/dist/{cli-wsed24mk.js → cli-987hg0aa.js} +2 -2
- package/dist/{cli-9n76jszt.js → cli-9httdpyq.js} +1 -1
- package/dist/{cli-f3va018d.js → cli-aky8tctm.js} +26 -93
- package/dist/{cli-sgyqyyxe.js → cli-c07r9a2m.js} +2 -1
- package/dist/{cli-c41wjx0r.js → cli-fgqxwrpv.js} +291 -12
- package/dist/{cli-01vfmq8c.js → cli-ftfvggzx.js} +607 -164
- package/dist/{cli-ytxs7mn1.js → cli-g8djp6qe.js} +17 -0
- package/dist/{cli-avkd6mqv.js → cli-h967nb6r.js} +551 -0
- package/dist/{cli-r9ee9c4w.js → cli-j55n64b6.js} +23 -22
- package/dist/{cli-9p33cz9h.js → cli-j57qkrpq.js} +86 -28
- package/dist/{cli-tjmghmm5.js → cli-q67y4wzm.js} +6 -3
- package/dist/{cli-n6zq3kbd.js → cli-rj19hc34.js} +1 -1
- package/dist/{cli-jytrw2na.js → cli-rjehvrts.js} +4 -4
- package/dist/{cli-tpv97m71.js → cli-rqz8qbw7.js} +245 -75
- package/dist/{cli-p37sg0a9.js → cli-s8eet254.js} +1 -1
- package/dist/cli-t1ze64h3.js +811 -0
- package/dist/{cli-6jjzxv3b.js → cli-ws6rqycm.js} +3 -2
- package/dist/{cli-xf5b7y1f.js → cli-wscdvcqz.js} +13 -5
- package/dist/{cli-5j7bm4tb.js → cli-x24pshtq.js} +5 -5
- package/dist/{cli-nbp6m6gp.js → cli-xberxa1r.js} +51 -26
- package/dist/{cli-vx9p3qdt.js → cli-xhaeg2nc.js} +1 -1
- package/dist/cli.js +29 -15
- package/dist/{codex-adapter-gns3s6c3.js → codex-adapter-cnm8wk11.js} +5 -5
- package/dist/{codex-session-runner-gqnxys5w.js → codex-session-runner-gfg547zj.js} +5 -5
- package/dist/{commands-hxy2y2sp.js → commands-e3bn0p75.js} +3 -3
- package/dist/{db-w8ghhsgf.js → db-03v9kzr2.js} +17 -3
- package/dist/{dispatcher-cp1rknjz.js → dispatcher-nrmg8yht.js} +14 -14
- package/dist/{dsh-adapter-1dwg97zv.js → dsh-adapter-k03nfqa6.js} +2 -2
- package/dist/{handlers-egfhr351.js → handlers-aqcedbyb.js} +17 -17
- package/dist/{hook-x4kftwzt.js → hook-7vkskkre.js} +19 -11
- package/dist/{hook-fbxjqfjz.js → hook-ybvmrmhq.js} +4 -4
- package/dist/{http-c7jfx990.js → http-2eqa8t24.js} +432 -195
- package/dist/{index-4k9hct21.js → index-1yks3qcr.js} +14 -14
- package/dist/{index-jtvpgb8f.js → index-a3vn96n8.js} +30 -27
- package/dist/{index-dc2ek6bw.js → index-c6zsv1bd.js} +5 -5
- package/dist/{index-yy07ef2s.js → index-chgdkq36.js} +14 -14
- package/dist/{index-r6bty6tb.js → index-x71bvq0p.js} +15 -15
- package/dist/{keepalive-f7ekefyc.js → keepalive-vtkf5n99.js} +5 -5
- package/dist/{lead-h053syts.js → lead-h3sp357x.js} +26 -26
- package/dist/{maintenance-a3w6zs54.js → maintenance-jzeqtk3q.js} +6 -6
- package/dist/{oauth-refresh-sweep-vra1dhkd.js → oauth-refresh-sweep-qp3dzmdp.js} +4 -4
- package/dist/{onboard-mfgzeqxs.js → onboard-tmsp0qbz.js} +278 -81
- package/dist/{opencode-adapter-z64xjzec.js → opencode-adapter-x6x2da1z.js} +3 -3
- package/dist/{otel-impl-2ez6xk57.js → otel-impl-9mz73q65.js} +416 -301
- package/dist/{pi-mono-adapter-2b7mw16r.js → pi-mono-adapter-m6dvv0dz.js} +2 -2
- package/dist/{pricing-refresh-6sw65tck.js → pricing-refresh-v1cbr9te.js} +5 -5
- package/dist/{rbac-roles-nm6924sy.js → rbac-roles-c9wqz83x.js} +4 -4
- package/dist/{render-v2-f026j6c4.js → render-v2-gryrh9h8.js} +4 -4
- package/dist/{setup-w606fr1q.js → setup-q9720bt6.js} +2 -2
- package/dist/{virtual-modules-8qheft18.js → virtual-modules-3htpgn8x.js} +1 -1
- package/dist/{work-objects-ezmggdne.js → work-objects-b76x7deb.js} +17 -17
- package/dist/{worker-1mbyv29k.js → worker-6vrxbv41.js} +26 -26
- package/openapi.json +1626 -629
- package/package.json +2 -1
- package/src/apps/catalog.generated.json +229 -0
- package/src/apps/page-validator.ts +71 -16
- package/src/apps/row-store.ts +66 -11
- package/src/apps/sync.ts +59 -17
- package/src/artifact-sdk/browser-sdk.ts +436 -2
- package/src/be/approval-sweeps.ts +109 -0
- package/src/be/boot-seeds.ts +40 -0
- package/src/be/chunking.ts +1 -1
- package/src/be/db/agents.ts +20 -0
- package/src/be/db/approvals.ts +78 -0
- package/src/be/db/tasks/read.ts +197 -39
- package/src/be/db/tasks/write.ts +41 -9
- package/src/be/db-client.ts +107 -48
- package/src/be/db.ts +219 -76
- package/src/be/events.ts +33 -1
- package/src/be/memory/graph-expansion.ts +10 -3
- package/src/be/memory/providers/sqlite-store.ts +55 -13
- package/src/be/memory/raters/retrieval.ts +5 -2
- package/src/be/memory/reranker.ts +26 -2
- package/src/be/memory/types.ts +2 -0
- package/src/be/migrations/166_approval_request_auto_cancellation.sql +9 -0
- package/src/be/migrations/171_memory_retrieval_relevance.sql +8 -0
- package/src/be/migrations/175_workflow_runs_list_index.sql +11 -0
- package/src/be/migrations/177_events_event_agent_created_index.sql +11 -0
- package/src/be/models-catalog.ts +2 -0
- package/src/be/modelsdev-cache.json +1857 -0
- package/src/be/modelsdev-cache.ts +14 -1
- package/src/be/pricing-normalize.ts +2 -0
- package/src/be/scripts/typecheck.ts +1 -0
- package/src/be/seed-pricing.ts +3 -1
- package/src/be/seed-skills/bundled-files.generated.json +1 -1
- package/src/be/steering.ts +53 -3
- package/src/be/swarm-config-guard.ts +3 -0
- package/src/be/task-citations.ts +29 -0
- package/src/cli.tsx +14 -1
- package/src/commands/onboard/compose-generator.ts +8 -3
- package/src/commands/onboard/container-engine.ts +220 -0
- package/src/commands/onboard/manifest.ts +1 -0
- package/src/commands/onboard/steps/health-check.tsx +4 -1
- package/src/commands/onboard/steps/prereq-check.tsx +50 -54
- package/src/commands/onboard/steps/start.tsx +31 -16
- package/src/commands/onboard/types.ts +7 -2
- package/src/commands/onboard.tsx +39 -4
- package/src/commands/profile-sync.ts +79 -26
- package/src/commands/runner.ts +8 -1
- package/src/extensions/catalog.generated.json +17 -0
- package/src/extensions/contract-types.generated.ts +1 -1
- package/src/heartbeat/heartbeat.ts +263 -103
- package/src/hooks/hook.ts +21 -12
- package/src/http/agents.ts +35 -0
- package/src/http/approval-requests.ts +136 -64
- package/src/http/apps.ts +14 -3
- package/src/http/config.ts +15 -1
- package/src/http/events.ts +62 -2
- package/src/http/index.ts +6 -4
- package/src/http/memory.ts +21 -7
- package/src/http/schedules.ts +1 -1
- package/src/http/session-data.ts +9 -4
- package/src/http/sessions.ts +25 -18
- package/src/http/stats.ts +19 -7
- package/src/http/status.ts +11 -1
- package/src/http/tasks.ts +40 -8
- package/src/http/webhooks.ts +67 -46
- package/src/http/workflows.ts +15 -20
- package/src/linear/outbound.ts +37 -15
- package/src/otel-impl.ts +151 -1
- package/src/otel.ts +14 -3
- package/src/prompts/memories.ts +19 -2
- package/src/providers/claude-managed-models.ts +8 -0
- package/src/providers/modelsdev-reasoning.json +551 -0
- package/src/rbac/legacy-policy.ts +16 -0
- package/src/rbac/permissions.ts +4 -0
- package/src/scheduler/schedule-task.ts +3 -0
- package/src/scripts-runtime/sdk-allowlist.ts +1 -0
- package/src/scripts-runtime/swarm-sdk.ts +9 -0
- package/src/scripts-runtime/types/stdlib.d.ts +1 -0
- package/src/scripts-runtime/types/swarm-extension.d.ts +1 -0
- package/src/scripts-runtime/types/swarm-sdk.d.ts +1 -0
- package/src/server.ts +12 -7
- package/src/slack/app.ts +35 -4
- package/src/slack/connection-state.ts +17 -0
- package/src/tasks/context-key.ts +1 -0
- package/src/tasks/worker-follow-up.ts +6 -2
- package/src/tests/agent-task-activity.test.ts +74 -0
- package/src/tests/agents-list-model-display.test.ts +6 -1
- package/src/tests/approval-auto-cancellation-migration.test.ts +104 -0
- package/src/tests/approval-requests.test.ts +395 -0
- package/src/tests/approval-sweeps-config.test.ts +47 -0
- package/src/tests/approval-sweeps.test.ts +508 -0
- package/src/tests/apps-kibo-blocks.test.ts +77 -0
- package/src/tests/apps-spike.test.ts +56 -1
- package/src/tests/apps-sync-engine.test.ts +20 -9
- package/src/tests/boot-seeds.test.ts +170 -0
- package/src/tests/cancel-approval-request-tool.test.ts +115 -0
- package/src/tests/claude-managed-adapter.test.ts +6 -0
- package/src/tests/config-key-filter.test.ts +80 -0
- package/src/tests/context-window.test.ts +1 -0
- package/src/tests/db-client-busy-retry.test.ts +96 -0
- package/src/tests/db-client.test.ts +60 -1
- package/src/tests/db-task-read-contract.test.ts +10 -0
- package/src/tests/db-task-write-contract.test.ts +9 -0
- package/src/tests/docs-index-footprint.test.js +3 -3
- package/src/tests/events-http.test.ts +103 -0
- package/src/tests/extensions-catalog.test.ts +22 -17
- package/src/tests/extensions-pre-task.test.ts +107 -0
- package/src/tests/fixtures/kibo-blocks-definition.json.txt +105 -0
- package/src/tests/follow-up-config-inheritance.test.ts +260 -0
- package/src/tests/heartbeat-orphan-resume.test.ts +84 -0
- package/src/tests/heartbeat-reboot-live.test.ts +95 -0
- package/src/tests/heartbeat-stall-cas.test.ts +80 -0
- package/src/tests/heartbeat.test.ts +99 -3
- package/src/tests/hook-mcp-config.test.ts +29 -0
- package/src/tests/http-api-integration.test.ts +16 -2
- package/src/tests/kv-storage.test.ts +62 -0
- package/src/tests/linear-outbound-sync.test.ts +120 -5
- package/src/tests/list-endpoint-slimming.test.ts +91 -0
- package/src/tests/memory-graph-organic-route.test.ts +155 -0
- package/src/tests/memory-http-recall-gating.test.ts +14 -4
- package/src/tests/memory-hybrid.test.ts +161 -1
- package/src/tests/memory-prompt-recall-crowdout.test.ts +297 -0
- package/src/tests/memory.test.ts +22 -0
- package/src/tests/onboard-container-engine.test.ts +240 -0
- package/src/tests/otel-event-loop-and-logs.test.ts +99 -0
- package/src/tests/pricing-routes.test.ts +14 -0
- package/src/tests/profile-sync-warning.test.ts +8 -4
- package/src/tests/promote-draft-task-route.test.ts +19 -0
- package/src/tests/provider-registration.test.ts +296 -42
- package/src/tests/rbac-engine.test.ts +30 -0
- package/src/tests/reasoning-effort.test.ts +8 -0
- package/src/tests/script-connections.test.ts +7 -0
- package/src/tests/sdk-allowlist.test.ts +39 -0
- package/src/tests/session-costs-model-key-normalize.test.ts +1 -0
- package/src/tests/sessions-detail-batch.test.ts +250 -0
- package/src/tests/sessions.test.ts +118 -0
- package/src/tests/slack-config.test.ts +22 -0
- package/src/tests/sonnet-5-5-registry.test.ts +44 -0
- package/src/tests/status.test.ts +36 -1
- package/src/tests/swarm-diff.test.ts +316 -10
- package/src/tests/task-completion-idempotency.test.ts +2 -1
- package/src/tests/tasks-requested-by-filter.test.ts +3 -1
- package/src/tests/tool-annotations.test.ts +1 -1
- package/src/tests/ui-list-slim-projections.test.ts +158 -0
- package/src/tests/webhooks-task-blocked.test.ts +127 -0
- package/src/tests/workflow-approval-recovery.test.ts +203 -0
- package/src/tests/workflow-hitl-routing.test.ts +22 -1
- package/src/tests/workflow-hitl-slack-card.test.ts +52 -0
- package/src/tests/workflow-http-v2.test.ts +99 -8
- package/src/tests/workflow-retry-validation.test.ts +56 -0
- package/src/tests/workflow-tla-races.test.ts +389 -0
- package/src/tests/workflow-validation-port-routing.test.ts +113 -1
- package/src/tools/cancel-approval-request.ts +57 -0
- package/src/tools/memory-search.ts +1 -0
- package/src/tools/request-human-input.ts +4 -2
- package/src/tools/send-task.ts +4 -0
- package/src/tools/templates.ts +19 -0
- package/src/tools/tool-config.ts +2 -1
- package/src/tools/workflows/list-workflow-runs.ts +1 -0
- package/src/types.ts +38 -0
- package/src/utils/context-window.ts +1 -0
- package/src/workflows/approval-cancel.ts +152 -0
- package/src/workflows/approval-notifications.ts +84 -0
- package/src/workflows/checkpoint.ts +10 -5
- package/src/workflows/definition.ts +21 -0
- package/src/workflows/engine.ts +152 -21
- package/src/workflows/executors/human-in-the-loop.ts +1 -1
- package/src/workflows/recovery.ts +100 -45
- package/src/workflows/resume.ts +49 -18
- package/src/workflows/retry-poller.ts +71 -21
- package/templates/extensions/README.md +1 -0
- package/templates/extensions/github-sender-allowlist/README.md +18 -0
- package/templates/extensions/github-sender-allowlist/hooks.ts +55 -0
- package/templates/extensions/github-sender-allowlist/manifest.json +8 -0
- package/templates/skills/apps/content.md +20 -1
- package/templates/skills/code-quality/content.md +26 -1
- package/templates/skills/pages/content.md +16 -4
- package/templates/skills/pages/files/examples/multiplayer-board.html +3 -12
- package/dist/cli-2daksqps.js +0 -378
- package/dist/rbac-roles-3gxmnb20.js +0 -37
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { normalizeDate, normalizeDateRequired } from "../date-utils";
|
|
2
|
+
import { getDbClient } from "./runtime";
|
|
3
|
+
|
|
4
|
+
export interface ApprovalRequestListFilters {
|
|
5
|
+
status?: string;
|
|
6
|
+
workflowRunId?: string;
|
|
7
|
+
limit?: number;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/** WHERE + LIMIT shared by the full and slim approval list reads (default limit 100). */
|
|
11
|
+
export function approvalRequestListClause(filters?: ApprovalRequestListFilters): {
|
|
12
|
+
sql: string;
|
|
13
|
+
params: (string | number)[];
|
|
14
|
+
} {
|
|
15
|
+
const conditions: string[] = [];
|
|
16
|
+
const params: (string | number)[] = [];
|
|
17
|
+
if (filters?.status) {
|
|
18
|
+
conditions.push("status = ?");
|
|
19
|
+
params.push(filters.status);
|
|
20
|
+
}
|
|
21
|
+
if (filters?.workflowRunId) {
|
|
22
|
+
conditions.push("workflowRunId = ?");
|
|
23
|
+
params.push(filters.workflowRunId);
|
|
24
|
+
}
|
|
25
|
+
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
26
|
+
params.push(filters?.limit ?? 100);
|
|
27
|
+
return { sql: `${where} ORDER BY createdAt DESC LIMIT ?`, params };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Slim approval list row: what the approvals list page draws. The question
|
|
32
|
+
* bodies, answers, approvers and notification targets are dropped (read them
|
|
33
|
+
* via `getApprovalRequestById`); `questionCount` replaces `questions`.
|
|
34
|
+
*/
|
|
35
|
+
export interface ApprovalRequestSummary {
|
|
36
|
+
id: string;
|
|
37
|
+
title: string;
|
|
38
|
+
questionCount: number;
|
|
39
|
+
workflowRunId: string | null;
|
|
40
|
+
workflowRunStepId: string | null;
|
|
41
|
+
sourceTaskId: string | null;
|
|
42
|
+
status: "pending" | "approved" | "rejected" | "timeout" | "cancelled";
|
|
43
|
+
resolvedBy: string | null;
|
|
44
|
+
resolvedAt: string | null;
|
|
45
|
+
timeoutSeconds: number | null;
|
|
46
|
+
expiresAt: string | null;
|
|
47
|
+
createdBy?: string;
|
|
48
|
+
createdAt: string;
|
|
49
|
+
updatedAt: string;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
type ApprovalRequestSummaryRow = Omit<ApprovalRequestSummary, "questionCount" | "createdBy"> & {
|
|
53
|
+
questionCount: number | null;
|
|
54
|
+
created_by: string | null;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export async function listApprovalRequestSummaries(
|
|
58
|
+
filters?: ApprovalRequestListFilters,
|
|
59
|
+
): Promise<ApprovalRequestSummary[]> {
|
|
60
|
+
const { sql, params } = approvalRequestListClause(filters);
|
|
61
|
+
// Project in SQL so the heavy JSON columns are never read or parsed.
|
|
62
|
+
const rows = await getDbClient().query<ApprovalRequestSummaryRow>(
|
|
63
|
+
`SELECT id, title, workflowRunId, workflowRunStepId, sourceTaskId, status, resolvedBy,
|
|
64
|
+
resolvedAt, timeoutSeconds, expiresAt, created_by, createdAt, updatedAt,
|
|
65
|
+
json_array_length(questions) AS questionCount
|
|
66
|
+
FROM approval_requests ${sql}`,
|
|
67
|
+
params,
|
|
68
|
+
);
|
|
69
|
+
return rows.map(({ created_by, questionCount, ...row }) => ({
|
|
70
|
+
...row,
|
|
71
|
+
questionCount: questionCount ?? 0,
|
|
72
|
+
resolvedAt: normalizeDate(row.resolvedAt),
|
|
73
|
+
expiresAt: normalizeDate(row.expiresAt),
|
|
74
|
+
createdBy: created_by ?? undefined,
|
|
75
|
+
createdAt: normalizeDateRequired(row.createdAt),
|
|
76
|
+
updatedAt: normalizeDateRequired(row.updatedAt),
|
|
77
|
+
}));
|
|
78
|
+
}
|
package/src/be/db/tasks/read.ts
CHANGED
|
@@ -5,6 +5,7 @@ import type {
|
|
|
5
5
|
AgentTaskSource,
|
|
6
6
|
AgentTaskStatus,
|
|
7
7
|
AgentTaskSummary,
|
|
8
|
+
AgentTaskTimelineItem,
|
|
8
9
|
FollowUpConfig,
|
|
9
10
|
ProviderName,
|
|
10
11
|
ReasoningEffort,
|
|
@@ -117,6 +118,97 @@ export type AgentTaskRow = {
|
|
|
117
118
|
routingAffinity: string | null;
|
|
118
119
|
};
|
|
119
120
|
|
|
121
|
+
/**
|
|
122
|
+
* `task` as the list projections read it: the full text, or the UTF-8 byte
|
|
123
|
+
* prefix `TASK_PREVIEW_SQL` selects.
|
|
124
|
+
*/
|
|
125
|
+
type TaskPreviewSource = { task: string | Uint8Array | null };
|
|
126
|
+
type AgentTaskListRow = Omit<AgentTaskRow, "task"> & TaskPreviewSource;
|
|
127
|
+
|
|
128
|
+
/** Columns the slim list mapper reads; `task` may already be a SQL prefix. */
|
|
129
|
+
type AgentTaskSummaryRow = Pick<
|
|
130
|
+
AgentTaskListRow,
|
|
131
|
+
| "id"
|
|
132
|
+
| "key"
|
|
133
|
+
| "agentId"
|
|
134
|
+
| "creatorAgentId"
|
|
135
|
+
| "task"
|
|
136
|
+
| "title"
|
|
137
|
+
| "status"
|
|
138
|
+
| "source"
|
|
139
|
+
| "taskType"
|
|
140
|
+
| "tags"
|
|
141
|
+
| "priority"
|
|
142
|
+
| "dependsOn"
|
|
143
|
+
| "offeredTo"
|
|
144
|
+
| "acceptedAt"
|
|
145
|
+
| "parentTaskId"
|
|
146
|
+
| "scheduleId"
|
|
147
|
+
| "model"
|
|
148
|
+
| "modelTier"
|
|
149
|
+
| "effort"
|
|
150
|
+
| "provider"
|
|
151
|
+
| "requestedByUserId"
|
|
152
|
+
| "progress"
|
|
153
|
+
| "createdAt"
|
|
154
|
+
| "lastUpdatedAt"
|
|
155
|
+
| "finishedAt"
|
|
156
|
+
| "peakContextPercent"
|
|
157
|
+
| "totalCostUsd"
|
|
158
|
+
>;
|
|
159
|
+
|
|
160
|
+
type AgentTaskTimelineRow = Pick<
|
|
161
|
+
AgentTaskListRow,
|
|
162
|
+
| "id"
|
|
163
|
+
| "agentId"
|
|
164
|
+
| "parentTaskId"
|
|
165
|
+
| "task"
|
|
166
|
+
| "title"
|
|
167
|
+
| "status"
|
|
168
|
+
| "createdAt"
|
|
169
|
+
| "lastUpdatedAt"
|
|
170
|
+
| "finishedAt"
|
|
171
|
+
| "peakContextTokens"
|
|
172
|
+
| "totalCostUsd"
|
|
173
|
+
>;
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* SQL column lists for the list projections. `task` is cut to the UTF-8 bytes
|
|
177
|
+
* that can hold one char past the preview length (a UTF-16 unit is at most 4
|
|
178
|
+
* bytes), so the JS side never materializes the full prompt, output or
|
|
179
|
+
* provider blobs. The cut runs on the BLOB: SQLite's text `substr` stops at
|
|
180
|
+
* the first embedded NUL, which the full-text preview keeps. `taskPreview`
|
|
181
|
+
* decodes the bytes and truncates them exactly as it would the full text (the
|
|
182
|
+
* extra char keeps the "…" marker; a code point split at the byte cut lands
|
|
183
|
+
* past it).
|
|
184
|
+
*/
|
|
185
|
+
const TASK_PREVIEW_BYTES = 4 * (TASK_PREVIEW_LENGTH + 1);
|
|
186
|
+
const TASK_PREVIEW_SQL = `substr(CAST(agent_tasks.task AS BLOB), 1, ${TASK_PREVIEW_BYTES}) AS task`;
|
|
187
|
+
// ignoreBOM keeps a leading U+FEFF, which the full-text preview keeps too.
|
|
188
|
+
const taskPreviewDecoder = new TextDecoder("utf-8", { ignoreBOM: true });
|
|
189
|
+
|
|
190
|
+
function taskPreview(task: TaskPreviewSource["task"]): string {
|
|
191
|
+
const text = task instanceof Uint8Array ? taskPreviewDecoder.decode(task) : task;
|
|
192
|
+
return dependencies.previewText(text, TASK_PREVIEW_LENGTH);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const LIST_COLUMNS = {
|
|
196
|
+
full: "agent_tasks.*",
|
|
197
|
+
slim: `agent_tasks.id, agent_tasks."key", agent_tasks.agentId, agent_tasks.creatorAgentId,
|
|
198
|
+
${TASK_PREVIEW_SQL}, agent_tasks.title, agent_tasks.status, agent_tasks.source,
|
|
199
|
+
agent_tasks.taskType, agent_tasks.tags, agent_tasks.priority, agent_tasks.dependsOn,
|
|
200
|
+
agent_tasks.offeredTo, agent_tasks.acceptedAt, agent_tasks.parentTaskId,
|
|
201
|
+
agent_tasks.scheduleId, agent_tasks.model, agent_tasks.modelTier, agent_tasks.effort,
|
|
202
|
+
agent_tasks.provider, agent_tasks.requestedByUserId, agent_tasks.progress,
|
|
203
|
+
agent_tasks.createdAt, agent_tasks.lastUpdatedAt, agent_tasks.finishedAt,
|
|
204
|
+
agent_tasks.peakContextPercent`,
|
|
205
|
+
timeline: `agent_tasks.id, agent_tasks.agentId, agent_tasks.parentTaskId, ${TASK_PREVIEW_SQL},
|
|
206
|
+
agent_tasks.title, agent_tasks.status, agent_tasks.createdAt, agent_tasks.lastUpdatedAt,
|
|
207
|
+
agent_tasks.finishedAt, agent_tasks.peakContextTokens`,
|
|
208
|
+
} as const;
|
|
209
|
+
|
|
210
|
+
type TaskListFields = keyof typeof LIST_COLUMNS;
|
|
211
|
+
|
|
120
212
|
export function rowToAgentTask(row: AgentTaskRow): AgentTask {
|
|
121
213
|
let followUpConfig: FollowUpConfig | undefined;
|
|
122
214
|
if (row.followUpConfig) {
|
|
@@ -251,36 +343,54 @@ export function rowToAgentTask(row: AgentTaskRow): AgentTask {
|
|
|
251
343
|
* context-window fields). The preview is long enough for pool-triage; the full
|
|
252
344
|
* brief is on `get-task-details` / `GET /api/tasks/{id}`.
|
|
253
345
|
*/
|
|
254
|
-
export function rowToAgentTaskSummary(row:
|
|
255
|
-
|
|
346
|
+
export function rowToAgentTaskSummary(row: AgentTaskSummaryRow): AgentTaskSummary {
|
|
347
|
+
return {
|
|
348
|
+
id: row.id,
|
|
349
|
+
key: row.key,
|
|
350
|
+
agentId: row.agentId,
|
|
351
|
+
creatorAgentId: row.creatorAgentId ?? undefined,
|
|
352
|
+
task: taskPreview(row.task),
|
|
353
|
+
title: row.title ?? undefined,
|
|
354
|
+
status: row.status,
|
|
355
|
+
source: row.source,
|
|
356
|
+
taskType: row.taskType ?? undefined,
|
|
357
|
+
tags: row.tags ? JSON.parse(row.tags) : [],
|
|
358
|
+
priority: row.priority ?? 50,
|
|
359
|
+
dependsOn: row.dependsOn ? JSON.parse(row.dependsOn) : [],
|
|
360
|
+
offeredTo: row.offeredTo ?? undefined,
|
|
361
|
+
acceptedAt: row.acceptedAt ?? undefined,
|
|
362
|
+
parentTaskId: row.parentTaskId ?? undefined,
|
|
363
|
+
scheduleId: row.scheduleId ?? undefined,
|
|
364
|
+
model: row.model ?? undefined,
|
|
365
|
+
modelTier: parseModelTier(row.modelTier) ?? undefined,
|
|
366
|
+
effort: ReasoningEffortSchema.safeParse(row.effort).success
|
|
367
|
+
? (row.effort as ReasoningEffort)
|
|
368
|
+
: undefined,
|
|
369
|
+
provider: (row.provider as ProviderName | null) ?? undefined,
|
|
370
|
+
requestedByUserId: row.requestedByUserId ?? undefined,
|
|
371
|
+
progress: row.progress ?? undefined,
|
|
372
|
+
createdAt: row.createdAt,
|
|
373
|
+
lastUpdatedAt: row.lastUpdatedAt,
|
|
374
|
+
finishedAt: row.finishedAt ?? undefined,
|
|
375
|
+
peakContextPercent: row.peakContextPercent ?? undefined,
|
|
376
|
+
totalCostUsd: row.totalCostUsd ?? undefined,
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/** Timeline list-row mapper: only what the dashboard timeline draws. */
|
|
381
|
+
function rowToAgentTaskTimelineItem(row: AgentTaskTimelineRow): AgentTaskTimelineItem {
|
|
256
382
|
return {
|
|
257
|
-
id:
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
dependsOn: t.dependsOn,
|
|
269
|
-
offeredTo: t.offeredTo,
|
|
270
|
-
acceptedAt: t.acceptedAt,
|
|
271
|
-
parentTaskId: t.parentTaskId,
|
|
272
|
-
scheduleId: t.scheduleId,
|
|
273
|
-
model: t.model,
|
|
274
|
-
modelTier: t.modelTier,
|
|
275
|
-
effort: t.effort,
|
|
276
|
-
provider: t.provider,
|
|
277
|
-
requestedByUserId: t.requestedByUserId,
|
|
278
|
-
progress: t.progress,
|
|
279
|
-
createdAt: t.createdAt,
|
|
280
|
-
lastUpdatedAt: t.lastUpdatedAt,
|
|
281
|
-
finishedAt: t.finishedAt,
|
|
282
|
-
peakContextPercent: t.peakContextPercent,
|
|
283
|
-
totalCostUsd: t.totalCostUsd,
|
|
383
|
+
id: row.id,
|
|
384
|
+
agentId: row.agentId,
|
|
385
|
+
parentTaskId: row.parentTaskId ?? undefined,
|
|
386
|
+
task: taskPreview(row.task),
|
|
387
|
+
title: row.title ?? undefined,
|
|
388
|
+
status: row.status,
|
|
389
|
+
createdAt: row.createdAt,
|
|
390
|
+
lastUpdatedAt: row.lastUpdatedAt,
|
|
391
|
+
finishedAt: row.finishedAt ?? undefined,
|
|
392
|
+
peakContextTokens: row.peakContextTokens ?? undefined,
|
|
393
|
+
totalCostUsd: row.totalCostUsd ?? undefined,
|
|
284
394
|
};
|
|
285
395
|
}
|
|
286
396
|
|
|
@@ -321,6 +431,33 @@ export async function hasNonTerminalResumeChild(parentId: string): Promise<boole
|
|
|
321
431
|
return row !== undefined && row !== null;
|
|
322
432
|
}
|
|
323
433
|
|
|
434
|
+
/**
|
|
435
|
+
* Superseded tasks that never got a resume child. Both supersede paths
|
|
436
|
+
* (heartbeat remediation and `POST /api/tasks/:id/supersede`) supersede
|
|
437
|
+
* first and create the resume in a later, separate write, so a process
|
|
438
|
+
* crash between the two leaves this state. Workflow steps are failed, not
|
|
439
|
+
* superseded, and are excluded. `finishedBefore` keeps the sweep from racing
|
|
440
|
+
* an in-flight supersede; `finishedAfter` bounds how far back it repairs.
|
|
441
|
+
*/
|
|
442
|
+
export async function getSupersededTasksWithoutResume(
|
|
443
|
+
finishedBefore: string,
|
|
444
|
+
finishedAfter: string,
|
|
445
|
+
): Promise<AgentTask[]> {
|
|
446
|
+
const rows = await getDbClient().query<AgentTaskRow>(
|
|
447
|
+
`SELECT * FROM agent_tasks t
|
|
448
|
+
WHERE t.status = 'superseded'
|
|
449
|
+
AND t.workflowRunStepId IS NULL
|
|
450
|
+
AND t.finishedAt < ?
|
|
451
|
+
AND t.finishedAt > ?
|
|
452
|
+
AND NOT EXISTS (
|
|
453
|
+
SELECT 1 FROM agent_tasks c WHERE c.parentTaskId = t.id AND c.taskType = 'resume'
|
|
454
|
+
)
|
|
455
|
+
ORDER BY t.finishedAt ASC`,
|
|
456
|
+
[finishedBefore, finishedAfter],
|
|
457
|
+
);
|
|
458
|
+
return rows.map(rowToAgentTask);
|
|
459
|
+
}
|
|
460
|
+
|
|
324
461
|
/**
|
|
325
462
|
* True when a non-terminal `reroute-decision` child exists for `parentId`.
|
|
326
463
|
*
|
|
@@ -438,10 +575,14 @@ export function getAllTasks(
|
|
|
438
575
|
filters: TaskFilters | undefined,
|
|
439
576
|
opts: { slim: true },
|
|
440
577
|
): Promise<AgentTaskSummary[]>;
|
|
578
|
+
export function getAllTasks(
|
|
579
|
+
filters: TaskFilters | undefined,
|
|
580
|
+
opts: { fields: "timeline" },
|
|
581
|
+
): Promise<AgentTaskTimelineItem[]>;
|
|
441
582
|
export async function getAllTasks(
|
|
442
583
|
filters?: TaskFilters,
|
|
443
|
-
opts?: { slim?: boolean },
|
|
444
|
-
): Promise<AgentTask[] | AgentTaskSummary[]> {
|
|
584
|
+
opts?: { slim?: boolean; fields?: "timeline" },
|
|
585
|
+
): Promise<AgentTask[] | AgentTaskSummary[] | AgentTaskTimelineItem[]> {
|
|
445
586
|
const conditions: string[] = [];
|
|
446
587
|
const params: (string | AgentTaskStatus)[] = [];
|
|
447
588
|
|
|
@@ -544,16 +685,31 @@ export async function getAllTasks(
|
|
|
544
685
|
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
545
686
|
const limit = filters?.limit ?? 25;
|
|
546
687
|
const offset = filters?.offset ?? 0;
|
|
547
|
-
const
|
|
688
|
+
const orderColumns =
|
|
548
689
|
filters?.orderBy === "createdAt"
|
|
549
|
-
? "createdAt DESC, rowid DESC"
|
|
550
|
-
: "lastUpdatedAt DESC, priority DESC";
|
|
551
|
-
const
|
|
690
|
+
? ["createdAt DESC", "rowid DESC"]
|
|
691
|
+
: ["lastUpdatedAt DESC", "priority DESC"];
|
|
692
|
+
const fields: TaskListFields =
|
|
693
|
+
opts?.fields === "timeline" ? "timeline" : opts?.slim ? "slim" : "full";
|
|
694
|
+
// Page the rowids first, then read the projected columns and the cost
|
|
695
|
+
// subquery for that page only, so no candidate row's prompt/output blobs
|
|
696
|
+
// are materialized unless the row is on the page. The page query keeps the
|
|
697
|
+
// exact ORDER BY: a `rowid` tiebreak there makes SQLite drop the
|
|
698
|
+
// lastUpdatedAt index for a full scan. The outer sort adds it only to fix
|
|
699
|
+
// the order of exact ties inside the page.
|
|
700
|
+
const query = `WITH page AS (
|
|
701
|
+
SELECT rowid AS rid FROM agent_tasks ${whereClause}
|
|
702
|
+
ORDER BY ${orderColumns.join(", ")} LIMIT ${limit} OFFSET ${offset}
|
|
703
|
+
)
|
|
704
|
+
SELECT ${LIST_COLUMNS[fields]},
|
|
552
705
|
(SELECT SUM(totalCostUsd) FROM session_costs WHERE session_costs.taskId = agent_tasks.id) AS totalCostUsd
|
|
553
|
-
FROM agent_tasks
|
|
554
|
-
ORDER BY ${
|
|
706
|
+
FROM page JOIN agent_tasks ON agent_tasks.rowid = page.rid
|
|
707
|
+
ORDER BY ${[...new Set([...orderColumns, "rowid DESC"])].map((column) => `agent_tasks.${column}`).join(", ")}`;
|
|
555
708
|
|
|
556
|
-
|
|
709
|
+
if (fields === "timeline") {
|
|
710
|
+
const rows = await getDbClient().query<AgentTaskTimelineRow>(query, params);
|
|
711
|
+
return rows.map(rowToAgentTaskTimelineItem);
|
|
712
|
+
}
|
|
557
713
|
|
|
558
714
|
// Filter for ready tasks (dependencies met) if requested. Both the full and
|
|
559
715
|
// the slim row shapes carry `id` + `dependsOn`, so the same predicate works.
|
|
@@ -569,12 +725,14 @@ export async function getAllTasks(
|
|
|
569
725
|
return items.filter((_, i) => readyFlags[i]);
|
|
570
726
|
};
|
|
571
727
|
|
|
572
|
-
if (
|
|
728
|
+
if (fields === "slim") {
|
|
729
|
+
const rows = await getDbClient().query<AgentTaskSummaryRow>(query, params);
|
|
573
730
|
let tasks = rows.map(rowToAgentTaskSummary);
|
|
574
731
|
if (filters?.readyOnly) tasks = await filterReady(tasks);
|
|
575
732
|
return tasks;
|
|
576
733
|
}
|
|
577
734
|
|
|
735
|
+
const rows = await getDbClient().query<AgentTaskRow>(query, params);
|
|
578
736
|
let tasks = rows.map(rowToAgentTask);
|
|
579
737
|
if (filters?.readyOnly) tasks = await filterReady(tasks);
|
|
580
738
|
return tasks;
|
package/src/be/db/tasks/write.ts
CHANGED
|
@@ -436,7 +436,24 @@ export async function completeTask(
|
|
|
436
436
|
return row ? rowToAgentTask(row) : null;
|
|
437
437
|
}
|
|
438
438
|
|
|
439
|
-
export async function failTask(
|
|
439
|
+
export async function failTask(
|
|
440
|
+
id: string,
|
|
441
|
+
reason: string,
|
|
442
|
+
opts: {
|
|
443
|
+
/**
|
|
444
|
+
* Compare-and-swap on the `lastUpdatedAt` the caller observed. The
|
|
445
|
+
* heartbeat passes it so a progress write that lands after its read
|
|
446
|
+
* cancels the remediation instead of being overwritten.
|
|
447
|
+
*/
|
|
448
|
+
expectedLastUpdatedAt?: string;
|
|
449
|
+
/**
|
|
450
|
+
* `false` skips the dependent cascade. Only for a caller that settles the
|
|
451
|
+
* dependents itself — the reboot sweep re-points them to the retry child,
|
|
452
|
+
* then cascades whatever is left.
|
|
453
|
+
*/
|
|
454
|
+
cascadeDependents?: boolean;
|
|
455
|
+
} = {},
|
|
456
|
+
): Promise<AgentTask | null> {
|
|
440
457
|
const oldTask = await getTaskById(id);
|
|
441
458
|
if (!oldTask) return null;
|
|
442
459
|
|
|
@@ -453,8 +470,15 @@ export async function failTask(id: string, reason: string): Promise<AgentTask |
|
|
|
453
470
|
// terminal transition can land during the await above).
|
|
454
471
|
const row = await getDbClient().get<AgentTaskRow>(
|
|
455
472
|
`UPDATE agent_tasks SET status = 'failed', failureReason = ?, finishedAt = ?, lastUpdatedAt = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
|
|
456
|
-
WHERE id = ? AND status NOT IN ('completed', 'failed', 'cancelled', 'superseded')
|
|
457
|
-
|
|
473
|
+
WHERE id = ? AND status NOT IN ('completed', 'failed', 'cancelled', 'superseded')
|
|
474
|
+
AND (? IS NULL OR lastUpdatedAt = ?) RETURNING *`,
|
|
475
|
+
[
|
|
476
|
+
scrubbedReason,
|
|
477
|
+
finishedAt,
|
|
478
|
+
id,
|
|
479
|
+
opts.expectedLastUpdatedAt ?? null,
|
|
480
|
+
opts.expectedLastUpdatedAt ?? null,
|
|
481
|
+
],
|
|
458
482
|
);
|
|
459
483
|
if (row && oldTask) {
|
|
460
484
|
dependencies.emitTaskLifecycleTelemetryAfterCommit(
|
|
@@ -511,10 +535,12 @@ export async function failTask(id: string, reason: string): Promise<AgentTask |
|
|
|
511
535
|
|
|
512
536
|
// Cascade-fail any non-terminal tasks that depend on this one.
|
|
513
537
|
// The cascade is recursive (transitive closure) and cycle-safe.
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
538
|
+
if (opts.cascadeDependents !== false) {
|
|
539
|
+
try {
|
|
540
|
+
await dependencies.cascadeFailDependents(id, "failed");
|
|
541
|
+
} catch (err) {
|
|
542
|
+
console.error("[failTask] cascade-fail dependents error:", err);
|
|
543
|
+
}
|
|
518
544
|
}
|
|
519
545
|
}
|
|
520
546
|
return row ? rowToAgentTask(row) : null;
|
|
@@ -653,7 +679,12 @@ export async function cancelTask(id: string, reason?: string): Promise<AgentTask
|
|
|
653
679
|
*/
|
|
654
680
|
export async function supersedeTask(
|
|
655
681
|
id: string,
|
|
656
|
-
args: {
|
|
682
|
+
args: {
|
|
683
|
+
reason: string;
|
|
684
|
+
resumeTaskId: string | null;
|
|
685
|
+
/** Compare-and-swap on the observed `lastUpdatedAt`; see `failTask`. */
|
|
686
|
+
expectedLastUpdatedAt?: string;
|
|
687
|
+
},
|
|
657
688
|
): Promise<AgentTask | null> {
|
|
658
689
|
const oldTask = await getTaskById(id);
|
|
659
690
|
if (!oldTask) return null;
|
|
@@ -670,8 +701,9 @@ export async function supersedeTask(
|
|
|
670
701
|
finishedAt = ?,
|
|
671
702
|
lastUpdatedAt = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
|
|
672
703
|
WHERE id = ? AND status NOT IN ('completed', 'failed', 'cancelled', 'superseded')
|
|
704
|
+
AND (? IS NULL OR lastUpdatedAt = ?)
|
|
673
705
|
RETURNING *`,
|
|
674
|
-
[finishedAt, id],
|
|
706
|
+
[finishedAt, id, args.expectedLastUpdatedAt ?? null, args.expectedLastUpdatedAt ?? null],
|
|
675
707
|
);
|
|
676
708
|
|
|
677
709
|
if (row && oldTask) {
|