@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,110 @@
1
+ import type { Tool, ToolSet } from "ai";
2
+ import type { ReferenceCatalogEntry } from "../subtasks/catalog.js";
3
+ import type { SubtaskTypeRegistry } from "../subtasks/subtask-types.js";
4
+ import type { SubtaskDraft } from "../subtasks/types.js";
5
+ /**
6
+ * The **control tools** — the calls that end a round — and the one thing they all
7
+ * need that the SDK cannot do for them: checking their own input.
8
+ *
9
+ * A work tool carries an `execute`, so the SDK validates its input against the
10
+ * tool's schema, runs it, and feeds any failure — bad input, unknown tool, a throw
11
+ * from inside — back into the loop as a failed tool result. The model sees what
12
+ * went wrong and gets to try again, for free, for every work tool that exists or
13
+ * ever will.
14
+ *
15
+ * A control tool has no `execute`. The call *is* the round's output, so the loop
16
+ * halts on it and none of that machinery runs: **its input is never validated at
17
+ * all**. That is not a subtlety, it is a hole. A `final_reply` whose `text` was
18
+ * blank passed `nonBlank` untouched and was delivered to the user as an empty
19
+ * message; a `delegate` whose payload was not a decomposition at all reached
20
+ * {@link resolveDecomposition} and failed there on a raw `TypeError`.
21
+ *
22
+ * So each control tool declares a {@link ControlTool.parse}, and the round runs it
23
+ * where the SDK would have: between the call and any use of it. `parse` either
24
+ * produces the {@link TurnDecision} or throws, and a throw is handed straight back
25
+ * to the model as a failed result for that call — the same repair loop, from the
26
+ * same shape of feedback, that a work tool gets from the SDK. Adding a control tool
27
+ * means writing its `parse`; it inherits validation and repair by existing.
28
+ */
29
+ /** What one round decided. A future `escalate` is another variant here. */
30
+ export type TurnDecision = {
31
+ kind: "reply";
32
+ text: string;
33
+ } | {
34
+ kind: "delegate";
35
+ reply: string;
36
+ drafts: SubtaskDraft[];
37
+ };
38
+ /**
39
+ * Thrown by a {@link ControlTool.parse} for a call the round cannot use.
40
+ *
41
+ * Its `message` is written for the **model**, not for a log: it is what comes back
42
+ * in the failed tool result, and it is the only thing the next attempt knows about
43
+ * why this one was rejected. Say what was wrong with the call.
44
+ */
45
+ export declare class ControlCallError extends Error {
46
+ constructor(message: string);
47
+ }
48
+ /** One control tool: what the model sees, and how the round reads what comes back. */
49
+ export interface ControlTool {
50
+ /** The tool's name, as declared to the model. */
51
+ readonly name: string;
52
+ /** The tool as the model sees it — no `execute`, by definition. */
53
+ readonly tool: Tool;
54
+ /**
55
+ * How this ending competes with another emitted in the same step. The highest
56
+ * precedence among the tools actually called wins, and the rest are dropped.
57
+ *
58
+ * It ranks by **commitment**: `delegate` starts durable work that a reply cannot
59
+ * undo, so a `delegate` alongside a `final_reply` means the reply is the
60
+ * acknowledgment *for* that work, not an answer instead of it. A future
61
+ * `escalate` slots in by saying how committal it is, and nothing else changes.
62
+ */
63
+ readonly precedence: number;
64
+ /**
65
+ * Pick the one call that counts out of **every** call the attempt made to this
66
+ * tool, or throw a {@link ControlCallError} if a repeat is itself the error.
67
+ *
68
+ * Separate from {@link ControlTool.parse} because what a repeat means is the
69
+ * tool's own business — a second `delegate` would start a second batch of durable
70
+ * work and is refused, while a repeated `final_reply` is just the model restating
71
+ * its answer and the last one stands — and the round needs that answer *before* it
72
+ * has a decision: the call it shows back to the model in a repair has to be the
73
+ * same one validation rejected, not whichever happened to come first.
74
+ *
75
+ * Never called with an empty array: a tool with no calls did not end the round.
76
+ */
77
+ select(inputs: readonly unknown[]): unknown;
78
+ /**
79
+ * Turn the selected call into the round's decision, or throw a
80
+ * {@link ControlCallError} describing what is wrong with it.
81
+ *
82
+ * The input is `unknown` on purpose. Nothing has checked it yet — that is this
83
+ * method's job, and typing it as anything else would be the assumption that
84
+ * caused the hole this interface exists to close.
85
+ */
86
+ parse(input: unknown): TurnDecision;
87
+ }
88
+ /**
89
+ * The control tools for one round, in the order the model is shown them.
90
+ *
91
+ * Built per round rather than defined once, because validating a `delegate` needs
92
+ * this round's reference catalog — the same values the model was shown as
93
+ * `[ref N]` markers, which is what makes "reference 4" checkable at all.
94
+ *
95
+ * `delegate` is withheld from a `final` round, which has no budget left to spend on
96
+ * work. `final_reply` is declared on every round including that one: withhold both
97
+ * and the round has no legal way to end.
98
+ */
99
+ export declare function controlTools(opts: {
100
+ catalog: ReferenceCatalogEntry[];
101
+ /** Whether this round may still hand out work (`false` on a `final` round). */
102
+ delegable: boolean;
103
+ /** The installed subtask types — what `delegate` may name. */
104
+ types: SubtaskTypeRegistry;
105
+ /** `CoreConfig.maxSubtasks`, the per-round fan-out bound. */
106
+ maxSubtasks: number;
107
+ }): ControlTool[];
108
+ /** The tools as the SDK takes them, in declaration order. */
109
+ export declare function controlToolSet(tools: ControlTool[]): ToolSet;
110
+ //# sourceMappingURL=control.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"control.d.ts","sourceRoot":"","sources":["../../src/agent/control.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAOxC,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAMpE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AACxE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAGzD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,2EAA2E;AAC3E,MAAM,MAAM,YAAY,GACpB;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC/B;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,YAAY,EAAE,CAAA;CAAE,CAAC;AAEhE;;;;;;GAMG;AACH,qBAAa,gBAAiB,SAAQ,KAAK;gBAC7B,OAAO,EAAE,MAAM;CAI5B;AAED,sFAAsF;AACtF,MAAM,WAAW,WAAW;IAC1B,iDAAiD;IACjD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,mEAAmE;IACnE,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;IACpB;;;;;;;;OAQG;IACH,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B;;;;;;;;;;;;OAYG;IACH,MAAM,CAAC,MAAM,EAAE,SAAS,OAAO,EAAE,GAAG,OAAO,CAAC;IAC5C;;;;;;;OAOG;IACH,KAAK,CAAC,KAAK,EAAE,OAAO,GAAG,YAAY,CAAC;CACrC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE;IACjC,OAAO,EAAE,qBAAqB,EAAE,CAAC;IACjC,+EAA+E;IAC/E,SAAS,EAAE,OAAO,CAAC;IACnB,8DAA8D;IAC9D,KAAK,EAAE,mBAAmB,CAAC;IAC3B,6DAA6D;IAC7D,WAAW,EAAE,MAAM,CAAC;CACrB,GAAG,WAAW,EAAE,CAqEhB;AAiCD,6DAA6D;AAC7D,wBAAgB,cAAc,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,OAAO,CAE5D"}
@@ -0,0 +1,115 @@
1
+ import { FINAL_REPLY_TOOL_NAME, finalReplyInputSchema, finalReplyTool } from "./final-reply.js";
2
+ import { makeDecompositionProposalSchema, resolveDecomposition } from "../subtasks/decomposition.js";
3
+ import { DELEGATE_TOOL_NAME, makeDelegateTool } from "../subtasks/delegate.js";
4
+ /**
5
+ * Thrown by a {@link ControlTool.parse} for a call the round cannot use.
6
+ *
7
+ * Its `message` is written for the **model**, not for a log: it is what comes back
8
+ * in the failed tool result, and it is the only thing the next attempt knows about
9
+ * why this one was rejected. Say what was wrong with the call.
10
+ */
11
+ export class ControlCallError extends Error {
12
+ constructor(message) {
13
+ super(message);
14
+ this.name = "ControlCallError";
15
+ }
16
+ }
17
+ /**
18
+ * The control tools for one round, in the order the model is shown them.
19
+ *
20
+ * Built per round rather than defined once, because validating a `delegate` needs
21
+ * this round's reference catalog — the same values the model was shown as
22
+ * `[ref N]` markers, which is what makes "reference 4" checkable at all.
23
+ *
24
+ * `delegate` is withheld from a `final` round, which has no budget left to spend on
25
+ * work. `final_reply` is declared on every round including that one: withhold both
26
+ * and the round has no legal way to end.
27
+ */
28
+ export function controlTools(opts) {
29
+ const tools = [
30
+ {
31
+ name: FINAL_REPLY_TOOL_NAME,
32
+ tool: finalReplyTool,
33
+ precedence: 0,
34
+ // The last call of a repeated set: a model that restated its answer meant
35
+ // the restatement.
36
+ select: (inputs) => inputs.at(-1),
37
+ parse(input) {
38
+ const parsed = finalReplyInputSchema.safeParse(input);
39
+ if (!parsed.success) {
40
+ throw new ControlCallError(`${FINAL_REPLY_TOOL_NAME} input is invalid — ${issues(parsed.error)}`);
41
+ }
42
+ return { kind: "reply", text: parsed.data.text.trim() };
43
+ }
44
+ }
45
+ ];
46
+ // A round with no registered types has nothing to delegate to, so `delegate`
47
+ // is withheld for the same reason a `final` round withholds it: the tool would
48
+ // advertise an empty enum the model has no legal way to satisfy.
49
+ if (opts.delegable && opts.types.keys.length > 0) {
50
+ const proposalSchema = makeDecompositionProposalSchema(opts.types, opts.maxSubtasks);
51
+ tools.push({
52
+ name: DELEGATE_TOOL_NAME,
53
+ tool: makeDelegateTool(opts.types, opts.maxSubtasks),
54
+ precedence: 1,
55
+ select(inputs) {
56
+ if (inputs.length > 1) {
57
+ throw new ControlCallError(`${DELEGATE_TOOL_NAME} was called ${inputs.length} times in one turn. ` +
58
+ `Delegate once, with every subtask this round needs in that single call.`);
59
+ }
60
+ return inputs[0];
61
+ },
62
+ parse(input) {
63
+ const parsed = proposalSchema.safeParse(input);
64
+ if (!parsed.success) {
65
+ throw new ControlCallError(`${DELEGATE_TOOL_NAME} input is invalid — ${issues(parsed.error)}`);
66
+ }
67
+ // Everything past the schema — unknown reference indexes, dependency
68
+ // cycles, a type's required params — throws its own error, already worded
69
+ // for the model.
70
+ const { reply, drafts } = resolveDecomposition({
71
+ ...parsed.data,
72
+ subtasks: parsed.data.subtasks.map((s) => ({
73
+ ...s,
74
+ params: definedParams(s.params)
75
+ }))
76
+ }, opts.catalog, opts.types);
77
+ return { kind: "delegate", reply, drafts };
78
+ }
79
+ });
80
+ }
81
+ return tools;
82
+ }
83
+ /**
84
+ * Drop params the model sent as an explicit `undefined`.
85
+ *
86
+ * The delegate schema declares the union of every type's param keys, all optional,
87
+ * so one tool schema can serve every type (see `subtaskParamProperties`). A model
88
+ * that names a key and leaves it empty has sent no param, and forwarding the key
89
+ * with an `undefined` value would only make a missing required param report itself
90
+ * as a *present* one. Whether what survives satisfies the type is still
91
+ * `validateSubtaskParams`'s call, downstream.
92
+ */
93
+ function definedParams(params) {
94
+ if (!params)
95
+ return undefined;
96
+ const entries = Object.entries(params).filter(([, v]) => v !== undefined);
97
+ return entries.length > 0
98
+ ? Object.fromEntries(entries)
99
+ : undefined;
100
+ }
101
+ /**
102
+ * A zod failure as one line the model can act on: which field, and what was wrong.
103
+ * The same rendering {@link file://./subtasks/subtask-types.ts validateSubtaskParams}
104
+ * uses, so every rejection a control call can produce reads the same way.
105
+ */
106
+ function issues(error) {
107
+ return error.issues
108
+ .map((i) => `${i.path.join(".") || "(root)"}: ${i.message}`)
109
+ .join("; ");
110
+ }
111
+ /** The tools as the SDK takes them, in declaration order. */
112
+ export function controlToolSet(tools) {
113
+ return Object.fromEntries(tools.map((c) => [c.name, c.tool]));
114
+ }
115
+ //# sourceMappingURL=control.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"control.js","sourceRoot":"","sources":["../../src/agent/control.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,49 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * The `final_reply` tool — the main agent answering the user itself, and the other
4
+ * way a round can end.
5
+ *
6
+ * A round used to end either by calling `delegate` or by stopping with plain text,
7
+ * and that asymmetry was the bug: to the code, "the model answered the user" and
8
+ * "the model narrated an action it never took" were the same outcome — a non-empty
9
+ * string. A model that wrote "I'll start the game" and emitted no call completed the
10
+ * Task successfully, having done nothing.
11
+ *
12
+ * So prose is no longer an outcome. Both endings are now named tools, the round runs
13
+ * with `toolChoice: "required"`, and a round that ends any other way has failed its
14
+ * attempt. The *choice* is still entirely the model's — the point of the design (see
15
+ * {@link file://./turn.ts turn.ts}) was never that the model be steered toward
16
+ * delegating, only that it not be forced. Picking between two named tools is also a
17
+ * far easier discrimination for a small model than picking between prose and a tool,
18
+ * which is what the weaker fallback models kept getting wrong.
19
+ *
20
+ * This lives outside `subtasks/` deliberately: replying is not a subtask concept.
21
+ */
22
+ export declare const FINAL_REPLY_TOOL_NAME = "final_reply";
23
+ /**
24
+ * The tool as the model sees it. **Without `execute`**, exactly like
25
+ * {@link file://./subtasks/delegate.ts delegateTool}: the call *is* the round's
26
+ * output, so there is nothing for the SDK to run and the loop halts on it.
27
+ *
28
+ * Unlike `delegate`, this call is never reconstructed in a later round's history —
29
+ * a past reply is stored as, and replayed as, ordinary assistant text (history is
30
+ * text-only by design). The tool exists to constrain *generation*, not to become a
31
+ * new shape in the transcript.
32
+ */
33
+ /**
34
+ * The call's input, exported as the zod schema rather than only as the tool's
35
+ * `inputSchema`, because the round has to run it itself.
36
+ *
37
+ * A tool with no `execute` never has its input validated by the SDK — the loop
38
+ * halts on the call and nothing checks it. That is what let a blank `text` through
39
+ * to the user despite {@link nonBlank}. The round now parses every control call
40
+ * with the tool's own schema before using it (see
41
+ * {@link file://./control.ts control.ts}), and needs the schema in hand to do it.
42
+ */
43
+ export declare const finalReplyInputSchema: z.ZodObject<{
44
+ text: z.ZodString;
45
+ }, z.core.$strip>;
46
+ export declare const finalReplyTool: import("ai").Tool<{
47
+ text: string;
48
+ }, never, import("@ai-sdk/provider-utils").Context>;
49
+ //# sourceMappingURL=final-reply.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"final-reply.d.ts","sourceRoot":"","sources":["../../src/agent/final-reply.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,eAAO,MAAM,qBAAqB,gBAAgB,CAAC;AAEnD;;;;;;;;;GASG;AACH;;;;;;;;;GASG;AACH,eAAO,MAAM,qBAAqB;;iBAIhC,CAAC;AAEH,eAAO,MAAM,cAAc;;mDAIzB,CAAC"}
@@ -0,0 +1,52 @@
1
+ import { tool } from "ai";
2
+ import { z } from "zod";
3
+ import { nonBlank } from "../subtasks/decomposition.js";
4
+ /**
5
+ * The `final_reply` tool — the main agent answering the user itself, and the other
6
+ * way a round can end.
7
+ *
8
+ * A round used to end either by calling `delegate` or by stopping with plain text,
9
+ * and that asymmetry was the bug: to the code, "the model answered the user" and
10
+ * "the model narrated an action it never took" were the same outcome — a non-empty
11
+ * string. A model that wrote "I'll start the game" and emitted no call completed the
12
+ * Task successfully, having done nothing.
13
+ *
14
+ * So prose is no longer an outcome. Both endings are now named tools, the round runs
15
+ * with `toolChoice: "required"`, and a round that ends any other way has failed its
16
+ * attempt. The *choice* is still entirely the model's — the point of the design (see
17
+ * {@link file://./turn.ts turn.ts}) was never that the model be steered toward
18
+ * delegating, only that it not be forced. Picking between two named tools is also a
19
+ * far easier discrimination for a small model than picking between prose and a tool,
20
+ * which is what the weaker fallback models kept getting wrong.
21
+ *
22
+ * This lives outside `subtasks/` deliberately: replying is not a subtask concept.
23
+ */
24
+ export const FINAL_REPLY_TOOL_NAME = "final_reply";
25
+ /**
26
+ * The tool as the model sees it. **Without `execute`**, exactly like
27
+ * {@link file://./subtasks/delegate.ts delegateTool}: the call *is* the round's
28
+ * output, so there is nothing for the SDK to run and the loop halts on it.
29
+ *
30
+ * Unlike `delegate`, this call is never reconstructed in a later round's history —
31
+ * a past reply is stored as, and replayed as, ordinary assistant text (history is
32
+ * text-only by design). The tool exists to constrain *generation*, not to become a
33
+ * new shape in the transcript.
34
+ */
35
+ /**
36
+ * The call's input, exported as the zod schema rather than only as the tool's
37
+ * `inputSchema`, because the round has to run it itself.
38
+ *
39
+ * A tool with no `execute` never has its input validated by the SDK — the loop
40
+ * halts on the call and nothing checks it. That is what let a blank `text` through
41
+ * to the user despite {@link nonBlank}. The round now parses every control call
42
+ * with the tool's own schema before using it (see
43
+ * {@link file://./control.ts control.ts}), and needs the schema in hand to do it.
44
+ */
45
+ export const finalReplyInputSchema = z.object({
46
+ text: nonBlank("text").describe("Your reply, in your own voice. This is shown to the user verbatim, so it must be the complete answer and must not be blank.")
47
+ });
48
+ export const finalReplyTool = tool({
49
+ description: "Answer the user and end this round. Use this whenever the request is yours to answer — anything about this conversation, your own history, memory, or tools, and anything you can settle with the tools available to you here, including work that has already come back to you.",
50
+ inputSchema: finalReplyInputSchema
51
+ });
52
+ //# sourceMappingURL=final-reply.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"final-reply.js","sourceRoot":"","sources":["../../src/agent/final-reply.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,IAAI,CAAC;AAC1B,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AAExD;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,MAAM,CAAC,MAAM,qBAAqB,GAAG,aAAa,CAAC;AAEnD;;;;;;;;;GASG;AACH;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC5C,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,QAAQ,CAC7B,6HAA6H,CAC9H;CACF,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,cAAc,GAAG,IAAI,CAAC;IACjC,WAAW,EACT,kRAAkR;IACpR,WAAW,EAAE,qBAAqB;CACnC,CAAC,CAAC"}
@@ -0,0 +1,83 @@
1
+ import type { ModelMessage } from "ai";
2
+ import type { SessionMessage } from "agents/experimental/memory/session";
3
+ /**
4
+ * Session-history glue for the agent runtime: parse the gateway-authored `<turn>`
5
+ * provenance wrapper, and bridge between plain text and the Agents-SDK Sessions
6
+ * store. No A2A types cross this boundary — the {@link file://../a2a/inbound.ts
7
+ * A2A adapter} has already reduced the inbound message to a plain string.
8
+ *
9
+ * The gateway inlines a `<turn from="…" id="…" channel="…" at="…">…</turn>` tag
10
+ * into the message text in multi-actor channels so the model (and, in later
11
+ * phases, recall) can attribute "who said what". This agent only *parses* that
12
+ * wrapper — it never authors one.
13
+ *
14
+ * We persist only the user turn and the assistant's final text: intra-turn tool
15
+ * steps stay inside the single `generateText` call, so stored history is plain
16
+ * text messages and the conversion to AI-SDK `ModelMessage`s is trivial.
17
+ */
18
+ /** The fields recovered from a gateway-rendered `<turn>` wrapper. */
19
+ export interface ParsedTurn {
20
+ from: string;
21
+ /** Slack user id, as rendered. */
22
+ id: string;
23
+ channel: string;
24
+ /** ISO-8601 instant. */
25
+ at: string;
26
+ /** The raw inner body. */
27
+ body: string;
28
+ }
29
+ /**
30
+ * Recover the structured provenance from a gateway-authored turn. Returns null
31
+ * for any text that isn't a `<turn>` wrapper (plain messages, assistant replies),
32
+ * so callers can treat the provenance as optional.
33
+ */
34
+ export declare function parseTurn(text: string): ParsedTurn | null;
35
+ /**
36
+ * Deterministic Session-message ids for the task round loop.
37
+ *
38
+ * A round runs inside a durable Workflow step that can re-run after a crash, so
39
+ * its Session appends must be exactly-once. `Session.appendMessage` already
40
+ * dedupes on message id (an id that exists is not re-written), so deriving the id
41
+ * from the task id + round makes the append idempotent for free — no
42
+ * read-then-write race, no duplicate turns in history on replay. Pair with
43
+ * {@link file://./session.ts appendOnce}, which reads the durable text back so a
44
+ * retry that re-inferred still returns the *stored* reply.
45
+ *
46
+ * These ids share the Session id-space with random UUIDs (plain turns) and the
47
+ * SDK's `compaction_`-prefixed summaries; the `task:` prefix cannot collide with
48
+ * either.
49
+ */
50
+ /** Id of the inbound user turn for a task (appended once, by round 0). */
51
+ export declare function taskUserMessageId(taskId: string): string;
52
+ /**
53
+ * Id of the acknowledgment a **delegating** round publishes — the message the
54
+ * user sees while that round's Subtasks run. Also the anchor a later round finds
55
+ * to reattach the round's `delegate` call to (see
56
+ * {@link file://./turn.ts renderTurnMessages}), which is why it is derived from
57
+ * the round rather than stored.
58
+ */
59
+ export declare function roundAckMessageId(taskId: string, round: number): string;
60
+ /**
61
+ * Recognize an acknowledgment id — **any** Task's, not just the one being
62
+ * rendered. A Session outlives the Task that wrote it and is shared by every Task
63
+ * from the same caller, so its history accumulates acks the current render has no
64
+ * branches for: earlier Tasks' acks, and (in the window between a round's ack
65
+ * append and its Subtask rows) this Task's own. Both are the agent's scaffolding
66
+ * rather than conversation, so both must stay out of the reference catalog; the
67
+ * `branches`-derived anchor map alone cannot see either.
68
+ *
69
+ * Returns the parsed Task id and round so the caller can tell whose ack it is.
70
+ */
71
+ export declare function parseRoundAckMessageId(id: string): {
72
+ taskId: string;
73
+ round: number;
74
+ } | null;
75
+ /** Id of the terminal reply — the round in which the agent answered the user. */
76
+ export declare function finalReplyMessageId(taskId: string): string;
77
+ /** A Sessions-store message with a caller-chosen (deterministic) id. */
78
+ export declare function deterministicSessionMessage(id: string, role: "user" | "assistant", text: string): SessionMessage;
79
+ /** Concatenate the text parts of a stored session message. */
80
+ export declare function sessionText(m: SessionMessage): string;
81
+ /** Convert stored history to AI-SDK model messages (user/assistant text only). */
82
+ export declare function toModelMessages(history: SessionMessage[]): ModelMessage[];
83
+ //# sourceMappingURL=history.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"history.d.ts","sourceRoot":"","sources":["../../src/agent/history.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AACvC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAEzE;;;;;;;;;;;;;;GAcG;AAEH,qEAAqE;AACrE,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,kCAAkC;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,wBAAwB;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,0BAA0B;IAC1B,IAAI,EAAE,MAAM,CAAC;CACd;AAyBD;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAYzD;AAED;;;;;;;;;;;;;;GAcG;AAEH,0EAA0E;AAC1E,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAExD;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAEvE;AAMD;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CACpC,EAAE,EAAE,MAAM,GACT;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAG1C;AAED,iFAAiF;AACjF,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED,wEAAwE;AACxE,wBAAgB,2BAA2B,CACzC,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,MAAM,GAAG,WAAW,EAC1B,IAAI,EAAE,MAAM,GACX,cAAc,CAOhB;AAED,8DAA8D;AAC9D,wBAAgB,WAAW,CAAC,CAAC,EAAE,cAAc,GAAG,MAAM,CAKrD;AAED,kFAAkF;AAClF,wBAAgB,eAAe,CAAC,OAAO,EAAE,cAAc,EAAE,GAAG,YAAY,EAAE,CAOzE"}
@@ -0,0 +1,117 @@
1
+ const TURN_TAG_RE = /^<turn\b([^>]*)>([\s\S]*)<\/turn>$/;
2
+ const ATTR_RE = /(\w+)="([^"]*)"/g;
3
+ function parseAttrs(raw) {
4
+ const out = {};
5
+ let m;
6
+ ATTR_RE.lastIndex = 0;
7
+ while ((m = ATTR_RE.exec(raw)) !== null)
8
+ out[m[1]] = m[2];
9
+ return out;
10
+ }
11
+ const ATTR_UNESCAPES = {
12
+ amp: "&",
13
+ lt: "<",
14
+ gt: ">",
15
+ quot: '"'
16
+ };
17
+ /** Reverse the gateway's attribute escaping — single pass so `&amp;` round-trips. */
18
+ function unescAttr(value) {
19
+ return value.replace(/&(amp|lt|gt|quot);/g, (_, e) => ATTR_UNESCAPES[e]);
20
+ }
21
+ /**
22
+ * Recover the structured provenance from a gateway-authored turn. Returns null
23
+ * for any text that isn't a `<turn>` wrapper (plain messages, assistant replies),
24
+ * so callers can treat the provenance as optional.
25
+ */
26
+ export function parseTurn(text) {
27
+ const m = TURN_TAG_RE.exec(text);
28
+ if (!m)
29
+ return null;
30
+ const attrs = parseAttrs(m[1]);
31
+ if (!attrs.from || !attrs.id || !attrs.channel || !attrs.at)
32
+ return null;
33
+ return {
34
+ from: unescAttr(attrs.from),
35
+ id: unescAttr(attrs.id),
36
+ channel: unescAttr(attrs.channel),
37
+ at: unescAttr(attrs.at),
38
+ body: m[2]
39
+ };
40
+ }
41
+ /**
42
+ * Deterministic Session-message ids for the task round loop.
43
+ *
44
+ * A round runs inside a durable Workflow step that can re-run after a crash, so
45
+ * its Session appends must be exactly-once. `Session.appendMessage` already
46
+ * dedupes on message id (an id that exists is not re-written), so deriving the id
47
+ * from the task id + round makes the append idempotent for free — no
48
+ * read-then-write race, no duplicate turns in history on replay. Pair with
49
+ * {@link file://./session.ts appendOnce}, which reads the durable text back so a
50
+ * retry that re-inferred still returns the *stored* reply.
51
+ *
52
+ * These ids share the Session id-space with random UUIDs (plain turns) and the
53
+ * SDK's `compaction_`-prefixed summaries; the `task:` prefix cannot collide with
54
+ * either.
55
+ */
56
+ /** Id of the inbound user turn for a task (appended once, by round 0). */
57
+ export function taskUserMessageId(taskId) {
58
+ return `task:${taskId}:user`;
59
+ }
60
+ /**
61
+ * Id of the acknowledgment a **delegating** round publishes — the message the
62
+ * user sees while that round's Subtasks run. Also the anchor a later round finds
63
+ * to reattach the round's `delegate` call to (see
64
+ * {@link file://./turn.ts renderTurnMessages}), which is why it is derived from
65
+ * the round rather than stored.
66
+ */
67
+ export function roundAckMessageId(taskId, round) {
68
+ return `task:${taskId}:round:${round}:ack`;
69
+ }
70
+ /** Matches {@link roundAckMessageId}; the trailing suffix is fixed, so the greedy
71
+ * task-id group cannot swallow it. */
72
+ const ROUND_ACK_ID = /^task:(.+):round:(\d+):ack$/;
73
+ /**
74
+ * Recognize an acknowledgment id — **any** Task's, not just the one being
75
+ * rendered. A Session outlives the Task that wrote it and is shared by every Task
76
+ * from the same caller, so its history accumulates acks the current render has no
77
+ * branches for: earlier Tasks' acks, and (in the window between a round's ack
78
+ * append and its Subtask rows) this Task's own. Both are the agent's scaffolding
79
+ * rather than conversation, so both must stay out of the reference catalog; the
80
+ * `branches`-derived anchor map alone cannot see either.
81
+ *
82
+ * Returns the parsed Task id and round so the caller can tell whose ack it is.
83
+ */
84
+ export function parseRoundAckMessageId(id) {
85
+ const match = ROUND_ACK_ID.exec(id);
86
+ return match ? { taskId: match[1], round: Number(match[2]) } : null;
87
+ }
88
+ /** Id of the terminal reply — the round in which the agent answered the user. */
89
+ export function finalReplyMessageId(taskId) {
90
+ return `task:${taskId}:reply:final`;
91
+ }
92
+ /** A Sessions-store message with a caller-chosen (deterministic) id. */
93
+ export function deterministicSessionMessage(id, role, text) {
94
+ return {
95
+ id,
96
+ role,
97
+ createdAt: new Date(),
98
+ parts: [{ type: "text", text }]
99
+ };
100
+ }
101
+ /** Concatenate the text parts of a stored session message. */
102
+ export function sessionText(m) {
103
+ return m.parts
104
+ .filter((p) => p.type === "text" && typeof p.text === "string")
105
+ .map((p) => p.text)
106
+ .join("");
107
+ }
108
+ /** Convert stored history to AI-SDK model messages (user/assistant text only). */
109
+ export function toModelMessages(history) {
110
+ return history
111
+ .filter((m) => m.role === "user" || m.role === "assistant")
112
+ .map((m) => ({
113
+ role: m.role,
114
+ content: sessionText(m)
115
+ }));
116
+ }
117
+ //# sourceMappingURL=history.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"history.js","sourceRoot":"","sources":["../../src/agent/history.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * `@loopingai/core/agent` — the primitives a loop is built from.
3
+ *
4
+ * Core ships no loop. `turn.ts` / `loop.ts` are the agent's, because how a round
5
+ * ends is the one thing every agent genuinely differs on. What core owns is
6
+ * everything both predecessor loops needed identically: the session, the model
7
+ * pair with its fallback, the budget, and the control-tool abstraction that
8
+ * turns "the model called something that ends the round" into a checked value.
9
+ */
10
+ export { newTurnBudget, stepAllowance, type TurnBudget } from "./budget.js";
11
+ export { createModelRuntime, type Embed, type GatewayMetadata, type ModelOverrides, type ModelPair, type ModelRuntime, type ModelRuntimeDeps } from "./model.js";
12
+ export { appendOnce, archivingCompaction, buildAgentSession, type AgentSessionOptions, type SessionHost, type SessionLike } from "./session.js";
13
+ export { deterministicSessionMessage, finalReplyMessageId, parseRoundAckMessageId, parseTurn, roundAckMessageId, sessionText, taskUserMessageId, toModelMessages, type ParsedTurn } from "./history.js";
14
+ export { buildIntermediateContentHandler, isTransientAiError, type OnContent } from "./inference.js";
15
+ export { ControlCallError, controlTools, controlToolSet, type ControlTool, type TurnDecision } from "./control.js";
16
+ export { FINAL_REPLY_TOOL_NAME, finalReplyInputSchema, finalReplyTool } from "./final-reply.js";
17
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/agent/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AAE5E,OAAO,EACL,kBAAkB,EAClB,KAAK,KAAK,EACV,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,SAAS,EACd,KAAK,YAAY,EACjB,KAAK,gBAAgB,EACtB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,UAAU,EACV,mBAAmB,EACnB,iBAAiB,EACjB,KAAK,mBAAmB,EACxB,KAAK,WAAW,EAChB,KAAK,WAAW,EACjB,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,2BAA2B,EAC3B,mBAAmB,EACnB,sBAAsB,EACtB,SAAS,EACT,iBAAiB,EACjB,WAAW,EACX,iBAAiB,EACjB,eAAe,EACf,KAAK,UAAU,EAChB,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,+BAA+B,EAC/B,kBAAkB,EAClB,KAAK,SAAS,EACf,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,gBAAgB,EAChB,YAAY,EACZ,cAAc,EACd,KAAK,WAAW,EAChB,KAAK,YAAY,EAClB,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,qBAAqB,EACrB,qBAAqB,EACrB,cAAc,EACf,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * `@loopingai/core/agent` — the primitives a loop is built from.
3
+ *
4
+ * Core ships no loop. `turn.ts` / `loop.ts` are the agent's, because how a round
5
+ * ends is the one thing every agent genuinely differs on. What core owns is
6
+ * everything both predecessor loops needed identically: the session, the model
7
+ * pair with its fallback, the budget, and the control-tool abstraction that
8
+ * turns "the model called something that ends the round" into a checked value.
9
+ */
10
+ export { newTurnBudget, stepAllowance } from "./budget.js";
11
+ export { createModelRuntime } from "./model.js";
12
+ export { appendOnce, archivingCompaction, buildAgentSession } from "./session.js";
13
+ export { deterministicSessionMessage, finalReplyMessageId, parseRoundAckMessageId, parseTurn, roundAckMessageId, sessionText, taskUserMessageId, toModelMessages } from "./history.js";
14
+ export { buildIntermediateContentHandler, isTransientAiError } from "./inference.js";
15
+ export { ControlCallError, controlTools, controlToolSet } from "./control.js";
16
+ export { FINAL_REPLY_TOOL_NAME, finalReplyInputSchema, finalReplyTool } from "./final-reply.js";
17
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/agent/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,aAAa,EAAE,aAAa,EAAmB,MAAM,aAAa,CAAC;AAE5E,OAAO,EACL,kBAAkB,EAOnB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,UAAU,EACV,mBAAmB,EACnB,iBAAiB,EAIlB,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,2BAA2B,EAC3B,mBAAmB,EACnB,sBAAsB,EACtB,SAAS,EACT,iBAAiB,EACjB,WAAW,EACX,iBAAiB,EACjB,eAAe,EAEhB,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,+BAA+B,EAC/B,kBAAkB,EAEnB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,gBAAgB,EAChB,YAAY,EACZ,cAAc,EAGf,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,qBAAqB,EACrB,qBAAqB,EACrB,cAAc,EACf,MAAM,kBAAkB,CAAC"}