agentfootprint 9.26.0 → 9.28.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 (115) hide show
  1. package/dist/adapters/google/aiPlatform.js +438 -0
  2. package/dist/adapters/google/aiPlatform.js.map +1 -0
  3. package/dist/adapters/hosting/googleAgentEngine.js +372 -0
  4. package/dist/adapters/hosting/googleAgentEngine.js.map +1 -0
  5. package/dist/adapters/identity/google.js +275 -0
  6. package/dist/adapters/identity/google.js.map +1 -0
  7. package/dist/adapters/memory/agentcore.js +19 -0
  8. package/dist/adapters/memory/agentcore.js.map +1 -1
  9. package/dist/adapters/memory/memoryBank.js +823 -0
  10. package/dist/adapters/memory/memoryBank.js.map +1 -0
  11. package/dist/core/agent/stages/routeTurn.js +13 -1
  12. package/dist/core/agent/stages/routeTurn.js.map +1 -1
  13. package/dist/esm/adapters/google/aiPlatform.d.ts +453 -0
  14. package/dist/esm/adapters/google/aiPlatform.js +424 -0
  15. package/dist/esm/adapters/google/aiPlatform.js.map +1 -0
  16. package/dist/esm/adapters/hosting/googleAgentEngine.d.ts +156 -0
  17. package/dist/esm/adapters/hosting/googleAgentEngine.js +368 -0
  18. package/dist/esm/adapters/hosting/googleAgentEngine.js.map +1 -0
  19. package/dist/esm/adapters/identity/google.d.ts +179 -0
  20. package/dist/esm/adapters/identity/google.js +271 -0
  21. package/dist/esm/adapters/identity/google.js.map +1 -0
  22. package/dist/esm/adapters/memory/agentcore.d.ts +19 -0
  23. package/dist/esm/adapters/memory/agentcore.js +19 -0
  24. package/dist/esm/adapters/memory/agentcore.js.map +1 -1
  25. package/dist/esm/adapters/memory/memoryBank.d.ts +390 -0
  26. package/dist/esm/adapters/memory/memoryBank.js +817 -0
  27. package/dist/esm/adapters/memory/memoryBank.js.map +1 -0
  28. package/dist/esm/core/agent/stages/routeTurn.js +13 -1
  29. package/dist/esm/core/agent/stages/routeTurn.js.map +1 -1
  30. package/dist/esm/events/payloads.d.ts +23 -0
  31. package/dist/esm/hosting-providers.d.ts +7 -0
  32. package/dist/esm/hosting-providers.js +6 -0
  33. package/dist/esm/hosting-providers.js.map +1 -1
  34. package/dist/esm/identity.d.ts +1 -0
  35. package/dist/esm/identity.js +5 -0
  36. package/dist/esm/identity.js.map +1 -1
  37. package/dist/esm/lib/injection-engine/buildInjectionEngineSubflow.js +9 -0
  38. package/dist/esm/lib/injection-engine/buildInjectionEngineSubflow.js.map +1 -1
  39. package/dist/esm/lib/injection-engine/routingPolicy.d.ts +8 -0
  40. package/dist/esm/lib/injection-engine/routingPolicy.js.map +1 -1
  41. package/dist/esm/lib/injection-engine/skillGraph.d.ts +11 -2
  42. package/dist/esm/lib/injection-engine/skillGraph.js +25 -1
  43. package/dist/esm/lib/injection-engine/skillGraph.js.map +1 -1
  44. package/dist/esm/lib/injection-engine/skillIntent.d.ts +13 -5
  45. package/dist/esm/lib/injection-engine/skillIntent.js +12 -2
  46. package/dist/esm/lib/injection-engine/skillIntent.js.map +1 -1
  47. package/dist/esm/lib/injection-engine/skillMatch.d.ts +40 -0
  48. package/dist/esm/lib/injection-engine/skillMatch.js +60 -0
  49. package/dist/esm/lib/injection-engine/skillMatch.js.map +1 -1
  50. package/dist/esm/memory-providers.d.ts +1 -0
  51. package/dist/esm/memory-providers.js +7 -0
  52. package/dist/esm/memory-providers.js.map +1 -1
  53. package/dist/esm/recorders/observability/AgentThinkingTraceRecorder.d.ts +4 -0
  54. package/dist/esm/recorders/observability/AgentThinkingTraceRecorder.js +4 -1
  55. package/dist/esm/recorders/observability/AgentThinkingTraceRecorder.js.map +1 -1
  56. package/dist/esm/recorders/observability/commentary/artifactPhrases.d.ts +50 -0
  57. package/dist/esm/recorders/observability/commentary/artifactPhrases.js +88 -0
  58. package/dist/esm/recorders/observability/commentary/artifactPhrases.js.map +1 -0
  59. package/dist/esm/recorders/observability/commentary/commentaryTemplates.js +233 -9
  60. package/dist/esm/recorders/observability/commentary/commentaryTemplates.js.map +1 -1
  61. package/dist/hosting-providers.js +10 -1
  62. package/dist/hosting-providers.js.map +1 -1
  63. package/dist/identity.js +8 -1
  64. package/dist/identity.js.map +1 -1
  65. package/dist/lib/injection-engine/buildInjectionEngineSubflow.js +9 -0
  66. package/dist/lib/injection-engine/buildInjectionEngineSubflow.js.map +1 -1
  67. package/dist/lib/injection-engine/routingPolicy.js.map +1 -1
  68. package/dist/lib/injection-engine/skillGraph.js +25 -1
  69. package/dist/lib/injection-engine/skillGraph.js.map +1 -1
  70. package/dist/lib/injection-engine/skillIntent.js +12 -2
  71. package/dist/lib/injection-engine/skillIntent.js.map +1 -1
  72. package/dist/lib/injection-engine/skillMatch.js +61 -1
  73. package/dist/lib/injection-engine/skillMatch.js.map +1 -1
  74. package/dist/memory-providers.js +12 -1
  75. package/dist/memory-providers.js.map +1 -1
  76. package/dist/recorders/observability/AgentThinkingTraceRecorder.js +4 -1
  77. package/dist/recorders/observability/AgentThinkingTraceRecorder.js.map +1 -1
  78. package/dist/recorders/observability/commentary/artifactPhrases.js +94 -0
  79. package/dist/recorders/observability/commentary/artifactPhrases.js.map +1 -0
  80. package/dist/recorders/observability/commentary/commentaryTemplates.js +233 -9
  81. package/dist/recorders/observability/commentary/commentaryTemplates.js.map +1 -1
  82. package/dist/types/adapters/google/aiPlatform.d.ts +454 -0
  83. package/dist/types/adapters/google/aiPlatform.d.ts.map +1 -0
  84. package/dist/types/adapters/hosting/googleAgentEngine.d.ts +157 -0
  85. package/dist/types/adapters/hosting/googleAgentEngine.d.ts.map +1 -0
  86. package/dist/types/adapters/identity/google.d.ts +180 -0
  87. package/dist/types/adapters/identity/google.d.ts.map +1 -0
  88. package/dist/types/adapters/memory/agentcore.d.ts +19 -0
  89. package/dist/types/adapters/memory/agentcore.d.ts.map +1 -1
  90. package/dist/types/adapters/memory/memoryBank.d.ts +391 -0
  91. package/dist/types/adapters/memory/memoryBank.d.ts.map +1 -0
  92. package/dist/types/core/agent/stages/routeTurn.d.ts.map +1 -1
  93. package/dist/types/events/payloads.d.ts +23 -0
  94. package/dist/types/events/payloads.d.ts.map +1 -1
  95. package/dist/types/hosting-providers.d.ts +7 -0
  96. package/dist/types/hosting-providers.d.ts.map +1 -1
  97. package/dist/types/identity.d.ts +1 -0
  98. package/dist/types/identity.d.ts.map +1 -1
  99. package/dist/types/lib/injection-engine/buildInjectionEngineSubflow.d.ts.map +1 -1
  100. package/dist/types/lib/injection-engine/routingPolicy.d.ts +8 -0
  101. package/dist/types/lib/injection-engine/routingPolicy.d.ts.map +1 -1
  102. package/dist/types/lib/injection-engine/skillGraph.d.ts +11 -2
  103. package/dist/types/lib/injection-engine/skillGraph.d.ts.map +1 -1
  104. package/dist/types/lib/injection-engine/skillIntent.d.ts +13 -5
  105. package/dist/types/lib/injection-engine/skillIntent.d.ts.map +1 -1
  106. package/dist/types/lib/injection-engine/skillMatch.d.ts +40 -0
  107. package/dist/types/lib/injection-engine/skillMatch.d.ts.map +1 -1
  108. package/dist/types/memory-providers.d.ts +1 -0
  109. package/dist/types/memory-providers.d.ts.map +1 -1
  110. package/dist/types/recorders/observability/AgentThinkingTraceRecorder.d.ts +4 -0
  111. package/dist/types/recorders/observability/AgentThinkingTraceRecorder.d.ts.map +1 -1
  112. package/dist/types/recorders/observability/commentary/artifactPhrases.d.ts +51 -0
  113. package/dist/types/recorders/observability/commentary/artifactPhrases.d.ts.map +1 -0
  114. package/dist/types/recorders/observability/commentary/commentaryTemplates.d.ts.map +1 -1
  115. package/package.json +24 -15
@@ -0,0 +1,368 @@
1
+ /**
2
+ * agentEngineSessions — conversations in Vertex AI's own session service, so a
3
+ * fleet shares them.
4
+ *
5
+ * `memorySessions()` loses everything on restart and says so. `sqliteSessions()`
6
+ * survives a restart on ONE machine and says so. The row above both — *many
7
+ * containers, one conversation* — is where a managed session service belongs,
8
+ * and on this column that service is the `sessions` collection under a
9
+ * reasoning engine.
10
+ *
11
+ * ── The name, said once ─────────────────────────────────────────────────────
12
+ * The product was **Agent Engine**, is now **Agent Runtime**, and the API
13
+ * resource is still spelled `reasoningEngines`. This factory keeps the name it
14
+ * was designed under; where the product name and the API disagree, the API is
15
+ * the one that has not moved.
16
+ *
17
+ * ── The fit, and it is a good one ───────────────────────────────────────────
18
+ * `Session.sessionState` is an arbitrary JSON `Struct`. A `CheckpointEnvelope`
19
+ * is arbitrary JSON. So the envelope goes in whole, under one key, and comes
20
+ * back whole — no event log to fold, no blob encoding to get wrong, no
21
+ * per-turn append. That is a materially better fit than the other column's
22
+ * session store, which had to learn the hard way that an object handed to an
23
+ * event blob comes back as somebody else's `toString()`.
24
+ *
25
+ * ── The four facts that shaped the code, all read off the installed SDK ─────
26
+ * 1. **`sessions.create` takes a caller-supplied `sessionId`.** So our session
27
+ * id IS the resource id and `hydrate` is one `get` by name. No mapping
28
+ * table, no listing to find a conversation.
29
+ * 2. **`create` and `delete` answer a long-running Operation; `get` and
30
+ * `patch` answer the Session.** Every write here therefore waits for the
31
+ * operation to report `done` before it returns — a `persist` that returned
32
+ * early would make the very next `hydrate` a race whose failure mode is
33
+ * "no conversation", which nobody can tell from a new user.
34
+ * 3. **`Session.userId` is required and immutable.** Our port's
35
+ * `persist(sessionId, envelope)` carries no user, so one has to be
36
+ * resolved — see {@link AgentEngineSessionsOptions.userId}. Immutable
37
+ * means the first write decides forever, which is exactly the ownership
38
+ * rule this library already enforces in its own stores; here the service
39
+ * enforces it for us.
40
+ * 4. **`ttl` is input-only with a 24-hour floor**, and `expireTime` always
41
+ * comes back. Sliding expiry is free; an hour-long TTL is not available at
42
+ * any price.
43
+ *
44
+ * ── The laws it inherits rather than re-implements ──────────────────────────
45
+ * `checkEnvelope` runs on the way OUT and on the way IN, so an envelope whose
46
+ * `format` this runtime does not know is refused by name, and a session that
47
+ * is PRESENT but unreadable is refused by name too. Only a session that was
48
+ * never written hydrates as `undefined`. A conversation that exists and cannot
49
+ * be read must never be answered with a fresh start — that failure is
50
+ * indistinguishable, from the outside, from a brand-new user.
51
+ */
52
+ import { checkEnvelope, envelopeOwner, envelopeTranscript } from '../../hosting/envelope.js';
53
+ import { UnreadableEnvelopeError } from '../../hosting/errors.js';
54
+ import { awaitOperation, buildAiPlatformClient, DEFAULT_OPERATION_TIMEOUT_MS, googleSdkFailure, isAlreadyExists, isNotFound, resolveEngine, safeResourceId, } from '../google/aiPlatform.js';
55
+ const ADAPTER = 'agentEngineSessions';
56
+ /**
57
+ * The `sessionState` key the envelope lives under.
58
+ *
59
+ * One key, namespaced, rather than spreading the envelope's own fields across
60
+ * `sessionState`: the struct belongs to whoever owns the reasoning engine, an
61
+ * agent framework is a guest in it, and a guest that scatters `format` and
62
+ * `data` at the top level collides with the next guest. Namespacing also makes
63
+ * the console readable — one entry that says whose it is.
64
+ */
65
+ export const SESSION_STATE_KEY = 'agentfootprint.envelope';
66
+ /** What a conversation that named nobody is stored under. */
67
+ export const DEFAULT_USER_ID = 'agentfootprint-anonymous';
68
+ /**
69
+ * Conversations in Vertex AI's session service — the store that survives a
70
+ * fleet, not just a restart.
71
+ *
72
+ * **Status: contract-shaped and tested; awaiting field use.** Every call is
73
+ * exercised through an injected client and pinned against the really-installed
74
+ * SDK. None of it has yet answered a request from Google in a real project.
75
+ *
76
+ * @example A standing agent whose conversations are shared across instances
77
+ * import { standingAgent, nodeHost } from 'agentfootprint/hosting';
78
+ * import { agentEngineSessions } from 'agentfootprint/hosting';
79
+ *
80
+ * const handle = await standingAgent({
81
+ * agentFactory: () => buildAgent(),
82
+ * host: nodeHost({ port: 8080 }),
83
+ * sessions: agentEngineSessions({
84
+ * project: 'my-project',
85
+ * location: 'us-central1',
86
+ * reasoningEngine: '1234567890',
87
+ * }),
88
+ * });
89
+ */
90
+ export function agentEngineSessions(options) {
91
+ const scope = resolveEngine(ADAPTER, options);
92
+ const client = buildAiPlatformClient(ADAPTER, options, scope);
93
+ const sessions = client.projects.locations.reasoningEngines.sessions;
94
+ const operationTimeoutMs = options.operationTimeoutMs ?? DEFAULT_OPERATION_TIMEOUT_MS;
95
+ const resolveUserId = userIdResolver(options.userId);
96
+ let closed = false;
97
+ const open = (verb) => {
98
+ if (!closed)
99
+ return;
100
+ throw new Error(`[hosting] the ${ADAPTER} store for '${scope.parent}' is closed, so it cannot ${verb}. ` +
101
+ `close() is final by design — reconnecting behind you would hide a shutdown-ordering ` +
102
+ `bug rather than surface it. Build a new store if you need one after closing this.`);
103
+ };
104
+ const nameOf = (sessionId) => `${scope.parent}/sessions/${safeResourceId(sessionId)}`;
105
+ return {
106
+ parent: scope.parent,
107
+ async hydrate(sessionId) {
108
+ open('hydrate a session');
109
+ let session;
110
+ try {
111
+ session = (await sessions.get({ name: nameOf(sessionId) }))?.data;
112
+ }
113
+ catch (err) {
114
+ // The ONE failure that means "no conversation". Everything else is a
115
+ // failure to READ, which is a different fact and never answered with a
116
+ // fresh start.
117
+ if (isNotFound(err))
118
+ return undefined;
119
+ throw googleSdkFailure(ADAPTER, 'sessions.get', err);
120
+ }
121
+ if (session === undefined)
122
+ return undefined;
123
+ const state = session.sessionState;
124
+ // A session with no state at all was created by something that is not
125
+ // this library — or by us, and never written to. Nothing here ever
126
+ // claimed to be a conversation, so it is an absence.
127
+ if (state === null || state === undefined)
128
+ return undefined;
129
+ const stored = state[SESSION_STATE_KEY];
130
+ if (stored === undefined)
131
+ return undefined;
132
+ // Present and not an object: a conversation EXISTS here and this runtime
133
+ // cannot read it. Refused by name, never as `undefined`.
134
+ if (stored === null || typeof stored !== 'object') {
135
+ throw new UnreadableEnvelopeError(stored, sessionId);
136
+ }
137
+ // Validated HERE as well as in the composer, so a refusal points at the
138
+ // store that produced the bytes rather than at whoever read them next.
139
+ return checkEnvelope(stored, sessionId);
140
+ },
141
+ async persist(sessionId, envelope) {
142
+ open('persist a session');
143
+ // Checked on the way IN as well as out: a session this store could not
144
+ // read back is one it has no business writing.
145
+ const checked = checkEnvelope(envelope, sessionId);
146
+ const name = nameOf(sessionId);
147
+ const body = {
148
+ sessionState: { [SESSION_STATE_KEY]: checked },
149
+ ...(options.ttl !== undefined && { ttl: options.ttl }),
150
+ };
151
+ // PATCH first, CREATE on 404 — rather than the other way round.
152
+ //
153
+ // The steady state of a conversation is "it already exists": a session
154
+ // is created once and written on every turn after that. Trying create
155
+ // first would mean one guaranteed-to-fail call per turn for the life of
156
+ // every conversation, and would burn a long-running operation to learn
157
+ // something a patch answers directly.
158
+ try {
159
+ await sessions.patch({
160
+ name,
161
+ // Only the fields we own. Without a mask a patch is a REPLACE, and a
162
+ // replace would drop `userId` — which is immutable, so the service
163
+ // would refuse the write and a conversation would stop persisting.
164
+ updateMask: maskFor(body),
165
+ requestBody: body,
166
+ });
167
+ return;
168
+ }
169
+ catch (err) {
170
+ if (!isNotFound(err))
171
+ throw googleSdkFailure(ADAPTER, 'sessions.patch', err);
172
+ }
173
+ const userId = resolveUserId(sessionId, checked);
174
+ let created;
175
+ try {
176
+ created = await sessions.create({
177
+ parent: scope.parent,
178
+ sessionId: safeResourceId(sessionId),
179
+ requestBody: { ...body, userId },
180
+ });
181
+ }
182
+ catch (err) {
183
+ // Two writers opened the same conversation at once and the other one
184
+ // won. That is not a failure: the session exists now, which is all
185
+ // this call wanted, so the patch below writes our state onto it.
186
+ if (!isAlreadyExists(err))
187
+ throw googleSdkFailure(ADAPTER, 'sessions.create', err);
188
+ try {
189
+ await sessions.patch({ name, updateMask: maskFor(body), requestBody: body });
190
+ }
191
+ catch (patchErr) {
192
+ throw googleSdkFailure(ADAPTER, 'sessions.patch', patchErr);
193
+ }
194
+ return;
195
+ }
196
+ // OUTSIDE the try on purpose: this refusal is already sanitized and
197
+ // already says what to do, and re-wrapping it would replace a precise
198
+ // diagnosis with a generic one.
199
+ await awaitOperation(ADAPTER, sessions.operations, created?.data, `creating session '${sessionId}'`, operationTimeoutMs);
200
+ },
201
+ async listByUser(userId, listOptions) {
202
+ open('list a user’s sessions');
203
+ const limit = Math.max(1, Math.floor(listOptions?.limit ?? DEFAULT_PAGE));
204
+ let page;
205
+ try {
206
+ page = (await sessions.list({
207
+ parent: scope.parent,
208
+ // The service's own filter over its own immutable field, with the
209
+ // caller's id as a quoted literal — see quoteFilterValue.
210
+ filter: `user_id=${quoteFilterValue(userId)}`,
211
+ orderBy: 'update_time desc',
212
+ pageSize: limit,
213
+ ...(listOptions?.cursor !== undefined && { pageToken: listOptions.cursor }),
214
+ }))?.data;
215
+ }
216
+ catch (err) {
217
+ throw googleSdkFailure(ADAPTER, 'sessions.list', err);
218
+ }
219
+ const summaries = (page?.sessions ?? []).map((session) => {
220
+ const sessionId = lastSegment(session.name);
221
+ const stored = session.sessionState?.[SESSION_STATE_KEY];
222
+ // A listing must not fail because ONE row is unreadable — a sidebar
223
+ // that 500s over a corrupt conversation is worse than one that shows
224
+ // it with an honest zero. The transcript op reads the envelope itself
225
+ // and refuses there, which is where a reader can act on it.
226
+ const readable = stored !== null && typeof stored === 'object' ? stored : undefined;
227
+ return {
228
+ sessionId,
229
+ savedAt: toMillis(session.updateTime ?? session.createTime),
230
+ format: formatOf(readable),
231
+ messageCount: readable === undefined ? 0 : envelopeTranscript(readable).length,
232
+ };
233
+ });
234
+ const cursor = page?.nextPageToken;
235
+ return {
236
+ sessions: summaries,
237
+ ...(typeof cursor === 'string' && cursor !== '' && { cursor }),
238
+ };
239
+ },
240
+ async ownerOf(sessionId) {
241
+ open('read a session’s owner');
242
+ try {
243
+ const session = (await sessions.get({ name: nameOf(sessionId) }))?.data;
244
+ const userId = session?.userId;
245
+ // `undefined` for "no such session" AND for a session stored under the
246
+ // anonymous placeholder — the deliberate ambiguity the composer's one
247
+ // not-found rests on. A store that answered those differently would
248
+ // hand a caller an oracle for which session ids are real.
249
+ return typeof userId === 'string' && userId !== '' && userId !== DEFAULT_USER_ID
250
+ ? userId
251
+ : undefined;
252
+ }
253
+ catch (err) {
254
+ if (isNotFound(err))
255
+ return undefined;
256
+ throw googleSdkFailure(ADAPTER, 'sessions.get', err);
257
+ }
258
+ },
259
+ async forget(sessionId) {
260
+ open('forget a session');
261
+ let deleted;
262
+ try {
263
+ deleted = await sessions.delete({ name: nameOf(sessionId) });
264
+ }
265
+ catch (err) {
266
+ // Already gone is the outcome this asked for.
267
+ if (isNotFound(err))
268
+ return;
269
+ throw googleSdkFailure(ADAPTER, 'sessions.delete', err);
270
+ }
271
+ await awaitOperation(ADAPTER, sessions.operations, deleted?.data, `deleting session '${sessionId}'`, operationTimeoutMs);
272
+ },
273
+ close() {
274
+ closed = true;
275
+ },
276
+ };
277
+ }
278
+ // ─── Internals ───────────────────────────────────────────────────────
279
+ /** How many rows one `listByUser` page carries when the caller names no limit. */
280
+ const DEFAULT_PAGE = 50;
281
+ /**
282
+ * A user id as an AIP-160 string literal — **backslash first, then the quote.**
283
+ *
284
+ * The order is the whole point. This grammar honours backslash escapes, so
285
+ * escaping only the quote leaves the escape character itself free to escape our
286
+ * escape: a user id of `\" OR user_id!=` renders as `user_id="\\" OR user_id!=""`,
287
+ * where `\\` is a literal backslash, the quote after it CLOSES the literal, and
288
+ * the rest of the id is filter syntax the service evaluates. The listing then
289
+ * matches every session with a non-empty user id and hands back other people's
290
+ * conversation ids, timestamps and message counts.
291
+ *
292
+ * The benign case matters too: any id merely ENDING in a backslash swallows the
293
+ * closing quote and the call fails with a malformed-filter 400 whose text
294
+ * {@link googleSdkFailure} withholds — a local error delivered as a censored
295
+ * remote one. Escaping the backslash first fixes both.
296
+ *
297
+ * A user id is caller data on every column of this library. It is never
298
+ * concatenated into a query language without passing through here.
299
+ */
300
+ function quoteFilterValue(value) {
301
+ return `"${value.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`;
302
+ }
303
+ /**
304
+ * Resolve the immutable `userId` a session is created under.
305
+ *
306
+ * See {@link AgentEngineSessionsOptions.userId} for why the default reads the
307
+ * envelope rather than inventing anything.
308
+ */
309
+ function userIdResolver(option) {
310
+ if (typeof option === 'function') {
311
+ return (sessionId, envelope) => {
312
+ const resolved = option(sessionId, envelope);
313
+ if (typeof resolved !== 'string' || resolved.trim() === '') {
314
+ throw new TypeError(`${ADAPTER}: the 'userId' resolver returned ${JSON.stringify(resolved)} for session ` +
315
+ `'${sessionId}'. The service requires a non-empty user id on create and will not ` +
316
+ `let it be changed afterwards, so this refuses rather than storing the ` +
317
+ `conversation under a name nobody chose.\n` +
318
+ ` Return '${DEFAULT_USER_ID}' explicitly if an anonymous conversation is what ` +
319
+ `you mean.`);
320
+ }
321
+ return resolved;
322
+ };
323
+ }
324
+ if (typeof option === 'string') {
325
+ if (option.trim() === '') {
326
+ throw new TypeError(`${ADAPTER}: 'userId' was an empty string. The service requires one on create and ` +
327
+ `treats it as immutable. Pass a real id, a resolver function, or leave it unset to ` +
328
+ `derive the owner from the conversation itself.`);
329
+ }
330
+ return () => option;
331
+ }
332
+ // The default: the principal the conversation was signed with, so the
333
+ // service's owner and this library's own ownership index agree.
334
+ return (_sessionId, envelope) => envelopeOwner(envelope) ?? DEFAULT_USER_ID;
335
+ }
336
+ /**
337
+ * The update mask for a patch.
338
+ *
339
+ * A patch with no mask REPLACES the resource, which would clear `userId` — and
340
+ * `userId` is immutable, so the service refuses the write and the conversation
341
+ * silently stops persisting. Naming the fields we own is the whole fix.
342
+ */
343
+ function maskFor(body) {
344
+ const fields = ['sessionState'];
345
+ if (body.ttl !== undefined)
346
+ fields.push('ttl');
347
+ return fields.join(',');
348
+ }
349
+ /** The id out of a resource name, which is everything after the last `/`. */
350
+ function lastSegment(name) {
351
+ if (typeof name !== 'string')
352
+ return '';
353
+ const at = name.lastIndexOf('/');
354
+ return at === -1 ? name : name.slice(at + 1);
355
+ }
356
+ /** An RFC 3339 timestamp as unix milliseconds, or 0 when it is not one. */
357
+ function toMillis(value) {
358
+ if (typeof value !== 'string')
359
+ return 0;
360
+ const parsed = Date.parse(value);
361
+ return Number.isFinite(parsed) ? parsed : 0;
362
+ }
363
+ /** The stored envelope's `format`, without trusting it to be one of ours. */
364
+ function formatOf(stored) {
365
+ const format = stored?.format;
366
+ return typeof format === 'string' ? format : 'unknown';
367
+ }
368
+ //# sourceMappingURL=googleAgentEngine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"googleAgentEngine.js","sourceRoot":"","sources":["../../../../src/adapters/hosting/googleAgentEngine.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,179 @@
1
+ /**
2
+ * googleIdentity — the {@link CredentialProvider} port over Google's own
3
+ * credential machinery (peer-dep `google-auth-library`).
4
+ *
5
+ * import { googleIdentity } from 'agentfootprint/security';
6
+ * const credentials = googleIdentity();
7
+ *
8
+ * ── What it is, and what it deliberately is not ─────────────────────────────
9
+ * This is the **narrow** adapter: it vends *Google* access tokens for *Google*
10
+ * APIs, from whatever credential the environment already has — Application
11
+ * Default Credentials on Cloud Run or GKE, a workload-identity federation
12
+ * config, a service account, optionally impersonating another service account.
13
+ * That is one job and it is done completely.
14
+ *
15
+ * It is **not** a token vault and does not pretend to be one. The other
16
+ * column's identity adapter can vend a *GitHub* token for a *user* because
17
+ * that service runs a vault with per-user OAuth grants behind it. Google's
18
+ * equivalent — the Agent Identity auth manager — is Preview with no Node
19
+ * surface, so `mode: 'user'` here is **refused by name** rather than quietly
20
+ * served with a machine token. A machine token returned where a user token was
21
+ * asked for is the exact silent downgrade the port exists to prevent: the call
22
+ * succeeds, the data comes back, and it was the agent's access rather than the
23
+ * person's. See {@link googleIdentity} for the refusal's wording.
24
+ *
25
+ * ── The one-hour fact, and where it bites ───────────────────────────────────
26
+ * A Google OAuth access token lives about an hour. That is fine wherever the
27
+ * credential is fetched per use — which is how `ctx.credential` works, so the
28
+ * ordinary path is unaffected. It bites in exactly one place, and it is worth
29
+ * naming because it looks like it should work:
30
+ *
31
+ * > **The OpenAI-compatible endpoint trap.** Google publishes an
32
+ * > OpenAI-compatible Gemini endpoint, and `openai({ baseURL, apiKey })` does
33
+ * > reach it. If you fill that `apiKey` with a token from here, it works for
34
+ * > an hour and then every call fails with a 401 — because `apiKey` is a
35
+ * > STRING captured when the provider is constructed, and a long-lived agent
36
+ * > process outlives it. There is no refresh home in the OpenAI provider's
37
+ * > options for a credential that expires. Use the native `gemini()` provider,
38
+ * > which reads ADC through the SDK and refreshes underneath you.
39
+ *
40
+ * `expiresAt` is reported on every issued credential so a caller that caches
41
+ * one can tell. This adapter never caches: {@link CachedGoogleClient} keeps
42
+ * the *client*, and the client's own refresh logic keeps the token fresh.
43
+ *
44
+ * ── Secrets ─────────────────────────────────────────────────────────────────
45
+ * The `sdkFailure` law, same as every other credential-touching adapter here:
46
+ * the library's own message never comes through, because auth libraries echo
47
+ * request detail into failure text and a message thrown from a
48
+ * `CredentialProvider` reaches the LLM as a tool result AND rides
49
+ * `agentfootprint.credential.failed` to every sink. What comes through is the
50
+ * operation that failed and the error's NAME. The original is not attached as
51
+ * `cause` — a cause travels into every serializer that walks own properties,
52
+ * which would undo all of it in one `JSON.stringify`.
53
+ *
54
+ * Pattern: Adapter (GoF) + lazy peer-dep load — `google-auth-library` is
55
+ * required the first time `getCredential` runs, or never if you inject one.
56
+ */
57
+ import type { CredentialProvider } from '../../identity/types.js';
58
+ /** The scope every Google Cloud data-plane API accepts. */
59
+ export declare const CLOUD_PLATFORM_SCOPE = "https://www.googleapis.com/auth/cloud-platform";
60
+ /**
61
+ * The slice of an auth client this adapter calls — a `GoogleAuth`, an
62
+ * `OAuth2Client` and an `Impersonated` all satisfy it, which is why nothing
63
+ * here switches on which one it has.
64
+ */
65
+ export interface GoogleAuthClientLike {
66
+ /** The current access token, refreshed if the library thinks it is stale. */
67
+ getAccessToken(): Promise<{
68
+ token?: string | null;
69
+ } | string | null | undefined>;
70
+ /** Where the library records the expiry it knows about, in unix ms. */
71
+ readonly credentials?: {
72
+ readonly expiry_date?: number | null;
73
+ };
74
+ }
75
+ /** The slice of a `GoogleAuth` this adapter calls. */
76
+ export interface GoogleAuthLike {
77
+ getClient(): Promise<GoogleAuthClientLike>;
78
+ }
79
+ /** The slice of `google-auth-library` this adapter loads. */
80
+ export interface GoogleAuthSdkModule {
81
+ readonly GoogleAuth?: new (options: {
82
+ scopes?: readonly string[];
83
+ }) => GoogleAuthLike;
84
+ readonly Impersonated?: new (options: {
85
+ sourceClient: GoogleAuthClientLike;
86
+ targetPrincipal: string;
87
+ targetScopes: string[];
88
+ delegates?: string[];
89
+ lifetime?: number;
90
+ }) => GoogleAuthClientLike;
91
+ }
92
+ /** Impersonate another service account for the tokens this provider vends. */
93
+ export interface GoogleImpersonation {
94
+ /**
95
+ * The service account to act as, e.g.
96
+ * `'agent-runner@my-project.iam.gserviceaccount.com'`. The credential the
97
+ * environment already has must hold `roles/iam.serviceAccountTokenCreator`
98
+ * on it; without that the exchange fails with a 403 whose text this adapter
99
+ * withholds, so the permission is worth checking before you wonder why.
100
+ */
101
+ readonly targetPrincipal: string;
102
+ /** A delegation chain, when one account cannot impersonate the target directly. */
103
+ readonly delegates?: readonly string[];
104
+ /**
105
+ * How long the impersonated token lives, in seconds. The service's own
106
+ * ceiling is 3600 (one hour) unless the org policy raises it, and asking for
107
+ * more than it allows is refused by Google rather than clamped.
108
+ */
109
+ readonly lifetimeSeconds?: number;
110
+ }
111
+ /** Options for {@link googleIdentity}. */
112
+ export interface GoogleIdentityOptions {
113
+ /**
114
+ * The OAuth scopes to request. Default `[cloud-platform]`, which is what
115
+ * every Google Cloud data-plane API accepts.
116
+ *
117
+ * A request's own `scopes` win when it names any — a tool that knows it only
118
+ * needs read access should say so, and this is where that is honoured.
119
+ */
120
+ readonly scopes?: readonly string[];
121
+ /**
122
+ * Act as another service account. Off by default; see
123
+ * {@link GoogleImpersonation}.
124
+ */
125
+ readonly impersonate?: GoogleImpersonation;
126
+ /**
127
+ * Which downstream services this provider will answer for.
128
+ *
129
+ * Unset — the default — it answers for ANY `service`, because the token it
130
+ * vends is a Google credential and the caller knows better than this adapter
131
+ * which Google API they are about to call.
132
+ *
133
+ * Set it and a request for a service outside the list is refused BY NAME
134
+ * rather than served. That is the useful setting in a deployment where tools
135
+ * declare `needs: [{ credential: 'github' }]` alongside Google ones: without
136
+ * it, this provider would happily hand a Google access token to the tool
137
+ * that wanted a GitHub one, and the failure would surface as a puzzling 401
138
+ * from GitHub rather than as a wiring error here.
139
+ */
140
+ readonly services?: readonly string[];
141
+ /** Stable provider id (default `'google-identity'`). */
142
+ readonly id?: string;
143
+ /**
144
+ * @internal Test seam — a pre-built auth client. Bypasses the SDK entirely,
145
+ * so the suite runs with no package and no credential.
146
+ */
147
+ readonly _client?: GoogleAuthClientLike;
148
+ /** @internal Test seam — the SDK module, to exercise the real construction. */
149
+ readonly _sdk?: GoogleAuthSdkModule;
150
+ }
151
+ /**
152
+ * Vend Google access tokens from whatever credential this environment has.
153
+ *
154
+ * **Status: contract-shaped and tested; awaiting field use.** Every path is
155
+ * exercised through an injected client and the loaded surface is pinned
156
+ * against the really-installed package. None of it has yet answered a real
157
+ * Google token request in a live project.
158
+ *
159
+ * @throws when `mode: 'user'` is requested — Google's per-user token vault has
160
+ * no Node surface, and a machine token returned in its place would be a
161
+ * silent downgrade.
162
+ * @throws when `services` is configured and the request names another one.
163
+ *
164
+ * @example A tool that calls a Google API with the deployment's own identity
165
+ * const agent = Agent.create({ provider, credentials: googleIdentity() })
166
+ * .tool(defineTool({
167
+ * name: 'read_sheet',
168
+ * needs: [{ credential: 'sheets', scopes: ['https://www.googleapis.com/auth/spreadsheets.readonly'] }],
169
+ * execute: async (args, ctx) =>
170
+ * fetch(url, { headers: ctx.credential!.toHeaders() }).then((r) => r.text()),
171
+ * }))
172
+ * .build();
173
+ *
174
+ * @example Acting as a dedicated service account
175
+ * googleIdentity({
176
+ * impersonate: { targetPrincipal: 'agent-runner@my-project.iam.gserviceaccount.com' },
177
+ * });
178
+ */
179
+ export declare function googleIdentity(options?: GoogleIdentityOptions): CredentialProvider;