@bevel-software/platform-mcp-core 0.23.0 → 0.25.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 (56) hide show
  1. package/dist/chain-example.d.ts +58 -0
  2. package/dist/chain-example.d.ts.map +1 -0
  3. package/dist/chain-example.js +259 -0
  4. package/dist/chain-example.js.map +1 -0
  5. package/dist/chain-runtime.d.ts +122 -0
  6. package/dist/chain-runtime.d.ts.map +1 -0
  7. package/dist/chain-runtime.js +352 -0
  8. package/dist/chain-runtime.js.map +1 -0
  9. package/dist/google-service-account/google-auth-http.protocol.d.ts +24 -0
  10. package/dist/google-service-account/google-auth-http.protocol.d.ts.map +1 -0
  11. package/dist/google-service-account/google-auth-http.protocol.js +47 -0
  12. package/dist/google-service-account/google-auth-http.protocol.js.map +1 -0
  13. package/dist/google-service-account/google-service-account.auth.d.ts +45 -0
  14. package/dist/google-service-account/google-service-account.auth.d.ts.map +1 -0
  15. package/dist/google-service-account/google-service-account.auth.js +153 -0
  16. package/dist/google-service-account/google-service-account.auth.js.map +1 -0
  17. package/dist/google-service-account/google-service-account.token-source.d.ts +28 -0
  18. package/dist/google-service-account/google-service-account.token-source.d.ts.map +1 -0
  19. package/dist/google-service-account/google-service-account.token-source.js +206 -0
  20. package/dist/google-service-account/google-service-account.token-source.js.map +1 -0
  21. package/dist/google-service-account/index.d.ts +5 -0
  22. package/dist/google-service-account/index.d.ts.map +1 -0
  23. package/dist/google-service-account/index.js +7 -0
  24. package/dist/google-service-account/index.js.map +1 -0
  25. package/dist/google-service-account/service-account-token.contract.d.ts +33 -0
  26. package/dist/google-service-account/service-account-token.contract.d.ts.map +1 -0
  27. package/dist/google-service-account/service-account-token.contract.js +14 -0
  28. package/dist/google-service-account/service-account-token.contract.js.map +1 -0
  29. package/dist/index.d.ts +6 -3
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.js +10 -3
  32. package/dist/index.js.map +1 -1
  33. package/dist/meta-tools.d.ts +100 -1
  34. package/dist/meta-tools.d.ts.map +1 -1
  35. package/dist/meta-tools.js +181 -53
  36. package/dist/meta-tools.js.map +1 -1
  37. package/dist/results.d.ts +20 -0
  38. package/dist/results.d.ts.map +1 -1
  39. package/dist/results.js +111 -1
  40. package/dist/results.js.map +1 -1
  41. package/dist/retired-tools.d.ts +0 -12
  42. package/dist/retired-tools.d.ts.map +1 -1
  43. package/dist/retired-tools.js +0 -22
  44. package/dist/retired-tools.js.map +1 -1
  45. package/package.json +3 -2
  46. package/src/chain-example.ts +315 -0
  47. package/src/chain-runtime.ts +382 -0
  48. package/src/google-service-account/google-auth-http.protocol.ts +62 -0
  49. package/src/google-service-account/google-service-account.auth.ts +161 -0
  50. package/src/google-service-account/google-service-account.token-source.ts +239 -0
  51. package/src/google-service-account/index.ts +15 -0
  52. package/src/google-service-account/service-account-token.contract.ts +38 -0
  53. package/src/index.ts +47 -2
  54. package/src/meta-tools.ts +212 -58
  55. package/src/results.ts +106 -1
  56. package/src/retired-tools.ts +0 -22
@@ -0,0 +1,352 @@
1
+ import { utcpNameToTsInterfaceName } from './code-mode-names.js';
2
+ import { describeToolFailure } from './results.js';
3
+ /**
4
+ * What runs a `call_tool_chain` chain, for every surface that offers one: the
5
+ * hosted MCP proxy, the local `hexis-mcp` server and the in-process agent's
6
+ * Mastra tool.
7
+ *
8
+ * Three things live here because all three surfaces need them to be the same:
9
+ *
10
+ * - the BROWSER GLOBALS a chain is promised (`atob`, `btoa`, `TextEncoder`,
11
+ * `TextDecoder`). `@utcp/code-mode` runs the chain in a bare `isolated-vm`
12
+ * isolate — plain V8, so no Node globals (`Buffer`) and no web platform
13
+ * (`atob`), and a chain could not decode base64 or bytes at all. They are
14
+ * added as a PRELUDE to the chain's own source rather than injected into
15
+ * the isolate's context, because the isolate is created inside
16
+ * `callToolChain` and is reachable from nowhere else (see
17
+ * {@link CHAIN_RUNTIME_PRELUDE});
18
+ * - the ANSWER for a chain that failed. `callToolChain` does not throw when
19
+ * the chain dies: it resolves `{ result: null, logs: ['[ERROR] Code
20
+ * execution failed: …'] }`, so a surface that read only `result` reported
21
+ * `success: true` with a null result and the agent was told nothing. Every
22
+ * failure — a timeout, an undefined namespace, a tool that threw — becomes
23
+ * an error that says what happened;
24
+ * - the promise that a chain ALWAYS answers. A request that never settles is
25
+ * what reaches an agent as a dropped connection rather than as something it
26
+ * can act on, so the runner is raced against a watchdog (see
27
+ * {@link WATCHDOG_GRACE_MS}).
28
+ */
29
+ /**
30
+ * The largest `timeout` a chain may ask for, in milliseconds. One constant: it
31
+ * is the schema's bound on every surface AND the figure the timeout error tells
32
+ * the agent it may raise `timeout` to, and those two drifting apart would send
33
+ * an agent to retry with a value the schema then clamps straight back down.
34
+ */
35
+ export const CHAIN_TIMEOUT_MAX_MS = 120_000;
36
+ /** The smallest `timeout` a chain may ask for, in milliseconds. */
37
+ export const CHAIN_TIMEOUT_MIN_MS = 1_000;
38
+ /** The `timeout` a chain that does not ask for one gets, in milliseconds. */
39
+ export const CHAIN_TIMEOUT_DEFAULT_MS = 30_000;
40
+ /**
41
+ * How long past its own `timeout` the runner is given to answer before the
42
+ * watchdog answers for it. `callToolChain` enforces the timeout itself and so
43
+ * normally settles well inside this; the watchdog covers the case where it does
44
+ * not, where the alternative is a request that hangs until the client gives up
45
+ * — the dropped connection an agent cannot tell from a crash.
46
+ */
47
+ const WATCHDOG_GRACE_MS = 5_000;
48
+ /** The log line `@utcp/code-mode` records when a chain's code fails. */
49
+ const CHAIN_FAILURE_LOG = '[ERROR] Code execution failed: ';
50
+ /** The runner's own wording for a chain it stopped at the timeout. */
51
+ const RUNNER_TIMEOUT = /Script execution timeout after (\d+)\s*ms/;
52
+ /** `isolated-vm`'s wording when V8 itself terminated the script at the deadline. */
53
+ const ISOLATE_TERMINATED = /Script execution timed out|execution was terminated|script execution interrupted/i;
54
+ /** `isolated-vm`'s wording when the chain exhausted the isolate's heap. */
55
+ const ISOLATE_OUT_OF_MEMORY = /memory limit|out of memory|allocation failed/i;
56
+ /** The identifier a chain named that the isolate has no binding for. */
57
+ const UNDEFINED_IDENTIFIER = /ReferenceError: ([A-Za-z_$][\w$]*) is not defined/;
58
+ /**
59
+ * The browser globals, as ONE physical line of plain ES5 JavaScript.
60
+ *
61
+ * One line on purpose. The prelude is prepended to the agent's own chain
62
+ * source, and a failed chain is reported with its stack in it; a multi-line
63
+ * prelude would shift every line number in that stack and point the agent at
64
+ * the wrong line of its own code. Prepended WITHOUT a trailing newline for the
65
+ * same reason, so the chain's first line stays line one. (The one casualty is
66
+ * a `'use strict'` directive written as a chain's first statement, which is no
67
+ * longer in first position and so no longer applies. A chain is a handful of
68
+ * statements against a tool catalog, and strict mode is not something the
69
+ * chain protocol ever offered.)
70
+ *
71
+ * `atob`/`btoa` follow WHATWG forgiving-base64: ASCII whitespace is stripped,
72
+ * the padding is optional, and anything else throws rather than decoding to
73
+ * silent garbage. `TextEncoder`/`TextDecoder` are UTF-8 only — that is the
74
+ * encoding the Specification asks for, and a decoder that took a `label` it
75
+ * then ignored would quietly answer mojibake. `TextDecoder` drops a leading
76
+ * byte-order mark the way a browser's default does, and keeps it under
77
+ * `{ ignoreBOM: true }`: a chain decoding a UTF-8 file written on Windows would
78
+ * otherwise find a stray `\uFEFF` at the front of it, which breaks a
79
+ * `JSON.parse` and every exact-match comparison. It honours `{ stream: true }`:
80
+ * a chain that decodes bytes in pieces gets a character whose bytes straddle
81
+ * two pieces whole, where a decoder that started afresh on every call returned
82
+ * two replacement characters for it — and a call without the option ends the
83
+ * stream, answering a sequence left unfinished as one. Each is defined only when the
84
+ * runtime does not already have it, so a future `@utcp/code-mode` that ships
85
+ * them natively wins.
86
+ */
87
+ export const CHAIN_RUNTIME_PRELUDE = [
88
+ '(function(g){',
89
+ "var A='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';",
90
+ 'if(typeof g.btoa!=="function"){g.btoa=function(input){',
91
+ 'var s=String(input),i,o="";',
92
+ 'for(i=0;i<s.length;i++){if(s.charCodeAt(i)>255)throw new Error("InvalidCharacterError: btoa() takes a string whose every character is in the Latin-1 range (0-255). To base64 text, encode it to UTF-8 bytes with TextEncoder first.");}',
93
+ 'for(i=0;i<s.length;i+=3){',
94
+ 'var b0=s.charCodeAt(i),b1=s.charCodeAt(i+1),b2=s.charCodeAt(i+2),h1=!isNaN(b1),h2=!isNaN(b2);',
95
+ 'o+=A.charAt(b0>>2)+A.charAt(((b0&3)<<4)|(h1?b1>>4:0));',
96
+ 'o+=(h1?A.charAt(((b1&15)<<2)|(h2?b2>>6:0)):"=")+(h2?A.charAt(b2&63):"=");',
97
+ '}return o;};}',
98
+ 'if(typeof g.atob!=="function"){g.atob=function(input){',
99
+ 'var s=String(input).replace(/[\\t\\n\\f\\r ]/g,"");',
100
+ 'if(s.length%4===0)s=s.replace(/==?$/,"");',
101
+ 'if(s.length%4===1||/[^+\\/0-9A-Za-z]/.test(s))throw new Error("InvalidCharacterError: atob() was given a string that is not valid base64.");',
102
+ 'var o="",buf=0,bits=0,i;',
103
+ 'for(i=0;i<s.length;i++){buf=(buf<<6)|A.indexOf(s.charAt(i));bits+=6;',
104
+ 'if(bits>=8){bits-=8;o+=String.fromCharCode((buf>>bits)&255);}}',
105
+ 'return o;};}',
106
+ 'if(typeof g.TextEncoder!=="function"){',
107
+ 'var TE=function TextEncoder(){};',
108
+ 'TE.prototype.encoding="utf-8";',
109
+ 'TE.prototype.encode=function(input){',
110
+ 'var s=String(input===undefined?"":input),out=[],i=0;',
111
+ 'while(i<s.length){var c=s.codePointAt(i);i+=c>65535?2:1;',
112
+ 'if(c<128)out.push(c);',
113
+ 'else if(c<2048)out.push(192|(c>>6),128|(c&63));',
114
+ 'else if(c>=55296&&c<=57343)out.push(239,191,189);',
115
+ 'else if(c<65536)out.push(224|(c>>12),128|((c>>6)&63),128|(c&63));',
116
+ 'else out.push(240|(c>>18),128|((c>>12)&63),128|((c>>6)&63),128|(c&63));}',
117
+ 'return new Uint8Array(out);};',
118
+ 'g.TextEncoder=TE;}',
119
+ 'if(typeof g.TextDecoder!=="function"){',
120
+ 'var TD=function TextDecoder(label,options){',
121
+ 'var e=String(label===undefined?"utf-8":label).toLowerCase();',
122
+ 'if(e!=="utf-8"&&e!=="utf8"&&e!=="unicode-1-1-utf-8")throw new RangeError("TextDecoder(): the tool-chain runtime decodes UTF-8 only, not \\""+label+"\\".");',
123
+ 'this.ignoreBOM=!!(options&&options.ignoreBOM);};',
124
+ 'TD.prototype.encoding="utf-8";',
125
+ 'TD.prototype.ignoreBOM=false;',
126
+ // `p` is what one call leaves for the next under `{ stream: true }`: the
127
+ // sequence in progress (need, seen, c and the bounds on its next byte) and
128
+ // whether the stream has produced its first character yet, which is the
129
+ // only place a byte-order mark is one.
130
+ 'TD.prototype.decode=function(input,options){',
131
+ 'var st=!!(options&&options.stream),ig=this.ignoreBOM,p=this._p||[0,0,0,128,191,0];',
132
+ 'var b=(input===undefined||input===null)?new Uint8Array(0):(input instanceof Uint8Array?input:(input instanceof ArrayBuffer?new Uint8Array(input):(input&&input.buffer instanceof ArrayBuffer?new Uint8Array(input.buffer,input.byteOffset,input.byteLength):new Uint8Array(input))));',
133
+ 'var o="",n=b.length,need=p[0],seen=p[1],c=p[2],lo=p[3],hi=p[4],bom=p[5],i=0,x,t;',
134
+ 'while(i<n){x=b[i];',
135
+ 'if(need===0){i++;',
136
+ 'if(x<128){bom=1;o+=String.fromCharCode(x);}',
137
+ 'else if(x>=194&&x<=223){need=1;c=x&31;}',
138
+ 'else if(x>=224&&x<=239){if(x===224)lo=160;if(x===237)hi=159;need=2;c=x&15;}',
139
+ 'else if(x>=240&&x<=244){if(x===240)lo=144;if(x===244)hi=143;need=3;c=x&7;}',
140
+ 'else{bom=1;o+="\\uFFFD";}',
141
+ 'continue;}',
142
+ 'if(x<lo||x>hi){need=0;seen=0;c=0;lo=128;hi=191;bom=1;o+="\\uFFFD";continue;}',
143
+ 'lo=128;hi=191;i++;c=(c<<6)|(x&63);seen++;',
144
+ 'if(seen===need){',
145
+ 'if(c===65279&&!bom&&!ig){}',
146
+ 'else if(c<=65535)o+=String.fromCharCode(c);',
147
+ 'else{t=c-65536;o+=String.fromCharCode(55296+(t>>10),56320+(t&1023));}',
148
+ 'bom=1;need=0;seen=0;c=0;}}',
149
+ 'if(st){this._p=[need,seen,c,lo,hi,bom];return o;}',
150
+ 'if(need!==0)o+="\\uFFFD";',
151
+ 'this._p=null;',
152
+ 'return o;};',
153
+ 'g.TextDecoder=TD;}',
154
+ '})(globalThis);',
155
+ ].join('');
156
+ /**
157
+ * The agent's chain source with the runtime prelude in front of it, on the same
158
+ * physical line, so a stack from the chain still names the chain's own line
159
+ * numbers (see {@link CHAIN_RUNTIME_PRELUDE}).
160
+ */
161
+ export function withChainRuntime(code) {
162
+ return `${CHAIN_RUNTIME_PRELUDE}${code}`;
163
+ }
164
+ /**
165
+ * What a chain can actually call. `@utcp/code-mode` gives every tool whose UTCP
166
+ * name is `MANUAL.tool` a `global.MANUAL` object, so the namespaces are the
167
+ * sanitized manual names — and a tool registered under a bare name becomes a
168
+ * global function rather than a namespace, which is why those are listed apart.
169
+ */
170
+ export async function chainNamespaces(client) {
171
+ const namespaces = new Set();
172
+ const bare = new Set();
173
+ for (const tool of await client.config.tool_repository.getTools()) {
174
+ const tsName = utcpNameToTsInterfaceName(tool.name);
175
+ const dot = tsName.indexOf('.');
176
+ if (dot > 0)
177
+ namespaces.add(tsName.slice(0, dot));
178
+ else
179
+ bare.add(tsName);
180
+ }
181
+ return { namespaces: [...namespaces].sort(), bare: [...bare].sort() };
182
+ }
183
+ /** What a chain is told when it timed out: the limit it hit, and how to raise it. */
184
+ export function chainTimeoutMessage(timeoutMs) {
185
+ return (`The tool chain timed out after ${timeoutMs} ms and was stopped, so it has no result. ` +
186
+ `Raise \`timeout\` (milliseconds, up to a maximum of ${CHAIN_TIMEOUT_MAX_MS}) and run it again, ` +
187
+ 'or split the work across several shorter chains. The connection is unaffected — your next tool call works as usual.');
188
+ }
189
+ /** What a chain is told when it exhausted the isolate's heap. */
190
+ export function chainOutOfMemoryMessage(reason) {
191
+ return (`The tool chain ran out of memory and was stopped, so it has no result (${reason}). ` +
192
+ 'Return less from the chain — filter, map or count inside it instead of accumulating every record — ' +
193
+ 'or split the work across several chains. The connection is unaffected — your next tool call works as usual.');
194
+ }
195
+ /** What a chain is told when it named something the runtime has no binding for. */
196
+ export function unknownNamespaceMessage(identifier, found) {
197
+ const { namespaces, bare } = found;
198
+ const existing = namespaces.length
199
+ ? `The tool namespaces this connection exposes are: ${namespaces.join(', ')}.`
200
+ : 'This connection exposes no tool namespaces at all.';
201
+ const example = namespaces[0] ? ` Call a tool as \`${namespaces[0]}.<tool>({ body: { … } })\`.` : '';
202
+ const bareNote = bare.length ? ` Callable without a namespace: ${bare.join(', ')}.` : '';
203
+ return (`ReferenceError: ${identifier} is not defined — "${identifier}" is not one of them. ` +
204
+ `${existing}${example}${bareNote} Use \`list_tools\` for the exact callable names.`);
205
+ }
206
+ /**
207
+ * Why a RESOLVED `callToolChain` is a failure, or undefined when it is not one.
208
+ *
209
+ * `callToolChain` resolves rather than throws on a dead chain, reporting it as
210
+ * a `[ERROR] Code execution failed: …` log line with a null result. Both halves
211
+ * are required: a chain that returned a value succeeded however it logged.
212
+ *
213
+ * The LAST such line, not the last line: the runner appends its own entry in a
214
+ * `catch` and then, in the `finally`, a `[WARN] Tool call "…" abandoned` line
215
+ * per tool call still in flight — so a chain that timed out mid-call has the
216
+ * reason second-from-last or further back. A chain that prints that exact
217
+ * prefix itself through `console.error` AND returns null is read as a failure;
218
+ * that trade is deliberate, since the alternative — a dead chain reported as a
219
+ * success with a null result — is the bug this replaces.
220
+ */
221
+ function failureReason(outcome) {
222
+ if (outcome.result !== null && outcome.result !== undefined)
223
+ return undefined;
224
+ for (let i = outcome.logs.length - 1; i >= 0; i -= 1) {
225
+ const line = outcome.logs[i];
226
+ if (typeof line === 'string' && line.startsWith(CHAIN_FAILURE_LOG)) {
227
+ return line.slice(CHAIN_FAILURE_LOG.length);
228
+ }
229
+ }
230
+ return undefined;
231
+ }
232
+ /**
233
+ * Turn the runner's reason into the sentence the agent reads. A timeout, an
234
+ * exhausted heap and an undefined namespace each get their own; anything else
235
+ * passes through exactly as the runner reported it, so a real cause is never
236
+ * replaced by a guess.
237
+ */
238
+ export async function describeChainFailure(client, reason, timeoutMs) {
239
+ // Memory before termination: an isolate killed for its heap reports being
240
+ // disposed too, and calling that a timeout would send the agent to raise a
241
+ // limit that was never the problem.
242
+ if (ISOLATE_OUT_OF_MEMORY.test(reason))
243
+ return chainOutOfMemoryMessage(reason);
244
+ const runnerTimeout = RUNNER_TIMEOUT.exec(reason);
245
+ if (runnerTimeout)
246
+ return chainTimeoutMessage(Number(runnerTimeout[1]));
247
+ if (ISOLATE_TERMINATED.test(reason))
248
+ return chainTimeoutMessage(timeoutMs);
249
+ const undefinedIdentifier = UNDEFINED_IDENTIFIER.exec(reason);
250
+ if (undefinedIdentifier) {
251
+ // The namespace list costs one catalog read, so it is fetched only on the
252
+ // failure that needs it. A catalog that cannot be read must not replace the
253
+ // chain's own reason with an unrelated one: the agent keeps the
254
+ // ReferenceError, just without the list.
255
+ try {
256
+ return unknownNamespaceMessage(undefinedIdentifier[1], await chainNamespaces(client));
257
+ }
258
+ catch {
259
+ return reason;
260
+ }
261
+ }
262
+ return reason;
263
+ }
264
+ /**
265
+ * Run a chain and ALWAYS answer: with its value, or with the reason it has
266
+ * none. Never throws, and never leaves a caller waiting past the chain's own
267
+ * timeout plus {@link WATCHDOG_GRACE_MS}.
268
+ *
269
+ * What the caller does with an error is still the caller's (the hosted proxy
270
+ * maps a retired tool's name onto its own message first, for instance); this
271
+ * decides only whether there IS one and what it says about the runtime.
272
+ */
273
+ export async function runToolChain(client, code, timeoutMs) {
274
+ let watchdog;
275
+ try {
276
+ const watchdogFired = new Promise((resolve) => {
277
+ watchdog = setTimeout(() => resolve('watchdog'), timeoutMs + WATCHDOG_GRACE_MS);
278
+ // A watchdog nobody is waiting on must not hold a CLI's exit open.
279
+ watchdog.unref?.();
280
+ });
281
+ const settled = await Promise.race([
282
+ client.callToolChain(withChainRuntime(code), timeoutMs).then((outcome) => ({ outcome })),
283
+ watchdogFired,
284
+ ]);
285
+ if (settled === 'watchdog')
286
+ return { ok: false, error: chainTimeoutMessage(timeoutMs), logs: [] };
287
+ const logs = Array.isArray(settled.outcome.logs) ? settled.outcome.logs : [];
288
+ const reason = failureReason({ result: settled.outcome.result, logs });
289
+ if (reason !== undefined) {
290
+ return { ok: false, error: await describeChainFailure(client, reason, timeoutMs), logs };
291
+ }
292
+ return { ok: true, result: settled.outcome.result, logs };
293
+ }
294
+ catch (err) {
295
+ // Everything the runner throws BEFORE the chain starts (a catalog read, a
296
+ // registry outage) and anything a tool bridge rethrows. The http transport
297
+ // carries a status and a body on a tool failure, and both are worth more to
298
+ // the agent than the message on its own.
299
+ //
300
+ // `describeToolFailure` is what reads the PROVIDER's own reason out of such
301
+ // a failure — `response.data.error`, with the machine-readable `kind` kept
302
+ // beside it on a typed refusal. The MCP dispatcher used to call it on the
303
+ // thrown error itself; now that the catch lives here, taking `err.message`
304
+ // instead would hand the agent a generic transport line and drop the half
305
+ // it can act on.
306
+ //
307
+ // Under a guard even so. `describeToolFailure` is total — it reads `err`
308
+ // through try/catch — but THIS function's contract is that it never throws,
309
+ // and that contract is what the "no chain failure reaches the agent as a
310
+ // dropped connection" criterion rests on. A guard here keeps it local
311
+ // rather than resting on another module staying total.
312
+ let error;
313
+ try {
314
+ error = describeToolFailure(err);
315
+ }
316
+ catch {
317
+ // Describing it is the first thing that can fail, so the fallback is
318
+ // itself layered: the thrown value's own message if it can be read, and
319
+ // a fixed sentence if even that throws. An opaque answer is still an
320
+ // ANSWER — what must never happen is a rejection out of this catch.
321
+ try {
322
+ error = err instanceof Error ? err.message : String(err);
323
+ }
324
+ catch {
325
+ error = '(indescribable tool failure)';
326
+ }
327
+ }
328
+ let status;
329
+ let data;
330
+ try {
331
+ const e = err;
332
+ status = e?.status ?? e?.response?.status;
333
+ data = e?.data ?? e?.response?.data;
334
+ }
335
+ catch {
336
+ // A throwing getter or Proxy says nothing about the failure; the message
337
+ // above already stands on its own.
338
+ }
339
+ return {
340
+ ok: false,
341
+ error,
342
+ logs: [],
343
+ ...(typeof status === 'number' ? { status } : {}),
344
+ ...(data !== undefined ? { data } : {}),
345
+ };
346
+ }
347
+ finally {
348
+ if (watchdog)
349
+ clearTimeout(watchdog);
350
+ }
351
+ }
352
+ //# sourceMappingURL=chain-runtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chain-runtime.js","sourceRoot":"","sources":["../src/chain-runtime.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ import { type CallTemplate, type IUtcpClient } from '@utcp/sdk';
2
+ import { HttpCommunicationProtocol } from '@utcp/http';
3
+ import type { IServiceAccountTokenSource } from './service-account-token.contract.js';
4
+ /**
5
+ * The stock UTCP `http` protocol, taught one more auth type. A call whose
6
+ * template names `google_service_account` is handed to the stock protocol with
7
+ * that auth swapped for the bearer token it resolves to (as `oauth2_user`,
8
+ * which the stock protocol sends as `Authorization: Bearer <token>` and strips
9
+ * on a cross-origin redirect). Every other call passes through untouched.
10
+ */
11
+ export declare class GoogleAuthHttpProtocol extends HttpCommunicationProtocol {
12
+ private readonly tokens;
13
+ constructor(tokens: IServiceAccountTokenSource);
14
+ callTool(caller: IUtcpClient, toolName: string, toolArgs: Record<string, unknown>, toolCallTemplate: CallTemplate): Promise<unknown>;
15
+ callToolStreaming(caller: IUtcpClient, toolName: string, toolArgs: Record<string, unknown>, toolCallTemplate: CallTemplate): AsyncGenerator<unknown, void, unknown>;
16
+ private withBearerToken;
17
+ }
18
+ /**
19
+ * Put the service-account-aware protocol in place of the stock `http` one,
20
+ * minting tokens from `tokens`. Exported so a suite can answer for Google;
21
+ * a process never needs to call it, since loading this module already has.
22
+ */
23
+ export declare function installGoogleServiceAccountAuth(tokens?: IServiceAccountTokenSource): void;
24
+ //# sourceMappingURL=google-auth-http.protocol.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"google-auth-http.protocol.d.ts","sourceRoot":"","sources":["../../src/google-service-account/google-auth-http.protocol.ts"],"names":[],"mappings":"AAAA,OAAO,EAAyB,KAAK,YAAY,EAAE,KAAK,WAAW,EAAE,MAAM,WAAW,CAAC;AACvF,OAAO,EAAE,yBAAyB,EAAyB,MAAM,YAAY,CAAC;AAG9E,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,qCAAqC,CAAC;AAEtF;;;;;;GAMG;AACH,qBAAa,sBAAuB,SAAQ,yBAAyB;IACvD,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE,0BAA0B;IAIhD,QAAQ,CACrB,MAAM,EAAE,WAAW,EACnB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,gBAAgB,EAAE,YAAY,GAC7B,OAAO,CAAC,OAAO,CAAC;IAIH,iBAAiB,CAC/B,MAAM,EAAE,WAAW,EACnB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,gBAAgB,EAAE,YAAY,GAC7B,cAAc,CAAC,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC;YAI3B,eAAe;CAM9B;AAED;;;;GAIG;AACH,wBAAgB,+BAA+B,CAC7C,MAAM,GAAE,0BAAkE,GACzE,IAAI,CAEN"}
@@ -0,0 +1,47 @@
1
+ import { CommunicationProtocol } from '@utcp/sdk';
2
+ import { HttpCommunicationProtocol } from '@utcp/http';
3
+ import { isGoogleServiceAccountAuth } from './google-service-account.auth.js';
4
+ import { GoogleServiceAccountTokenSource } from './google-service-account.token-source.js';
5
+ /**
6
+ * The stock UTCP `http` protocol, taught one more auth type. A call whose
7
+ * template names `google_service_account` is handed to the stock protocol with
8
+ * that auth swapped for the bearer token it resolves to (as `oauth2_user`,
9
+ * which the stock protocol sends as `Authorization: Bearer <token>` and strips
10
+ * on a cross-origin redirect). Every other call passes through untouched.
11
+ */
12
+ export class GoogleAuthHttpProtocol extends HttpCommunicationProtocol {
13
+ tokens;
14
+ constructor(tokens) {
15
+ super();
16
+ this.tokens = tokens;
17
+ }
18
+ async callTool(caller, toolName, toolArgs, toolCallTemplate) {
19
+ return super.callTool(caller, toolName, toolArgs, await this.withBearerToken(toolCallTemplate));
20
+ }
21
+ async *callToolStreaming(caller, toolName, toolArgs, toolCallTemplate) {
22
+ yield* super.callToolStreaming(caller, toolName, toolArgs, await this.withBearerToken(toolCallTemplate));
23
+ }
24
+ async withBearerToken(template) {
25
+ const auth = template.auth;
26
+ if (!isGoogleServiceAccountAuth(auth))
27
+ return template;
28
+ const accessToken = await this.tokens.accessToken(auth);
29
+ return { ...template, auth: { auth_type: 'oauth2_user', access_token: accessToken } };
30
+ }
31
+ }
32
+ /**
33
+ * Put the service-account-aware protocol in place of the stock `http` one,
34
+ * minting tokens from `tokens`. Exported so a suite can answer for Google;
35
+ * a process never needs to call it, since loading this module already has.
36
+ */
37
+ export function installGoogleServiceAccountAuth(tokens = new GoogleServiceAccountTokenSource()) {
38
+ CommunicationProtocol.communicationProtocols['http'] = new GoogleAuthHttpProtocol(tokens);
39
+ }
40
+ // Installed on module load, the way `@utcp/http` installs the protocol this
41
+ // one replaces: once per process, before any client exists (a client copies
42
+ // the registry when it is built). The import of `@utcp/http` above has already
43
+ // run its own registration by the time this line does, whatever order the
44
+ // importing module lists the two in. One token cache then serves the process;
45
+ // its entries are keyed by the key itself, so knowledge bases never share one.
46
+ installGoogleServiceAccountAuth();
47
+ //# sourceMappingURL=google-auth-http.protocol.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"google-auth-http.protocol.js","sourceRoot":"","sources":["../../src/google-service-account/google-auth-http.protocol.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAuC,MAAM,WAAW,CAAC;AACvF,OAAO,EAAE,yBAAyB,EAAyB,MAAM,YAAY,CAAC;AAC9E,OAAO,EAAE,0BAA0B,EAAE,MAAM,kCAAkC,CAAC;AAC9E,OAAO,EAAE,+BAA+B,EAAE,MAAM,0CAA0C,CAAC;AAG3F;;;;;;GAMG;AACH,MAAM,OAAO,sBAAuB,SAAQ,yBAAyB;IACtC;IAA7B,YAA6B,MAAkC;QAC7D,KAAK,EAAE,CAAC;QADmB,WAAM,GAAN,MAAM,CAA4B;IAE/D,CAAC;IAEQ,KAAK,CAAC,QAAQ,CACrB,MAAmB,EACnB,QAAgB,EAChB,QAAiC,EACjC,gBAA8B;QAE9B,OAAO,KAAK,CAAC,QAAQ,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,IAAI,CAAC,eAAe,CAAC,gBAAgB,CAAC,CAAC,CAAC;IAClG,CAAC;IAEQ,KAAK,CAAC,CAAC,iBAAiB,CAC/B,MAAmB,EACnB,QAAgB,EAChB,QAAiC,EACjC,gBAA8B;QAE9B,KAAK,CAAC,CAAC,KAAK,CAAC,iBAAiB,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,IAAI,CAAC,eAAe,CAAC,gBAAgB,CAAC,CAAC,CAAC;IAC3G,CAAC;IAEO,KAAK,CAAC,eAAe,CAAC,QAAsB;QAClD,MAAM,IAAI,GAAI,QAA6B,CAAC,IAAI,CAAC;QACjD,IAAI,CAAC,0BAA0B,CAAC,IAAI,CAAC;YAAE,OAAO,QAAQ,CAAC;QACvD,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QACxD,OAAO,EAAE,GAAG,QAAQ,EAAE,IAAI,EAAE,EAAE,SAAS,EAAE,aAAa,EAAE,YAAY,EAAE,WAAW,EAAE,EAAkB,CAAC;IACxG,CAAC;CACF;AAED;;;;GAIG;AACH,MAAM,UAAU,+BAA+B,CAC7C,SAAqC,IAAI,+BAA+B,EAAE;IAE1E,qBAAqB,CAAC,sBAAsB,CAAC,MAAM,CAAC,GAAG,IAAI,sBAAsB,CAAC,MAAM,CAAC,CAAC;AAC5F,CAAC;AAED,4EAA4E;AAC5E,4EAA4E;AAC5E,+EAA+E;AAC/E,0EAA0E;AAC1E,8EAA8E;AAC9E,+EAA+E;AAC/E,+BAA+B,EAAE,CAAC"}
@@ -0,0 +1,45 @@
1
+ import { type GoogleServiceAccountAuth } from './service-account-token.contract.js';
2
+ /** Whether a call template's `auth` asks for a Google service-account token. */
3
+ export declare function isGoogleServiceAccountAuth(auth: unknown): auth is GoogleServiceAccountAuth;
4
+ /**
5
+ * Where in `doc` a `google_service_account` auth block sits that nothing will
6
+ * act on, as a phrase for a refusal, or null when every block is the `auth` of
7
+ * one of `toolCallTemplates` and that template is an `http` one.
8
+ *
9
+ * `toolCallTemplates` are the templates tools are really called through, which
10
+ * only the caller knows: the document's shape is its business. A block
11
+ * anywhere else is read by nothing, whatever the object around it looks like:
12
+ * a template the document never registers, an `auth_tools`, a block at the
13
+ * root of a file that discovers its tools from a url.
14
+ *
15
+ * UTCP validates an auth type on any call template that takes an `auth`, but
16
+ * only the `http` protocol mints the token. Every other protocol sends an auth
17
+ * type it does not know as no credentials at all, so such a tool would save
18
+ * cleanly and then call Google unauthenticated.
19
+ *
20
+ * Every block in the document is found, at any depth (see
21
+ * `googleServiceAccountBlocks`), and the first one in document order that
22
+ * nothing will act on is the one named.
23
+ */
24
+ export declare function findUnservedGoogleServiceAccountAuth(doc: unknown, toolCallTemplates: readonly unknown[]): string | null;
25
+ /**
26
+ * Whether any `google_service_account` block in `doc` has its key WRITTEN IN
27
+ * THE DOCUMENT: a `credentials` that is anything but one variable reference.
28
+ *
29
+ * The document is a `.tool`, which is knowledge-base content: it is committed
30
+ * to the repository, and read by everyone and every agent that can read the
31
+ * knowledge base. A key written into it is a key all of them hold, for as
32
+ * long as the history keeps it. So the place for the key is the vault, and
33
+ * `credentials` only ever names the variable.
34
+ *
35
+ * Asked of the document as its author wrote it, wherever the block sits: a
36
+ * key in a block nothing will act on is just as readable. It cannot be asked
37
+ * where the auth type itself is validated, because that also runs on the
38
+ * template AFTER its variables were substituted, where `credentials` is the
39
+ * key and has to be.
40
+ *
41
+ * Says only that one was found. It never returns, quotes or measures the
42
+ * value: what it would be describing is the secret.
43
+ */
44
+ export declare function holdsLiteralGoogleServiceAccountKey(doc: unknown): boolean;
45
+ //# sourceMappingURL=google-service-account.auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"google-service-account.auth.d.ts","sourceRoot":"","sources":["../../src/google-service-account/google-service-account.auth.ts"],"names":[],"mappings":"AAEA,OAAO,EAAoC,KAAK,wBAAwB,EAAE,MAAM,qCAAqC,CAAC;AA0BtH,gFAAgF;AAChF,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,IAAI,wBAAwB,CAM1F;AAYD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,oCAAoC,CAAC,GAAG,EAAE,OAAO,EAAE,iBAAiB,EAAE,SAAS,OAAO,EAAE,GAAG,MAAM,GAAG,IAAI,CASvH;AAKD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,mCAAmC,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAOzE"}
@@ -0,0 +1,153 @@
1
+ import { AuthSerializer, Serializer } from '@utcp/sdk';
2
+ import { z } from 'zod';
3
+ import { GOOGLE_SERVICE_ACCOUNT_AUTH_TYPE } from './service-account-token.contract.js';
4
+ const GoogleServiceAccountAuthSchema = z.object({
5
+ auth_type: z.literal(GOOGLE_SERVICE_ACCOUNT_AUTH_TYPE),
6
+ credentials: z
7
+ .string()
8
+ .min(1)
9
+ .describe('The service-account key JSON. Recommended to use a vault variable like "${GOOGLE_SA_KEY}".'),
10
+ // Trimmed before the length check, so a blank scope is refused here rather
11
+ // than reaching Google as an empty one.
12
+ scopes: z
13
+ .union([z.string().trim().min(1), z.array(z.string().trim().min(1)).min(1)])
14
+ .describe('OAuth scopes for the token, e.g. "https://www.googleapis.com/auth/adwords".'),
15
+ subject: z.string().trim().min(1).optional().describe('User to impersonate under domain-wide delegation.'),
16
+ });
17
+ class GoogleServiceAccountAuthSerializer extends Serializer {
18
+ toDict(obj) {
19
+ return { ...obj };
20
+ }
21
+ validateDict(obj) {
22
+ return GoogleServiceAccountAuthSchema.parse(obj);
23
+ }
24
+ }
25
+ /** Whether a call template's `auth` asks for a Google service-account token. */
26
+ export function isGoogleServiceAccountAuth(auth) {
27
+ return (typeof auth === 'object' &&
28
+ auth !== null &&
29
+ auth.auth_type === GOOGLE_SERVICE_ACCOUNT_AUTH_TYPE);
30
+ }
31
+ /** The one call template type whose protocol turns the key into a token. */
32
+ const SERVED_CALL_TEMPLATE_TYPE = 'http';
33
+ /**
34
+ * The other call template types an author is likely to have reached for, which
35
+ * the answer below may name. A type outside this list is the file's own text,
36
+ * and is described rather than quoted back.
37
+ */
38
+ const NAMEABLE_CALL_TEMPLATE_TYPES = ['sse', 'streamable_http', 'mcp', 'cli'];
39
+ /**
40
+ * Where in `doc` a `google_service_account` auth block sits that nothing will
41
+ * act on, as a phrase for a refusal, or null when every block is the `auth` of
42
+ * one of `toolCallTemplates` and that template is an `http` one.
43
+ *
44
+ * `toolCallTemplates` are the templates tools are really called through, which
45
+ * only the caller knows: the document's shape is its business. A block
46
+ * anywhere else is read by nothing, whatever the object around it looks like:
47
+ * a template the document never registers, an `auth_tools`, a block at the
48
+ * root of a file that discovers its tools from a url.
49
+ *
50
+ * UTCP validates an auth type on any call template that takes an `auth`, but
51
+ * only the `http` protocol mints the token. Every other protocol sends an auth
52
+ * type it does not know as no credentials at all, so such a tool would save
53
+ * cleanly and then call Google unauthenticated.
54
+ *
55
+ * Every block in the document is found, at any depth (see
56
+ * `googleServiceAccountBlocks`), and the first one in document order that
57
+ * nothing will act on is the one named.
58
+ */
59
+ export function findUnservedGoogleServiceAccountAuth(doc, toolCallTemplates) {
60
+ const served = new Set(toolCallTemplates);
61
+ for (const { parent, key } of googleServiceAccountBlocks(doc)) {
62
+ // Judged by where it sits, so once per place it appears: an aliased
63
+ // block can be served in one place and ignored in another.
64
+ const where = unservedPlacement(parent, key, served);
65
+ if (where)
66
+ return where;
67
+ }
68
+ return null;
69
+ }
70
+ /** `${NAME}` or `$NAME`, as UTCP spells a variable, and nothing else around it. */
71
+ const ONE_VARIABLE_REFERENCE = /^\s*(?:\$\{[a-zA-Z0-9_]+\}|\$[a-zA-Z0-9_]+)\s*$/;
72
+ /**
73
+ * Whether any `google_service_account` block in `doc` has its key WRITTEN IN
74
+ * THE DOCUMENT: a `credentials` that is anything but one variable reference.
75
+ *
76
+ * The document is a `.tool`, which is knowledge-base content: it is committed
77
+ * to the repository, and read by everyone and every agent that can read the
78
+ * knowledge base. A key written into it is a key all of them hold, for as
79
+ * long as the history keeps it. So the place for the key is the vault, and
80
+ * `credentials` only ever names the variable.
81
+ *
82
+ * Asked of the document as its author wrote it, wherever the block sits: a
83
+ * key in a block nothing will act on is just as readable. It cannot be asked
84
+ * where the auth type itself is validated, because that also runs on the
85
+ * template AFTER its variables were substituted, where `credentials` is the
86
+ * key and has to be.
87
+ *
88
+ * Says only that one was found. It never returns, quotes or measures the
89
+ * value: what it would be describing is the secret.
90
+ */
91
+ export function holdsLiteralGoogleServiceAccountKey(doc) {
92
+ for (const { block } of googleServiceAccountBlocks(doc)) {
93
+ const { credentials } = block;
94
+ // Not a string is not a key either; the auth type's own schema refuses it.
95
+ if (typeof credentials === 'string' && !ONE_VARIABLE_REFERENCE.test(credentials))
96
+ return true;
97
+ }
98
+ return false;
99
+ }
100
+ /**
101
+ * Every `google_service_account` auth block in `doc`, at any depth, with the
102
+ * object it sits in and the key it sits under, in document order.
103
+ *
104
+ * The walk keeps its own stack, so a deeply nested document cannot overflow
105
+ * the call stack, and it does not re-enter an object it has seen: a YAML
106
+ * anchor aliased inside itself parses to a cyclic object. A block is yielded
107
+ * once per place it appears, since an aliased one sits in several.
108
+ */
109
+ function* googleServiceAccountBlocks(doc) {
110
+ const seen = new WeakSet();
111
+ const pending = [{ node: doc }];
112
+ for (let next = pending.pop(); next; next = pending.pop()) {
113
+ const { node, parent, key } = next;
114
+ if (!node || typeof node !== 'object')
115
+ continue;
116
+ if (isGoogleServiceAccountAuth(node)) {
117
+ yield { block: node, parent, key };
118
+ continue;
119
+ }
120
+ if (seen.has(node))
121
+ continue;
122
+ seen.add(node);
123
+ // Pushed in reverse, so blocks come out in document order.
124
+ if (Array.isArray(node)) {
125
+ for (let i = node.length - 1; i >= 0; i--)
126
+ pending.push({ node: node[i] });
127
+ }
128
+ else {
129
+ const obj = node;
130
+ const keys = Object.keys(obj);
131
+ for (let i = keys.length - 1; i >= 0; i--)
132
+ pending.push({ node: obj[keys[i]], parent: obj, key: keys[i] });
133
+ }
134
+ }
135
+ }
136
+ /** Why a block under `parent[key]` is acted on by nothing, or null when it is. */
137
+ function unservedPlacement(parent, key, served) {
138
+ if (!parent || key !== 'auth' || typeof parent.call_template_type !== 'string') {
139
+ return "somewhere that is not a call template's `auth`";
140
+ }
141
+ const type = parent.call_template_type.toLowerCase().trim();
142
+ if (type !== SERVED_CALL_TEMPLATE_TYPE) {
143
+ return NAMEABLE_CALL_TEMPLATE_TYPES.includes(type) ? `a \`${type}\` call template` : 'a call template that is not an `http` one';
144
+ }
145
+ return served.has(parent) ? null : 'an `http` call template that no tool is called through';
146
+ }
147
+ // Register the auth type on module load, so a `.tool` naming it validates
148
+ // wherever UTCP parses a call template: the inline manual route, the preview,
149
+ // and the client re-validating a template after substituting its variables.
150
+ // UTCP's registry is process-wide and the type carries no state, so one
151
+ // registration serves every knowledge base. Idempotent (safe under hot-reload).
152
+ AuthSerializer.registerAuth(GOOGLE_SERVICE_ACCOUNT_AUTH_TYPE, new GoogleServiceAccountAuthSerializer(), true);
153
+ //# sourceMappingURL=google-service-account.auth.js.map