agentfootprint 9.64.0 → 9.65.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 (43) hide show
  1. package/dist/adapters/hosting/foundryResponses.js +110 -0
  2. package/dist/adapters/hosting/foundryResponses.js.map +1 -0
  3. package/dist/adapters/hosting/responsesWire.js +322 -0
  4. package/dist/adapters/hosting/responsesWire.js.map +1 -0
  5. package/dist/esm/adapters/hosting/foundryResponses.d.ts +89 -0
  6. package/dist/esm/adapters/hosting/foundryResponses.js +106 -0
  7. package/dist/esm/adapters/hosting/foundryResponses.js.map +1 -0
  8. package/dist/esm/adapters/hosting/responsesWire.d.ts +75 -0
  9. package/dist/esm/adapters/hosting/responsesWire.js +316 -0
  10. package/dist/esm/adapters/hosting/responsesWire.js.map +1 -0
  11. package/dist/esm/hosting/errors.d.ts +43 -0
  12. package/dist/esm/hosting/errors.js +52 -0
  13. package/dist/esm/hosting/errors.js.map +1 -1
  14. package/dist/esm/hosting/httpHost.d.ts +133 -4
  15. package/dist/esm/hosting/httpHost.js +120 -22
  16. package/dist/esm/hosting/httpHost.js.map +1 -1
  17. package/dist/esm/hosting/index.d.ts +2 -2
  18. package/dist/esm/hosting/index.js +1 -1
  19. package/dist/esm/hosting/index.js.map +1 -1
  20. package/dist/esm/hosting-providers.d.ts +17 -0
  21. package/dist/esm/hosting-providers.js +19 -0
  22. package/dist/esm/hosting-providers.js.map +1 -1
  23. package/dist/hosting/errors.js +55 -1
  24. package/dist/hosting/errors.js.map +1 -1
  25. package/dist/hosting/httpHost.js +119 -21
  26. package/dist/hosting/httpHost.js.map +1 -1
  27. package/dist/hosting/index.js +4 -2
  28. package/dist/hosting/index.js.map +1 -1
  29. package/dist/hosting-providers.js +30 -1
  30. package/dist/hosting-providers.js.map +1 -1
  31. package/dist/types/adapters/hosting/foundryResponses.d.ts +90 -0
  32. package/dist/types/adapters/hosting/foundryResponses.d.ts.map +1 -0
  33. package/dist/types/adapters/hosting/responsesWire.d.ts +76 -0
  34. package/dist/types/adapters/hosting/responsesWire.d.ts.map +1 -0
  35. package/dist/types/hosting/errors.d.ts +43 -0
  36. package/dist/types/hosting/errors.d.ts.map +1 -1
  37. package/dist/types/hosting/httpHost.d.ts +133 -4
  38. package/dist/types/hosting/httpHost.d.ts.map +1 -1
  39. package/dist/types/hosting/index.d.ts +2 -2
  40. package/dist/types/hosting/index.d.ts.map +1 -1
  41. package/dist/types/hosting-providers.d.ts +17 -0
  42. package/dist/types/hosting-providers.d.ts.map +1 -1
  43. package/package.json +1 -1
@@ -0,0 +1,110 @@
1
+ "use strict";
2
+ /**
3
+ * adapters/hosting/foundryResponses — an agent behind Microsoft Foundry's
4
+ * hosted-agent contract.
5
+ *
6
+ * ── The split, and why it is here ────────────────────────────────────────────
7
+ * Two different things are needed to serve a Foundry hosted agent, and only one
8
+ * of them is Microsoft's:
9
+ *
10
+ * • The **Responses protocol** — `input` items, a `response` object, the
11
+ * named streaming lifecycle. That is a protocol several runtimes speak, so
12
+ * it lives in `responsesWire.ts` with no vendor in it.
13
+ * • The **hosting contract** — port 8088, `POST /responses`, a `HEAD` probe
14
+ * on the same path, `GET /readiness` answering `{"status":"healthy"}`, and
15
+ * `agent_session_id` as a session alias. That IS this runtime's contract,
16
+ * the same way `/invocations` and `X-Amzn-…` are another's, and it is what
17
+ * this file supplies.
18
+ *
19
+ * So this file is a configuration, not an implementation. Everything hard —
20
+ * draining on close, aborting when the caller hangs up, failing a handler that
21
+ * throws or answers nothing — is `httpHost`'s, shared with every other adapter
22
+ * rather than reimplemented here where the two would drift.
23
+ *
24
+ * ── What it does NOT do ──────────────────────────────────────────────────────
25
+ * **Workflow Visualizer topology is not provided.** The Inspector can invoke
26
+ * this host and render its answer; it does not learn that the answer came from
27
+ * four composed agents, because nothing in the Responses protocol carries that
28
+ * and inventing a channel for it would be claiming a compatibility this has
29
+ * never demonstrated. An agent's internal structure is readable from
30
+ * agentfootprint's own recorders.
31
+ *
32
+ * Everything the protocol does not carry — tool calls, image and file input,
33
+ * function-call output, structured output — is refused by name. See
34
+ * `responsesWire.ts`.
35
+ *
36
+ * This is an **inbound hosting adapter**: it is the door callers arrive at. It
37
+ * is not a model provider, and it has nothing to do with which model the agent
38
+ * calls — for Foundry models, that is `openai()` with the endpoint's base URL.
39
+ *
40
+ * @example An agent behind the Inspector
41
+ * import { foundryResponsesHost, memorySessions, standingAgent } from 'agentfootprint/hosting';
42
+ *
43
+ * const handle = await standingAgent({
44
+ * agent,
45
+ * sessions: memorySessions(),
46
+ * host: foundryResponsesHost(),
47
+ * });
48
+ * process.on('SIGTERM', () => void handle.close());
49
+ */
50
+ Object.defineProperty(exports, "__esModule", { value: true });
51
+ exports.foundryResponsesHost = exports.FOUNDRY_SESSION_FIELDS = exports.FOUNDRY_READINESS_PATH = exports.FOUNDRY_INVOKE_PATH = exports.DEFAULT_FOUNDRY_PORT = void 0;
52
+ const httpHost_js_1 = require("../../hosting/httpHost.js");
53
+ const responsesWire_js_1 = require("./responsesWire.js");
54
+ /** The port a Foundry hosted agent is expected on. */
55
+ exports.DEFAULT_FOUNDRY_PORT = 8088;
56
+ /** The path that takes a turn. */
57
+ exports.FOUNDRY_INVOKE_PATH = '/responses';
58
+ /** The path a Foundry hosted agent answers a readiness probe on. */
59
+ exports.FOUNDRY_READINESS_PATH = '/readiness';
60
+ /**
61
+ * Session aliases this contract accepts, in precedence order.
62
+ *
63
+ * `conversation` is the protocol's own; `agent_session_id` is this runtime's;
64
+ * `session_id` is the spelling several clients send anyway. First one present
65
+ * wins, so a caller using any of the three reaches the same session.
66
+ */
67
+ exports.FOUNDRY_SESSION_FIELDS = [
68
+ 'conversation',
69
+ 'agent_session_id',
70
+ 'session_id',
71
+ ];
72
+ /**
73
+ * One mebibyte: large enough for any resume or job description this door was
74
+ * built to carry, small enough that a hostile caller cannot spend the heap.
75
+ */
76
+ const DEFAULT_MAX_BODY_BYTES = 1_048_576;
77
+ /**
78
+ * An {@link HttpHost} speaking Foundry's hosted-agent contract.
79
+ *
80
+ * @param options - Port, interface, model label and body ceiling.
81
+ */
82
+ function foundryResponsesHost(options = {}) {
83
+ const { server, onUnhandled, model, hostname } = options;
84
+ // `port` and `hostname` name a socket a caller-owned server already bound, so
85
+ // they are passed only when this host is the one binding. Defaulting the port
86
+ // unconditionally would turn "attach to my server" into a refusal.
87
+ const socket = server
88
+ ? { server }
89
+ : {
90
+ port: options.port ?? exports.DEFAULT_FOUNDRY_PORT,
91
+ ...(hostname === undefined ? {} : { hostname }),
92
+ ...(onUnhandled === undefined ? {} : { onUnhandled }),
93
+ };
94
+ return (0, httpHost_js_1.httpHost)({
95
+ name: 'foundryResponsesHost',
96
+ wire: (0, responsesWire_js_1.responsesWire)({
97
+ ...(model === undefined ? {} : { defaultModel: model }),
98
+ sessionFields: exports.FOUNDRY_SESSION_FIELDS,
99
+ health: { status: 'healthy' },
100
+ }),
101
+ invokePath: exports.FOUNDRY_INVOKE_PATH,
102
+ healthPath: exports.FOUNDRY_READINESS_PATH,
103
+ // The Inspector asks whether the door is there before it uses it.
104
+ invokeHeadProbe: true,
105
+ maxBodyBytes: options.maxBodyBytes ?? DEFAULT_MAX_BODY_BYTES,
106
+ ...socket,
107
+ });
108
+ }
109
+ exports.foundryResponsesHost = foundryResponsesHost;
110
+ //# sourceMappingURL=foundryResponses.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"foundryResponses.js","sourceRoot":"","sources":["../../../src/adapters/hosting/foundryResponses.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;;;AAIH,2DAAoE;AACpE,yDAAmD;AAEnD,sDAAsD;AACzC,QAAA,oBAAoB,GAAG,IAAI,CAAC;AAEzC,kCAAkC;AACrB,QAAA,mBAAmB,GAAG,YAAY,CAAC;AAEhD,oEAAoE;AACvD,QAAA,sBAAsB,GAAG,YAAY,CAAC;AAEnD;;;;;;GAMG;AACU,QAAA,sBAAsB,GAAsB;IACvD,cAAc;IACd,kBAAkB;IAClB,YAAY;CACb,CAAC;AAEF;;;GAGG;AACH,MAAM,sBAAsB,GAAG,SAAS,CAAC;AAqBzC;;;;GAIG;AACH,SAAgB,oBAAoB,CAAC,UAAuC,EAAE;IAC5E,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC;IACzD,8EAA8E;IAC9E,8EAA8E;IAC9E,mEAAmE;IACnE,MAAM,MAAM,GAAG,MAAM;QACnB,CAAC,CAAC,EAAE,MAAM,EAAE;QACZ,CAAC,CAAC;YACE,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,4BAAoB;YAC1C,GAAG,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC;YAC/C,GAAG,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC;SACtD,CAAC;IAEN,OAAO,IAAA,sBAAQ,EAAC;QACd,IAAI,EAAE,sBAAsB;QAC5B,IAAI,EAAE,IAAA,gCAAa,EAAC;YAClB,GAAG,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC;YACvD,aAAa,EAAE,8BAAsB;YACrC,MAAM,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE;SAC9B,CAAC;QACF,UAAU,EAAE,2BAAmB;QAC/B,UAAU,EAAE,8BAAsB;QAClC,kEAAkE;QAClE,eAAe,EAAE,IAAI;QACrB,YAAY,EAAE,OAAO,CAAC,YAAY,IAAI,sBAAsB;QAC5D,GAAG,MAAM;KACV,CAAC,CAAC;AACL,CAAC;AA3BD,oDA2BC"}
@@ -0,0 +1,322 @@
1
+ "use strict";
2
+ /**
3
+ * adapters/hosting/responsesWire — the Responses protocol, as an `HttpWire`.
4
+ *
5
+ * ── What this is, and what it deliberately is not ────────────────────────────
6
+ * This file speaks the **Responses** wire protocol: a request carries `input`
7
+ * as text or as message items, a reply is a `response` object with an id and a
8
+ * status, and a stream is a LIFECYCLE of named events that open the response,
9
+ * announce output, carry deltas, and close each part before closing the
10
+ * response itself.
11
+ *
12
+ * That protocol is nobody's product. It is the shape a Responses-speaking
13
+ * client sends and expects, and several hosted runtimes speak it. So it lives
14
+ * here as a dialect on its own, and the runtimes that CONFIGURE it — their
15
+ * paths, their ports, their probe bodies, their session aliases — live in their
16
+ * own files beside this one. Keeping the split means the next runtime that
17
+ * speaks Responses is a configuration rather than a copy, and it means no
18
+ * vendor's spelling ends up in the protocol.
19
+ *
20
+ * ── The subset ───────────────────────────────────────────────────────────────
21
+ * A text turn, streamed or not, with a session. Deliberately not the whole
22
+ * Responses API: tool calls, reasoning items, image/file input, structured
23
+ * output and function-call output are NOT carried, and each is REFUSED by name
24
+ * rather than dropped — a request whose content this dialect cannot represent
25
+ * is answered 400 before a turn is paid for, never answered 200 with the parts
26
+ * it happened to understand.
27
+ *
28
+ * @example A host that speaks Responses on paths of its own choosing
29
+ * httpHost({
30
+ * name: 'myResponsesHost',
31
+ * wire: responsesWire({ defaultModel: 'my-agent' }),
32
+ * invokePath: '/responses',
33
+ * healthPath: '/health',
34
+ * });
35
+ */
36
+ Object.defineProperty(exports, "__esModule", { value: true });
37
+ exports.responsesWire = exports.readResponsesSession = exports.readResponsesInput = exports.PUBLIC_FAILURE_MESSAGE = exports.DEFAULT_SESSION_FIELDS = void 0;
38
+ const node_crypto_1 = require("node:crypto");
39
+ const errors_js_1 = require("../../hosting/errors.js");
40
+ /** Body fields that may carry a session id, in the order they are consulted. */
41
+ exports.DEFAULT_SESSION_FIELDS = ['conversation', 'session_id'];
42
+ /**
43
+ * What a caller is told when a handler THREW.
44
+ *
45
+ * A thrown error's message is the author's note to their own logs — a query, a
46
+ * path, a token in a connection string — and none of it is the caller's. A
47
+ * handler that CHOSE to fail chose its words for the caller, so those travel
48
+ * unchanged. The host tells this dialect which happened; it never guesses from
49
+ * the message.
50
+ */
51
+ exports.PUBLIC_FAILURE_MESSAGE = 'The agent could not complete this request.';
52
+ const DEFAULT_MODEL = 'agentfootprint';
53
+ function isRecord(value) {
54
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
55
+ }
56
+ function nonEmptyString(value) {
57
+ return typeof value === 'string' && value.trim() !== '' ? value : undefined;
58
+ }
59
+ /**
60
+ * The text of one message's content, or a refusal naming what was in it.
61
+ *
62
+ * Every non-text part is refused rather than skipped: a resume with an attached
63
+ * image, silently reduced to the text around it, is a wrong answer delivered
64
+ * confidently.
65
+ */
66
+ function inputTextFromContent(content) {
67
+ if (typeof content === 'string')
68
+ return content;
69
+ if (!Array.isArray(content)) {
70
+ throw new errors_js_1.WireRequestRefusal('unsupported_input', 'message content must be text or an array of input_text parts');
71
+ }
72
+ const text = [];
73
+ for (const part of content) {
74
+ if (!isRecord(part) || part.type !== 'input_text' || typeof part.text !== 'string') {
75
+ const kind = isRecord(part) && typeof part.type === 'string' ? part.type : 'unknown';
76
+ throw new errors_js_1.WireRequestRefusal('unsupported_input', `this dialect carries text input only; '${kind}' input is not supported`);
77
+ }
78
+ text.push(part.text);
79
+ }
80
+ return text.join('');
81
+ }
82
+ /** The turn's text: a bare string, or the user message items that carry it. */
83
+ function readResponsesInput(input) {
84
+ if (typeof input === 'string')
85
+ return input;
86
+ if (input === undefined || input === null) {
87
+ throw new errors_js_1.WireRequestRefusal('invalid_input', 'input is required');
88
+ }
89
+ if (!Array.isArray(input)) {
90
+ throw new errors_js_1.WireRequestRefusal('invalid_input', 'input must be text or an array of Responses message items');
91
+ }
92
+ const messages = [];
93
+ for (const item of input) {
94
+ if (!isRecord(item)) {
95
+ throw new errors_js_1.WireRequestRefusal('invalid_input', 'each input item must be an object');
96
+ }
97
+ // `type` is optional in the Responses grammar for a plain message.
98
+ if (item.type !== undefined && item.type !== 'message') {
99
+ throw new errors_js_1.WireRequestRefusal('unsupported_input', `this dialect carries message items only; '${String(item.type)}' items are not supported`);
100
+ }
101
+ if (item.role !== 'user') {
102
+ throw new errors_js_1.WireRequestRefusal('unsupported_input', `this dialect carries user messages only; role '${String(item.role)}' is not supported`);
103
+ }
104
+ messages.push(inputTextFromContent(item.content));
105
+ }
106
+ return messages.join('\n\n');
107
+ }
108
+ exports.readResponsesInput = readResponsesInput;
109
+ /** The session this request claims, by whichever of its aliases it used. */
110
+ function readResponsesSession(body, fields) {
111
+ for (const field of fields) {
112
+ const value = body[field];
113
+ // `conversation` is an object in the Responses grammar and a bare string in
114
+ // several dialects of it. Both are read, because refusing the spelling a
115
+ // client already sends teaches nothing.
116
+ const found = isRecord(value) ? nonEmptyString(value.id) : nonEmptyString(value);
117
+ if (found !== undefined)
118
+ return found;
119
+ }
120
+ return undefined;
121
+ }
122
+ exports.readResponsesSession = readResponsesSession;
123
+ function identityFor(facts, options) {
124
+ const body = facts?.body ?? {};
125
+ const nonce = (0, node_crypto_1.randomUUID)().replaceAll('-', '');
126
+ return {
127
+ responseId: `resp_${nonce}`,
128
+ messageId: `msg_${nonce}`,
129
+ createdAt: Math.floor(Date.now() / 1000),
130
+ model: nonEmptyString(body.model) ?? options.defaultModel,
131
+ sessionId: readResponsesSession(body, options.sessionFields),
132
+ };
133
+ }
134
+ function outputPart(text) {
135
+ return { type: 'output_text', text, annotations: [], logprobs: [] };
136
+ }
137
+ function outputItem(identity, text, status) {
138
+ return {
139
+ id: identity.messageId,
140
+ type: 'message',
141
+ status,
142
+ role: 'assistant',
143
+ content: status === 'completed' ? [outputPart(text)] : [],
144
+ };
145
+ }
146
+ function responseObject(identity, status, text = '', error = null) {
147
+ return {
148
+ id: identity.responseId,
149
+ object: 'response',
150
+ created_at: identity.createdAt,
151
+ status,
152
+ error,
153
+ incomplete_details: null,
154
+ instructions: null,
155
+ parallel_tool_calls: true,
156
+ model: identity.model,
157
+ output: status === 'completed' ? [outputItem(identity, text, 'completed')] : [],
158
+ conversation: identity.sessionId === undefined ? null : { id: identity.sessionId },
159
+ usage: null,
160
+ };
161
+ }
162
+ /**
163
+ * What a caller may be told about a failure.
164
+ *
165
+ * A refusal this library authored carries a `code` and says what it says. A
166
+ * handler that threw carries none, and its words do not travel.
167
+ */
168
+ function errorBody(message, code, origin) {
169
+ if (origin === 'threw') {
170
+ return { code: 'server_error', message: exports.PUBLIC_FAILURE_MESSAGE, type: 'server_error' };
171
+ }
172
+ return code === undefined
173
+ ? { code: 'server_error', message, type: 'server_error' }
174
+ : { code, message, type: 'invalid_request_error' };
175
+ }
176
+ /**
177
+ * The nine-event lifecycle one streamed response is made of.
178
+ *
179
+ * Holds this response's identity and its sequence counter, which is why it is
180
+ * built per request: two callers sharing a counter would each see a stream that
181
+ * skips numbers, and two sharing an id would see each other's response.
182
+ */
183
+ function responsesFraming(identity) {
184
+ let sequence = 0;
185
+ let outputStarted = false;
186
+ let streamed = '';
187
+ let sawDelta = false;
188
+ const frame = (event, fields) => ({
189
+ event,
190
+ data: { type: event, sequence_number: sequence++, ...fields },
191
+ });
192
+ /**
193
+ * The two frames that must precede any text. Emitted on the first delta
194
+ * rather than at open, because a response that fails before producing a word
195
+ * never opened an output item and should not claim it did.
196
+ */
197
+ const begin = () => {
198
+ if (outputStarted)
199
+ return [];
200
+ outputStarted = true;
201
+ return [
202
+ frame('response.output_item.added', {
203
+ output_index: 0,
204
+ item: outputItem(identity, '', 'in_progress'),
205
+ }),
206
+ frame('response.content_part.added', {
207
+ item_id: identity.messageId,
208
+ output_index: 0,
209
+ content_index: 0,
210
+ part: outputPart(''),
211
+ }),
212
+ ];
213
+ };
214
+ const delta = (text) => {
215
+ if (text === '')
216
+ return [];
217
+ const frames = begin();
218
+ sawDelta = true;
219
+ streamed += text;
220
+ frames.push(frame('response.output_text.delta', {
221
+ item_id: identity.messageId,
222
+ output_index: 0,
223
+ content_index: 0,
224
+ delta: text,
225
+ logprobs: [],
226
+ }));
227
+ return frames;
228
+ };
229
+ return {
230
+ open: () => [
231
+ frame('response.created', { response: responseObject(identity, 'in_progress') }),
232
+ frame('response.in_progress', { response: responseObject(identity, 'in_progress') }),
233
+ ],
234
+ chunk: (text) => delta(text),
235
+ complete: (output) => {
236
+ const frames = [];
237
+ // A handler that streamed nothing still owes the caller its text, and one
238
+ // that streamed a PREFIX of it owes the rest. Anything else — a final
239
+ // output that contradicts what was streamed — is left as it was streamed:
240
+ // frames already on the wire cannot be taken back, and re-sending the
241
+ // whole text would duplicate what the caller already rendered.
242
+ if (!sawDelta)
243
+ frames.push(...delta(output));
244
+ else if (output.startsWith(streamed) && output.length > streamed.length) {
245
+ frames.push(...delta(output.slice(streamed.length)));
246
+ }
247
+ frames.push(...begin());
248
+ // THE COMPLETION IS AUTHORITATIVE — this library's law, and it outranks
249
+ // the protocol's own tidiness. When a handler's chunks were a preview of
250
+ // different final text rather than a prefix of it, the deltas already on
251
+ // the wire cannot be recalled, and this reports the ANSWER rather than
252
+ // the preview. The two then disagree, which is a documented limit of
253
+ // streaming a preview; reporting the preview as the answer would be a
254
+ // wrong answer, which is not.
255
+ const text = output;
256
+ frames.push(frame('response.output_text.done', {
257
+ item_id: identity.messageId,
258
+ output_index: 0,
259
+ content_index: 0,
260
+ text,
261
+ logprobs: [],
262
+ }), frame('response.content_part.done', {
263
+ item_id: identity.messageId,
264
+ output_index: 0,
265
+ content_index: 0,
266
+ part: outputPart(text),
267
+ }), frame('response.output_item.done', {
268
+ output_index: 0,
269
+ item: outputItem(identity, text, 'completed'),
270
+ }), frame('response.completed', { response: responseObject(identity, 'completed', text) }));
271
+ return frames;
272
+ },
273
+ failure: (message, code, origin) => [
274
+ frame('response.failed', {
275
+ response: responseObject(identity, 'failed', '', errorBody(message, code, origin)),
276
+ }),
277
+ ],
278
+ };
279
+ }
280
+ /**
281
+ * An `HttpWire` speaking the Responses protocol.
282
+ *
283
+ * Pair it with `httpHost` and the paths your deployment contract names. The
284
+ * host keeps its own promises — draining, aborting on disconnect, failing a
285
+ * handler that throws or answers nothing — and this supplies only the dialect.
286
+ */
287
+ function responsesWire(options = {}) {
288
+ const resolved = {
289
+ defaultModel: options.defaultModel ?? DEFAULT_MODEL,
290
+ sessionFields: options.sessionFields ?? exports.DEFAULT_SESSION_FIELDS,
291
+ };
292
+ const health = options.health ?? { status: 'ok' };
293
+ return {
294
+ readRequest(facts) {
295
+ const input = readResponsesInput(facts.body.input);
296
+ if (input.trim() === '') {
297
+ throw new errors_js_1.WireRequestRefusal('invalid_input', 'input text must not be empty');
298
+ }
299
+ const sessionId = readResponsesSession(facts.body, resolved.sessionFields);
300
+ return { input, ...(sessionId === undefined ? {} : { sessionId }) };
301
+ },
302
+ // This dialect's callers choose in the body and never touch `Accept`.
303
+ wantsStream: (facts) => facts.body.stream === true,
304
+ stream: (facts) => responsesFraming(identityFor(facts, resolved)),
305
+ health: () => health,
306
+ output: (output, facts) => responseObject(identityFor(facts, resolved), 'completed', output),
307
+ chunk: (text) => ({ type: 'response.output_text.delta', delta: text }),
308
+ failure: (message, code, facts, origin) => {
309
+ // A terminal that ended an ACCEPTED response is reported as a failed
310
+ // response object — that is what the protocol says a response that got
311
+ // as far as running and then stopped looks like. A coded refusal ended
312
+ // the REQUEST before any of that, and gets the error envelope a client
313
+ // reads off a 4xx.
314
+ if (code === undefined) {
315
+ return responseObject(identityFor(facts, resolved), 'failed', '', errorBody(message, undefined, origin));
316
+ }
317
+ return { error: errorBody(message, code, origin) };
318
+ },
319
+ };
320
+ }
321
+ exports.responsesWire = responsesWire;
322
+ //# 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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"}
@@ -0,0 +1,89 @@
1
+ /**
2
+ * adapters/hosting/foundryResponses — an agent behind Microsoft Foundry's
3
+ * hosted-agent contract.
4
+ *
5
+ * ── The split, and why it is here ────────────────────────────────────────────
6
+ * Two different things are needed to serve a Foundry hosted agent, and only one
7
+ * of them is Microsoft's:
8
+ *
9
+ * • The **Responses protocol** — `input` items, a `response` object, the
10
+ * named streaming lifecycle. That is a protocol several runtimes speak, so
11
+ * it lives in `responsesWire.ts` with no vendor in it.
12
+ * • The **hosting contract** — port 8088, `POST /responses`, a `HEAD` probe
13
+ * on the same path, `GET /readiness` answering `{"status":"healthy"}`, and
14
+ * `agent_session_id` as a session alias. That IS this runtime's contract,
15
+ * the same way `/invocations` and `X-Amzn-…` are another's, and it is what
16
+ * this file supplies.
17
+ *
18
+ * So this file is a configuration, not an implementation. Everything hard —
19
+ * draining on close, aborting when the caller hangs up, failing a handler that
20
+ * throws or answers nothing — is `httpHost`'s, shared with every other adapter
21
+ * rather than reimplemented here where the two would drift.
22
+ *
23
+ * ── What it does NOT do ──────────────────────────────────────────────────────
24
+ * **Workflow Visualizer topology is not provided.** The Inspector can invoke
25
+ * this host and render its answer; it does not learn that the answer came from
26
+ * four composed agents, because nothing in the Responses protocol carries that
27
+ * and inventing a channel for it would be claiming a compatibility this has
28
+ * never demonstrated. An agent's internal structure is readable from
29
+ * agentfootprint's own recorders.
30
+ *
31
+ * Everything the protocol does not carry — tool calls, image and file input,
32
+ * function-call output, structured output — is refused by name. See
33
+ * `responsesWire.ts`.
34
+ *
35
+ * This is an **inbound hosting adapter**: it is the door callers arrive at. It
36
+ * is not a model provider, and it has nothing to do with which model the agent
37
+ * calls — for Foundry models, that is `openai()` with the endpoint's base URL.
38
+ *
39
+ * @example An agent behind the Inspector
40
+ * import { foundryResponsesHost, memorySessions, standingAgent } from 'agentfootprint/hosting';
41
+ *
42
+ * const handle = await standingAgent({
43
+ * agent,
44
+ * sessions: memorySessions(),
45
+ * host: foundryResponsesHost(),
46
+ * });
47
+ * process.on('SIGTERM', () => void handle.close());
48
+ */
49
+ /// <reference types="node" />
50
+ import type { IncomingMessage, Server, ServerResponse } from 'node:http';
51
+ import { type HttpHost } from '../../hosting/httpHost.js';
52
+ /** The port a Foundry hosted agent is expected on. */
53
+ export declare const DEFAULT_FOUNDRY_PORT = 8088;
54
+ /** The path that takes a turn. */
55
+ export declare const FOUNDRY_INVOKE_PATH = "/responses";
56
+ /** The path a Foundry hosted agent answers a readiness probe on. */
57
+ export declare const FOUNDRY_READINESS_PATH = "/readiness";
58
+ /**
59
+ * Session aliases this contract accepts, in precedence order.
60
+ *
61
+ * `conversation` is the protocol's own; `agent_session_id` is this runtime's;
62
+ * `session_id` is the spelling several clients send anyway. First one present
63
+ * wins, so a caller using any of the three reaches the same session.
64
+ */
65
+ export declare const FOUNDRY_SESSION_FIELDS: readonly string[];
66
+ export interface FoundryResponsesHostOptions {
67
+ /** Port to bind. Default {@link DEFAULT_FOUNDRY_PORT}. Pass `0` for an ephemeral test port. */
68
+ readonly port?: number;
69
+ /** Interface to bind. Default `'0.0.0.0'` — a container's door has to be reachable from outside it. */
70
+ readonly hostname?: string;
71
+ /** Model label echoed in `response` objects when the request names none. */
72
+ readonly model?: string;
73
+ /** Ceiling on a request body. Default one mebibyte. */
74
+ readonly maxBodyBytes?: number;
75
+ /**
76
+ * A `node:http` server you own, to ATTACH these routes to rather than binding
77
+ * one. Refused together with `port`/`hostname`, which would name a socket
78
+ * this host does not bind.
79
+ */
80
+ readonly server?: Server;
81
+ /** Answer a path this host does not own. Private-server mode only. */
82
+ readonly onUnhandled?: (req: IncomingMessage, res: ServerResponse) => void;
83
+ }
84
+ /**
85
+ * An {@link HttpHost} speaking Foundry's hosted-agent contract.
86
+ *
87
+ * @param options - Port, interface, model label and body ceiling.
88
+ */
89
+ export declare function foundryResponsesHost(options?: FoundryResponsesHostOptions): HttpHost;
@@ -0,0 +1,106 @@
1
+ /**
2
+ * adapters/hosting/foundryResponses — an agent behind Microsoft Foundry's
3
+ * hosted-agent contract.
4
+ *
5
+ * ── The split, and why it is here ────────────────────────────────────────────
6
+ * Two different things are needed to serve a Foundry hosted agent, and only one
7
+ * of them is Microsoft's:
8
+ *
9
+ * • The **Responses protocol** — `input` items, a `response` object, the
10
+ * named streaming lifecycle. That is a protocol several runtimes speak, so
11
+ * it lives in `responsesWire.ts` with no vendor in it.
12
+ * • The **hosting contract** — port 8088, `POST /responses`, a `HEAD` probe
13
+ * on the same path, `GET /readiness` answering `{"status":"healthy"}`, and
14
+ * `agent_session_id` as a session alias. That IS this runtime's contract,
15
+ * the same way `/invocations` and `X-Amzn-…` are another's, and it is what
16
+ * this file supplies.
17
+ *
18
+ * So this file is a configuration, not an implementation. Everything hard —
19
+ * draining on close, aborting when the caller hangs up, failing a handler that
20
+ * throws or answers nothing — is `httpHost`'s, shared with every other adapter
21
+ * rather than reimplemented here where the two would drift.
22
+ *
23
+ * ── What it does NOT do ──────────────────────────────────────────────────────
24
+ * **Workflow Visualizer topology is not provided.** The Inspector can invoke
25
+ * this host and render its answer; it does not learn that the answer came from
26
+ * four composed agents, because nothing in the Responses protocol carries that
27
+ * and inventing a channel for it would be claiming a compatibility this has
28
+ * never demonstrated. An agent's internal structure is readable from
29
+ * agentfootprint's own recorders.
30
+ *
31
+ * Everything the protocol does not carry — tool calls, image and file input,
32
+ * function-call output, structured output — is refused by name. See
33
+ * `responsesWire.ts`.
34
+ *
35
+ * This is an **inbound hosting adapter**: it is the door callers arrive at. It
36
+ * is not a model provider, and it has nothing to do with which model the agent
37
+ * calls — for Foundry models, that is `openai()` with the endpoint's base URL.
38
+ *
39
+ * @example An agent behind the Inspector
40
+ * import { foundryResponsesHost, memorySessions, standingAgent } from 'agentfootprint/hosting';
41
+ *
42
+ * const handle = await standingAgent({
43
+ * agent,
44
+ * sessions: memorySessions(),
45
+ * host: foundryResponsesHost(),
46
+ * });
47
+ * process.on('SIGTERM', () => void handle.close());
48
+ */
49
+ import { httpHost } from '../../hosting/httpHost.js';
50
+ import { responsesWire } from './responsesWire.js';
51
+ /** The port a Foundry hosted agent is expected on. */
52
+ export const DEFAULT_FOUNDRY_PORT = 8088;
53
+ /** The path that takes a turn. */
54
+ export const FOUNDRY_INVOKE_PATH = '/responses';
55
+ /** The path a Foundry hosted agent answers a readiness probe on. */
56
+ export const FOUNDRY_READINESS_PATH = '/readiness';
57
+ /**
58
+ * Session aliases this contract accepts, in precedence order.
59
+ *
60
+ * `conversation` is the protocol's own; `agent_session_id` is this runtime's;
61
+ * `session_id` is the spelling several clients send anyway. First one present
62
+ * wins, so a caller using any of the three reaches the same session.
63
+ */
64
+ export const FOUNDRY_SESSION_FIELDS = [
65
+ 'conversation',
66
+ 'agent_session_id',
67
+ 'session_id',
68
+ ];
69
+ /**
70
+ * One mebibyte: large enough for any resume or job description this door was
71
+ * built to carry, small enough that a hostile caller cannot spend the heap.
72
+ */
73
+ const DEFAULT_MAX_BODY_BYTES = 1_048_576;
74
+ /**
75
+ * An {@link HttpHost} speaking Foundry's hosted-agent contract.
76
+ *
77
+ * @param options - Port, interface, model label and body ceiling.
78
+ */
79
+ export function foundryResponsesHost(options = {}) {
80
+ const { server, onUnhandled, model, hostname } = options;
81
+ // `port` and `hostname` name a socket a caller-owned server already bound, so
82
+ // they are passed only when this host is the one binding. Defaulting the port
83
+ // unconditionally would turn "attach to my server" into a refusal.
84
+ const socket = server
85
+ ? { server }
86
+ : {
87
+ port: options.port ?? DEFAULT_FOUNDRY_PORT,
88
+ ...(hostname === undefined ? {} : { hostname }),
89
+ ...(onUnhandled === undefined ? {} : { onUnhandled }),
90
+ };
91
+ return httpHost({
92
+ name: 'foundryResponsesHost',
93
+ wire: responsesWire({
94
+ ...(model === undefined ? {} : { defaultModel: model }),
95
+ sessionFields: FOUNDRY_SESSION_FIELDS,
96
+ health: { status: 'healthy' },
97
+ }),
98
+ invokePath: FOUNDRY_INVOKE_PATH,
99
+ healthPath: FOUNDRY_READINESS_PATH,
100
+ // The Inspector asks whether the door is there before it uses it.
101
+ invokeHeadProbe: true,
102
+ maxBodyBytes: options.maxBodyBytes ?? DEFAULT_MAX_BODY_BYTES,
103
+ ...socket,
104
+ });
105
+ }
106
+ //# sourceMappingURL=foundryResponses.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"foundryResponses.js","sourceRoot":"","sources":["../../../../src/adapters/hosting/foundryResponses.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AAIH,OAAO,EAAE,QAAQ,EAAiB,MAAM,2BAA2B,CAAC;AACpE,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAEnD,sDAAsD;AACtD,MAAM,CAAC,MAAM,oBAAoB,GAAG,IAAI,CAAC;AAEzC,kCAAkC;AAClC,MAAM,CAAC,MAAM,mBAAmB,GAAG,YAAY,CAAC;AAEhD,oEAAoE;AACpE,MAAM,CAAC,MAAM,sBAAsB,GAAG,YAAY,CAAC;AAEnD;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAsB;IACvD,cAAc;IACd,kBAAkB;IAClB,YAAY;CACb,CAAC;AAEF;;;GAGG;AACH,MAAM,sBAAsB,GAAG,SAAS,CAAC;AAqBzC;;;;GAIG;AACH,MAAM,UAAU,oBAAoB,CAAC,UAAuC,EAAE;IAC5E,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC;IACzD,8EAA8E;IAC9E,8EAA8E;IAC9E,mEAAmE;IACnE,MAAM,MAAM,GAAG,MAAM;QACnB,CAAC,CAAC,EAAE,MAAM,EAAE;QACZ,CAAC,CAAC;YACE,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,oBAAoB;YAC1C,GAAG,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC;YAC/C,GAAG,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC;SACtD,CAAC;IAEN,OAAO,QAAQ,CAAC;QACd,IAAI,EAAE,sBAAsB;QAC5B,IAAI,EAAE,aAAa,CAAC;YAClB,GAAG,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC;YACvD,aAAa,EAAE,sBAAsB;YACrC,MAAM,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE;SAC9B,CAAC;QACF,UAAU,EAAE,mBAAmB;QAC/B,UAAU,EAAE,sBAAsB;QAClC,kEAAkE;QAClE,eAAe,EAAE,IAAI;QACrB,YAAY,EAAE,OAAO,CAAC,YAAY,IAAI,sBAAsB;QAC5D,GAAG,MAAM;KACV,CAAC,CAAC;AACL,CAAC"}