agentfootprint 9.64.0 → 9.66.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.
- package/dist/adapters/hosting/foundryResponses.js +110 -0
- package/dist/adapters/hosting/foundryResponses.js.map +1 -0
- package/dist/adapters/hosting/responsesWire.js +322 -0
- package/dist/adapters/hosting/responsesWire.js.map +1 -0
- package/dist/adapters/identity/agentcore.js +118 -2
- package/dist/adapters/identity/agentcore.js.map +1 -1
- package/dist/adapters/observability/agentcore.js +61 -1
- package/dist/adapters/observability/agentcore.js.map +1 -1
- package/dist/adapters/observability/otel.js +12 -3
- package/dist/adapters/observability/otel.js.map +1 -1
- package/dist/esm/adapters/hosting/foundryResponses.d.ts +89 -0
- package/dist/esm/adapters/hosting/foundryResponses.js +106 -0
- package/dist/esm/adapters/hosting/foundryResponses.js.map +1 -0
- package/dist/esm/adapters/hosting/responsesWire.d.ts +75 -0
- package/dist/esm/adapters/hosting/responsesWire.js +316 -0
- package/dist/esm/adapters/hosting/responsesWire.js.map +1 -0
- package/dist/esm/adapters/identity/agentcore.d.ts +115 -1
- package/dist/esm/adapters/identity/agentcore.js +117 -2
- package/dist/esm/adapters/identity/agentcore.js.map +1 -1
- package/dist/esm/adapters/observability/agentcore.d.ts +50 -0
- package/dist/esm/adapters/observability/agentcore.js +59 -0
- package/dist/esm/adapters/observability/agentcore.js.map +1 -1
- package/dist/esm/adapters/observability/otel.d.ts +73 -3
- package/dist/esm/adapters/observability/otel.js +12 -3
- package/dist/esm/adapters/observability/otel.js.map +1 -1
- package/dist/esm/hosting/errors.d.ts +43 -0
- package/dist/esm/hosting/errors.js +52 -0
- package/dist/esm/hosting/errors.js.map +1 -1
- package/dist/esm/hosting/httpHost.d.ts +133 -4
- package/dist/esm/hosting/httpHost.js +120 -22
- package/dist/esm/hosting/httpHost.js.map +1 -1
- package/dist/esm/hosting/index.d.ts +2 -2
- package/dist/esm/hosting/index.js +1 -1
- package/dist/esm/hosting/index.js.map +1 -1
- package/dist/esm/hosting-providers.d.ts +17 -0
- package/dist/esm/hosting-providers.js +19 -0
- package/dist/esm/hosting-providers.js.map +1 -1
- package/dist/esm/identity.d.ts +1 -1
- package/dist/esm/identity.js +5 -1
- package/dist/esm/identity.js.map +1 -1
- package/dist/esm/observability-providers.d.ts +1 -1
- package/dist/esm/observability-providers.js +5 -1
- package/dist/esm/observability-providers.js.map +1 -1
- package/dist/hosting/errors.js +55 -1
- package/dist/hosting/errors.js.map +1 -1
- package/dist/hosting/httpHost.js +119 -21
- package/dist/hosting/httpHost.js.map +1 -1
- package/dist/hosting/index.js +4 -2
- package/dist/hosting/index.js.map +1 -1
- package/dist/hosting-providers.js +30 -1
- package/dist/hosting-providers.js.map +1 -1
- package/dist/identity.js +5 -1
- package/dist/identity.js.map +1 -1
- package/dist/observability-providers.js +6 -1
- package/dist/observability-providers.js.map +1 -1
- package/dist/types/adapters/hosting/foundryResponses.d.ts +90 -0
- package/dist/types/adapters/hosting/foundryResponses.d.ts.map +1 -0
- package/dist/types/adapters/hosting/responsesWire.d.ts +76 -0
- package/dist/types/adapters/hosting/responsesWire.d.ts.map +1 -0
- package/dist/types/adapters/identity/agentcore.d.ts +115 -1
- package/dist/types/adapters/identity/agentcore.d.ts.map +1 -1
- package/dist/types/adapters/observability/agentcore.d.ts +50 -0
- package/dist/types/adapters/observability/agentcore.d.ts.map +1 -1
- package/dist/types/adapters/observability/otel.d.ts +73 -3
- package/dist/types/adapters/observability/otel.d.ts.map +1 -1
- package/dist/types/hosting/errors.d.ts +43 -0
- package/dist/types/hosting/errors.d.ts.map +1 -1
- package/dist/types/hosting/httpHost.d.ts +133 -4
- package/dist/types/hosting/httpHost.d.ts.map +1 -1
- package/dist/types/hosting/index.d.ts +2 -2
- package/dist/types/hosting/index.d.ts.map +1 -1
- package/dist/types/hosting-providers.d.ts +17 -0
- package/dist/types/hosting-providers.d.ts.map +1 -1
- package/dist/types/identity.d.ts +1 -1
- package/dist/types/identity.d.ts.map +1 -1
- package/dist/types/observability-providers.d.ts +1 -1
- package/dist/types/observability-providers.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* adapters/hosting/responsesWire — the Responses protocol, as an `HttpWire`.
|
|
3
|
+
*
|
|
4
|
+
* ── What this is, and what it deliberately is not ────────────────────────────
|
|
5
|
+
* This file speaks the **Responses** wire protocol: a request carries `input`
|
|
6
|
+
* as text or as message items, a reply is a `response` object with an id and a
|
|
7
|
+
* status, and a stream is a LIFECYCLE of named events that open the response,
|
|
8
|
+
* announce output, carry deltas, and close each part before closing the
|
|
9
|
+
* response itself.
|
|
10
|
+
*
|
|
11
|
+
* That protocol is nobody's product. It is the shape a Responses-speaking
|
|
12
|
+
* client sends and expects, and several hosted runtimes speak it. So it lives
|
|
13
|
+
* here as a dialect on its own, and the runtimes that CONFIGURE it — their
|
|
14
|
+
* paths, their ports, their probe bodies, their session aliases — live in their
|
|
15
|
+
* own files beside this one. Keeping the split means the next runtime that
|
|
16
|
+
* speaks Responses is a configuration rather than a copy, and it means no
|
|
17
|
+
* vendor's spelling ends up in the protocol.
|
|
18
|
+
*
|
|
19
|
+
* ── The subset ───────────────────────────────────────────────────────────────
|
|
20
|
+
* A text turn, streamed or not, with a session. Deliberately not the whole
|
|
21
|
+
* Responses API: tool calls, reasoning items, image/file input, structured
|
|
22
|
+
* output and function-call output are NOT carried, and each is REFUSED by name
|
|
23
|
+
* rather than dropped — a request whose content this dialect cannot represent
|
|
24
|
+
* is answered 400 before a turn is paid for, never answered 200 with the parts
|
|
25
|
+
* it happened to understand.
|
|
26
|
+
*
|
|
27
|
+
* @example A host that speaks Responses on paths of its own choosing
|
|
28
|
+
* httpHost({
|
|
29
|
+
* name: 'myResponsesHost',
|
|
30
|
+
* wire: responsesWire({ defaultModel: 'my-agent' }),
|
|
31
|
+
* invokePath: '/responses',
|
|
32
|
+
* healthPath: '/health',
|
|
33
|
+
* });
|
|
34
|
+
*/
|
|
35
|
+
import { randomUUID } from 'node:crypto';
|
|
36
|
+
import { WireRequestRefusal } from '../../hosting/errors.js';
|
|
37
|
+
/** Body fields that may carry a session id, in the order they are consulted. */
|
|
38
|
+
export const DEFAULT_SESSION_FIELDS = ['conversation', 'session_id'];
|
|
39
|
+
/**
|
|
40
|
+
* What a caller is told when a handler THREW.
|
|
41
|
+
*
|
|
42
|
+
* A thrown error's message is the author's note to their own logs — a query, a
|
|
43
|
+
* path, a token in a connection string — and none of it is the caller's. A
|
|
44
|
+
* handler that CHOSE to fail chose its words for the caller, so those travel
|
|
45
|
+
* unchanged. The host tells this dialect which happened; it never guesses from
|
|
46
|
+
* the message.
|
|
47
|
+
*/
|
|
48
|
+
export const PUBLIC_FAILURE_MESSAGE = 'The agent could not complete this request.';
|
|
49
|
+
const DEFAULT_MODEL = 'agentfootprint';
|
|
50
|
+
function isRecord(value) {
|
|
51
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
52
|
+
}
|
|
53
|
+
function nonEmptyString(value) {
|
|
54
|
+
return typeof value === 'string' && value.trim() !== '' ? value : undefined;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* The text of one message's content, or a refusal naming what was in it.
|
|
58
|
+
*
|
|
59
|
+
* Every non-text part is refused rather than skipped: a resume with an attached
|
|
60
|
+
* image, silently reduced to the text around it, is a wrong answer delivered
|
|
61
|
+
* confidently.
|
|
62
|
+
*/
|
|
63
|
+
function inputTextFromContent(content) {
|
|
64
|
+
if (typeof content === 'string')
|
|
65
|
+
return content;
|
|
66
|
+
if (!Array.isArray(content)) {
|
|
67
|
+
throw new WireRequestRefusal('unsupported_input', 'message content must be text or an array of input_text parts');
|
|
68
|
+
}
|
|
69
|
+
const text = [];
|
|
70
|
+
for (const part of content) {
|
|
71
|
+
if (!isRecord(part) || part.type !== 'input_text' || typeof part.text !== 'string') {
|
|
72
|
+
const kind = isRecord(part) && typeof part.type === 'string' ? part.type : 'unknown';
|
|
73
|
+
throw new WireRequestRefusal('unsupported_input', `this dialect carries text input only; '${kind}' input is not supported`);
|
|
74
|
+
}
|
|
75
|
+
text.push(part.text);
|
|
76
|
+
}
|
|
77
|
+
return text.join('');
|
|
78
|
+
}
|
|
79
|
+
/** The turn's text: a bare string, or the user message items that carry it. */
|
|
80
|
+
export function readResponsesInput(input) {
|
|
81
|
+
if (typeof input === 'string')
|
|
82
|
+
return input;
|
|
83
|
+
if (input === undefined || input === null) {
|
|
84
|
+
throw new WireRequestRefusal('invalid_input', 'input is required');
|
|
85
|
+
}
|
|
86
|
+
if (!Array.isArray(input)) {
|
|
87
|
+
throw new WireRequestRefusal('invalid_input', 'input must be text or an array of Responses message items');
|
|
88
|
+
}
|
|
89
|
+
const messages = [];
|
|
90
|
+
for (const item of input) {
|
|
91
|
+
if (!isRecord(item)) {
|
|
92
|
+
throw new WireRequestRefusal('invalid_input', 'each input item must be an object');
|
|
93
|
+
}
|
|
94
|
+
// `type` is optional in the Responses grammar for a plain message.
|
|
95
|
+
if (item.type !== undefined && item.type !== 'message') {
|
|
96
|
+
throw new WireRequestRefusal('unsupported_input', `this dialect carries message items only; '${String(item.type)}' items are not supported`);
|
|
97
|
+
}
|
|
98
|
+
if (item.role !== 'user') {
|
|
99
|
+
throw new WireRequestRefusal('unsupported_input', `this dialect carries user messages only; role '${String(item.role)}' is not supported`);
|
|
100
|
+
}
|
|
101
|
+
messages.push(inputTextFromContent(item.content));
|
|
102
|
+
}
|
|
103
|
+
return messages.join('\n\n');
|
|
104
|
+
}
|
|
105
|
+
/** The session this request claims, by whichever of its aliases it used. */
|
|
106
|
+
export function readResponsesSession(body, fields) {
|
|
107
|
+
for (const field of fields) {
|
|
108
|
+
const value = body[field];
|
|
109
|
+
// `conversation` is an object in the Responses grammar and a bare string in
|
|
110
|
+
// several dialects of it. Both are read, because refusing the spelling a
|
|
111
|
+
// client already sends teaches nothing.
|
|
112
|
+
const found = isRecord(value) ? nonEmptyString(value.id) : nonEmptyString(value);
|
|
113
|
+
if (found !== undefined)
|
|
114
|
+
return found;
|
|
115
|
+
}
|
|
116
|
+
return undefined;
|
|
117
|
+
}
|
|
118
|
+
function identityFor(facts, options) {
|
|
119
|
+
const body = facts?.body ?? {};
|
|
120
|
+
const nonce = randomUUID().replaceAll('-', '');
|
|
121
|
+
return {
|
|
122
|
+
responseId: `resp_${nonce}`,
|
|
123
|
+
messageId: `msg_${nonce}`,
|
|
124
|
+
createdAt: Math.floor(Date.now() / 1000),
|
|
125
|
+
model: nonEmptyString(body.model) ?? options.defaultModel,
|
|
126
|
+
sessionId: readResponsesSession(body, options.sessionFields),
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
function outputPart(text) {
|
|
130
|
+
return { type: 'output_text', text, annotations: [], logprobs: [] };
|
|
131
|
+
}
|
|
132
|
+
function outputItem(identity, text, status) {
|
|
133
|
+
return {
|
|
134
|
+
id: identity.messageId,
|
|
135
|
+
type: 'message',
|
|
136
|
+
status,
|
|
137
|
+
role: 'assistant',
|
|
138
|
+
content: status === 'completed' ? [outputPart(text)] : [],
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
function responseObject(identity, status, text = '', error = null) {
|
|
142
|
+
return {
|
|
143
|
+
id: identity.responseId,
|
|
144
|
+
object: 'response',
|
|
145
|
+
created_at: identity.createdAt,
|
|
146
|
+
status,
|
|
147
|
+
error,
|
|
148
|
+
incomplete_details: null,
|
|
149
|
+
instructions: null,
|
|
150
|
+
parallel_tool_calls: true,
|
|
151
|
+
model: identity.model,
|
|
152
|
+
output: status === 'completed' ? [outputItem(identity, text, 'completed')] : [],
|
|
153
|
+
conversation: identity.sessionId === undefined ? null : { id: identity.sessionId },
|
|
154
|
+
usage: null,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* What a caller may be told about a failure.
|
|
159
|
+
*
|
|
160
|
+
* A refusal this library authored carries a `code` and says what it says. A
|
|
161
|
+
* handler that threw carries none, and its words do not travel.
|
|
162
|
+
*/
|
|
163
|
+
function errorBody(message, code, origin) {
|
|
164
|
+
if (origin === 'threw') {
|
|
165
|
+
return { code: 'server_error', message: PUBLIC_FAILURE_MESSAGE, type: 'server_error' };
|
|
166
|
+
}
|
|
167
|
+
return code === undefined
|
|
168
|
+
? { code: 'server_error', message, type: 'server_error' }
|
|
169
|
+
: { code, message, type: 'invalid_request_error' };
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* The nine-event lifecycle one streamed response is made of.
|
|
173
|
+
*
|
|
174
|
+
* Holds this response's identity and its sequence counter, which is why it is
|
|
175
|
+
* built per request: two callers sharing a counter would each see a stream that
|
|
176
|
+
* skips numbers, and two sharing an id would see each other's response.
|
|
177
|
+
*/
|
|
178
|
+
function responsesFraming(identity) {
|
|
179
|
+
let sequence = 0;
|
|
180
|
+
let outputStarted = false;
|
|
181
|
+
let streamed = '';
|
|
182
|
+
let sawDelta = false;
|
|
183
|
+
const frame = (event, fields) => ({
|
|
184
|
+
event,
|
|
185
|
+
data: { type: event, sequence_number: sequence++, ...fields },
|
|
186
|
+
});
|
|
187
|
+
/**
|
|
188
|
+
* The two frames that must precede any text. Emitted on the first delta
|
|
189
|
+
* rather than at open, because a response that fails before producing a word
|
|
190
|
+
* never opened an output item and should not claim it did.
|
|
191
|
+
*/
|
|
192
|
+
const begin = () => {
|
|
193
|
+
if (outputStarted)
|
|
194
|
+
return [];
|
|
195
|
+
outputStarted = true;
|
|
196
|
+
return [
|
|
197
|
+
frame('response.output_item.added', {
|
|
198
|
+
output_index: 0,
|
|
199
|
+
item: outputItem(identity, '', 'in_progress'),
|
|
200
|
+
}),
|
|
201
|
+
frame('response.content_part.added', {
|
|
202
|
+
item_id: identity.messageId,
|
|
203
|
+
output_index: 0,
|
|
204
|
+
content_index: 0,
|
|
205
|
+
part: outputPart(''),
|
|
206
|
+
}),
|
|
207
|
+
];
|
|
208
|
+
};
|
|
209
|
+
const delta = (text) => {
|
|
210
|
+
if (text === '')
|
|
211
|
+
return [];
|
|
212
|
+
const frames = begin();
|
|
213
|
+
sawDelta = true;
|
|
214
|
+
streamed += text;
|
|
215
|
+
frames.push(frame('response.output_text.delta', {
|
|
216
|
+
item_id: identity.messageId,
|
|
217
|
+
output_index: 0,
|
|
218
|
+
content_index: 0,
|
|
219
|
+
delta: text,
|
|
220
|
+
logprobs: [],
|
|
221
|
+
}));
|
|
222
|
+
return frames;
|
|
223
|
+
};
|
|
224
|
+
return {
|
|
225
|
+
open: () => [
|
|
226
|
+
frame('response.created', { response: responseObject(identity, 'in_progress') }),
|
|
227
|
+
frame('response.in_progress', { response: responseObject(identity, 'in_progress') }),
|
|
228
|
+
],
|
|
229
|
+
chunk: (text) => delta(text),
|
|
230
|
+
complete: (output) => {
|
|
231
|
+
const frames = [];
|
|
232
|
+
// A handler that streamed nothing still owes the caller its text, and one
|
|
233
|
+
// that streamed a PREFIX of it owes the rest. Anything else — a final
|
|
234
|
+
// output that contradicts what was streamed — is left as it was streamed:
|
|
235
|
+
// frames already on the wire cannot be taken back, and re-sending the
|
|
236
|
+
// whole text would duplicate what the caller already rendered.
|
|
237
|
+
if (!sawDelta)
|
|
238
|
+
frames.push(...delta(output));
|
|
239
|
+
else if (output.startsWith(streamed) && output.length > streamed.length) {
|
|
240
|
+
frames.push(...delta(output.slice(streamed.length)));
|
|
241
|
+
}
|
|
242
|
+
frames.push(...begin());
|
|
243
|
+
// THE COMPLETION IS AUTHORITATIVE — this library's law, and it outranks
|
|
244
|
+
// the protocol's own tidiness. When a handler's chunks were a preview of
|
|
245
|
+
// different final text rather than a prefix of it, the deltas already on
|
|
246
|
+
// the wire cannot be recalled, and this reports the ANSWER rather than
|
|
247
|
+
// the preview. The two then disagree, which is a documented limit of
|
|
248
|
+
// streaming a preview; reporting the preview as the answer would be a
|
|
249
|
+
// wrong answer, which is not.
|
|
250
|
+
const text = output;
|
|
251
|
+
frames.push(frame('response.output_text.done', {
|
|
252
|
+
item_id: identity.messageId,
|
|
253
|
+
output_index: 0,
|
|
254
|
+
content_index: 0,
|
|
255
|
+
text,
|
|
256
|
+
logprobs: [],
|
|
257
|
+
}), frame('response.content_part.done', {
|
|
258
|
+
item_id: identity.messageId,
|
|
259
|
+
output_index: 0,
|
|
260
|
+
content_index: 0,
|
|
261
|
+
part: outputPart(text),
|
|
262
|
+
}), frame('response.output_item.done', {
|
|
263
|
+
output_index: 0,
|
|
264
|
+
item: outputItem(identity, text, 'completed'),
|
|
265
|
+
}), frame('response.completed', { response: responseObject(identity, 'completed', text) }));
|
|
266
|
+
return frames;
|
|
267
|
+
},
|
|
268
|
+
failure: (message, code, origin) => [
|
|
269
|
+
frame('response.failed', {
|
|
270
|
+
response: responseObject(identity, 'failed', '', errorBody(message, code, origin)),
|
|
271
|
+
}),
|
|
272
|
+
],
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* An `HttpWire` speaking the Responses protocol.
|
|
277
|
+
*
|
|
278
|
+
* Pair it with `httpHost` and the paths your deployment contract names. The
|
|
279
|
+
* host keeps its own promises — draining, aborting on disconnect, failing a
|
|
280
|
+
* handler that throws or answers nothing — and this supplies only the dialect.
|
|
281
|
+
*/
|
|
282
|
+
export function responsesWire(options = {}) {
|
|
283
|
+
const resolved = {
|
|
284
|
+
defaultModel: options.defaultModel ?? DEFAULT_MODEL,
|
|
285
|
+
sessionFields: options.sessionFields ?? DEFAULT_SESSION_FIELDS,
|
|
286
|
+
};
|
|
287
|
+
const health = options.health ?? { status: 'ok' };
|
|
288
|
+
return {
|
|
289
|
+
readRequest(facts) {
|
|
290
|
+
const input = readResponsesInput(facts.body.input);
|
|
291
|
+
if (input.trim() === '') {
|
|
292
|
+
throw new WireRequestRefusal('invalid_input', 'input text must not be empty');
|
|
293
|
+
}
|
|
294
|
+
const sessionId = readResponsesSession(facts.body, resolved.sessionFields);
|
|
295
|
+
return { input, ...(sessionId === undefined ? {} : { sessionId }) };
|
|
296
|
+
},
|
|
297
|
+
// This dialect's callers choose in the body and never touch `Accept`.
|
|
298
|
+
wantsStream: (facts) => facts.body.stream === true,
|
|
299
|
+
stream: (facts) => responsesFraming(identityFor(facts, resolved)),
|
|
300
|
+
health: () => health,
|
|
301
|
+
output: (output, facts) => responseObject(identityFor(facts, resolved), 'completed', output),
|
|
302
|
+
chunk: (text) => ({ type: 'response.output_text.delta', delta: text }),
|
|
303
|
+
failure: (message, code, facts, origin) => {
|
|
304
|
+
// A terminal that ended an ACCEPTED response is reported as a failed
|
|
305
|
+
// response object — that is what the protocol says a response that got
|
|
306
|
+
// as far as running and then stopped looks like. A coded refusal ended
|
|
307
|
+
// the REQUEST before any of that, and gets the error envelope a client
|
|
308
|
+
// reads off a 4xx.
|
|
309
|
+
if (code === undefined) {
|
|
310
|
+
return responseObject(identityFor(facts, resolved), 'failed', '', errorBody(message, undefined, origin));
|
|
311
|
+
}
|
|
312
|
+
return { error: errorBody(message, code, origin) };
|
|
313
|
+
},
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
//# sourceMappingURL=responsesWire.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"responsesWire.js","sourceRoot":"","sources":["../../../../src/adapters/hosting/responsesWire.ts"],"names":[],"mappings":"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"}
|
|
@@ -103,7 +103,7 @@ export interface AgentCoreIdentityClientLike {
|
|
|
103
103
|
getResourceOauth2Token(input: {
|
|
104
104
|
readonly resourceCredentialProviderName: string;
|
|
105
105
|
readonly scopes: readonly string[];
|
|
106
|
-
readonly oauth2Flow: 'M2M' | 'USER_FEDERATION';
|
|
106
|
+
readonly oauth2Flow: 'M2M' | 'USER_FEDERATION' | 'ON_BEHALF_OF_TOKEN_EXCHANGE';
|
|
107
107
|
readonly forceAuthentication: boolean;
|
|
108
108
|
readonly workloadIdentityToken?: string;
|
|
109
109
|
}): Promise<AgentCoreOauthResponse>;
|
|
@@ -126,6 +126,25 @@ export interface AgentCoreIdentityClientLike {
|
|
|
126
126
|
}): Promise<{
|
|
127
127
|
readonly workloadAccessToken?: string;
|
|
128
128
|
}>;
|
|
129
|
+
/** Optional — required only for services named in `apiKeyServices`. AgentCore
|
|
130
|
+
* keeps API keys in the same vault as OAuth tokens, behind a different
|
|
131
|
+
* operation, so this is a sibling of `getResourceOauth2Token` rather than a
|
|
132
|
+
* mode of it. */
|
|
133
|
+
getResourceApiKey?(input: {
|
|
134
|
+
readonly resourceCredentialProviderName: string;
|
|
135
|
+
/** REQUIRED on the wire — `GetResourceApiKeyRequest` declares it non-optional. */
|
|
136
|
+
readonly workloadIdentityToken: string;
|
|
137
|
+
}): Promise<{
|
|
138
|
+
readonly apiKey?: string;
|
|
139
|
+
}>;
|
|
140
|
+
/** Optional — required only by {@link completeAgentCoreAuthorization}. Tells
|
|
141
|
+
* AgentCore that the person behind `sessionId` finished consenting, which is
|
|
142
|
+
* what releases the token into the vault for the next vend. */
|
|
143
|
+
completeResourceTokenAuth?(input: {
|
|
144
|
+
readonly sessionId: string;
|
|
145
|
+
readonly userToken?: string;
|
|
146
|
+
readonly userId?: string;
|
|
147
|
+
}): Promise<void>;
|
|
129
148
|
}
|
|
130
149
|
export interface AgentCoreIdentityOptions {
|
|
131
150
|
readonly region?: string;
|
|
@@ -165,6 +184,42 @@ export interface AgentCoreIdentityOptions {
|
|
|
165
184
|
readonly principal?: string;
|
|
166
185
|
readonly tenant?: string;
|
|
167
186
|
}) => string | undefined;
|
|
187
|
+
/**
|
|
188
|
+
* How a `mode: 'user'` request gets its token (9.66.0). Default `'consent'`.
|
|
189
|
+
*
|
|
190
|
+
* `'consent'` — AgentCore's `USER_FEDERATION`. If the vault has no grant
|
|
191
|
+
* for this (workload, user), the result is an
|
|
192
|
+
* `authorization-required` with a URL to send the person to.
|
|
193
|
+
* They approve once; later calls return a token directly.
|
|
194
|
+
* `'exchange'` — AgentCore's `ON_BEHALF_OF_TOKEN_EXCHANGE`. The person's
|
|
195
|
+
* own login is TRADED for a scoped downstream token, with no
|
|
196
|
+
* consent screen at any point.
|
|
197
|
+
*
|
|
198
|
+
* `'exchange'` is not simply the nicer one. It works only where the
|
|
199
|
+
* downstream provider was configured for it (an on-behalf-of exchange grant
|
|
200
|
+
* on the credential provider) and where trading the user's session for
|
|
201
|
+
* downstream access is a decision your organisation has already made — the
|
|
202
|
+
* consent screen is what asks the person, and this flow is the deployment
|
|
203
|
+
* saying it does not need to. Choose it deliberately.
|
|
204
|
+
*
|
|
205
|
+
* `mode: 'machine'` is unaffected: M2M has no user to act for.
|
|
206
|
+
*/
|
|
207
|
+
readonly userFlow?: 'consent' | 'exchange';
|
|
208
|
+
/**
|
|
209
|
+
* Services whose credential is an API KEY, not an OAuth token (9.66.0).
|
|
210
|
+
*
|
|
211
|
+
* AgentCore's vault holds both kinds behind two different operations, and the
|
|
212
|
+
* provider NAME alone does not say which — so a request for a service named
|
|
213
|
+
* here is vended with `GetResourceApiKey` and comes back as an
|
|
214
|
+
* {@link apiKey} credential. Everything else takes the OAuth path.
|
|
215
|
+
*
|
|
216
|
+
* There is no auto-detection on purpose: guessing would mean calling one
|
|
217
|
+
* operation, catching a failure, and retrying with the other, which turns a
|
|
218
|
+
* configuration mistake into two round-trips and an ambiguous error.
|
|
219
|
+
*/
|
|
220
|
+
readonly apiKeyServices?: readonly string[];
|
|
221
|
+
/** Header an API-key credential is sent in. Default `'x-api-key'`. */
|
|
222
|
+
readonly apiKeyHeader?: string;
|
|
168
223
|
/** Stable provider id (default 'agentcore-identity'). */
|
|
169
224
|
readonly id?: string;
|
|
170
225
|
/** Test seam — inject a client implementing {@link AgentCoreIdentityClientLike}.
|
|
@@ -184,6 +239,65 @@ export interface BedrockAgentCoreIdentitySdkModule {
|
|
|
184
239
|
readonly GetResourceOauth2TokenCommand?: new (input: unknown) => unknown;
|
|
185
240
|
readonly GetWorkloadAccessTokenForUserIdCommand?: new (input: unknown) => unknown;
|
|
186
241
|
readonly GetWorkloadAccessTokenForJWTCommand?: new (input: unknown) => unknown;
|
|
242
|
+
readonly GetResourceApiKeyCommand?: new (input: unknown) => unknown;
|
|
243
|
+
readonly CompleteResourceTokenAuthCommand?: new (input: unknown) => unknown;
|
|
187
244
|
}
|
|
188
245
|
/** Build a {@link CredentialProvider} backed by AWS Bedrock AgentCore Identity. */
|
|
189
246
|
export declare function agentCoreIdentity(options?: AgentCoreIdentityOptions): CredentialProvider;
|
|
247
|
+
/** Connection options for {@link completeAgentCoreAuthorization}. */
|
|
248
|
+
export interface CompleteAgentCoreAuthorizationOptions {
|
|
249
|
+
/** The 3LO round-trip this completes — the `sessionId` from the
|
|
250
|
+
* `authorization-required` result the agent returned. */
|
|
251
|
+
readonly sessionId: string;
|
|
252
|
+
/** The person's own IdP-issued JWT, if your callback has it. Preferred: it is
|
|
253
|
+
* the artifact their provider signed. */
|
|
254
|
+
readonly userToken?: string;
|
|
255
|
+
/** The user id you asserted for them, when no JWT is available. Exactly one
|
|
256
|
+
* of `userToken` / `userId` is required. */
|
|
257
|
+
readonly userId?: string;
|
|
258
|
+
readonly region?: string;
|
|
259
|
+
/** Test seam — same client surface the provider uses. */
|
|
260
|
+
readonly _client?: AgentCoreIdentityClientLike;
|
|
261
|
+
/** @internal Test injection — the AWS SDK module. */
|
|
262
|
+
readonly _sdk?: BedrockAgentCoreIdentitySdkModule;
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Tell AgentCore the person finished consenting, so the token lands in the vault.
|
|
266
|
+
*
|
|
267
|
+
* ── Where this runs, and why it is not a provider method ─────────────────────
|
|
268
|
+
* A 3LO consent has three actors and two processes. The agent asks for a
|
|
269
|
+
* credential and gets back `authorization-required` with a URL and a session
|
|
270
|
+
* id; the PERSON opens that URL in their browser and approves; AgentCore then
|
|
271
|
+
* redirects their browser to a callback route **your web app** owns. That route
|
|
272
|
+
* is where this belongs — a different process from the agent run, often a
|
|
273
|
+
* different service — so it takes its own connection options rather than
|
|
274
|
+
* pretending to be a method on a provider that route has never seen.
|
|
275
|
+
*
|
|
276
|
+
* Your route's job before calling this is the part nobody else can do: confirm
|
|
277
|
+
* the browser session really belongs to the user you are about to name. This
|
|
278
|
+
* function is the handshake, not the authentication.
|
|
279
|
+
*
|
|
280
|
+
* ── After it returns ─────────────────────────────────────────────────────────
|
|
281
|
+
* Nothing is handed back — success is an empty acknowledgement. The token now
|
|
282
|
+
* exists in the vault for that (workload, user), and the way to obtain it is to
|
|
283
|
+
* run the agent's request again: the next `getCredential` for that service
|
|
284
|
+
* returns `issued` where the last one returned `authorization-required`. In
|
|
285
|
+
* agentfootprint terms the consent is a PAUSE, and this is what makes the
|
|
286
|
+
* resume succeed.
|
|
287
|
+
*
|
|
288
|
+
* The authorization URL and its session are short-lived (AWS documents ten
|
|
289
|
+
* minutes) — a person who wanders off has to start the consent again, and the
|
|
290
|
+
* refusal you get is AgentCore's, not this adapter's.
|
|
291
|
+
*
|
|
292
|
+
* @example An Express-style callback route
|
|
293
|
+
* app.get('/oauth/callback', async (req, res) => {
|
|
294
|
+
* const user = await requireSignedInUser(req); // yours, and load-bearing
|
|
295
|
+
* await completeAgentCoreAuthorization({
|
|
296
|
+
* sessionId: String(req.query.session),
|
|
297
|
+
* userId: user.id,
|
|
298
|
+
* region: 'us-east-1',
|
|
299
|
+
* });
|
|
300
|
+
* res.send('Approved — you can return to the assistant.');
|
|
301
|
+
* });
|
|
302
|
+
*/
|
|
303
|
+
export declare function completeAgentCoreAuthorization(options: CompleteAgentCoreAuthorizationOptions): Promise<void>;
|
|
@@ -83,7 +83,7 @@
|
|
|
83
83
|
* `getCredential` first runs (or never, if you inject `_client` / `_sdk`).
|
|
84
84
|
*/
|
|
85
85
|
import { lazyRequire } from '../../lib/lazyRequire.js';
|
|
86
|
-
import { bearer } from '../../identity/kinds.js';
|
|
86
|
+
import { apiKey, bearer } from '../../identity/kinds.js';
|
|
87
87
|
/**
|
|
88
88
|
* Re-raise a failed SDK call **without its text** (9.12.0).
|
|
89
89
|
*
|
|
@@ -220,6 +220,20 @@ function createIdentityClient(options) {
|
|
|
220
220
|
async getWorkloadAccessTokenForUserId(input) {
|
|
221
221
|
return ((await send(mod.GetWorkloadAccessTokenForUserIdCommand, 'GetWorkloadAccessTokenForUserIdCommand', input)) ?? {});
|
|
222
222
|
},
|
|
223
|
+
async getResourceApiKey(input) {
|
|
224
|
+
const r = (await send(mod.GetResourceApiKeyCommand, 'GetResourceApiKeyCommand', input));
|
|
225
|
+
return { ...(r?.apiKey !== undefined && { apiKey: r.apiKey }) };
|
|
226
|
+
},
|
|
227
|
+
async completeResourceTokenAuth(input) {
|
|
228
|
+
// `CompleteResourceTokenAuthRequest` is `{ sessionUri, userIdentifier }`,
|
|
229
|
+
// where userIdentifier is a UNION with exactly one member set — the
|
|
230
|
+
// person's own token, or the id the agent asserts for them. Success is an
|
|
231
|
+
// empty 200, so there is nothing to map back.
|
|
232
|
+
await send(mod.CompleteResourceTokenAuthCommand, 'CompleteResourceTokenAuthCommand', {
|
|
233
|
+
sessionUri: input.sessionId,
|
|
234
|
+
userIdentifier: input.userToken !== undefined ? { userToken: input.userToken } : { userId: input.userId },
|
|
235
|
+
});
|
|
236
|
+
},
|
|
223
237
|
async getWorkloadAccessTokenForJWT(input) {
|
|
224
238
|
// `GetWorkloadAccessTokenForJWTRequest` is `{ workloadName, userToken }`,
|
|
225
239
|
// both required, and the response is `{ workloadAccessToken }` — verified
|
|
@@ -307,14 +321,50 @@ export function agentCoreIdentity(options = {}) {
|
|
|
307
321
|
}
|
|
308
322
|
return requireWorkloadAccessToken(await c.getWorkloadAccessTokenForUserId({ workloadName: options.workloadName, userId }), 'GetWorkloadAccessTokenForUserId');
|
|
309
323
|
};
|
|
324
|
+
const userFlow = options.userFlow ?? 'consent';
|
|
325
|
+
const apiKeyServices = new Set(options.apiKeyServices ?? []);
|
|
326
|
+
const apiKeyHeader = options.apiKeyHeader ?? 'x-api-key';
|
|
310
327
|
return {
|
|
311
328
|
id: options.id ?? 'agentcore-identity',
|
|
312
329
|
async getCredential(req) {
|
|
313
330
|
const workloadIdentityToken = await resolveWorkloadToken(req);
|
|
331
|
+
// An API-key service never touches the OAuth path: different operation,
|
|
332
|
+
// different credential kind, and no consent round-trip exists for it.
|
|
333
|
+
if (apiKeyServices.has(req.service)) {
|
|
334
|
+
const c = getClient();
|
|
335
|
+
if (!c.getResourceApiKey) {
|
|
336
|
+
throw new Error(`agentCoreIdentity: '${req.service}' is listed in \`apiKeyServices\` but the ` +
|
|
337
|
+
'injected `_client` has no getResourceApiKey. Implement it, or drop the service ' +
|
|
338
|
+
'from the list to vend it as OAuth.');
|
|
339
|
+
}
|
|
340
|
+
if (!workloadIdentityToken) {
|
|
341
|
+
// Required on `GetResourceApiKeyRequest`, exactly as on the OAuth
|
|
342
|
+
// call — verified against the real SDK's types, not assumed. Sending
|
|
343
|
+
// without one buys an opaque ValidationException; this names the two
|
|
344
|
+
// ways to supply it instead.
|
|
345
|
+
throw new Error(`agentCoreIdentity: GetResourceApiKey for '${req.service}' requires a workload ` +
|
|
346
|
+
'identity token, and none was available for this request.\n' +
|
|
347
|
+
' Inside AgentCore Runtime the container is given one — pass it as ' +
|
|
348
|
+
'`workloadIdentityToken`.\n' +
|
|
349
|
+
' Elsewhere, configure `workloadName` so a per-user token is resolved first.');
|
|
350
|
+
}
|
|
351
|
+
const keyRes = await c.getResourceApiKey({
|
|
352
|
+
resourceCredentialProviderName: req.service,
|
|
353
|
+
workloadIdentityToken,
|
|
354
|
+
});
|
|
355
|
+
if (!keyRes.apiKey) {
|
|
356
|
+
throw new Error(`agentCoreIdentity: GetResourceApiKey for '${req.service}' returned no key.`);
|
|
357
|
+
}
|
|
358
|
+
return { status: 'issued', credential: apiKey(keyRes.apiKey, apiKeyHeader) };
|
|
359
|
+
}
|
|
314
360
|
const res = await getClient().getResourceOauth2Token({
|
|
315
361
|
resourceCredentialProviderName: req.service,
|
|
316
362
|
scopes: req.scopes ?? [],
|
|
317
|
-
oauth2Flow: req.mode === 'user'
|
|
363
|
+
oauth2Flow: req.mode === 'user'
|
|
364
|
+
? userFlow === 'exchange'
|
|
365
|
+
? 'ON_BEHALF_OF_TOKEN_EXCHANGE'
|
|
366
|
+
: 'USER_FEDERATION'
|
|
367
|
+
: 'M2M',
|
|
318
368
|
forceAuthentication: req.forceReauth ?? false,
|
|
319
369
|
...(workloadIdentityToken && { workloadIdentityToken }),
|
|
320
370
|
});
|
|
@@ -338,4 +388,69 @@ export function agentCoreIdentity(options = {}) {
|
|
|
338
388
|
},
|
|
339
389
|
};
|
|
340
390
|
}
|
|
391
|
+
/**
|
|
392
|
+
* Tell AgentCore the person finished consenting, so the token lands in the vault.
|
|
393
|
+
*
|
|
394
|
+
* ── Where this runs, and why it is not a provider method ─────────────────────
|
|
395
|
+
* A 3LO consent has three actors and two processes. The agent asks for a
|
|
396
|
+
* credential and gets back `authorization-required` with a URL and a session
|
|
397
|
+
* id; the PERSON opens that URL in their browser and approves; AgentCore then
|
|
398
|
+
* redirects their browser to a callback route **your web app** owns. That route
|
|
399
|
+
* is where this belongs — a different process from the agent run, often a
|
|
400
|
+
* different service — so it takes its own connection options rather than
|
|
401
|
+
* pretending to be a method on a provider that route has never seen.
|
|
402
|
+
*
|
|
403
|
+
* Your route's job before calling this is the part nobody else can do: confirm
|
|
404
|
+
* the browser session really belongs to the user you are about to name. This
|
|
405
|
+
* function is the handshake, not the authentication.
|
|
406
|
+
*
|
|
407
|
+
* ── After it returns ─────────────────────────────────────────────────────────
|
|
408
|
+
* Nothing is handed back — success is an empty acknowledgement. The token now
|
|
409
|
+
* exists in the vault for that (workload, user), and the way to obtain it is to
|
|
410
|
+
* run the agent's request again: the next `getCredential` for that service
|
|
411
|
+
* returns `issued` where the last one returned `authorization-required`. In
|
|
412
|
+
* agentfootprint terms the consent is a PAUSE, and this is what makes the
|
|
413
|
+
* resume succeed.
|
|
414
|
+
*
|
|
415
|
+
* The authorization URL and its session are short-lived (AWS documents ten
|
|
416
|
+
* minutes) — a person who wanders off has to start the consent again, and the
|
|
417
|
+
* refusal you get is AgentCore's, not this adapter's.
|
|
418
|
+
*
|
|
419
|
+
* @example An Express-style callback route
|
|
420
|
+
* app.get('/oauth/callback', async (req, res) => {
|
|
421
|
+
* const user = await requireSignedInUser(req); // yours, and load-bearing
|
|
422
|
+
* await completeAgentCoreAuthorization({
|
|
423
|
+
* sessionId: String(req.query.session),
|
|
424
|
+
* userId: user.id,
|
|
425
|
+
* region: 'us-east-1',
|
|
426
|
+
* });
|
|
427
|
+
* res.send('Approved — you can return to the assistant.');
|
|
428
|
+
* });
|
|
429
|
+
*/
|
|
430
|
+
export async function completeAgentCoreAuthorization(options) {
|
|
431
|
+
if (!options.sessionId) {
|
|
432
|
+
throw new Error('completeAgentCoreAuthorization: `sessionId` is required — it is the `sessionId` from ' +
|
|
433
|
+
'the `authorization-required` result that started this consent.');
|
|
434
|
+
}
|
|
435
|
+
if ((options.userToken === undefined) === (options.userId === undefined)) {
|
|
436
|
+
// Both or neither. Naming a person twice is ambiguous; naming them zero
|
|
437
|
+
// times completes a consent for nobody.
|
|
438
|
+
throw new Error('completeAgentCoreAuthorization: pass exactly one of `userToken` (the person’s own JWT, ' +
|
|
439
|
+
'preferred) or `userId` (an id you assert for them).');
|
|
440
|
+
}
|
|
441
|
+
const client = options._client ??
|
|
442
|
+
createIdentityClient({
|
|
443
|
+
...(options.region !== undefined && { region: options.region }),
|
|
444
|
+
...(options._sdk !== undefined && { _sdk: options._sdk }),
|
|
445
|
+
});
|
|
446
|
+
if (!client.completeResourceTokenAuth) {
|
|
447
|
+
throw new Error('completeAgentCoreAuthorization: the injected `_client` has no ' +
|
|
448
|
+
'completeResourceTokenAuth. Implement it, or omit `_client` to use the SDK.');
|
|
449
|
+
}
|
|
450
|
+
await client.completeResourceTokenAuth({
|
|
451
|
+
sessionId: options.sessionId,
|
|
452
|
+
...(options.userToken !== undefined && { userToken: options.userToken }),
|
|
453
|
+
...(options.userId !== undefined && { userId: options.userId }),
|
|
454
|
+
});
|
|
455
|
+
}
|
|
341
456
|
//# sourceMappingURL=agentcore.js.map
|