@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,211 @@
1
+ import { MockAgent, SnapshotAgent } from "undici";
2
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
3
+ import path from "node:path";
4
+ import { VCR_CONTROL_ORIGIN, CASSETTE_NAME_RE } from "./vcr-shared.js";
5
+ /**
6
+ * undici stamps every cassette entry with two volatile fields we never want
7
+ * committed: `callCount` — which its recorder *mutates on every replay* (to walk
8
+ * sequential responses) — and `timestamp`, the record-time clock. Neither is
9
+ * needed for playback (`callCount` defaults to 0 on load; `timestamp` is unused),
10
+ * so we delete them from a freshly recorded cassette. Removing them is also what
11
+ * stops a plain `npm test` from dirtying cassettes: with no persisted `callCount`
12
+ * there is nothing for a replay to bump — and {@link VcrAgent.close} no longer
13
+ * writes in playback anyway.
14
+ */
15
+ function stripVolatileFields(file) {
16
+ const entries = JSON.parse(readFileSync(file, "utf8"));
17
+ for (const { snapshot } of entries) {
18
+ delete snapshot.callCount;
19
+ delete snapshot.timestamp;
20
+ }
21
+ writeFileSync(file, JSON.stringify(entries, null, 2));
22
+ }
23
+ function originHost(origin) {
24
+ if (!origin)
25
+ return null;
26
+ try {
27
+ return new URL(String(origin)).host;
28
+ }
29
+ catch {
30
+ return null;
31
+ }
32
+ }
33
+ const CONTROL_HOST = new URL(VCR_CONTROL_ORIGIN).host;
34
+ /**
35
+ * The single agent Miniflare accepts as `fetchMock` (it allows only one), fanning
36
+ * outbound worker requests out by role:
37
+ *
38
+ * - **Control channel** ({@link VCR_CONTROL_ORIGIN}) → ordinary MockAgent
39
+ * interceptors whose reply callbacks flip the *active cassette*. This is how a
40
+ * spec running in workerd (no filesystem) tells this Node-side agent which
41
+ * test is running — see `setupRecording` (vcr-spec.ts).
42
+ * - **Passthrough hosts** (the gateway JWKS) → ordinary MockAgent interceptors,
43
+ * unchanged from before VCR existed.
44
+ * - **Everything else, while a cassette is active** → that cassette's undici
45
+ * {@link SnapshotAgent} record/replay. In playback a request with no matching
46
+ * recording throws `No snapshot found` — offline, never the network.
47
+ * - **Everything else, with no active cassette** → ordinary MockAgent behavior
48
+ * (`disableNetConnect()` → offline error). A non-recorded test never reaches
49
+ * the network.
50
+ *
51
+ * There is **no registry**: cassettes are keyed per test (file + describe + test
52
+ * name) by the spec helper, activated one at a time over the control channel. A
53
+ * new recorded spec touches only its own `.spec` file.
54
+ *
55
+ * Why `VcrAgent extends MockAgent` rather than `SnapshotAgent`: a `SnapshotAgent`'s
56
+ * `dispatch` fully overrides `MockAgent`'s (it never falls back to the interceptor
57
+ * path), so one `SnapshotAgent` cannot also serve the control/interceptor fixtures
58
+ * — and cannot serve more than one cassette. Keeping one internal `SnapshotAgent`
59
+ * per cassette and dispatching to the active one, with an ordinary
60
+ * `MockAgent.dispatch` fallback for control/passthrough/offline, leaves the
61
+ * interceptor path exactly as it always worked. Miniflare validates `fetchMock`
62
+ * with `instanceof MockAgent`, which `VcrAgent` satisfies directly.
63
+ */
64
+ export class VcrAgent extends MockAgent {
65
+ #snapshotsDir;
66
+ #record;
67
+ #passthrough;
68
+ #excludeHeaders;
69
+ #ignoreHeaders;
70
+ /** One SnapshotAgent per cassette name, created lazily on first activation. */
71
+ #agents = new Map();
72
+ #active = null;
73
+ #activeName = null;
74
+ constructor(options) {
75
+ super();
76
+ this.#snapshotsDir = options.snapshotsDir;
77
+ this.#record = options.record;
78
+ this.#passthrough = new Set(options.passthroughHosts);
79
+ this.#excludeHeaders = options.excludeHeaders;
80
+ this.#ignoreHeaders = options.ignoreHeaders;
81
+ this.#registerControl();
82
+ }
83
+ /** Persistent interceptors backing the control channel (see class doc). */
84
+ #registerControl() {
85
+ const control = this.get(VCR_CONTROL_ORIGIN);
86
+ control
87
+ // RegExp path so any `?cassette=…` query matches; the name is read from
88
+ // opts.path (the body is a ReadableStream and can't be read synchronously).
89
+ .intercept({ path: /^\/use(\?|$)/, method: "POST" })
90
+ .reply((opts) => this.#onUse(opts))
91
+ .persist();
92
+ control
93
+ .intercept({ path: "/release", method: "POST" })
94
+ .reply(() => {
95
+ this.#active = null;
96
+ this.#activeName = null;
97
+ return { statusCode: 204 };
98
+ })
99
+ .persist();
100
+ }
101
+ /** Activate the named cassette (get-or-create its SnapshotAgent). `opts.path`
102
+ * is undici's reply-callback request path, e.g. `/use?cassette=<name>`. */
103
+ #onUse(opts) {
104
+ const name = new URL(opts.path, VCR_CONTROL_ORIGIN).searchParams.get("cassette") ?? "";
105
+ if (!CASSETTE_NAME_RE.test(name)) {
106
+ return { statusCode: 400, data: `invalid cassette name: ${name}` };
107
+ }
108
+ // Cheap tripwire: recorded specs are expected to run sequentially, so a
109
+ // second cassette activating while another is held means parallel recorded
110
+ // tests are sharing this agent — surface it loudly rather than mis-record.
111
+ if (this.#activeName !== null && this.#activeName !== name) {
112
+ return {
113
+ statusCode: 409,
114
+ data: `cassette ${this.#activeName} still active`
115
+ };
116
+ }
117
+ const file = path.join(this.#snapshotsDir, name);
118
+ // Playback with no cassette → 404, which the spec turns into a "record it"
119
+ // error. In record mode the file is created on first write.
120
+ if (!this.#record && !existsSync(file)) {
121
+ return { statusCode: 404, data: `no cassette ${name}` };
122
+ }
123
+ this.#active = this.#agentFor(name, file);
124
+ this.#activeName = name;
125
+ return { statusCode: 204 };
126
+ }
127
+ #agentFor(name, file) {
128
+ let agent = this.#agents.get(name);
129
+ if (!agent) {
130
+ agent = new SnapshotAgent({
131
+ // `"record"`, not undici's `"update"`: update mode replays any request
132
+ // already in the cassette and only hits the network for new ones —
133
+ // silently stale the moment recorded traffic no longer matches live
134
+ // behavior (e.g. a re-used game id). `"record"` never loads or matches
135
+ // existing entries, so a recording run always reflects the live API.
136
+ mode: this.#record ? "record" : "playback",
137
+ snapshotPath: file,
138
+ excludeHeaders: this.#excludeHeaders,
139
+ ignoreHeaders: this.#ignoreHeaders,
140
+ autoFlush: this.#record
141
+ });
142
+ this.#agents.set(name, agent);
143
+ }
144
+ return agent;
145
+ }
146
+ dispatch(opts, handler) {
147
+ const host = originHost(opts.origin);
148
+ const vcr = host !== null &&
149
+ host !== CONTROL_HOST &&
150
+ !this.#passthrough.has(host) &&
151
+ this.#active !== null;
152
+ if (vcr)
153
+ return this.#active.dispatch(opts, handler);
154
+ return super.dispatch(opts, handler);
155
+ }
156
+ /**
157
+ * Closes every cassette's `SnapshotAgent`, then this agent.
158
+ *
159
+ * Record mode: `SnapshotAgent.close()` saves each cassette, stops its
160
+ * recorder's auto-flush timer, and closes its real sockets — all required (see
161
+ * {@link closeVcr}). We then {@link stripVolatileFields} from each saved file
162
+ * so the committed cassette carries no `callCount`/`timestamp`.
163
+ *
164
+ * Playback mode: undici's `SnapshotAgent.close()` *unconditionally* re-saves
165
+ * the cassette, writing back the `callCount` its recorder mutates on every
166
+ * replay — the side effect that dirtied cassettes on a plain `npm test`. There
167
+ * is nothing to save and no real socket in playback, so we skip that save
168
+ * entirely and only stop each recorder's timers.
169
+ */
170
+ async close() {
171
+ if (this.#record) {
172
+ await Promise.all([...this.#agents.values()].map((a) => a.close()));
173
+ for (const name of this.#agents.keys()) {
174
+ stripVolatileFields(path.join(this.#snapshotsDir, name));
175
+ }
176
+ }
177
+ else {
178
+ for (const a of this.#agents.values())
179
+ a.getRecorder().destroy();
180
+ }
181
+ await super.close();
182
+ }
183
+ }
184
+ export function createVcrAgent(options) {
185
+ const agent = new VcrAgent(options);
186
+ globalThis[VCR_KEY] = agent;
187
+ return agent;
188
+ }
189
+ /** globalThis slot so the Vitest globalSetup teardown can reach the live agent
190
+ * created during config evaluation (same process, possibly a different module
191
+ * realm — globalThis is the reliable channel). */
192
+ const VCR_KEY = "__VCR_AGENT__";
193
+ /**
194
+ * Flush and close the active VCR agent (all its cassettes). In record mode,
195
+ * undici's recorder arms a self-refreshing, non-`unref`ed auto-flush timer and
196
+ * the real `SnapshotAgent` keeps sockets open — both keep the process alive
197
+ * after tests finish and trip Vitest's "close timed out" at teardown. Calling
198
+ * this (from a Vitest `globalSetup` teardown, which runs before Vite closes its
199
+ * own server) stops them. A no-op if no agent was created, and in playback only
200
+ * stops recorder timers — {@link VcrAgent.close} deliberately does not re-save
201
+ * the cassette there (see its doc).
202
+ */
203
+ export async function closeVcr() {
204
+ const g = globalThis;
205
+ const agent = g[VCR_KEY];
206
+ if (!agent)
207
+ return;
208
+ g[VCR_KEY] = undefined;
209
+ await agent.close();
210
+ }
211
+ //# sourceMappingURL=vcr.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vcr.js","sourceRoot":"","sources":["../../src/testing/vcr.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,62 @@
1
+ import { type AgentManifest } from "../a2a/card.js";
2
+ import { type TurnStarter } from "../a2a/executor.js";
3
+ import type { AgentResolver } from "../a2a/agent-stub.js";
4
+ import { type A2ASecretsEnv } from "../env.js";
5
+ /**
6
+ * The A2A Worker: the zero-trust, no-shared-secrets edge every Looping agent
7
+ * puts in front of its Durable Object.
8
+ *
9
+ * Three routes, in order:
10
+ *
11
+ * 1. The card-signing **public** JWKS at the card's `jku`.
12
+ * 2. A **signed** AgentCard at `…/.well-known/agent-card.json`, so a gateway can
13
+ * verify and pin this agent's identity at registration ("G knows R").
14
+ * 3. **Verify the gateway's identity JWT** on every JSON-RPC call against the
15
+ * gateway's public JWKS ("R knows G"), then run the A2A JSON-RPC server for
16
+ * that call. {@link A2AExecutor} dispatches into the caller's agent DO — one
17
+ * instance per calling gateway-agent, keyed by the verified `identity.key`.
18
+ *
19
+ * No secret is ever shared in either direction: trust flows entirely on domains
20
+ * and asymmetric (Ed25519) signatures over public JWKS.
21
+ */
22
+ /** Default path serving the card-signing public JWKS (the card's `jku`). */
23
+ export declare const JWKS_PATH = "/.well-known/jwks.json";
24
+ export interface A2AWorkerOptions {
25
+ /** The transport-independent half of this agent's card. */
26
+ manifest: AgentManifest;
27
+ /** Resolve the agent DO stub for a verified caller. */
28
+ resolveAgent: AgentResolver;
29
+ /** Start the durable turn. Must be idempotent — see {@link TurnStarter}. */
30
+ startTurn: TurnStarter;
31
+ /** Path serving the public JWKS. Defaults to {@link JWKS_PATH}. */
32
+ jwksPath?: string;
33
+ /** Path this agent answers JSON-RPC on. Defaults to `/a2a`. */
34
+ rpcPath?: string;
35
+ /**
36
+ * Advertise `securitySchemes` on the card. **Defaults to `false`** — see
37
+ * `BuildCardOptions.advertiseSecuritySchemes` for why, and read that note
38
+ * before turning it on.
39
+ */
40
+ advertiseSecuritySchemes?: boolean;
41
+ /** Claim carrying the caller identity. Defaults to the Looping namespace. */
42
+ identityClaim?: string;
43
+ /**
44
+ * Require a `taskPushNotificationConfig` on every `SendMessage`.
45
+ *
46
+ * Defaults to `true`, which is the accept-and-notify contract: the agent
47
+ * accepts synchronously and delivers out of band, so a send with nowhere to
48
+ * call back is a request that can never be answered. Set `false` only for an
49
+ * agent that replies inline.
50
+ */
51
+ requirePushConfig?: boolean;
52
+ }
53
+ /**
54
+ * Build the Worker `fetch` handler.
55
+ *
56
+ * ```ts
57
+ * const handler = createA2AWorker({ manifest, resolveAgent: getAgent, startTurn });
58
+ * export default { fetch: handler } satisfies ExportedHandler<Env>;
59
+ * ```
60
+ */
61
+ export declare function createA2AWorker<TEnv extends A2ASecretsEnv>(options: A2AWorkerOptions): (request: Request, env: TEnv) => Promise<Response>;
62
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/worker/index.ts"],"names":[],"mappings":"AAOA,OAAO,EAKL,KAAK,aAAa,EACnB,MAAM,gBAAgB,CAAC;AAQxB,OAAO,EAAe,KAAK,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEnE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAuB,KAAK,aAAa,EAAE,MAAM,WAAW,CAAC;AAEpE;;;;;;;;;;;;;;;;GAgBG;AAEH,4EAA4E;AAC5E,eAAO,MAAM,SAAS,2BAA2B,CAAC;AAKlD,MAAM,WAAW,gBAAgB;IAC/B,2DAA2D;IAC3D,QAAQ,EAAE,aAAa,CAAC;IACxB,uDAAuD;IACvD,YAAY,EAAE,aAAa,CAAC;IAC5B,4EAA4E;IAC5E,SAAS,EAAE,WAAW,CAAC;IACvB,mEAAmE;IACnE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,+DAA+D;IAC/D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,wBAAwB,CAAC,EAAE,OAAO,CAAC;IACnC,6EAA6E;IAC7E,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;;;;OAOG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AA2ED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,IAAI,SAAS,aAAa,EACxD,OAAO,EAAE,gBAAgB,GACxB,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,IAAI,KAAK,OAAO,CAAC,QAAQ,CAAC,CAsHpD"}
@@ -0,0 +1,194 @@
1
+ import { AGENT_CARD_PATH, SendMessageRequest } from "@a2a-js/sdk";
2
+ import { DefaultRequestHandler, JsonRpcTransportHandler, validateVersion } from "@a2a-js/sdk/server";
3
+ import { RequestMalformedError, toJsonRpcError } from "@a2a-js/sdk/errors";
4
+ import { buildBaseCard, parsePrivateJwk, publicCardJwks, signCard } from "../a2a/card.js";
5
+ import { buildCallContext, extensionHeaders } from "../a2a/context.js";
6
+ import { GatewayAuthError, bearerToken, verifyGatewayToken } from "../a2a/verify.js";
7
+ import { A2AExecutor } from "../a2a/executor.js";
8
+ import { DurableTaskStore } from "../a2a/task-store.js";
9
+ import { parseGatewayOrigins } from "../env.js";
10
+ /**
11
+ * The A2A Worker: the zero-trust, no-shared-secrets edge every Looping agent
12
+ * puts in front of its Durable Object.
13
+ *
14
+ * Three routes, in order:
15
+ *
16
+ * 1. The card-signing **public** JWKS at the card's `jku`.
17
+ * 2. A **signed** AgentCard at `…/.well-known/agent-card.json`, so a gateway can
18
+ * verify and pin this agent's identity at registration ("G knows R").
19
+ * 3. **Verify the gateway's identity JWT** on every JSON-RPC call against the
20
+ * gateway's public JWKS ("R knows G"), then run the A2A JSON-RPC server for
21
+ * that call. {@link A2AExecutor} dispatches into the caller's agent DO — one
22
+ * instance per calling gateway-agent, keyed by the verified `identity.key`.
23
+ *
24
+ * No secret is ever shared in either direction: trust flows entirely on domains
25
+ * and asymmetric (Ed25519) signatures over public JWKS.
26
+ */
27
+ /** Default path serving the card-signing public JWKS (the card's `jku`). */
28
+ export const JWKS_PATH = "/.well-known/jwks.json";
29
+ /** The JSON-RPC method carrying a turn (v1.0 renamed v0.3's `message/send`). */
30
+ const SEND_MESSAGE_METHOD = "SendMessage";
31
+ function unauthorized(reason) {
32
+ return new Response(`unauthorized: ${reason}`, {
33
+ status: 401,
34
+ headers: { "www-authenticate": 'Bearer error="invalid_token"' }
35
+ });
36
+ }
37
+ /**
38
+ * A JSON-RPC error envelope for a failure raised before the SDK handler ran,
39
+ * echoing the request's `id` so a conformant client can correlate it. Errors are
40
+ * transported at HTTP 200 per JSON-RPC 2.0, matching what the SDK handler
41
+ * returns for the failures it maps itself.
42
+ *
43
+ * Takes an already-mapped `error` because the mapper has to match the error's
44
+ * origin: `@a2a-js/sdk/errors` and `@a2a-js/sdk/server` are separately bundled
45
+ * entry points that each carry their own copy of the error hierarchy, so a
46
+ * mapper only recognizes errors its own bundle constructed — the other one fails
47
+ * its `instanceof` check and flattens every semantic error to a generic internal
48
+ * error. Each call site below picks accordingly.
49
+ */
50
+ function jsonRpcErrorResponse(body, error) {
51
+ const id = body?.id ?? null;
52
+ return Response.json({ jsonrpc: "2.0", id, error });
53
+ }
54
+ /**
55
+ * Why a `SendMessage` fails the async-only contract, or `undefined` when it
56
+ * holds. (Other methods carry no config and are always fine.)
57
+ *
58
+ * Checked here, before the SDK handler, rather than in the executor: an executor
59
+ * throw is turned into a `failed` task, which a client reads as an accepted turn
60
+ * that never calls back. A rejected send has to surface as a JSON-RPC error.
61
+ */
62
+ function pushConfigError(rpcBody) {
63
+ if (rpcBody.method !== SEND_MESSAGE_METHOD)
64
+ return undefined;
65
+ // Decode through the generated codec rather than reading the raw body: the
66
+ // wire form is protobuf-JSON, so this is what normalizes field naming and
67
+ // oneof shapes into the typed request the SDK handler will also see.
68
+ let params;
69
+ try {
70
+ params = SendMessageRequest.fromJSON(rpcBody.params);
71
+ }
72
+ catch (err) {
73
+ return `params are not a valid SendMessageRequest: ${err.message}`;
74
+ }
75
+ const pushConfig = params.configuration?.taskPushNotificationConfig;
76
+ if (!pushConfig?.url) {
77
+ return ("configuration.taskPushNotificationConfig.url is required: this agent " +
78
+ "replies asynchronously via push notification (A2A §13.2)");
79
+ }
80
+ if (!pushConfig.token) {
81
+ return ("configuration.taskPushNotificationConfig.token is required: the gateway " +
82
+ "uses it to correlate the callback to the pending task (A2A §13.2)");
83
+ }
84
+ try {
85
+ new URL(pushConfig.url);
86
+ }
87
+ catch {
88
+ return `configuration.taskPushNotificationConfig.url is not a valid URL: ${pushConfig.url}`;
89
+ }
90
+ return undefined;
91
+ }
92
+ /**
93
+ * Build the Worker `fetch` handler.
94
+ *
95
+ * ```ts
96
+ * const handler = createA2AWorker({ manifest, resolveAgent: getAgent, startTurn });
97
+ * export default { fetch: handler } satisfies ExportedHandler<Env>;
98
+ * ```
99
+ */
100
+ export function createA2AWorker(options) {
101
+ const jwksPath = options.jwksPath ?? JWKS_PATH;
102
+ const requirePushConfig = options.requirePushConfig ?? true;
103
+ return async function fetch(request, env) {
104
+ const url = new URL(request.url);
105
+ const origin = url.origin;
106
+ const privateJwk = parsePrivateJwk(env.A2A_SIGNING_KEY);
107
+ const cardOptions = {
108
+ origin,
109
+ rpcPath: options.rpcPath,
110
+ advertiseSecuritySchemes: options.advertiseSecuritySchemes
111
+ };
112
+ // (1) Card-signing public JWKS — resolves the card's `jku` for the gateway.
113
+ if (request.method === "GET" && url.pathname === jwksPath) {
114
+ return Response.json(publicCardJwks(privateJwk), {
115
+ headers: { "cache-control": "public, max-age=3600" }
116
+ });
117
+ }
118
+ // (2) Signed AgentCard discovery. `signCard` returns the protobuf-JSON
119
+ // encoding — the exact document the signature is computed over.
120
+ if (request.method === "GET" && url.pathname.endsWith(AGENT_CARD_PATH)) {
121
+ const card = await signCard(buildBaseCard(options.manifest, cardOptions), { privateJwk, jku: `${origin}${jwksPath}` });
122
+ return Response.json(card);
123
+ }
124
+ // (3) A2A JSON-RPC — gateway-authenticated, dispatched into the caller's DO.
125
+ if (request.method === "POST") {
126
+ const token = bearerToken(request);
127
+ if (!token)
128
+ return unauthorized("missing gateway bearer token");
129
+ let identity;
130
+ try {
131
+ ({ identity } = await verifyGatewayToken(token, {
132
+ allowedOrigins: parseGatewayOrigins(env.GATEWAY_ORIGINS),
133
+ audience: origin,
134
+ identityClaim: options.identityClaim
135
+ }));
136
+ }
137
+ catch (err) {
138
+ const message = err instanceof GatewayAuthError ? err.message : "verification failed";
139
+ return unauthorized(message);
140
+ }
141
+ // The DO instance is keyed by the verified `identity.key`; without it the
142
+ // executor cannot route the call — refuse rather than fall back to a
143
+ // shared instance. Guaranteed non-null past this point.
144
+ if (!identity.key) {
145
+ return new Response("bad request: gateway identity missing key", {
146
+ status: 400
147
+ });
148
+ }
149
+ const body = await request.json();
150
+ // `typeof null === "object"`, so the null check is load-bearing: a body of
151
+ // literal `null` is valid JSON and would otherwise make `rpcBody` null,
152
+ // throwing on the property reads below instead of reaching the SDK
153
+ // handler, which maps it to the same -32602 that `[]` or a string gets.
154
+ const rpcBody = (typeof body === "object" && body !== null ? body : {});
155
+ const card = buildBaseCard(options.manifest, cardOptions);
156
+ const context = buildCallContext(request, identity);
157
+ // v1.0 negotiates the protocol version per request via the `A2A-Version`
158
+ // header, and the SDK leaves enforcement to the transport binding (its
159
+ // Express handlers do it; this Worker is the equivalent seam). An absent
160
+ // header is treated as `0.3` by the SDK, which a v1.0-only card does not
161
+ // advertise — so a legacy caller is rejected here rather than silently
162
+ // mis-served.
163
+ try {
164
+ validateVersion(context.requestedVersion, card, "JSONRPC");
165
+ }
166
+ catch (err) {
167
+ // Thrown by the server bundle — map it with the server bundle's mapper.
168
+ return jsonRpcErrorResponse(body, JsonRpcTransportHandler.mapToJSONRPCError(err));
169
+ }
170
+ if (requirePushConfig) {
171
+ const contractError = pushConfigError(rpcBody);
172
+ if (contractError) {
173
+ // Constructed from the errors bundle — map it with that bundle's.
174
+ return jsonRpcErrorResponse(body, toJsonRpcError(new RequestMalformedError(contractError)));
175
+ }
176
+ }
177
+ const handler = new DefaultRequestHandler(card, new DurableTaskStore(identity, options.resolveAgent), new A2AExecutor({
178
+ identity,
179
+ jku: `${origin}${jwksPath}`,
180
+ resolveAgent: options.resolveAgent,
181
+ startTurn: options.startTurn
182
+ }));
183
+ const rpc = new JsonRpcTransportHandler(handler);
184
+ const result = await rpc.handle(body, context);
185
+ // Streaming is not advertised; reject async generators outright.
186
+ if (Symbol.asyncIterator in result) {
187
+ return new Response("streaming not supported", { status: 501 });
188
+ }
189
+ return Response.json(result, { headers: extensionHeaders(context) });
190
+ }
191
+ return new Response("not found", { status: 404 });
192
+ };
193
+ }
194
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/worker/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ import { noDeprecatedObjectProperties } from "./no-deprecated-object-properties.js";
2
+
3
+ /**
4
+ * `@loopingai/core/eslint` — the lint rules this stack needs that no upstream
5
+ * plugin provides.
6
+ *
7
+ * Consume as a flat-config plugin:
8
+ *
9
+ * ```js
10
+ * import looping from "@loopingai/core/eslint";
11
+ *
12
+ * export default [
13
+ * {
14
+ * files: ["src/**\/*.ts"],
15
+ * plugins: { looping },
16
+ * languageOptions: {
17
+ * parserOptions: { projectService: true, tsconfigRootDir: import.meta.dirname }
18
+ * },
19
+ * rules: { "looping/no-deprecated-object-properties": "error" }
20
+ * }
21
+ * ];
22
+ * ```
23
+ *
24
+ * `no-deprecated-object-properties` is type-aware, so the consuming config must
25
+ * enable `projectService` (or `project`) for it to see anything.
26
+ */
27
+ export const rules = {
28
+ "no-deprecated-object-properties": noDeprecatedObjectProperties
29
+ };
30
+
31
+ export default { rules };
@@ -0,0 +1,81 @@
1
+ import { ESLintUtils } from "@typescript-eslint/utils";
2
+
3
+ /**
4
+ * Flags `@deprecated` properties used as keys in object literals.
5
+ *
6
+ * `@typescript-eslint/no-deprecated` cannot see these. For an object literal it
7
+ * calls `getTypeAtLocation(objectExpression)`, which yields the literal's own
8
+ * *inferred* type — an anonymous type whose properties carry no JSDoc — rather
9
+ * than the contextual type that actually declares the deprecation. (Its JSX
10
+ * branch uses `getContextualType` and does work.) So member access like
11
+ * `opts.system` is reported, but `generateText({ system })` is silently allowed,
12
+ * even when the object is written to an explicitly annotated variable.
13
+ *
14
+ * That blind spot covers essentially every options-bag API — the AI SDK's
15
+ * `generateText`/`streamText` among them — so this rule closes it by resolving
16
+ * the contextual type itself and checking each key against it.
17
+ *
18
+ * Upstream issue: typescript-eslint#10883.
19
+ */
20
+ export const noDeprecatedObjectProperties = ESLintUtils.RuleCreator.withoutDocs(
21
+ {
22
+ meta: {
23
+ type: "problem",
24
+ docs: {
25
+ description:
26
+ "Disallow `@deprecated` properties as keys in object literals"
27
+ },
28
+ messages: {
29
+ deprecated: "`{{name}}` is deprecated.{{reason}}"
30
+ },
31
+ schema: []
32
+ },
33
+ defaultOptions: [],
34
+ create(context) {
35
+ const services = ESLintUtils.getParserServices(context);
36
+ const checker = services.program.getTypeChecker();
37
+
38
+ /** Property name for a non-computed key, or undefined if not statically known. */
39
+ const staticName = (property) => {
40
+ if (property.computed) return undefined;
41
+ const { key } = property;
42
+ if (key.type === "Identifier") return key.name;
43
+ if (key.type === "Literal" && typeof key.value === "string")
44
+ return key.value;
45
+ return undefined;
46
+ };
47
+
48
+ return {
49
+ ObjectExpression(node) {
50
+ const tsNode = services.esTreeNodeToTSNodeMap.get(node);
51
+ // Undefined whenever the literal is not contextually typed (e.g. an
52
+ // unannotated `const`), in which case there is no declared property to
53
+ // consult and nothing to report.
54
+ const contextualType = checker.getContextualType(tsNode);
55
+ if (contextualType === undefined) return;
56
+
57
+ for (const property of node.properties) {
58
+ if (property.type !== "Property") continue; // skip SpreadElement
59
+ const name = staticName(property);
60
+ if (name === undefined) continue;
61
+
62
+ const symbol = contextualType.getProperty(name);
63
+ if (symbol === undefined) continue;
64
+
65
+ const tag = symbol
66
+ .getJsDocTags(checker)
67
+ .find((t) => t.name === "deprecated");
68
+ if (tag === undefined) continue;
69
+
70
+ const text = (tag.text ?? []).map((part) => part.text).join("");
71
+ context.report({
72
+ node: property.key,
73
+ messageId: "deprecated",
74
+ data: { name, reason: text ? ` ${text}` : "" }
75
+ });
76
+ }
77
+ }
78
+ };
79
+ }
80
+ }
81
+ );