@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,65 @@
1
+ /**
2
+ * The narrow file-store surface the resumable runner and its tool families use.
3
+ *
4
+ * Core declares the *interface* and the caps; it does not declare a backend.
5
+ * The predecessor backed this with `@cloudflare/shell`, which is experimental
6
+ * ("expect breaking changes") — so an agent that never delegates file work should
7
+ * not carry it. `@loopingai/plugins/workspace` supplies the shell-backed
8
+ * implementation and the model-facing `ws_read`/`ws_write`/`ws_list` tools; core
9
+ * only needs to be able to *name* a workspace, because {@link ToolFamilyContext}
10
+ * hands one to every tool family.
11
+ */
12
+ export interface WorkspaceHandle {
13
+ /** File content, or null if the file does not exist. */
14
+ read(path: string): Promise<string | null>;
15
+ /** Write (create or overwrite) a text file. Parent directories are created. */
16
+ write(path: string, content: string): Promise<void>;
17
+ /** Whether a file or directory exists at the path. */
18
+ exists(path: string): Promise<boolean>;
19
+ /** Delete a file. Returns whether a file was removed. */
20
+ remove(path: string): Promise<boolean>;
21
+ /** Immediate entries under `dir` (default root): their path and byte size. */
22
+ list(dir?: string): Promise<WorkspaceEntry[]>;
23
+ /** Parse a JSON file, or null if it does not exist. Throws on malformed JSON. */
24
+ readJson<T>(path: string): Promise<T | null>;
25
+ /** Serialize a value to a JSON file (pretty-printed). */
26
+ writeJson(path: string, value: unknown): Promise<void>;
27
+ }
28
+ export interface WorkspaceEntry {
29
+ path: string;
30
+ type: "file" | "directory" | "symlink";
31
+ size: number;
32
+ }
33
+ /**
34
+ * The backend surface {@link makeWorkspaceHandle} needs, declared **structurally**
35
+ * rather than as `Pick<Workspace, …>`.
36
+ *
37
+ * That is the whole reason core can stay free of `@cloudflare/shell`: a shell
38
+ * `Workspace` satisfies this by construction, and so does a plain in-memory fake
39
+ * in a test, without either being named here.
40
+ */
41
+ export interface WorkspaceBacking {
42
+ readFile(path: string): Promise<string | null>;
43
+ writeFile(path: string, content: string): Promise<void>;
44
+ exists(path: string): Promise<boolean>;
45
+ deleteFile(path: string): Promise<boolean>;
46
+ readDir(dir?: string): Promise<readonly WorkspaceEntry[]>;
47
+ getWorkspaceInfo(): Promise<{
48
+ fileCount: number;
49
+ }>;
50
+ }
51
+ /** Per-file byte ceiling — safely under the 2 MB Durable Object SQLite row limit. */
52
+ export declare const WORKSPACE_MAX_FILE_BYTES: number;
53
+ /** Max number of files in one workspace — a cheap guard against runaway writes. */
54
+ export declare const WORKSPACE_MAX_FILES = 200;
55
+ export declare class WorkspaceLimitError extends Error {
56
+ constructor(message: string);
57
+ }
58
+ /**
59
+ * Build a {@link WorkspaceHandle} over a shell workspace (or a test fake),
60
+ * enforcing the per-file and file-count caps. The caps degrade a misbehaving
61
+ * recipe to an explicit error rather than letting it exceed the DO row limit or
62
+ * fill storage.
63
+ */
64
+ export declare function makeWorkspaceHandle(ws: WorkspaceBacking): WorkspaceHandle;
65
+ //# sourceMappingURL=workspace.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../../src/subagent/workspace.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,MAAM,WAAW,eAAe;IAC9B,wDAAwD;IACxD,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC3C,+EAA+E;IAC/E,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpD,sDAAsD;IACtD,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACvC,yDAAyD;IACzD,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACvC,8EAA8E;IAC9E,IAAI,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAAC;IAC9C,iFAAiF;IACjF,QAAQ,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IAC7C,yDAAyD;IACzD,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACxD;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,CAAC;IACvC,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC/C,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxD,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACvC,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3C,OAAO,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,cAAc,EAAE,CAAC,CAAC;IAC1D,gBAAgB,IAAI,OAAO,CAAC;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACpD;AAED,qFAAqF;AACrF,eAAO,MAAM,wBAAwB,QAAa,CAAC;AACnD,mFAAmF;AACnF,eAAO,MAAM,mBAAmB,MAAM,CAAC;AAEvC,qBAAa,mBAAoB,SAAQ,KAAK;gBAChC,OAAO,EAAE,MAAM;CAI5B;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,gBAAgB,GAAG,eAAe,CA8CzE"}
@@ -0,0 +1,53 @@
1
+ /** Per-file byte ceiling — safely under the 2 MB Durable Object SQLite row limit. */
2
+ export const WORKSPACE_MAX_FILE_BYTES = 512 * 1024;
3
+ /** Max number of files in one workspace — a cheap guard against runaway writes. */
4
+ export const WORKSPACE_MAX_FILES = 200;
5
+ export class WorkspaceLimitError extends Error {
6
+ constructor(message) {
7
+ super(message);
8
+ this.name = "WorkspaceLimitError";
9
+ }
10
+ }
11
+ /**
12
+ * Build a {@link WorkspaceHandle} over a shell workspace (or a test fake),
13
+ * enforcing the per-file and file-count caps. The caps degrade a misbehaving
14
+ * recipe to an explicit error rather than letting it exceed the DO row limit or
15
+ * fill storage.
16
+ */
17
+ export function makeWorkspaceHandle(ws) {
18
+ const byteLength = (s) => new TextEncoder().encode(s).length;
19
+ return {
20
+ read: (path) => ws.readFile(path),
21
+ async write(path, content) {
22
+ const bytes = byteLength(content);
23
+ if (bytes > WORKSPACE_MAX_FILE_BYTES) {
24
+ throw new WorkspaceLimitError(`file "${path}" is ${bytes} bytes, over the ${WORKSPACE_MAX_FILE_BYTES}-byte limit`);
25
+ }
26
+ if (!(await ws.exists(path))) {
27
+ const { fileCount } = await ws.getWorkspaceInfo();
28
+ if (fileCount >= WORKSPACE_MAX_FILES) {
29
+ throw new WorkspaceLimitError(`workspace already holds ${fileCount} files (max ${WORKSPACE_MAX_FILES})`);
30
+ }
31
+ }
32
+ await ws.writeFile(path, content);
33
+ },
34
+ exists: (path) => ws.exists(path),
35
+ remove: (path) => ws.deleteFile(path),
36
+ async list(dir) {
37
+ const entries = await ws.readDir(dir);
38
+ return entries.map((e) => ({
39
+ path: e.path,
40
+ type: e.type,
41
+ size: e.size
42
+ }));
43
+ },
44
+ async readJson(path) {
45
+ const raw = await ws.readFile(path);
46
+ return raw === null ? null : JSON.parse(raw);
47
+ },
48
+ async writeJson(path, value) {
49
+ await this.write(path, JSON.stringify(value, null, 2));
50
+ }
51
+ };
52
+ }
53
+ //# sourceMappingURL=workspace.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspace.js","sourceRoot":"","sources":["../../src/subagent/workspace.ts"],"names":[],"mappings":"AAmDA,qFAAqF;AACrF,MAAM,CAAC,MAAM,wBAAwB,GAAG,GAAG,GAAG,IAAI,CAAC;AACnD,mFAAmF;AACnF,MAAM,CAAC,MAAM,mBAAmB,GAAG,GAAG,CAAC;AAEvC,MAAM,OAAO,mBAAoB,SAAQ,KAAK;IAC5C,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAC;IACpC,CAAC;CACF;AAED;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CAAC,EAAoB;IACtD,MAAM,UAAU,GAAG,CAAC,CAAS,EAAU,EAAE,CAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;IAE7E,OAAO;QACL,IAAI,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC;QAEjC,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,OAAO;YACvB,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;YAClC,IAAI,KAAK,GAAG,wBAAwB,EAAE,CAAC;gBACrC,MAAM,IAAI,mBAAmB,CAC3B,SAAS,IAAI,QAAQ,KAAK,oBAAoB,wBAAwB,aAAa,CACpF,CAAC;YACJ,CAAC;YACD,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;gBAC7B,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,EAAE,CAAC,gBAAgB,EAAE,CAAC;gBAClD,IAAI,SAAS,IAAI,mBAAmB,EAAE,CAAC;oBACrC,MAAM,IAAI,mBAAmB,CAC3B,2BAA2B,SAAS,eAAe,mBAAmB,GAAG,CAC1E,CAAC;gBACJ,CAAC;YACH,CAAC;YACD,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QACpC,CAAC;QAED,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC;QAEjC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC;QAErC,KAAK,CAAC,IAAI,CAAC,GAAG;YACZ,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YACtC,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBACzB,IAAI,EAAE,CAAC,CAAC,IAAI;gBACZ,IAAI,EAAE,CAAC,CAAC,IAAI;gBACZ,IAAI,EAAE,CAAC,CAAC,IAAI;aACb,CAAC,CAAC,CAAC;QACN,CAAC;QAED,KAAK,CAAC,QAAQ,CAAI,IAAY;YAC5B,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;YACpC,OAAO,GAAG,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAO,CAAC;QACtD,CAAC;QAED,KAAK,CAAC,SAAS,CAAC,IAAI,EAAE,KAAK;YACzB,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QACzD,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,40 @@
1
+ import type { SessionMessage } from "agents/experimental/memory/session";
2
+ import type { SubtaskReference } from "./types.js";
3
+ /**
4
+ * One entry of the ephemeral, per-round reference catalog: a verbatim
5
+ * `user`/`assistant` turn plus the 1-based index the model selects it by.
6
+ * Structurally a {@link SubtaskReference} with an `index`, so selecting an entry
7
+ * and snapshotting it onto a Subtask is a plain subset copy.
8
+ */
9
+ export interface ReferenceCatalogEntry extends SubtaskReference {
10
+ /** 1-based, ephemeral, per-round index the model references. */
11
+ index: number;
12
+ }
13
+ /**
14
+ * Whether a history message is a referenceable turn: a verbatim `user` or
15
+ * `assistant` turn with actual content.
16
+ *
17
+ * Compaction summaries are excluded via the SDK's `isCompactionMessage` (their
18
+ * `compaction_` id prefix) — they are generated text, never original conversation
19
+ * evidence. Whitespace-only turns are excluded because there is nothing to
20
+ * reference.
21
+ *
22
+ * This is the single eligibility rule, and it has exactly one caller:
23
+ * {@link file://../turn.ts renderTurnMessages} numbers the messages it accepts
24
+ * *and* marks those same messages with their `[ref N]` index, in one pass. One
25
+ * predicate, one walk — the marked messages and the catalog indices cannot drift.
26
+ *
27
+ * What that leaves eligible: verbatim `user` and `assistant` turns with content.
28
+ * Recall results, system prompts, and context blocks never appear as plain
29
+ * history messages, so they are excluded structurally. Whitespace-only turns are
30
+ * skipped — there is nothing to reference, and since the model only ever selects
31
+ * from the catalog the renderer returns, skipping them cannot misalign indices.
32
+ *
33
+ * The inbound user turn is appended to the Session before the round infers, so
34
+ * one catalog covers both the current task input and past turns. The selected
35
+ * entries' exact text is snapshotted onto the Subtask when the round delegates,
36
+ * so nothing is resolved lazily and later compaction cannot affect a Subtask in
37
+ * flight.
38
+ */
39
+ export declare function isCatalogEligible(message: SessionMessage): boolean;
40
+ //# sourceMappingURL=catalog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../../src/subtasks/catalog.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAGzE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD;;;;;GAKG;AACH,MAAM,WAAW,qBAAsB,SAAQ,gBAAgB;IAC7D,gEAAgE;IAChE,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAIlE"}
@@ -0,0 +1,36 @@
1
+ import { isCompactionMessage } from "agents/experimental/memory/utils";
2
+ import { sessionText } from "../agent/history.js";
3
+ /**
4
+ * Whether a history message is a referenceable turn: a verbatim `user` or
5
+ * `assistant` turn with actual content.
6
+ *
7
+ * Compaction summaries are excluded via the SDK's `isCompactionMessage` (their
8
+ * `compaction_` id prefix) — they are generated text, never original conversation
9
+ * evidence. Whitespace-only turns are excluded because there is nothing to
10
+ * reference.
11
+ *
12
+ * This is the single eligibility rule, and it has exactly one caller:
13
+ * {@link file://../turn.ts renderTurnMessages} numbers the messages it accepts
14
+ * *and* marks those same messages with their `[ref N]` index, in one pass. One
15
+ * predicate, one walk — the marked messages and the catalog indices cannot drift.
16
+ *
17
+ * What that leaves eligible: verbatim `user` and `assistant` turns with content.
18
+ * Recall results, system prompts, and context blocks never appear as plain
19
+ * history messages, so they are excluded structurally. Whitespace-only turns are
20
+ * skipped — there is nothing to reference, and since the model only ever selects
21
+ * from the catalog the renderer returns, skipping them cannot misalign indices.
22
+ *
23
+ * The inbound user turn is appended to the Session before the round infers, so
24
+ * one catalog covers both the current task input and past turns. The selected
25
+ * entries' exact text is snapshotted onto the Subtask when the round delegates,
26
+ * so nothing is resolved lazily and later compaction cannot affect a Subtask in
27
+ * flight.
28
+ */
29
+ export function isCatalogEligible(message) {
30
+ if (message.role !== "user" && message.role !== "assistant")
31
+ return false;
32
+ if (isCompactionMessage(message))
33
+ return false;
34
+ return sessionText(message).trim().length > 0;
35
+ }
36
+ //# sourceMappingURL=catalog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"catalog.js","sourceRoot":"","sources":["../../src/subtasks/catalog.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AACvE,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAclD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,UAAU,iBAAiB,CAAC,OAAuB;IACvD,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM,IAAI,OAAO,CAAC,IAAI,KAAK,WAAW;QAAE,OAAO,KAAK,CAAC;IAC1E,IAAI,mBAAmB,CAAC,OAAO,CAAC;QAAE,OAAO,KAAK,CAAC;IAC/C,OAAO,WAAW,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC;AAChD,CAAC"}
@@ -0,0 +1,90 @@
1
+ import { z } from "zod";
2
+ import type { ReferenceCatalogEntry } from "./catalog.js";
3
+ import { type SubtaskTypeRegistry } from "./subtask-types.js";
4
+ import type { DecompositionProposal, SubtaskDraft } from "./types.js";
5
+ /**
6
+ * Pure validation and resolution of a `delegate` call's input. No model, no
7
+ * Session, no database — given a {@link DecompositionProposal} and the ephemeral
8
+ * reference catalog it was generated against, this either produces the drafts to
9
+ * persist or throws.
10
+ *
11
+ * Two invariants live here:
12
+ *
13
+ * - **The model selects references by index only.** It emits catalog indices; this
14
+ * module copies the catalog entry's exact role+text onto the draft. Model output
15
+ * never becomes reference text, so a Subtask cannot carry a rewritten,
16
+ * summarized, or fabricated "quote" of the conversation.
17
+ * - **The dependency graph is a validated DAG.** Unknown, duplicate,
18
+ * self-referential, and cyclic edges are all rejected here. The data layer's
19
+ * `createDecomposition` re-checks the cheap structural rules as a storage guard,
20
+ * but full cycle detection is this module's job.
21
+ *
22
+ * Invalid output is never repaired: a throw fails the attempt, which falls back to
23
+ * the other model, and two failed attempts fail the parent Task. Silently
24
+ * synthesizing a general Subtask would deliver plausible work the user never asked
25
+ * for.
26
+ */
27
+ /** A model proposal that cannot be resolved into a valid decomposition. */
28
+ export declare class DecompositionValidationError extends Error {
29
+ constructor(message: string);
30
+ }
31
+ /**
32
+ * A required string that is not whitespace.
33
+ *
34
+ * Written as a `.regex()` rather than the equivalent `.refine()`, deliberately.
35
+ * Both are enforced wherever this schema is actually run, but a refinement is
36
+ * **invisible to the model**: custom checks do not survive JSON-Schema conversion,
37
+ * so the rule would be one the model is held to without ever being shown it.
38
+ * `/\S/` converts to `"pattern": "\\S"` next to `"minLength": 1`, so the
39
+ * constraint reaches the model as part of the tool schema, on every field, with
40
+ * no per-field `.describe()` needed to restate it.
41
+ *
42
+ * Being shown a rule is not the same as being held to it, and the difference used
43
+ * to be a hole. This schema reaches the model through a **control** tool, which has
44
+ * no `execute` — so the SDK's input validation never ran on it, and a blank value
45
+ * the model was shown the pattern for sailed through anyway. What enforces it is
46
+ * {@link file://../control.ts control.ts}, which parses every control call with its
47
+ * own schema before the round uses it.
48
+ */
49
+ export declare const nonBlank: (label: string) => z.ZodString;
50
+ /**
51
+ * The `delegate` tool's input schema. The per-round bound is enforced here
52
+ * (the model is told it, and the SDK rejects a call that breaks it) and again in
53
+ * the data layer, which owns the durable invariant.
54
+ *
55
+ * Blank strings are rejected at the schema edge rather than deep in execution: an
56
+ * empty `prompt` would otherwise burn a Subtask slot and only fail later, inside
57
+ * the child, with no useful diagnostic.
58
+ *
59
+ * `referenceIndexes` is optional because this one schema also has to describe the
60
+ * calls **reconstructed from durable rows** in later rounds, whose references
61
+ * were resolved to verbatim snapshots at the time and no longer have indices.
62
+ */
63
+ export declare function makeDecompositionProposalSchema(types: SubtaskTypeRegistry, maxSubtasks: number): z.ZodObject<{
64
+ reply: z.ZodString;
65
+ subtasks: z.ZodArray<z.ZodObject<{
66
+ localKey: z.ZodString;
67
+ type: z.ZodEnum<{
68
+ [x: string]: string;
69
+ }>;
70
+ prompt: z.ZodString;
71
+ referenceIndexes: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
72
+ dependsOn: z.ZodArray<z.ZodString>;
73
+ params: z.ZodOptional<z.ZodObject<{
74
+ [x: string]: z.ZodType<string | undefined, unknown, z.core.$ZodTypeInternals<string | undefined, unknown>>;
75
+ }, z.core.$strip>>;
76
+ }, z.core.$strip>>;
77
+ }, z.core.$strip>;
78
+ /**
79
+ * Resolve a validated model proposal into the drafts to persist.
80
+ *
81
+ * Throws {@link DecompositionValidationError} on any structural problem: blank
82
+ * fields, duplicate local keys, unknown/duplicate reference indices, and unknown,
83
+ * duplicate, self-referential, or cyclic dependency edges. On success, array order
84
+ * is preserved — the data layer derives each Subtask's `ordinal` from it.
85
+ */
86
+ export declare function resolveDecomposition(proposal: DecompositionProposal, catalog: ReferenceCatalogEntry[], types: SubtaskTypeRegistry): {
87
+ reply: string;
88
+ drafts: SubtaskDraft[];
89
+ };
90
+ //# sourceMappingURL=decomposition.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decomposition.d.ts","sourceRoot":"","sources":["../../src/subtasks/decomposition.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAC1D,OAAO,EAEL,KAAK,mBAAmB,EACzB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EACV,qBAAqB,EACrB,YAAY,EAEb,MAAM,YAAY,CAAC;AAEpB;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,2EAA2E;AAC3E,qBAAa,4BAA6B,SAAQ,KAAK;gBACzC,OAAO,EAAE,MAAM;CAI5B;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,QAAQ,GAAI,OAAO,MAAM,gBAIqB,CAAC;AA2B5D;;;;;;;;;;;;GAYG;AACH,wBAAgB,+BAA+B,CAC7C,KAAK,EAAE,mBAAmB,EAC1B,WAAW,EAAE,MAAM;;;;;;;;;;;;;;kBAMpB;AAiED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,qBAAqB,EAC/B,OAAO,EAAE,qBAAqB,EAAE,EAChC,KAAK,EAAE,mBAAmB,GACzB;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,YAAY,EAAE,CAAA;CAAE,CA6D3C"}
@@ -0,0 +1,205 @@
1
+ import { z } from "zod";
2
+ import { SubtaskParamsError } from "./subtask-types.js";
3
+ /**
4
+ * Pure validation and resolution of a `delegate` call's input. No model, no
5
+ * Session, no database — given a {@link DecompositionProposal} and the ephemeral
6
+ * reference catalog it was generated against, this either produces the drafts to
7
+ * persist or throws.
8
+ *
9
+ * Two invariants live here:
10
+ *
11
+ * - **The model selects references by index only.** It emits catalog indices; this
12
+ * module copies the catalog entry's exact role+text onto the draft. Model output
13
+ * never becomes reference text, so a Subtask cannot carry a rewritten,
14
+ * summarized, or fabricated "quote" of the conversation.
15
+ * - **The dependency graph is a validated DAG.** Unknown, duplicate,
16
+ * self-referential, and cyclic edges are all rejected here. The data layer's
17
+ * `createDecomposition` re-checks the cheap structural rules as a storage guard,
18
+ * but full cycle detection is this module's job.
19
+ *
20
+ * Invalid output is never repaired: a throw fails the attempt, which falls back to
21
+ * the other model, and two failed attempts fail the parent Task. Silently
22
+ * synthesizing a general Subtask would deliver plausible work the user never asked
23
+ * for.
24
+ */
25
+ /** A model proposal that cannot be resolved into a valid decomposition. */
26
+ export class DecompositionValidationError extends Error {
27
+ constructor(message) {
28
+ super(message);
29
+ this.name = "DecompositionValidationError";
30
+ }
31
+ }
32
+ /**
33
+ * A required string that is not whitespace.
34
+ *
35
+ * Written as a `.regex()` rather than the equivalent `.refine()`, deliberately.
36
+ * Both are enforced wherever this schema is actually run, but a refinement is
37
+ * **invisible to the model**: custom checks do not survive JSON-Schema conversion,
38
+ * so the rule would be one the model is held to without ever being shown it.
39
+ * `/\S/` converts to `"pattern": "\\S"` next to `"minLength": 1`, so the
40
+ * constraint reaches the model as part of the tool schema, on every field, with
41
+ * no per-field `.describe()` needed to restate it.
42
+ *
43
+ * Being shown a rule is not the same as being held to it, and the difference used
44
+ * to be a hole. This schema reaches the model through a **control** tool, which has
45
+ * no `execute` — so the SDK's input validation never ran on it, and a blank value
46
+ * the model was shown the pattern for sailed through anyway. What enforces it is
47
+ * {@link file://../control.ts control.ts}, which parses every control call with its
48
+ * own schema before the round uses it.
49
+ */
50
+ export const nonBlank = (label) => z
51
+ .string()
52
+ .min(1)
53
+ .regex(/\S/, { message: `${label} must not be blank` });
54
+ function makeSubtaskProposalSchema(types) {
55
+ return z.object({
56
+ localKey: nonBlank("localKey"),
57
+ // A closed enum, not prose: an invented type is rejected by the tool schema
58
+ // itself rather than silently resolving to some default recipe.
59
+ type: z.enum(types.enumKeys()),
60
+ prompt: nonBlank("prompt"),
61
+ referenceIndexes: z.array(z.number().int().min(1)).optional(),
62
+ dependsOn: z.array(nonBlank("dependsOn entry")),
63
+ /**
64
+ * The type's required inputs — ids the model quotes from a tool result.
65
+ * Every key any type declares is named here, gathered from those types by
66
+ * `SubtaskTypeRegistry.paramProperties`; which of them a given type actually
67
+ * *requires* is the per-type contract, checked below.
68
+ *
69
+ * Named keys rather than a free-form record, deliberately: a record's value
70
+ * schema is the JSON Schema `additionalProperties` slot, which the AI SDK's
71
+ * strict-mode pass overwrites with `false` — turning "any string key" into
72
+ * "no key at all" and leaving the model no legal way to send params it was
73
+ * told to send. Explicit properties survive that pass, and say more besides.
74
+ */
75
+ params: z.object(types.paramProperties()).optional()
76
+ });
77
+ }
78
+ /**
79
+ * The `delegate` tool's input schema. The per-round bound is enforced here
80
+ * (the model is told it, and the SDK rejects a call that breaks it) and again in
81
+ * the data layer, which owns the durable invariant.
82
+ *
83
+ * Blank strings are rejected at the schema edge rather than deep in execution: an
84
+ * empty `prompt` would otherwise burn a Subtask slot and only fail later, inside
85
+ * the child, with no useful diagnostic.
86
+ *
87
+ * `referenceIndexes` is optional because this one schema also has to describe the
88
+ * calls **reconstructed from durable rows** in later rounds, whose references
89
+ * were resolved to verbatim snapshots at the time and no longer have indices.
90
+ */
91
+ export function makeDecompositionProposalSchema(types, maxSubtasks) {
92
+ return z.object({
93
+ reply: nonBlank("reply"),
94
+ subtasks: z.array(makeSubtaskProposalSchema(types)).min(1).max(maxSubtasks)
95
+ });
96
+ }
97
+ /**
98
+ * Reject an edge set that contains a cycle, by Kahn's algorithm over the
99
+ * draft-local keys: repeatedly remove nodes with no unresolved prerequisites; if
100
+ * any node survives, it is part of (or downstream of) a cycle.
101
+ */
102
+ function assertAcyclic(proposal) {
103
+ const remaining = new Map(proposal.subtasks.map((s) => [s.localKey, new Set(s.dependsOn)]));
104
+ let progressed = true;
105
+ while (progressed && remaining.size > 0) {
106
+ progressed = false;
107
+ for (const [key, deps] of remaining) {
108
+ // Ready when every prerequisite has already been removed.
109
+ if ([...deps].every((d) => !remaining.has(d))) {
110
+ remaining.delete(key);
111
+ progressed = true;
112
+ }
113
+ }
114
+ }
115
+ if (remaining.size > 0) {
116
+ throw new DecompositionValidationError(`dependency cycle among subtasks: ${[...remaining.keys()].join(", ")}`);
117
+ }
118
+ }
119
+ /**
120
+ * Snapshot the selected catalog entries onto a draft: validate every index against
121
+ * the catalog, reject duplicates, and copy each entry's exact role+text. Indexes
122
+ * are stored ascending so a Subtask's references read in conversation order
123
+ * regardless of the order the model listed them.
124
+ */
125
+ function resolveReferences(localKey, referenceIndexes, catalog) {
126
+ if (!referenceIndexes)
127
+ return [];
128
+ const seen = new Set();
129
+ for (const index of referenceIndexes) {
130
+ if (seen.has(index)) {
131
+ throw new DecompositionValidationError(`subtask ${localKey} references index ${index} more than once`);
132
+ }
133
+ seen.add(index);
134
+ if (index > catalog.length) {
135
+ throw new DecompositionValidationError(`subtask ${localKey} references unknown catalog index ${index} ` +
136
+ `(catalog has ${catalog.length} ${catalog.length === 1 ? "entry" : "entries"})`);
137
+ }
138
+ }
139
+ return [...referenceIndexes]
140
+ .sort((a, b) => a - b)
141
+ .map((index) => {
142
+ // The catalog is 1-based; copy role+text verbatim (never the model's words).
143
+ const entry = catalog[index - 1];
144
+ return { role: entry.role, text: entry.text };
145
+ });
146
+ }
147
+ /**
148
+ * Resolve a validated model proposal into the drafts to persist.
149
+ *
150
+ * Throws {@link DecompositionValidationError} on any structural problem: blank
151
+ * fields, duplicate local keys, unknown/duplicate reference indices, and unknown,
152
+ * duplicate, self-referential, or cyclic dependency edges. On success, array order
153
+ * is preserved — the data layer derives each Subtask's `ordinal` from it.
154
+ */
155
+ export function resolveDecomposition(proposal, catalog, types) {
156
+ const keys = new Set();
157
+ for (const s of proposal.subtasks) {
158
+ if (keys.has(s.localKey)) {
159
+ throw new DecompositionValidationError(`duplicate subtask local key: ${s.localKey}`);
160
+ }
161
+ keys.add(s.localKey);
162
+ }
163
+ // Every key must be registered before any edge is checked: an edge may point
164
+ // forward to a subtask defined later in the array.
165
+ for (const s of proposal.subtasks) {
166
+ const seen = new Set();
167
+ for (const dep of s.dependsOn) {
168
+ if (dep === s.localKey) {
169
+ throw new DecompositionValidationError(`subtask ${s.localKey} depends on itself`);
170
+ }
171
+ if (!keys.has(dep)) {
172
+ throw new DecompositionValidationError(`subtask ${s.localKey} depends on unknown key: ${dep}`);
173
+ }
174
+ if (seen.has(dep)) {
175
+ throw new DecompositionValidationError(`subtask ${s.localKey} depends on ${dep} more than once`);
176
+ }
177
+ seen.add(dep);
178
+ }
179
+ }
180
+ assertAcyclic(proposal);
181
+ const drafts = proposal.subtasks.map((s) => {
182
+ const type = s.type.trim();
183
+ let params;
184
+ try {
185
+ // Shape only. Whether an id names a row that exists — and is still usable —
186
+ // is a question for durable state, answered when the execution starts.
187
+ params = types.validateParams(type, s.params);
188
+ }
189
+ catch (err) {
190
+ if (!(err instanceof SubtaskParamsError))
191
+ throw err;
192
+ throw new DecompositionValidationError(`subtask ${s.localKey}: ${err.message}`);
193
+ }
194
+ return {
195
+ localKey: s.localKey,
196
+ type,
197
+ prompt: s.prompt.trim(),
198
+ references: resolveReferences(s.localKey, s.referenceIndexes, catalog),
199
+ dependsOn: [...s.dependsOn],
200
+ params
201
+ };
202
+ });
203
+ return { reply: proposal.reply.trim(), drafts };
204
+ }
205
+ //# sourceMappingURL=decomposition.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decomposition.js","sourceRoot":"","sources":["../../src/subtasks/decomposition.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,77 @@
1
+ import { type Tool } from "ai";
2
+ import type { SubtaskTypeRegistry } from "./subtask-types.js";
3
+ import type { CompositionBranch, DecompositionProposal, SubtaskId, SubtaskStatus } from "./types.js";
4
+ /**
5
+ * The `delegate` tool — the single act by which the main agent hands work to
6
+ * subagents, and the shape both ends of that act agree on.
7
+ *
8
+ * Delegation is a **durable tool call**: a round emits it for real (the model
9
+ * picks it and fills its input), the Workflow performs it over minutes or hours,
10
+ * and a later round reassembles the call with its result to keep working. The two
11
+ * halves are separated by a Workflow boundary, not by a fiction — nothing here is
12
+ * fabricated on the model's behalf.
13
+ *
14
+ * This module owns the tool's identity because every round depends on it
15
+ * agreeing: a later round pairs a synthesized `tool-result` to an earlier round's
16
+ * `tool-call` by name and id, and a mismatch would not throw — it would silently
17
+ * produce a malformed history that the model quietly misreads.
18
+ */
19
+ export declare const DELEGATE_TOOL_NAME = "delegate";
20
+ /**
21
+ * The tool as the model sees it. Deliberately **without `execute`**: the Workflow
22
+ * performs this call, durably, outside the inference — so there is nothing for the
23
+ * SDK to run, and the tool loop halts on the call rather than trying to continue
24
+ * past it. That is what makes it a *control* tool: unlike the agent's work tools
25
+ * (`recall`, `browser_*`, `set_context`), calling it ends the round.
26
+ *
27
+ * Its `inputSchema` is the delegation contract itself, and it is the **only**
28
+ * declaration of this tool. One schema has to serve both directions — the calls
29
+ * the model emits now, and the calls reconstructed from durable rows in later
30
+ * rounds — because a provider cannot be shown two shapes for one tool name in a
31
+ * single request. See {@link delegateCallInput} for how a durable row is rendered
32
+ * back into it.
33
+ *
34
+ * Built per registry rather than declared as a module constant: both the type
35
+ * enum in its schema and the catalogue in its description are facts about which
36
+ * plugins are installed, and in the predecessor repo both were frozen at import
37
+ * time — which is exactly what made the type set unoverridable.
38
+ */
39
+ export declare function makeDelegateTool(types: SubtaskTypeRegistry, maxSubtasks: number): Tool;
40
+ /**
41
+ * The call's id, derived from the parent Task and the round that emitted it —
42
+ * deterministic and replay-safe, the same discipline as the Session message ids
43
+ * it sits alongside (see {@link file://../history.ts}). Later rounds rebuild it
44
+ * rather than storing it.
45
+ */
46
+ export declare function delegateToolCallId(taskId: string, round: number): string;
47
+ /**
48
+ * One branch's outcome, as the tool result carries it. `output` is null for any
49
+ * branch that did not complete.
50
+ *
51
+ * There is no `error` field, and that is deliberate: internal diagnostics never
52
+ * reach the model. It discloses *that* something failed, in user-safe words; the
53
+ * durable row keeps the detail.
54
+ *
55
+ * A type alias, not an interface: this is serialized as the tool result's
56
+ * `JSONValue`, and only aliases get the implicit index signature that satisfies.
57
+ */
58
+ export type DelegateSubtaskOutcome = {
59
+ subtaskId: SubtaskId;
60
+ type: string;
61
+ status: SubtaskStatus;
62
+ output: string | null;
63
+ };
64
+ /**
65
+ * Rebuild one round's call input from its durable rows, in stable ordinal order.
66
+ * Typed as {@link DecompositionProposal} — the same type the model's own calls
67
+ * are validated into — so the reconstructed call and an emitted one cannot drift
68
+ * apart in shape.
69
+ *
70
+ * `referenceIndexes` is omitted rather than faked: this round's references were
71
+ * snapshotted verbatim onto the rows when it ran, and the catalog they were
72
+ * chosen from is long gone.
73
+ */
74
+ export declare function delegateCallInput(reply: string, branches: CompositionBranch[]): DecompositionProposal;
75
+ /** Rebuild one round's call result from its durable rows, in stable ordinal order. */
76
+ export declare function delegateCallOutput(branches: CompositionBranch[]): DelegateSubtaskOutcome[];
77
+ //# sourceMappingURL=delegate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"delegate.d.ts","sourceRoot":"","sources":["../../src/subtasks/delegate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,KAAK,IAAI,EAAE,MAAM,IAAI,CAAC;AAErC,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,KAAK,EACV,iBAAiB,EACjB,qBAAqB,EACrB,SAAS,EACT,aAAa,EACd,MAAM,YAAY,CAAC;AAEpB;;;;;;;;;;;;;;GAcG;AAEH,eAAO,MAAM,kBAAkB,aAAa,CAAC;AAE7C;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,mBAAmB,EAC1B,WAAW,EAAE,MAAM,GAClB,IAAI,CAQN;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAExE;AAeD;;;;;;;;;;GAUG;AACH,MAAM,MAAM,sBAAsB,GAAG;IACnC,SAAS,EAAE,SAAS,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,aAAa,CAAC;IACtB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB,CAAC;AAEF;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,iBAAiB,EAAE,GAC5B,qBAAqB,CAavB;AAED,sFAAsF;AACtF,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,iBAAiB,EAAE,GAC5B,sBAAsB,EAAE,CAU1B"}