@loopingai/core 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +674 -0
- package/README.md +279 -0
- package/dist/a2a/agent-stub.d.ts +61 -0
- package/dist/a2a/agent-stub.d.ts.map +1 -0
- package/dist/a2a/agent-stub.js +2 -0
- package/dist/a2a/agent-stub.js.map +1 -0
- package/dist/a2a/card.d.ts +119 -0
- package/dist/a2a/card.d.ts.map +1 -0
- package/dist/a2a/card.js +115 -0
- package/dist/a2a/card.js.map +1 -0
- package/dist/a2a/context.d.ts +14 -0
- package/dist/a2a/context.d.ts.map +1 -0
- package/dist/a2a/context.js +55 -0
- package/dist/a2a/context.js.map +1 -0
- package/dist/a2a/executor.d.ts +85 -0
- package/dist/a2a/executor.d.ts.map +1 -0
- package/dist/a2a/executor.js +107 -0
- package/dist/a2a/executor.js.map +1 -0
- package/dist/a2a/index.d.ts +18 -0
- package/dist/a2a/index.d.ts.map +1 -0
- package/dist/a2a/index.js +17 -0
- package/dist/a2a/index.js.map +1 -0
- package/dist/a2a/notify.d.ts +75 -0
- package/dist/a2a/notify.d.ts.map +1 -0
- package/dist/a2a/notify.js +161 -0
- package/dist/a2a/notify.js.map +1 -0
- package/dist/a2a/parts.d.ts +46 -0
- package/dist/a2a/parts.d.ts.map +1 -0
- package/dist/a2a/parts.js +95 -0
- package/dist/a2a/parts.js.map +1 -0
- package/dist/a2a/task-store.d.ts +45 -0
- package/dist/a2a/task-store.d.ts.map +1 -0
- package/dist/a2a/task-store.js +100 -0
- package/dist/a2a/task-store.js.map +1 -0
- package/dist/a2a/task.d.ts +86 -0
- package/dist/a2a/task.d.ts.map +1 -0
- package/dist/a2a/task.js +16 -0
- package/dist/a2a/task.js.map +1 -0
- package/dist/a2a/verify.d.ts +64 -0
- package/dist/a2a/verify.d.ts.map +1 -0
- package/dist/a2a/verify.js +122 -0
- package/dist/a2a/verify.js.map +1 -0
- package/dist/agent/budget.d.ts +47 -0
- package/dist/agent/budget.d.ts.map +1 -0
- package/dist/agent/budget.js +43 -0
- package/dist/agent/budget.js.map +1 -0
- package/dist/agent/control.d.ts +110 -0
- package/dist/agent/control.d.ts.map +1 -0
- package/dist/agent/control.js +115 -0
- package/dist/agent/control.js.map +1 -0
- package/dist/agent/final-reply.d.ts +49 -0
- package/dist/agent/final-reply.d.ts.map +1 -0
- package/dist/agent/final-reply.js +52 -0
- package/dist/agent/final-reply.js.map +1 -0
- package/dist/agent/history.d.ts +83 -0
- package/dist/agent/history.d.ts.map +1 -0
- package/dist/agent/history.js +117 -0
- package/dist/agent/history.js.map +1 -0
- package/dist/agent/index.d.ts +17 -0
- package/dist/agent/index.d.ts.map +1 -0
- package/dist/agent/index.js +17 -0
- package/dist/agent/index.js.map +1 -0
- package/dist/agent/inference.d.ts +52 -0
- package/dist/agent/inference.d.ts.map +1 -0
- package/dist/agent/inference.js +79 -0
- package/dist/agent/inference.js.map +1 -0
- package/dist/agent/model.d.ts +75 -0
- package/dist/agent/model.d.ts.map +1 -0
- package/dist/agent/model.js +62 -0
- package/dist/agent/model.js.map +1 -0
- package/dist/agent/session.d.ts +92 -0
- package/dist/agent/session.d.ts.map +1 -0
- package/dist/agent/session.js +80 -0
- package/dist/agent/session.js.map +1 -0
- package/dist/config.d.ts +159 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +92 -0
- package/dist/config.js.map +1 -0
- package/dist/contract/index.d.ts +10 -0
- package/dist/contract/index.d.ts.map +1 -0
- package/dist/contract/index.js +9 -0
- package/dist/contract/index.js.map +1 -0
- package/dist/contract/plugin.d.ts +163 -0
- package/dist/contract/plugin.d.ts.map +1 -0
- package/dist/contract/plugin.js +39 -0
- package/dist/contract/plugin.js.map +1 -0
- package/dist/contract/recipe.d.ts +170 -0
- package/dist/contract/recipe.d.ts.map +1 -0
- package/dist/contract/recipe.js +2 -0
- package/dist/contract/recipe.js.map +1 -0
- package/dist/contract/validation.d.ts +86 -0
- package/dist/contract/validation.d.ts.map +1 -0
- package/dist/contract/validation.js +76 -0
- package/dist/contract/validation.js.map +1 -0
- package/dist/db/db.d.ts +131 -0
- package/dist/db/db.d.ts.map +1 -0
- package/dist/db/db.js +91 -0
- package/dist/db/db.js.map +1 -0
- package/dist/db/index.d.ts +9 -0
- package/dist/db/index.d.ts.map +1 -0
- package/dist/db/index.js +9 -0
- package/dist/db/index.js.map +1 -0
- package/dist/db/migrations/index.d.ts +21 -0
- package/dist/db/migrations/index.d.ts.map +1 -0
- package/dist/db/migrations/index.js +54 -0
- package/dist/db/migrations/index.js.map +1 -0
- package/dist/db/models/subtasks.d.ts +102 -0
- package/dist/db/models/subtasks.d.ts.map +1 -0
- package/dist/db/models/subtasks.js +285 -0
- package/dist/db/models/subtasks.js.map +1 -0
- package/dist/db/models/tasks.d.ts +92 -0
- package/dist/db/models/tasks.d.ts.map +1 -0
- package/dist/db/models/tasks.js +212 -0
- package/dist/db/models/tasks.js.map +1 -0
- package/dist/db/schema.d.ts +488 -0
- package/dist/db/schema.d.ts.map +1 -0
- package/dist/db/schema.js +91 -0
- package/dist/db/schema.js.map +1 -0
- package/dist/env.d.ts +46 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +40 -0
- package/dist/env.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/platform.d.ts +47 -0
- package/dist/platform.d.ts.map +1 -0
- package/dist/platform.js +47 -0
- package/dist/platform.js.map +1 -0
- package/dist/runtime/index.d.ts +86 -0
- package/dist/runtime/index.d.ts.map +1 -0
- package/dist/runtime/index.js +123 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/runtime/tool-families.d.ts +33 -0
- package/dist/runtime/tool-families.d.ts.map +1 -0
- package/dist/runtime/tool-families.js +62 -0
- package/dist/runtime/tool-families.js.map +1 -0
- package/dist/subagent/fingerprint.d.ts +38 -0
- package/dist/subagent/fingerprint.d.ts.map +1 -0
- package/dist/subagent/fingerprint.js +97 -0
- package/dist/subagent/fingerprint.js.map +1 -0
- package/dist/subagent/index.d.ts +147 -0
- package/dist/subagent/index.d.ts.map +1 -0
- package/dist/subagent/index.js +299 -0
- package/dist/subagent/index.js.map +1 -0
- package/dist/subagent/prompt.d.ts +34 -0
- package/dist/subagent/prompt.d.ts.map +1 -0
- package/dist/subagent/prompt.js +46 -0
- package/dist/subagent/prompt.js.map +1 -0
- package/dist/subagent/run.d.ts +151 -0
- package/dist/subagent/run.d.ts.map +1 -0
- package/dist/subagent/run.js +491 -0
- package/dist/subagent/run.js.map +1 -0
- package/dist/subagent/workspace.d.ts +65 -0
- package/dist/subagent/workspace.d.ts.map +1 -0
- package/dist/subagent/workspace.js +53 -0
- package/dist/subagent/workspace.js.map +1 -0
- package/dist/subtasks/catalog.d.ts +40 -0
- package/dist/subtasks/catalog.d.ts.map +1 -0
- package/dist/subtasks/catalog.js +36 -0
- package/dist/subtasks/catalog.js.map +1 -0
- package/dist/subtasks/decomposition.d.ts +90 -0
- package/dist/subtasks/decomposition.d.ts.map +1 -0
- package/dist/subtasks/decomposition.js +205 -0
- package/dist/subtasks/decomposition.js.map +1 -0
- package/dist/subtasks/delegate.d.ts +77 -0
- package/dist/subtasks/delegate.d.ts.map +1 -0
- package/dist/subtasks/delegate.js +102 -0
- package/dist/subtasks/delegate.js.map +1 -0
- package/dist/subtasks/index.d.ts +11 -0
- package/dist/subtasks/index.d.ts.map +1 -0
- package/dist/subtasks/index.js +11 -0
- package/dist/subtasks/index.js.map +1 -0
- package/dist/subtasks/scheduler.d.ts +49 -0
- package/dist/subtasks/scheduler.d.ts.map +1 -0
- package/dist/subtasks/scheduler.js +48 -0
- package/dist/subtasks/scheduler.js.map +1 -0
- package/dist/subtasks/subtask-types.d.ts +100 -0
- package/dist/subtasks/subtask-types.d.ts.map +1 -0
- package/dist/subtasks/subtask-types.js +111 -0
- package/dist/subtasks/subtask-types.js.map +1 -0
- package/dist/subtasks/types.d.ts +321 -0
- package/dist/subtasks/types.d.ts.map +1 -0
- package/dist/subtasks/types.js +16 -0
- package/dist/subtasks/types.js.map +1 -0
- package/dist/testing/auth.d.ts +12 -0
- package/dist/testing/auth.d.ts.map +1 -0
- package/dist/testing/auth.js +25 -0
- package/dist/testing/auth.js.map +1 -0
- package/dist/testing/do.d.ts +30 -0
- package/dist/testing/do.d.ts.map +1 -0
- package/dist/testing/do.js +26 -0
- package/dist/testing/do.js.map +1 -0
- package/dist/testing/fake-session.d.ts +27 -0
- package/dist/testing/fake-session.d.ts.map +1 -0
- package/dist/testing/fake-session.js +38 -0
- package/dist/testing/fake-session.js.map +1 -0
- package/dist/testing/fixtures.d.ts +27 -0
- package/dist/testing/fixtures.d.ts.map +1 -0
- package/dist/testing/fixtures.js +67 -0
- package/dist/testing/fixtures.js.map +1 -0
- package/dist/testing/index.d.ts +31 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +31 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/testing/mock-model.d.ts +43 -0
- package/dist/testing/mock-model.d.ts.map +1 -0
- package/dist/testing/mock-model.js +62 -0
- package/dist/testing/mock-model.js.map +1 -0
- package/dist/testing/node.d.ts +24 -0
- package/dist/testing/node.d.ts.map +1 -0
- package/dist/testing/node.js +24 -0
- package/dist/testing/node.js.map +1 -0
- package/dist/testing/vcr-global-setup.d.ts +3 -0
- package/dist/testing/vcr-global-setup.d.ts.map +1 -0
- package/dist/testing/vcr-global-setup.js +9 -0
- package/dist/testing/vcr-global-setup.js.map +1 -0
- package/dist/testing/vcr-shared.d.ts +21 -0
- package/dist/testing/vcr-shared.d.ts.map +1 -0
- package/dist/testing/vcr-shared.js +21 -0
- package/dist/testing/vcr-shared.js.map +1 -0
- package/dist/testing/vcr-spec.d.ts +17 -0
- package/dist/testing/vcr-spec.d.ts.map +1 -0
- package/dist/testing/vcr-spec.js +72 -0
- package/dist/testing/vcr-spec.js.map +1 -0
- package/dist/testing/vcr.d.ts +81 -0
- package/dist/testing/vcr.d.ts.map +1 -0
- package/dist/testing/vcr.js +211 -0
- package/dist/testing/vcr.js.map +1 -0
- package/dist/worker/index.d.ts +62 -0
- package/dist/worker/index.d.ts.map +1 -0
- package/dist/worker/index.js +194 -0
- package/dist/worker/index.js.map +1 -0
- package/eslint-rules/index.js +31 -0
- package/eslint-rules/no-deprecated-object-properties.js +81 -0
- package/package.json +147 -0
- package/scripts/generate-keys.mjs +53 -0
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
import { and, eq, inArray, lt, max } from "drizzle-orm";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { subtasks } from "../schema.js";
|
|
4
|
+
const referenceSchema = z.object({
|
|
5
|
+
role: z.enum(["user", "assistant"]),
|
|
6
|
+
text: z.string()
|
|
7
|
+
});
|
|
8
|
+
const resultPartSchema = z.object({
|
|
9
|
+
kind: z.literal("text"),
|
|
10
|
+
text: z.string()
|
|
11
|
+
});
|
|
12
|
+
const referencesSchema = z.array(referenceSchema);
|
|
13
|
+
const resultPartsSchema = z.array(resultPartSchema);
|
|
14
|
+
const dependsOnSchema = z.array(z.number().int());
|
|
15
|
+
const paramsSchema = z.record(z.string(), z.string());
|
|
16
|
+
/**
|
|
17
|
+
* Query methods for the `subtasks` table (durable decomposed units of work).
|
|
18
|
+
*
|
|
19
|
+
* Bound to a drizzle handle by {@link AgentDB} and reached as `db.subtasks.*`.
|
|
20
|
+
* durable-sqlite is synchronous; the multi-statement `createDecomposition` runs
|
|
21
|
+
* inside an explicit `db.transaction` (drizzle maps it to
|
|
22
|
+
* `storage.transactionSync`), so a mid-create failure rolls back every statement
|
|
23
|
+
* instead of leaving a partial DAG. Guarded transitions filter on the expected
|
|
24
|
+
* current `status`, so a disallowed transition matches no row and is a no-op.
|
|
25
|
+
*/
|
|
26
|
+
export function makeSubtasks(db, opts) {
|
|
27
|
+
const rowToSubtask = (row) => ({
|
|
28
|
+
id: row.id,
|
|
29
|
+
taskId: row.taskId,
|
|
30
|
+
round: row.round,
|
|
31
|
+
ordinal: row.ordinal,
|
|
32
|
+
type: row.type,
|
|
33
|
+
recipeId: row.recipeId,
|
|
34
|
+
recipeVersion: row.recipeVersion,
|
|
35
|
+
prompt: row.prompt,
|
|
36
|
+
references: referencesSchema.parse(JSON.parse(row.referencesJson)),
|
|
37
|
+
dependsOn: dependsOnSchema.parse(JSON.parse(row.dependsOnJson)),
|
|
38
|
+
params: paramsSchema.parse(JSON.parse(row.paramsJson)),
|
|
39
|
+
status: row.status,
|
|
40
|
+
resultParts: row.resultPartsJson === null
|
|
41
|
+
? null
|
|
42
|
+
: resultPartsSchema.parse(JSON.parse(row.resultPartsJson)),
|
|
43
|
+
error: row.error,
|
|
44
|
+
createdAt: row.createdAt,
|
|
45
|
+
updatedAt: row.updatedAt,
|
|
46
|
+
completedAt: row.completedAt
|
|
47
|
+
});
|
|
48
|
+
const list = (taskId) => db
|
|
49
|
+
.select()
|
|
50
|
+
.from(subtasks)
|
|
51
|
+
.where(eq(subtasks.taskId, taskId))
|
|
52
|
+
.orderBy(subtasks.ordinal)
|
|
53
|
+
.all()
|
|
54
|
+
.map(rowToSubtask);
|
|
55
|
+
/** Rides `idx_subtasks_task_round` — no full-Task scan to reach one round. */
|
|
56
|
+
const listRound = (taskId, round) => db
|
|
57
|
+
.select()
|
|
58
|
+
.from(subtasks)
|
|
59
|
+
.where(and(eq(subtasks.taskId, taskId), eq(subtasks.round, round)))
|
|
60
|
+
.orderBy(subtasks.ordinal)
|
|
61
|
+
.all()
|
|
62
|
+
.map(rowToSubtask);
|
|
63
|
+
/** Guarded status update: applies only from a `from` status, returns whether it did. */
|
|
64
|
+
const transition = (id, from, set) => {
|
|
65
|
+
const now = Date.now();
|
|
66
|
+
const statuses = Array.isArray(from) ? from : [from];
|
|
67
|
+
const updated = db
|
|
68
|
+
.update(subtasks)
|
|
69
|
+
.set({ ...set, updatedAt: now })
|
|
70
|
+
.where(and(eq(subtasks.id, id), inArray(subtasks.status, statuses)))
|
|
71
|
+
.returning({ id: subtasks.id })
|
|
72
|
+
.all();
|
|
73
|
+
return updated.length > 0;
|
|
74
|
+
};
|
|
75
|
+
return {
|
|
76
|
+
/**
|
|
77
|
+
* Create one **round's** decomposition atomically: the entire
|
|
78
|
+
* check-validate-insert sequence runs in one synchronous `db.transaction`, so
|
|
79
|
+
* a failure anywhere rolls back every statement — no truncated subtask set, no
|
|
80
|
+
* nodes left with unwritten dependency edges. Idempotent on
|
|
81
|
+
* `(taskId, round)`: if this round already has Subtasks, returns them
|
|
82
|
+
* unchanged (a Workflow-step retry must not duplicate work); the unique
|
|
83
|
+
* `(task_id, ordinal)` index is the schema-level backstop. Enforces the 1..8
|
|
84
|
+
* per-round bound, unique local keys, and resolvable non-self dependency
|
|
85
|
+
* edges, then resolves draft-local dependency keys to the SQLite-assigned
|
|
86
|
+
* {@link SubtaskId}s. Full cycle/edge DAG validation lives in the turn
|
|
87
|
+
* operation; this is the storage guard.
|
|
88
|
+
*
|
|
89
|
+
* `ordinal` continues across rounds (it is the Task-wide position, and what
|
|
90
|
+
* the unique index is built on), so a later round's rows sort after the
|
|
91
|
+
* earlier ones in {@link list}. Dependency edges never cross a round: each
|
|
92
|
+
* round's DAG is self-contained, and the drafts handed here only carry keys
|
|
93
|
+
* from their own round.
|
|
94
|
+
*/
|
|
95
|
+
createDecomposition(taskId, round, drafts) {
|
|
96
|
+
return db.transaction(() => {
|
|
97
|
+
const existing = listRound(taskId, round);
|
|
98
|
+
if (existing.length > 0)
|
|
99
|
+
return existing;
|
|
100
|
+
if (drafts.length < 1 || drafts.length > opts.maxSubtasks) {
|
|
101
|
+
throw new Error(`decomposition must have 1..${opts.maxSubtasks} subtasks, got ${drafts.length}`);
|
|
102
|
+
}
|
|
103
|
+
// Register every local key first, rejecting duplicates. This is its own
|
|
104
|
+
// pass because the dependency check below reads the complete key set: an
|
|
105
|
+
// edge may point forward to a draft defined later, so every key must be
|
|
106
|
+
// known before any edge is validated.
|
|
107
|
+
const keys = new Set();
|
|
108
|
+
for (const d of drafts) {
|
|
109
|
+
if (keys.has(d.localKey)) {
|
|
110
|
+
throw new Error(`duplicate draft local key: ${d.localKey}`);
|
|
111
|
+
}
|
|
112
|
+
keys.add(d.localKey);
|
|
113
|
+
}
|
|
114
|
+
for (const d of drafts) {
|
|
115
|
+
for (const dep of d.dependsOn) {
|
|
116
|
+
// A subtask cannot depend on itself.
|
|
117
|
+
if (dep === d.localKey) {
|
|
118
|
+
throw new Error(`subtask ${d.localKey} depends on itself`);
|
|
119
|
+
}
|
|
120
|
+
// Every edge must resolve to a key in this decomposition.
|
|
121
|
+
if (!keys.has(dep)) {
|
|
122
|
+
throw new Error(`subtask ${d.localKey} depends on unknown key: ${dep}`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
// References must match the persisted shape before we serialize them.
|
|
126
|
+
referencesSchema.parse(d.references);
|
|
127
|
+
}
|
|
128
|
+
const now = Date.now();
|
|
129
|
+
const keyToId = new Map();
|
|
130
|
+
// Pass 1: insert nodes (deps empty for now) to assign ids. Ordinals
|
|
131
|
+
// continue above every earlier round's rows — from the current maximum,
|
|
132
|
+
// not a row count, so a gap left by {@link cleanup} deleting part of a
|
|
133
|
+
// Task cannot hand a later round an ordinal that is already taken. Both
|
|
134
|
+
// read the `(task_id, ordinal)` index; only this one stays monotonic.
|
|
135
|
+
const highest = db
|
|
136
|
+
.select({ max: max(subtasks.ordinal) })
|
|
137
|
+
.from(subtasks)
|
|
138
|
+
.where(eq(subtasks.taskId, taskId))
|
|
139
|
+
.get()?.max ?? null;
|
|
140
|
+
const firstOrdinal = highest === null ? 0 : highest + 1;
|
|
141
|
+
drafts.forEach((d, offset) => {
|
|
142
|
+
const { id } = db
|
|
143
|
+
.insert(subtasks)
|
|
144
|
+
.values({
|
|
145
|
+
taskId,
|
|
146
|
+
round,
|
|
147
|
+
ordinal: firstOrdinal + offset,
|
|
148
|
+
type: d.type,
|
|
149
|
+
recipeId: null,
|
|
150
|
+
recipeVersion: null,
|
|
151
|
+
prompt: d.prompt,
|
|
152
|
+
referencesJson: JSON.stringify(d.references),
|
|
153
|
+
dependsOnJson: "[]",
|
|
154
|
+
paramsJson: JSON.stringify(d.params ?? {}),
|
|
155
|
+
status: "pending",
|
|
156
|
+
resultPartsJson: null,
|
|
157
|
+
error: null,
|
|
158
|
+
createdAt: now,
|
|
159
|
+
updatedAt: now,
|
|
160
|
+
completedAt: null
|
|
161
|
+
})
|
|
162
|
+
.returning({ id: subtasks.id })
|
|
163
|
+
.get();
|
|
164
|
+
keyToId.set(d.localKey, id);
|
|
165
|
+
});
|
|
166
|
+
// Pass 2: rewrite dependency edges to real ids.
|
|
167
|
+
for (const d of drafts) {
|
|
168
|
+
if (d.dependsOn.length === 0)
|
|
169
|
+
continue;
|
|
170
|
+
const ids = d.dependsOn.map((k) => keyToId.get(k));
|
|
171
|
+
db.update(subtasks)
|
|
172
|
+
.set({ dependsOnJson: JSON.stringify(ids) })
|
|
173
|
+
.where(eq(subtasks.id, keyToId.get(d.localKey)))
|
|
174
|
+
.run();
|
|
175
|
+
}
|
|
176
|
+
return listRound(taskId, round);
|
|
177
|
+
});
|
|
178
|
+
},
|
|
179
|
+
/** Load one Subtask by id. */
|
|
180
|
+
get(id) {
|
|
181
|
+
const row = db.select().from(subtasks).where(eq(subtasks.id, id)).get();
|
|
182
|
+
return row ? rowToSubtask(row) : null;
|
|
183
|
+
},
|
|
184
|
+
/** List every round's Subtasks for a Task, in ordinal order. */
|
|
185
|
+
list(taskId) {
|
|
186
|
+
return list(taskId);
|
|
187
|
+
},
|
|
188
|
+
/**
|
|
189
|
+
* List one round's Subtasks, in ordinal order — the scheduler's view. Phase 2
|
|
190
|
+
* drives a single round's DAG at a time, so it must not see a sibling round's
|
|
191
|
+
* rows; composition, by contrast, reads {@link list} across all rounds.
|
|
192
|
+
*/
|
|
193
|
+
listRound(taskId, round) {
|
|
194
|
+
return listRound(taskId, round);
|
|
195
|
+
},
|
|
196
|
+
/**
|
|
197
|
+
* Begin execution: guarded `pending -> running`, recording the resolved
|
|
198
|
+
* Recipe id/version after-the-fact. Returns false if the Subtask was not
|
|
199
|
+
* pending (already started, terminal, or unknown).
|
|
200
|
+
*/
|
|
201
|
+
start(id, recipe) {
|
|
202
|
+
return transition(id, "pending", {
|
|
203
|
+
status: "running",
|
|
204
|
+
recipeId: recipe.recipeId,
|
|
205
|
+
recipeVersion: recipe.recipeVersion
|
|
206
|
+
});
|
|
207
|
+
},
|
|
208
|
+
/**
|
|
209
|
+
* Persist a successful terminal result: guarded `running -> completed`.
|
|
210
|
+
* Requires at least one non-empty text result part (a successful Recipe
|
|
211
|
+
* output invariant).
|
|
212
|
+
*/
|
|
213
|
+
complete(id, resultParts) {
|
|
214
|
+
const parts = resultPartsSchema.parse(resultParts);
|
|
215
|
+
if (!parts.some((p) => p.text.trim().length > 0)) {
|
|
216
|
+
throw new Error("completed subtask requires a non-empty text part");
|
|
217
|
+
}
|
|
218
|
+
return transition(id, "running", {
|
|
219
|
+
status: "completed",
|
|
220
|
+
resultPartsJson: JSON.stringify(parts),
|
|
221
|
+
completedAt: Date.now()
|
|
222
|
+
});
|
|
223
|
+
},
|
|
224
|
+
/**
|
|
225
|
+
* Persist a failure from either non-terminal status, with a diagnostic message.
|
|
226
|
+
*
|
|
227
|
+
* Both sides are reachable and both must land. A child's failed result arrives
|
|
228
|
+
* on a `running` row. The Workflow's last resort — `failSubtask`, once
|
|
229
|
+
* `execute:<id>` has exhausted every retry — can arrive on either:
|
|
230
|
+
* `executeSubtask` may throw before its `pending -> running` claim (a
|
|
231
|
+
* dependency-invariant fault) or after it (a transient child fault). Leaving
|
|
232
|
+
* either behind strands the row: a `pending` node re-enters the next wave
|
|
233
|
+
* forever, and a `running` node blocks its dependents, which {@link skip} only
|
|
234
|
+
* propagates past *failed* prerequisites.
|
|
235
|
+
*
|
|
236
|
+
* Returns false once the row is terminal — a late loser to the real result.
|
|
237
|
+
*/
|
|
238
|
+
fail(id, error) {
|
|
239
|
+
return transition(id, ["running", "pending"], {
|
|
240
|
+
status: "failed",
|
|
241
|
+
error,
|
|
242
|
+
completedAt: Date.now()
|
|
243
|
+
});
|
|
244
|
+
},
|
|
245
|
+
/** Skip a Subtask blocked by a failed/skipped dependency: guarded `pending -> skipped`. */
|
|
246
|
+
skip(id) {
|
|
247
|
+
return transition(id, "pending", { status: "skipped" });
|
|
248
|
+
},
|
|
249
|
+
/**
|
|
250
|
+
* Discard a late result after parent cancellation: guarded
|
|
251
|
+
* `running -> canceled`. The parent calls this when a child returned a
|
|
252
|
+
* terminal result but the Task was canceled while it ran — the result is
|
|
253
|
+
* dropped, and this leaves the row in a truthful terminal state instead of a
|
|
254
|
+
* `running` that never resolves. Returns false if the Subtask was not running.
|
|
255
|
+
*/
|
|
256
|
+
cancelRunning(id) {
|
|
257
|
+
return transition(id, "running", {
|
|
258
|
+
status: "canceled",
|
|
259
|
+
completedAt: Date.now()
|
|
260
|
+
});
|
|
261
|
+
},
|
|
262
|
+
/**
|
|
263
|
+
* Cancel every still-pending Subtask of a Task (parent cancellation).
|
|
264
|
+
* Running Subtasks are left alone here — the parent transitions those with
|
|
265
|
+
* {@link cancelRunning} once their in-flight result comes back and is
|
|
266
|
+
* discarded. Returns the number canceled.
|
|
267
|
+
*/
|
|
268
|
+
cancelPending(taskId) {
|
|
269
|
+
const now = Date.now();
|
|
270
|
+
const canceled = db
|
|
271
|
+
.update(subtasks)
|
|
272
|
+
.set({ status: "canceled", updatedAt: now })
|
|
273
|
+
.where(and(eq(subtasks.taskId, taskId), eq(subtasks.status, "pending")))
|
|
274
|
+
.returning({ id: subtasks.id })
|
|
275
|
+
.all();
|
|
276
|
+
return canceled.length;
|
|
277
|
+
},
|
|
278
|
+
/** Delete Subtasks older than 30 days (called by the weekly maintenance cron). */
|
|
279
|
+
cleanup() {
|
|
280
|
+
const cutoff = Date.now() - 30 * 24 * 60 * 60 * 1000;
|
|
281
|
+
db.delete(subtasks).where(lt(subtasks.createdAt, cutoff)).run();
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
//# sourceMappingURL=subtasks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subtasks.js","sourceRoot":"","sources":["../../../src/db/models/subtasks.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { Task, TaskState } from "@a2a-js/sdk";
|
|
2
|
+
import { type PlainTask } from "../../a2a/task.js";
|
|
3
|
+
import type { DB } from "../db.js";
|
|
4
|
+
/** Filters + paging for {@link makeTasks} `list` (the A2A `ListTasks` method). */
|
|
5
|
+
export interface TaskListQuery {
|
|
6
|
+
/** A2A context id to filter on; empty/absent means no filter. */
|
|
7
|
+
contextId?: string;
|
|
8
|
+
/** Task state to filter on, or `undefined` for any state. */
|
|
9
|
+
state?: TaskState;
|
|
10
|
+
/** Only tasks whose status was written at or after this epoch-ms instant. */
|
|
11
|
+
updatedAfter?: number;
|
|
12
|
+
/** Drop `artifacts` from the returned tasks (the spec's default). */
|
|
13
|
+
includeArtifacts: boolean;
|
|
14
|
+
/** Cap on each task's `history`; `undefined` keeps it whole, `0` drops it. */
|
|
15
|
+
historyLength?: number;
|
|
16
|
+
limit: number;
|
|
17
|
+
offset: number;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* A task's state, tolerating the `status`-less task the SDK's generated type
|
|
21
|
+
* permits (`TaskStatus | undefined`). Nothing we build omits it, so an
|
|
22
|
+
* unspecified state means the row came from somewhere unexpected — and it
|
|
23
|
+
* compares equal to none of the states the callers switch on.
|
|
24
|
+
*/
|
|
25
|
+
export declare function stateOf(task: Task): TaskState;
|
|
26
|
+
/**
|
|
27
|
+
* Query methods for the `notify_tasks` table (async A2A task state).
|
|
28
|
+
*
|
|
29
|
+
* Bound to a drizzle handle by {@link AgentDB} and reached as `db.tasks.*`.
|
|
30
|
+
* Migrations are owned by `AgentDB`, not this factory — it only issues queries.
|
|
31
|
+
*
|
|
32
|
+
* Rows hold the task in its **A2A wire form** (`Task.toJSON`), not the in-memory
|
|
33
|
+
* protobuf shape the SDK hands us. Those differ under v1.0 — enums are numbers
|
|
34
|
+
* in memory but `SCREAMING_SNAKE` strings on the wire, and `Part.content` is a
|
|
35
|
+
* `{ $case, value }` wrapper in memory but a bare named key on the wire — so a
|
|
36
|
+
* plain `JSON.stringify` would persist a shape that is neither valid A2A JSON
|
|
37
|
+
* nor stable across SDK versions. Encoding on write and decoding on read keeps
|
|
38
|
+
* the stored bytes the spec's own format, which is also exactly what
|
|
39
|
+
* `postNotification` puts on the wire.
|
|
40
|
+
*/
|
|
41
|
+
export declare function makeTasks(db: DB): {
|
|
42
|
+
/**
|
|
43
|
+
* Accept a turn: return the `submitted` task for the given `messageId`,
|
|
44
|
+
* creating it on first sight. Idempotent on `messageId` (the gateway's
|
|
45
|
+
* dedupe key, stable across dispatch retries).
|
|
46
|
+
*/
|
|
47
|
+
begin(input: {
|
|
48
|
+
messageId: string;
|
|
49
|
+
taskId: string;
|
|
50
|
+
contextId: string;
|
|
51
|
+
}): PlainTask;
|
|
52
|
+
/** Load a task by id (for `GetTask` via the Worker's `DurableTaskStore`). */
|
|
53
|
+
get(taskId: string): PlainTask | null;
|
|
54
|
+
/**
|
|
55
|
+
* A page of this caller's tasks for `ListTasks`, newest first, with the
|
|
56
|
+
* total matching count so the store can decide whether a next page exists.
|
|
57
|
+
*/
|
|
58
|
+
list(query: TaskListQuery): {
|
|
59
|
+
tasks: PlainTask[];
|
|
60
|
+
totalSize: number;
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* Upsert a task by id, preserving the `message_id` set by {@link begin}.
|
|
64
|
+
* Returns whether the write applied.
|
|
65
|
+
*
|
|
66
|
+
* Guarded exactly like {@link markWorking}, and for the same reason: a
|
|
67
|
+
* `canceled` row is terminal, so nothing may write a non-canceled state over
|
|
68
|
+
* it. That closes the window between a workflow's terminal build and its
|
|
69
|
+
* callback — the read-check-write is synchronous here, so a `CancelTask`
|
|
70
|
+
* landing mid-delivery makes this return `false` and the notify never fires.
|
|
71
|
+
* Writing `canceled` onto a live row stays allowed: that is how the a2a-js
|
|
72
|
+
* handler's own cancel branch records the cancellation.
|
|
73
|
+
*/
|
|
74
|
+
save(task: Task): boolean;
|
|
75
|
+
/**
|
|
76
|
+
* Move a task to `working` (a turn workflow's first step). Returns
|
|
77
|
+
* `"canceled"` when the row is already canceled, which is the caller's
|
|
78
|
+
* signal to stop the turn; every other outcome — unknown task, or a task
|
|
79
|
+
* already past `submitted` — is a no-op reported as `"ok"`, because a
|
|
80
|
+
* workflow replay legitimately re-runs this step.
|
|
81
|
+
*/
|
|
82
|
+
markWorking(taskId: string): "ok" | "canceled";
|
|
83
|
+
/**
|
|
84
|
+
* Flip the task to `canceled` and return it. Terminal: once this lands,
|
|
85
|
+
* {@link save} refuses every non-canceled write, so no completed or failed
|
|
86
|
+
* callback can be built from this row afterwards.
|
|
87
|
+
*/
|
|
88
|
+
cancel(taskId: string): PlainTask | null;
|
|
89
|
+
/** Delete all tasks older than 30 days (called by the maintenance cron). */
|
|
90
|
+
cleanup(): void;
|
|
91
|
+
};
|
|
92
|
+
//# sourceMappingURL=tasks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tasks.d.ts","sourceRoot":"","sources":["../../../src/db/models/tasks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE9C,OAAO,EAAkB,KAAK,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAGnE,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,UAAU,CAAC;AAMnC,kFAAkF;AAClF,MAAM,WAAW,aAAa;IAC5B,iEAAiE;IACjE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6DAA6D;IAC7D,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,6EAA6E;IAC7E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,qEAAqE;IACrE,gBAAgB,EAAE,OAAO,CAAC;IAC1B,8EAA8E;IAC9E,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;GAKG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,IAAI,GAAG,SAAS,CAE7C;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,SAAS,CAAC,EAAE,EAAE,EAAE;IAiC5B;;;;OAIG;iBACU;QACX,SAAS,EAAE,MAAM,CAAC;QAClB,MAAM,EAAE,MAAM,CAAC;QACf,SAAS,EAAE,MAAM,CAAC;KACnB,GAAG,SAAS;IAwBb,6EAA6E;gBACjE,MAAM,GAAG,SAAS,GAAG,IAAI;IAIrC;;;OAGG;gBACS,aAAa,GAAG;QAAE,KAAK,EAAE,SAAS,EAAE,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE;IAiCrE;;;;;;;;;;;OAWG;eACQ,IAAI,GAAG,OAAO;IAazB;;;;;;OAMG;wBACiB,MAAM,GAAG,IAAI,GAAG,UAAU;IAe9C;;;;OAIG;mBACY,MAAM,GAAG,SAAS,GAAG,IAAI;IAaxC,4EAA4E;eACjE,IAAI;EAKlB"}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import { Task, TaskState } from "@a2a-js/sdk";
|
|
2
|
+
import { and, count, desc, eq, gte, lt } from "drizzle-orm";
|
|
3
|
+
import { taskStateLabel } from "../../a2a/task.js";
|
|
4
|
+
import { buildSubmittedTask } from "../../a2a/notify.js";
|
|
5
|
+
import { notifyTasks } from "../schema.js";
|
|
6
|
+
function nowIso() {
|
|
7
|
+
return new Date().toISOString();
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* A task's state, tolerating the `status`-less task the SDK's generated type
|
|
11
|
+
* permits (`TaskStatus | undefined`). Nothing we build omits it, so an
|
|
12
|
+
* unspecified state means the row came from somewhere unexpected — and it
|
|
13
|
+
* compares equal to none of the states the callers switch on.
|
|
14
|
+
*/
|
|
15
|
+
export function stateOf(task) {
|
|
16
|
+
return task.status?.state ?? TaskState.TASK_STATE_UNSPECIFIED;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Query methods for the `notify_tasks` table (async A2A task state).
|
|
20
|
+
*
|
|
21
|
+
* Bound to a drizzle handle by {@link AgentDB} and reached as `db.tasks.*`.
|
|
22
|
+
* Migrations are owned by `AgentDB`, not this factory — it only issues queries.
|
|
23
|
+
*
|
|
24
|
+
* Rows hold the task in its **A2A wire form** (`Task.toJSON`), not the in-memory
|
|
25
|
+
* protobuf shape the SDK hands us. Those differ under v1.0 — enums are numbers
|
|
26
|
+
* in memory but `SCREAMING_SNAKE` strings on the wire, and `Part.content` is a
|
|
27
|
+
* `{ $case, value }` wrapper in memory but a bare named key on the wire — so a
|
|
28
|
+
* plain `JSON.stringify` would persist a shape that is neither valid A2A JSON
|
|
29
|
+
* nor stable across SDK versions. Encoding on write and decoding on read keeps
|
|
30
|
+
* the stored bytes the spec's own format, which is also exactly what
|
|
31
|
+
* `postNotification` puts on the wire.
|
|
32
|
+
*/
|
|
33
|
+
export function makeTasks(db) {
|
|
34
|
+
// Every row was written by the builders in `a2a/notify`, which produce exactly
|
|
35
|
+
// the narrowed {@link PlainTask} shape, so the decode lands back on it.
|
|
36
|
+
const parse = (row) => Task.fromJSON(JSON.parse(row.taskJson));
|
|
37
|
+
const serialize = (task) => JSON.stringify(Task.toJSON(task));
|
|
38
|
+
const readOne = (taskId) => {
|
|
39
|
+
const row = db
|
|
40
|
+
.select()
|
|
41
|
+
.from(notifyTasks)
|
|
42
|
+
.where(eq(notifyTasks.taskId, taskId))
|
|
43
|
+
.get();
|
|
44
|
+
return row ? parse(row) : null;
|
|
45
|
+
};
|
|
46
|
+
const upsert = (task) => {
|
|
47
|
+
const now = Date.now();
|
|
48
|
+
// Denormalized so `ListTasks` can filter on it without decoding every blob.
|
|
49
|
+
const columns = {
|
|
50
|
+
contextId: task.contextId,
|
|
51
|
+
state: taskStateLabel(stateOf(task)),
|
|
52
|
+
taskJson: serialize(task),
|
|
53
|
+
updatedAt: now
|
|
54
|
+
};
|
|
55
|
+
db.insert(notifyTasks)
|
|
56
|
+
.values({ taskId: task.id, messageId: null, createdAt: now, ...columns })
|
|
57
|
+
.onConflictDoUpdate({ target: notifyTasks.taskId, set: columns })
|
|
58
|
+
.run();
|
|
59
|
+
};
|
|
60
|
+
return {
|
|
61
|
+
/**
|
|
62
|
+
* Accept a turn: return the `submitted` task for the given `messageId`,
|
|
63
|
+
* creating it on first sight. Idempotent on `messageId` (the gateway's
|
|
64
|
+
* dedupe key, stable across dispatch retries).
|
|
65
|
+
*/
|
|
66
|
+
begin(input) {
|
|
67
|
+
const existing = db
|
|
68
|
+
.select()
|
|
69
|
+
.from(notifyTasks)
|
|
70
|
+
.where(eq(notifyTasks.messageId, input.messageId))
|
|
71
|
+
.get();
|
|
72
|
+
if (existing)
|
|
73
|
+
return parse(existing);
|
|
74
|
+
const task = buildSubmittedTask(input.taskId, input.contextId);
|
|
75
|
+
const now = Date.now();
|
|
76
|
+
db.insert(notifyTasks)
|
|
77
|
+
.values({
|
|
78
|
+
taskId: task.id,
|
|
79
|
+
messageId: input.messageId,
|
|
80
|
+
contextId: task.contextId,
|
|
81
|
+
state: taskStateLabel(task.status.state),
|
|
82
|
+
taskJson: serialize(task),
|
|
83
|
+
createdAt: now,
|
|
84
|
+
updatedAt: now
|
|
85
|
+
})
|
|
86
|
+
.run();
|
|
87
|
+
return task;
|
|
88
|
+
},
|
|
89
|
+
/** Load a task by id (for `GetTask` via the Worker's `DurableTaskStore`). */
|
|
90
|
+
get(taskId) {
|
|
91
|
+
return readOne(taskId);
|
|
92
|
+
},
|
|
93
|
+
/**
|
|
94
|
+
* A page of this caller's tasks for `ListTasks`, newest first, with the
|
|
95
|
+
* total matching count so the store can decide whether a next page exists.
|
|
96
|
+
*/
|
|
97
|
+
list(query) {
|
|
98
|
+
const filters = [];
|
|
99
|
+
if (query.contextId) {
|
|
100
|
+
filters.push(eq(notifyTasks.contextId, query.contextId));
|
|
101
|
+
}
|
|
102
|
+
if (query.state !== undefined) {
|
|
103
|
+
filters.push(eq(notifyTasks.state, taskStateLabel(query.state)));
|
|
104
|
+
}
|
|
105
|
+
if (query.updatedAfter !== undefined) {
|
|
106
|
+
filters.push(gte(notifyTasks.updatedAt, query.updatedAfter));
|
|
107
|
+
}
|
|
108
|
+
const where = filters.length > 0 ? and(...filters) : undefined;
|
|
109
|
+
const rows = db
|
|
110
|
+
.select()
|
|
111
|
+
.from(notifyTasks)
|
|
112
|
+
.where(where)
|
|
113
|
+
.orderBy(desc(notifyTasks.createdAt))
|
|
114
|
+
.limit(query.limit)
|
|
115
|
+
.offset(query.offset)
|
|
116
|
+
.all();
|
|
117
|
+
const total = db
|
|
118
|
+
.select({ value: count() })
|
|
119
|
+
.from(notifyTasks)
|
|
120
|
+
.where(where)
|
|
121
|
+
.get();
|
|
122
|
+
return {
|
|
123
|
+
tasks: rows.map((row) => project(parse(row), query)),
|
|
124
|
+
totalSize: total?.value ?? 0
|
|
125
|
+
};
|
|
126
|
+
},
|
|
127
|
+
/**
|
|
128
|
+
* Upsert a task by id, preserving the `message_id` set by {@link begin}.
|
|
129
|
+
* Returns whether the write applied.
|
|
130
|
+
*
|
|
131
|
+
* Guarded exactly like {@link markWorking}, and for the same reason: a
|
|
132
|
+
* `canceled` row is terminal, so nothing may write a non-canceled state over
|
|
133
|
+
* it. That closes the window between a workflow's terminal build and its
|
|
134
|
+
* callback — the read-check-write is synchronous here, so a `CancelTask`
|
|
135
|
+
* landing mid-delivery makes this return `false` and the notify never fires.
|
|
136
|
+
* Writing `canceled` onto a live row stays allowed: that is how the a2a-js
|
|
137
|
+
* handler's own cancel branch records the cancellation.
|
|
138
|
+
*/
|
|
139
|
+
save(task) {
|
|
140
|
+
const existing = readOne(task.id);
|
|
141
|
+
if (existing !== null &&
|
|
142
|
+
stateOf(existing) === TaskState.TASK_STATE_CANCELED &&
|
|
143
|
+
stateOf(task) !== TaskState.TASK_STATE_CANCELED) {
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
upsert(task);
|
|
147
|
+
return true;
|
|
148
|
+
},
|
|
149
|
+
/**
|
|
150
|
+
* Move a task to `working` (a turn workflow's first step). Returns
|
|
151
|
+
* `"canceled"` when the row is already canceled, which is the caller's
|
|
152
|
+
* signal to stop the turn; every other outcome — unknown task, or a task
|
|
153
|
+
* already past `submitted` — is a no-op reported as `"ok"`, because a
|
|
154
|
+
* workflow replay legitimately re-runs this step.
|
|
155
|
+
*/
|
|
156
|
+
markWorking(taskId) {
|
|
157
|
+
const task = readOne(taskId);
|
|
158
|
+
if (!task)
|
|
159
|
+
return "ok";
|
|
160
|
+
if (stateOf(task) === TaskState.TASK_STATE_CANCELED)
|
|
161
|
+
return "canceled";
|
|
162
|
+
if (stateOf(task) !== TaskState.TASK_STATE_SUBMITTED)
|
|
163
|
+
return "ok";
|
|
164
|
+
task.status = {
|
|
165
|
+
...task.status,
|
|
166
|
+
state: TaskState.TASK_STATE_WORKING,
|
|
167
|
+
message: task.status?.message,
|
|
168
|
+
timestamp: nowIso()
|
|
169
|
+
};
|
|
170
|
+
upsert(task);
|
|
171
|
+
return "ok";
|
|
172
|
+
},
|
|
173
|
+
/**
|
|
174
|
+
* Flip the task to `canceled` and return it. Terminal: once this lands,
|
|
175
|
+
* {@link save} refuses every non-canceled write, so no completed or failed
|
|
176
|
+
* callback can be built from this row afterwards.
|
|
177
|
+
*/
|
|
178
|
+
cancel(taskId) {
|
|
179
|
+
const task = readOne(taskId);
|
|
180
|
+
if (!task)
|
|
181
|
+
return null;
|
|
182
|
+
task.status = {
|
|
183
|
+
...task.status,
|
|
184
|
+
state: TaskState.TASK_STATE_CANCELED,
|
|
185
|
+
message: task.status?.message,
|
|
186
|
+
timestamp: nowIso()
|
|
187
|
+
};
|
|
188
|
+
upsert(task);
|
|
189
|
+
return task;
|
|
190
|
+
},
|
|
191
|
+
/** Delete all tasks older than 30 days (called by the maintenance cron). */
|
|
192
|
+
cleanup() {
|
|
193
|
+
const cutoff = Date.now() - 30 * 24 * 60 * 60 * 1000;
|
|
194
|
+
db.delete(notifyTasks).where(lt(notifyTasks.createdAt, cutoff)).run();
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Apply the `ListTasks` response-shaping options to one stored task. Rows are
|
|
200
|
+
* JSON blobs, so the repeated fields are defaulted rather than assumed present.
|
|
201
|
+
*/
|
|
202
|
+
function project(task, query) {
|
|
203
|
+
const history = task.history ?? [];
|
|
204
|
+
return {
|
|
205
|
+
...task,
|
|
206
|
+
artifacts: query.includeArtifacts ? (task.artifacts ?? []) : [],
|
|
207
|
+
history: query.historyLength === undefined
|
|
208
|
+
? history
|
|
209
|
+
: history.slice(Math.max(0, history.length - query.historyLength))
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
//# sourceMappingURL=tasks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tasks.js","sourceRoot":"","sources":["../../../src/db/models/tasks.ts"],"names":[],"mappings":"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"}
|