@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.
Files changed (238) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +279 -0
  3. package/dist/a2a/agent-stub.d.ts +61 -0
  4. package/dist/a2a/agent-stub.d.ts.map +1 -0
  5. package/dist/a2a/agent-stub.js +2 -0
  6. package/dist/a2a/agent-stub.js.map +1 -0
  7. package/dist/a2a/card.d.ts +119 -0
  8. package/dist/a2a/card.d.ts.map +1 -0
  9. package/dist/a2a/card.js +115 -0
  10. package/dist/a2a/card.js.map +1 -0
  11. package/dist/a2a/context.d.ts +14 -0
  12. package/dist/a2a/context.d.ts.map +1 -0
  13. package/dist/a2a/context.js +55 -0
  14. package/dist/a2a/context.js.map +1 -0
  15. package/dist/a2a/executor.d.ts +85 -0
  16. package/dist/a2a/executor.d.ts.map +1 -0
  17. package/dist/a2a/executor.js +107 -0
  18. package/dist/a2a/executor.js.map +1 -0
  19. package/dist/a2a/index.d.ts +18 -0
  20. package/dist/a2a/index.d.ts.map +1 -0
  21. package/dist/a2a/index.js +17 -0
  22. package/dist/a2a/index.js.map +1 -0
  23. package/dist/a2a/notify.d.ts +75 -0
  24. package/dist/a2a/notify.d.ts.map +1 -0
  25. package/dist/a2a/notify.js +161 -0
  26. package/dist/a2a/notify.js.map +1 -0
  27. package/dist/a2a/parts.d.ts +46 -0
  28. package/dist/a2a/parts.d.ts.map +1 -0
  29. package/dist/a2a/parts.js +95 -0
  30. package/dist/a2a/parts.js.map +1 -0
  31. package/dist/a2a/task-store.d.ts +45 -0
  32. package/dist/a2a/task-store.d.ts.map +1 -0
  33. package/dist/a2a/task-store.js +100 -0
  34. package/dist/a2a/task-store.js.map +1 -0
  35. package/dist/a2a/task.d.ts +86 -0
  36. package/dist/a2a/task.d.ts.map +1 -0
  37. package/dist/a2a/task.js +16 -0
  38. package/dist/a2a/task.js.map +1 -0
  39. package/dist/a2a/verify.d.ts +64 -0
  40. package/dist/a2a/verify.d.ts.map +1 -0
  41. package/dist/a2a/verify.js +122 -0
  42. package/dist/a2a/verify.js.map +1 -0
  43. package/dist/agent/budget.d.ts +47 -0
  44. package/dist/agent/budget.d.ts.map +1 -0
  45. package/dist/agent/budget.js +43 -0
  46. package/dist/agent/budget.js.map +1 -0
  47. package/dist/agent/control.d.ts +110 -0
  48. package/dist/agent/control.d.ts.map +1 -0
  49. package/dist/agent/control.js +115 -0
  50. package/dist/agent/control.js.map +1 -0
  51. package/dist/agent/final-reply.d.ts +49 -0
  52. package/dist/agent/final-reply.d.ts.map +1 -0
  53. package/dist/agent/final-reply.js +52 -0
  54. package/dist/agent/final-reply.js.map +1 -0
  55. package/dist/agent/history.d.ts +83 -0
  56. package/dist/agent/history.d.ts.map +1 -0
  57. package/dist/agent/history.js +117 -0
  58. package/dist/agent/history.js.map +1 -0
  59. package/dist/agent/index.d.ts +17 -0
  60. package/dist/agent/index.d.ts.map +1 -0
  61. package/dist/agent/index.js +17 -0
  62. package/dist/agent/index.js.map +1 -0
  63. package/dist/agent/inference.d.ts +52 -0
  64. package/dist/agent/inference.d.ts.map +1 -0
  65. package/dist/agent/inference.js +79 -0
  66. package/dist/agent/inference.js.map +1 -0
  67. package/dist/agent/model.d.ts +75 -0
  68. package/dist/agent/model.d.ts.map +1 -0
  69. package/dist/agent/model.js +62 -0
  70. package/dist/agent/model.js.map +1 -0
  71. package/dist/agent/session.d.ts +92 -0
  72. package/dist/agent/session.d.ts.map +1 -0
  73. package/dist/agent/session.js +80 -0
  74. package/dist/agent/session.js.map +1 -0
  75. package/dist/config.d.ts +159 -0
  76. package/dist/config.d.ts.map +1 -0
  77. package/dist/config.js +92 -0
  78. package/dist/config.js.map +1 -0
  79. package/dist/contract/index.d.ts +10 -0
  80. package/dist/contract/index.d.ts.map +1 -0
  81. package/dist/contract/index.js +9 -0
  82. package/dist/contract/index.js.map +1 -0
  83. package/dist/contract/plugin.d.ts +163 -0
  84. package/dist/contract/plugin.d.ts.map +1 -0
  85. package/dist/contract/plugin.js +39 -0
  86. package/dist/contract/plugin.js.map +1 -0
  87. package/dist/contract/recipe.d.ts +170 -0
  88. package/dist/contract/recipe.d.ts.map +1 -0
  89. package/dist/contract/recipe.js +2 -0
  90. package/dist/contract/recipe.js.map +1 -0
  91. package/dist/contract/validation.d.ts +86 -0
  92. package/dist/contract/validation.d.ts.map +1 -0
  93. package/dist/contract/validation.js +76 -0
  94. package/dist/contract/validation.js.map +1 -0
  95. package/dist/db/db.d.ts +131 -0
  96. package/dist/db/db.d.ts.map +1 -0
  97. package/dist/db/db.js +91 -0
  98. package/dist/db/db.js.map +1 -0
  99. package/dist/db/index.d.ts +9 -0
  100. package/dist/db/index.d.ts.map +1 -0
  101. package/dist/db/index.js +9 -0
  102. package/dist/db/index.js.map +1 -0
  103. package/dist/db/migrations/index.d.ts +21 -0
  104. package/dist/db/migrations/index.d.ts.map +1 -0
  105. package/dist/db/migrations/index.js +54 -0
  106. package/dist/db/migrations/index.js.map +1 -0
  107. package/dist/db/models/subtasks.d.ts +102 -0
  108. package/dist/db/models/subtasks.d.ts.map +1 -0
  109. package/dist/db/models/subtasks.js +285 -0
  110. package/dist/db/models/subtasks.js.map +1 -0
  111. package/dist/db/models/tasks.d.ts +92 -0
  112. package/dist/db/models/tasks.d.ts.map +1 -0
  113. package/dist/db/models/tasks.js +212 -0
  114. package/dist/db/models/tasks.js.map +1 -0
  115. package/dist/db/schema.d.ts +488 -0
  116. package/dist/db/schema.d.ts.map +1 -0
  117. package/dist/db/schema.js +91 -0
  118. package/dist/db/schema.js.map +1 -0
  119. package/dist/env.d.ts +46 -0
  120. package/dist/env.d.ts.map +1 -0
  121. package/dist/env.js +40 -0
  122. package/dist/env.js.map +1 -0
  123. package/dist/index.d.ts +20 -0
  124. package/dist/index.d.ts.map +1 -0
  125. package/dist/index.js +18 -0
  126. package/dist/index.js.map +1 -0
  127. package/dist/platform.d.ts +47 -0
  128. package/dist/platform.d.ts.map +1 -0
  129. package/dist/platform.js +47 -0
  130. package/dist/platform.js.map +1 -0
  131. package/dist/runtime/index.d.ts +86 -0
  132. package/dist/runtime/index.d.ts.map +1 -0
  133. package/dist/runtime/index.js +123 -0
  134. package/dist/runtime/index.js.map +1 -0
  135. package/dist/runtime/tool-families.d.ts +33 -0
  136. package/dist/runtime/tool-families.d.ts.map +1 -0
  137. package/dist/runtime/tool-families.js +62 -0
  138. package/dist/runtime/tool-families.js.map +1 -0
  139. package/dist/subagent/fingerprint.d.ts +38 -0
  140. package/dist/subagent/fingerprint.d.ts.map +1 -0
  141. package/dist/subagent/fingerprint.js +97 -0
  142. package/dist/subagent/fingerprint.js.map +1 -0
  143. package/dist/subagent/index.d.ts +147 -0
  144. package/dist/subagent/index.d.ts.map +1 -0
  145. package/dist/subagent/index.js +299 -0
  146. package/dist/subagent/index.js.map +1 -0
  147. package/dist/subagent/prompt.d.ts +34 -0
  148. package/dist/subagent/prompt.d.ts.map +1 -0
  149. package/dist/subagent/prompt.js +46 -0
  150. package/dist/subagent/prompt.js.map +1 -0
  151. package/dist/subagent/run.d.ts +151 -0
  152. package/dist/subagent/run.d.ts.map +1 -0
  153. package/dist/subagent/run.js +491 -0
  154. package/dist/subagent/run.js.map +1 -0
  155. package/dist/subagent/workspace.d.ts +65 -0
  156. package/dist/subagent/workspace.d.ts.map +1 -0
  157. package/dist/subagent/workspace.js +53 -0
  158. package/dist/subagent/workspace.js.map +1 -0
  159. package/dist/subtasks/catalog.d.ts +40 -0
  160. package/dist/subtasks/catalog.d.ts.map +1 -0
  161. package/dist/subtasks/catalog.js +36 -0
  162. package/dist/subtasks/catalog.js.map +1 -0
  163. package/dist/subtasks/decomposition.d.ts +90 -0
  164. package/dist/subtasks/decomposition.d.ts.map +1 -0
  165. package/dist/subtasks/decomposition.js +205 -0
  166. package/dist/subtasks/decomposition.js.map +1 -0
  167. package/dist/subtasks/delegate.d.ts +77 -0
  168. package/dist/subtasks/delegate.d.ts.map +1 -0
  169. package/dist/subtasks/delegate.js +102 -0
  170. package/dist/subtasks/delegate.js.map +1 -0
  171. package/dist/subtasks/index.d.ts +11 -0
  172. package/dist/subtasks/index.d.ts.map +1 -0
  173. package/dist/subtasks/index.js +11 -0
  174. package/dist/subtasks/index.js.map +1 -0
  175. package/dist/subtasks/scheduler.d.ts +49 -0
  176. package/dist/subtasks/scheduler.d.ts.map +1 -0
  177. package/dist/subtasks/scheduler.js +48 -0
  178. package/dist/subtasks/scheduler.js.map +1 -0
  179. package/dist/subtasks/subtask-types.d.ts +100 -0
  180. package/dist/subtasks/subtask-types.d.ts.map +1 -0
  181. package/dist/subtasks/subtask-types.js +111 -0
  182. package/dist/subtasks/subtask-types.js.map +1 -0
  183. package/dist/subtasks/types.d.ts +321 -0
  184. package/dist/subtasks/types.d.ts.map +1 -0
  185. package/dist/subtasks/types.js +16 -0
  186. package/dist/subtasks/types.js.map +1 -0
  187. package/dist/testing/auth.d.ts +12 -0
  188. package/dist/testing/auth.d.ts.map +1 -0
  189. package/dist/testing/auth.js +25 -0
  190. package/dist/testing/auth.js.map +1 -0
  191. package/dist/testing/do.d.ts +30 -0
  192. package/dist/testing/do.d.ts.map +1 -0
  193. package/dist/testing/do.js +26 -0
  194. package/dist/testing/do.js.map +1 -0
  195. package/dist/testing/fake-session.d.ts +27 -0
  196. package/dist/testing/fake-session.d.ts.map +1 -0
  197. package/dist/testing/fake-session.js +38 -0
  198. package/dist/testing/fake-session.js.map +1 -0
  199. package/dist/testing/fixtures.d.ts +27 -0
  200. package/dist/testing/fixtures.d.ts.map +1 -0
  201. package/dist/testing/fixtures.js +67 -0
  202. package/dist/testing/fixtures.js.map +1 -0
  203. package/dist/testing/index.d.ts +31 -0
  204. package/dist/testing/index.d.ts.map +1 -0
  205. package/dist/testing/index.js +31 -0
  206. package/dist/testing/index.js.map +1 -0
  207. package/dist/testing/mock-model.d.ts +43 -0
  208. package/dist/testing/mock-model.d.ts.map +1 -0
  209. package/dist/testing/mock-model.js +62 -0
  210. package/dist/testing/mock-model.js.map +1 -0
  211. package/dist/testing/node.d.ts +24 -0
  212. package/dist/testing/node.d.ts.map +1 -0
  213. package/dist/testing/node.js +24 -0
  214. package/dist/testing/node.js.map +1 -0
  215. package/dist/testing/vcr-global-setup.d.ts +3 -0
  216. package/dist/testing/vcr-global-setup.d.ts.map +1 -0
  217. package/dist/testing/vcr-global-setup.js +9 -0
  218. package/dist/testing/vcr-global-setup.js.map +1 -0
  219. package/dist/testing/vcr-shared.d.ts +21 -0
  220. package/dist/testing/vcr-shared.d.ts.map +1 -0
  221. package/dist/testing/vcr-shared.js +21 -0
  222. package/dist/testing/vcr-shared.js.map +1 -0
  223. package/dist/testing/vcr-spec.d.ts +17 -0
  224. package/dist/testing/vcr-spec.d.ts.map +1 -0
  225. package/dist/testing/vcr-spec.js +72 -0
  226. package/dist/testing/vcr-spec.js.map +1 -0
  227. package/dist/testing/vcr.d.ts +81 -0
  228. package/dist/testing/vcr.d.ts.map +1 -0
  229. package/dist/testing/vcr.js +211 -0
  230. package/dist/testing/vcr.js.map +1 -0
  231. package/dist/worker/index.d.ts +62 -0
  232. package/dist/worker/index.d.ts.map +1 -0
  233. package/dist/worker/index.js +194 -0
  234. package/dist/worker/index.js.map +1 -0
  235. package/eslint-rules/index.js +31 -0
  236. package/eslint-rules/no-deprecated-object-properties.js +81 -0
  237. package/package.json +147 -0
  238. 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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"}