@cuylabs/agent-foundry-agentserver-responses 4.9.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/index.js ADDED
@@ -0,0 +1,1163 @@
1
+ import {
2
+ AGENTSERVER_RESPONSES_PACKAGE_VERSION,
3
+ FoundryStorageError,
4
+ FoundryStorageNotFoundError,
5
+ FoundryStorageProvider,
6
+ FoundryStorageSettings,
7
+ InMemoryResponseProvider
8
+ } from "./chunk-DGMWFFNQ.js";
9
+ import {
10
+ RESPONSE_ID_HEADER,
11
+ SESSION_ID_HEADER,
12
+ TextResponse,
13
+ applyCreateResponseDefaults,
14
+ createResponseObject,
15
+ createSseEncoderState,
16
+ deriveSessionId,
17
+ encodeKeepAliveComment,
18
+ encodeSseEvent,
19
+ extractPartitionKey,
20
+ extractTextFromItems,
21
+ getInputExpanded,
22
+ isValidId,
23
+ newId,
24
+ newMessageItemId,
25
+ newOutputMessageItemId,
26
+ newResponseId,
27
+ normalizeCreateResponse,
28
+ normalizeResponseInput,
29
+ resolveAgentReference,
30
+ resolveConversationId,
31
+ resolveResponseId,
32
+ resolveSessionId,
33
+ startResponsesSseResponse,
34
+ toOutputItem,
35
+ validateResponseId
36
+ } from "./chunk-24VMS6GY.js";
37
+
38
+ // src/host.ts
39
+ import express from "express";
40
+ import {
41
+ AGENTSERVER_CORE_PACKAGE_VERSION,
42
+ agentServerLoggingMiddleware,
43
+ agentServerRequestHeadersMiddleware,
44
+ buildPlatformServerHeader,
45
+ closeServerWithTimeout,
46
+ configureAgentServerObservability,
47
+ configureExpressAgentServerApp,
48
+ defaultAgentServerLogger,
49
+ errorMiddleware,
50
+ formatError as formatError2,
51
+ logAgentServerStartupConfiguration,
52
+ notFoundMiddleware,
53
+ resolveAgentServerConfig
54
+ } from "@cuylabs/agent-foundry-agentserver-core";
55
+
56
+ // src/hosting/handler-provider.ts
57
+ function makeHandlerProvider(input) {
58
+ if (typeof input === "function") {
59
+ if (input.length === 0) {
60
+ return () => {
61
+ const handler = input();
62
+ return handler;
63
+ };
64
+ }
65
+ return () => ({ handle: input });
66
+ }
67
+ return () => input;
68
+ }
69
+
70
+ // src/hosting/http-utils.ts
71
+ import {
72
+ createErrorBody
73
+ } from "@cuylabs/agent-foundry-agentserver-core";
74
+ function supportsStreamProvider(provider) {
75
+ return typeof provider.appendStreamEvent === "function" && typeof provider.getStreamEvents === "function" && typeof provider.deleteStreamEvents === "function";
76
+ }
77
+ function queryParameters(req) {
78
+ const params = {};
79
+ for (const [key, value] of Object.entries(req.query)) {
80
+ if (typeof value === "string") {
81
+ params[key] = value;
82
+ }
83
+ }
84
+ return params;
85
+ }
86
+ function parseLimit(value) {
87
+ const raw = typeof value === "string" ? Number.parseInt(value, 10) : 20;
88
+ if (!Number.isInteger(raw) || raw < 1) {
89
+ return 20;
90
+ }
91
+ return Math.min(raw, 100);
92
+ }
93
+ function queryString(value) {
94
+ return typeof value === "string" && value ? value : void 0;
95
+ }
96
+ function modeFlagsFromResponse(response) {
97
+ return {
98
+ stream: response.stream === true,
99
+ background: response.background === true,
100
+ store: response.store !== false
101
+ };
102
+ }
103
+ function scopedActiveKey(responseId, isolation) {
104
+ return `${isolation.chatKey ?? ""}|${isolation.userKey ?? ""}|${responseId}`;
105
+ }
106
+ function errorMessage(error) {
107
+ if (error instanceof Error) {
108
+ return error.message;
109
+ }
110
+ return String(error);
111
+ }
112
+ function jsonParseErrorMiddleware(error, req, res, next) {
113
+ if (error instanceof SyntaxError) {
114
+ res.status(400).json(
115
+ createErrorBody("invalid_request", "request body must be valid JSON", {
116
+ type: "invalid_request_error",
117
+ requestId: req.agentServerRequestId
118
+ })
119
+ );
120
+ return;
121
+ }
122
+ next(error);
123
+ }
124
+
125
+ // src/hosting/routes.ts
126
+ import {
127
+ healthzHandler,
128
+ methodNotAllowed,
129
+ readinessHandler
130
+ } from "@cuylabs/agent-foundry-agentserver-core";
131
+
132
+ // src/openapi.ts
133
+ function buildResponsesOpenApiSpec(options = {}) {
134
+ const title = options.appName ?? "Foundry Responses Host";
135
+ return {
136
+ openapi: "3.0.3",
137
+ info: {
138
+ title,
139
+ version: "1.0.0"
140
+ },
141
+ paths: {
142
+ "/responses": {
143
+ post: {
144
+ summary: "Create a response",
145
+ responses: {
146
+ "200": { description: "Response created" }
147
+ }
148
+ }
149
+ },
150
+ "/responses/{response_id}": {
151
+ get: {
152
+ summary: "Get a response",
153
+ responses: {
154
+ "200": { description: "Response snapshot or SSE replay" },
155
+ "404": { description: "Response not found" }
156
+ }
157
+ },
158
+ delete: {
159
+ summary: "Delete a response",
160
+ responses: {
161
+ "200": { description: "Response deleted" },
162
+ "404": { description: "Response not found" }
163
+ }
164
+ }
165
+ },
166
+ "/responses/{response_id}/cancel": {
167
+ post: {
168
+ summary: "Cancel a background response",
169
+ responses: {
170
+ "200": { description: "Response cancelled" },
171
+ "404": { description: "Response not found" }
172
+ }
173
+ }
174
+ },
175
+ "/responses/{response_id}/input_items": {
176
+ get: {
177
+ summary: "List response input items",
178
+ responses: {
179
+ "200": { description: "Input items" },
180
+ "404": { description: "Response not found" }
181
+ }
182
+ }
183
+ },
184
+ "/readiness": {
185
+ get: {
186
+ summary: "Readiness probe",
187
+ responses: { "200": { description: "Healthy" } }
188
+ }
189
+ },
190
+ "/healthz": {
191
+ get: {
192
+ summary: "Liveness probe",
193
+ responses: { "200": { description: "Healthy" } }
194
+ }
195
+ }
196
+ }
197
+ };
198
+ }
199
+
200
+ // src/hosting/routes/cancel-response.ts
201
+ import {
202
+ createErrorBody as createErrorBody3,
203
+ resolveAgentIsolation
204
+ } from "@cuylabs/agent-foundry-agentserver-core";
205
+
206
+ // src/hosting/routes/route-utils.ts
207
+ import { createErrorBody as createErrorBody2 } from "@cuylabs/agent-foundry-agentserver-core";
208
+ function routeResponseId(req) {
209
+ const responseId = req.params.responseId;
210
+ return Array.isArray(responseId) ? responseId[0] ?? "" : responseId ?? "";
211
+ }
212
+ function validateRouteResponseId(responseId, res) {
213
+ const validation = isValidId(responseId, ["caresp"]);
214
+ if (validation.valid) {
215
+ return true;
216
+ }
217
+ res.status(400).json(
218
+ createErrorBody2(
219
+ "invalid_parameters",
220
+ validation.error ?? "invalid response_id"
221
+ )
222
+ );
223
+ return false;
224
+ }
225
+
226
+ // src/hosting/routes/cancel-response.ts
227
+ function cancelResponseHandler(options) {
228
+ return async (req, res) => {
229
+ const responseId = routeResponseId(req);
230
+ if (!validateRouteResponseId(responseId, res)) {
231
+ return;
232
+ }
233
+ const isolation = resolveAgentIsolation((name) => req.header(name));
234
+ const activeExecution = options.active.get(
235
+ scopedActiveKey(responseId, isolation)
236
+ );
237
+ if (!activeExecution) {
238
+ res.status(404).json(createErrorBody3("not_found", "Response not found"));
239
+ return;
240
+ }
241
+ if (!activeExecution.modeFlags.background) {
242
+ res.status(400).json(
243
+ createErrorBody3(
244
+ "invalid_request",
245
+ "Cannot cancel a synchronous response."
246
+ )
247
+ );
248
+ return;
249
+ }
250
+ activeExecution.controller.abort();
251
+ const response = await activeExecution.done.catch(
252
+ () => activeExecution.response
253
+ );
254
+ res.status(200).json(response);
255
+ };
256
+ }
257
+
258
+ // src/hosting/routes/create-response.ts
259
+ import {
260
+ addProtectedHeaders,
261
+ collectClientHeaders,
262
+ createErrorBody as createErrorBody4,
263
+ formatError,
264
+ resolveAgentIsolation as resolveAgentIsolation2
265
+ } from "@cuylabs/agent-foundry-agentserver-core";
266
+
267
+ // src/context.ts
268
+ var ResponseContext = class {
269
+ responseId;
270
+ request;
271
+ createdAt;
272
+ modeFlags;
273
+ sessionId;
274
+ previousResponseId;
275
+ conversationId;
276
+ clientHeaders;
277
+ queryParameters;
278
+ isolation;
279
+ isShutdownRequested = false;
280
+ provider;
281
+ historyLimit;
282
+ inputItems;
283
+ resolvedInputCache;
284
+ historyCache;
285
+ constructor(options) {
286
+ this.responseId = options.responseId;
287
+ this.request = options.request;
288
+ this.createdAt = options.createdAt ?? Math.floor(Date.now() / 1e3);
289
+ this.modeFlags = options.modeFlags;
290
+ this.sessionId = options.sessionId ?? options.responseId;
291
+ this.provider = options.provider;
292
+ this.inputItems = options.inputItems ?? getInputExpanded(options.request, options.responseId);
293
+ if (options.previousResponseId !== void 0) {
294
+ this.previousResponseId = options.previousResponseId;
295
+ }
296
+ if (options.conversationId !== void 0) {
297
+ this.conversationId = options.conversationId;
298
+ }
299
+ this.historyLimit = options.historyLimit ?? 100;
300
+ this.clientHeaders = options.clientHeaders ?? {};
301
+ this.queryParameters = options.queryParameters ?? {};
302
+ this.isolation = options.isolation ?? {};
303
+ }
304
+ async getInputItems(options = {}) {
305
+ const resolveReferences = options.resolveReferences !== false;
306
+ if (!resolveReferences) {
307
+ return this.inputItems;
308
+ }
309
+ if (this.resolvedInputCache) {
310
+ return this.resolvedInputCache;
311
+ }
312
+ const resolved = [];
313
+ const references = [];
314
+ for (const item of this.inputItems) {
315
+ if (item.type === "item_reference" && typeof item.id === "string") {
316
+ references.push({ index: resolved.length, id: item.id });
317
+ resolved.push(item);
318
+ } else {
319
+ resolved.push(item);
320
+ }
321
+ }
322
+ if (references.length > 0 && this.provider) {
323
+ const items = await this.provider.getItems(
324
+ references.map((reference) => reference.id),
325
+ { isolation: this.isolation }
326
+ );
327
+ for (let i = 0; i < references.length; i += 1) {
328
+ const reference = references[i];
329
+ const item = items[i];
330
+ if (reference && item) {
331
+ resolved[reference.index] = item;
332
+ }
333
+ }
334
+ }
335
+ this.resolvedInputCache = resolved.filter(
336
+ (item) => item.type !== "item_reference"
337
+ );
338
+ return this.resolvedInputCache;
339
+ }
340
+ async getInputText(options = {}) {
341
+ const items = await this.getInputItems(options);
342
+ return extractTextFromItems(items);
343
+ }
344
+ async getHistory() {
345
+ if (this.historyCache) {
346
+ return this.historyCache;
347
+ }
348
+ if (!this.provider) {
349
+ this.historyCache = [];
350
+ return this.historyCache;
351
+ }
352
+ if (this.provider.getHistoryItems) {
353
+ this.historyCache = await this.provider.getHistoryItems(
354
+ this.previousResponseId,
355
+ this.conversationId,
356
+ this.historyLimit,
357
+ { isolation: this.isolation }
358
+ );
359
+ return this.historyCache;
360
+ }
361
+ const historyItemIds = await this.provider.getHistoryItemIds(
362
+ this.previousResponseId,
363
+ this.conversationId,
364
+ this.historyLimit,
365
+ { isolation: this.isolation }
366
+ );
367
+ const items = await this.provider.getItems(historyItemIds, {
368
+ isolation: this.isolation
369
+ });
370
+ this.historyCache = items.filter(
371
+ (item) => item !== void 0
372
+ );
373
+ return this.historyCache;
374
+ }
375
+ async getInputItemsForPersistence() {
376
+ const items = await this.getInputItems({ resolveReferences: true });
377
+ return items.map((item) => toOutputItem(item, this.responseId));
378
+ }
379
+ };
380
+
381
+ // src/hosting/execution.ts
382
+ import {
383
+ ATTR_RESPONSES_ERROR_CODE,
384
+ ATTR_RESPONSES_ERROR_MESSAGE
385
+ } from "@cuylabs/agent-foundry-agentserver-core";
386
+ function createExecution(options) {
387
+ const controller = new AbortController();
388
+ const response = createResponseObject({
389
+ id: options.responseId,
390
+ request: options.request,
391
+ status: options.modeFlags.background ? "queued" : "in_progress",
392
+ output: [],
393
+ createdAt: options.context.createdAt
394
+ });
395
+ const execution = {
396
+ responseId: options.responseId,
397
+ request: options.request,
398
+ response,
399
+ inputItems: options.inputItems,
400
+ modeFlags: options.modeFlags,
401
+ isolation: options.isolation,
402
+ controller,
403
+ done: Promise.resolve(response)
404
+ };
405
+ options.active.set(
406
+ scopedActiveKey(options.responseId, options.isolation),
407
+ execution
408
+ );
409
+ execution.done = options.requestSpan.run(async () => {
410
+ let sequenceNumber = 0;
411
+ if (options.modeFlags.store) {
412
+ await options.provider.createResponse(response, options.inputItems, {
413
+ historyItemIds: options.historyItemIds,
414
+ isolation: options.isolation
415
+ });
416
+ }
417
+ try {
418
+ const result = await options.handler.handle(
419
+ options.request,
420
+ options.context,
421
+ controller.signal
422
+ );
423
+ for await (const event of normalizeHandlerResult(result)) {
424
+ const normalized = withSequenceNumber(
425
+ normalizeEvent(event),
426
+ sequenceNumber
427
+ );
428
+ sequenceNumber = normalized.sequence_number + 1;
429
+ applyEventToExecution(execution, normalized);
430
+ if (options.modeFlags.store) {
431
+ await options.streamProvider.appendStreamEvent(
432
+ options.responseId,
433
+ normalized,
434
+ { isolation: options.isolation }
435
+ );
436
+ }
437
+ await options.onEvent?.(normalized);
438
+ }
439
+ await completeExecutionIfNeeded(execution, options, sequenceNumber);
440
+ if (options.modeFlags.store) {
441
+ await options.provider.updateResponse(execution.response, {
442
+ isolation: options.isolation
443
+ });
444
+ }
445
+ return execution.response;
446
+ } catch (error) {
447
+ await failExecution(execution, options, error, sequenceNumber);
448
+ return execution.response;
449
+ } finally {
450
+ options.active.delete(
451
+ scopedActiveKey(options.responseId, options.isolation)
452
+ );
453
+ }
454
+ });
455
+ return execution;
456
+ }
457
+ async function completeExecutionIfNeeded(execution, options, sequenceNumber) {
458
+ if (isTerminalStatus(execution.response.status)) {
459
+ return;
460
+ }
461
+ execution.response = {
462
+ ...execution.response,
463
+ status: execution.controller.signal.aborted ? "cancelled" : "completed"
464
+ };
465
+ const terminalEvent = {
466
+ type: execution.controller.signal.aborted ? "response.cancelled" : "response.completed",
467
+ sequence_number: sequenceNumber,
468
+ response: execution.response
469
+ };
470
+ if (options.modeFlags.store) {
471
+ await options.streamProvider.appendStreamEvent(
472
+ options.responseId,
473
+ terminalEvent,
474
+ { isolation: options.isolation }
475
+ );
476
+ }
477
+ await options.onEvent?.(terminalEvent);
478
+ }
479
+ async function failExecution(execution, options, error, sequenceNumber) {
480
+ const aborted = execution.controller.signal.aborted;
481
+ execution.response = {
482
+ ...execution.response,
483
+ status: aborted ? "cancelled" : "failed",
484
+ error: aborted ? null : {
485
+ code: "server_error",
486
+ message: "internal server error",
487
+ type: "server_error"
488
+ }
489
+ };
490
+ options.requestSpan.recordError(
491
+ error,
492
+ aborted ? "cancelled" : "server_error"
493
+ );
494
+ options.requestSpan.span.setAttribute(
495
+ ATTR_RESPONSES_ERROR_CODE,
496
+ aborted ? "cancelled" : "server_error"
497
+ );
498
+ options.requestSpan.span.setAttribute(
499
+ ATTR_RESPONSES_ERROR_MESSAGE,
500
+ errorMessage(error)
501
+ );
502
+ const terminalEvent = {
503
+ type: aborted ? "response.cancelled" : "response.failed",
504
+ sequence_number: sequenceNumber,
505
+ response: execution.response
506
+ };
507
+ if (options.modeFlags.store) {
508
+ await options.streamProvider.appendStreamEvent(
509
+ options.responseId,
510
+ terminalEvent,
511
+ { isolation: options.isolation }
512
+ );
513
+ }
514
+ await options.onEvent?.(terminalEvent);
515
+ if (options.modeFlags.store) {
516
+ await options.provider.updateResponse(execution.response, {
517
+ isolation: options.isolation
518
+ });
519
+ }
520
+ }
521
+ function applyEventToExecution(execution, event) {
522
+ if (event.response) {
523
+ execution.response = event.response;
524
+ return;
525
+ }
526
+ if ((event.type === "response.output_item.done" || event.type === "response.output_item.added") && event.item) {
527
+ const outputIndex = typeof event.output_index === "number" ? event.output_index : execution.response.output.length;
528
+ const output = [...execution.response.output];
529
+ output[outputIndex] = event.item;
530
+ execution.response = { ...execution.response, output };
531
+ }
532
+ }
533
+ async function* normalizeHandlerResult(result) {
534
+ if (isAsyncIterable(result)) {
535
+ for await (const event of result) {
536
+ yield normalizeEvent(event);
537
+ }
538
+ return;
539
+ }
540
+ if (isIterable(result)) {
541
+ for (const event of result) {
542
+ yield normalizeEvent(event);
543
+ }
544
+ return;
545
+ }
546
+ throw new Error("response handler must return an iterable of events");
547
+ }
548
+ function normalizeEvent(event) {
549
+ if (typeof event.type !== "string" || !event.type) {
550
+ throw new Error("response stream event must include a string type");
551
+ }
552
+ return event;
553
+ }
554
+ function withSequenceNumber(event, fallback) {
555
+ const sequenceNumber = typeof event.sequence_number === "number" && event.sequence_number >= 0 ? event.sequence_number : fallback;
556
+ return { ...event, sequence_number: sequenceNumber };
557
+ }
558
+ function isTerminalStatus(status) {
559
+ return ["completed", "failed", "cancelled", "incomplete"].includes(status);
560
+ }
561
+ function isAsyncIterable(value) {
562
+ return typeof value === "object" && value !== null && Symbol.asyncIterator in value;
563
+ }
564
+ function isIterable(value) {
565
+ return typeof value === "object" && value !== null && Symbol.iterator in value;
566
+ }
567
+
568
+ // src/hosting/tracing.ts
569
+ import {
570
+ startAgentServerRequestSpan
571
+ } from "@cuylabs/agent-foundry-agentserver-core";
572
+ function startResponseRequestSpan(options) {
573
+ const span = startAgentServerRequestSpan({
574
+ config: options.config,
575
+ headers: (name) => options.req.header(name),
576
+ requestId: options.responseId,
577
+ operation: "invoke_agent",
578
+ operationName: "invoke_agent",
579
+ instrumentationScope: "Azure.AI.AgentServer.Responses",
580
+ responseId: options.responseId,
581
+ sessionId: options.sessionId,
582
+ conversationId: options.conversationId,
583
+ streaming: options.streaming,
584
+ correlationRequestId: options.req.agentServerRequestId
585
+ });
586
+ let finished = false;
587
+ options.res.once("finish", () => {
588
+ finished = true;
589
+ span.end();
590
+ });
591
+ options.res.once("close", () => {
592
+ if (finished) {
593
+ span.end();
594
+ return;
595
+ }
596
+ span.end(new Error("HTTP response closed before finish"));
597
+ });
598
+ return span;
599
+ }
600
+
601
+ // src/hosting/validation.ts
602
+ var ResponseValidationError = class extends Error {
603
+ constructor(message, code, param) {
604
+ super(message);
605
+ this.code = code;
606
+ this.param = param;
607
+ this.name = "ResponseValidationError";
608
+ }
609
+ };
610
+ function validateCreateResponseSemantics(request) {
611
+ const storeEnabled = request.store !== false;
612
+ if (request.background === true && !storeEnabled) {
613
+ throw new ResponseValidationError(
614
+ "background=true requires store=true",
615
+ "unsupported_parameter",
616
+ "background"
617
+ );
618
+ }
619
+ if (request.stream_options !== void 0 && request.stream !== true) {
620
+ throw new ResponseValidationError(
621
+ "stream_options requires stream=true",
622
+ "invalid_mode",
623
+ "stream"
624
+ );
625
+ }
626
+ const metadata = request.metadata;
627
+ if (isRecord(metadata)) {
628
+ if (Object.keys(metadata).length > 16) {
629
+ throw new ResponseValidationError(
630
+ "metadata must have at most 16 key-value pairs",
631
+ "invalid_request",
632
+ "metadata"
633
+ );
634
+ }
635
+ for (const [key, value] of Object.entries(metadata)) {
636
+ if (key.length > 64) {
637
+ throw new ResponseValidationError(
638
+ "metadata keys must be at most 64 characters",
639
+ "invalid_request",
640
+ "metadata"
641
+ );
642
+ }
643
+ if (typeof value === "string" && value.length > 512) {
644
+ throw new ResponseValidationError(
645
+ "metadata values must be at most 512 characters",
646
+ "invalid_request",
647
+ "metadata"
648
+ );
649
+ }
650
+ }
651
+ }
652
+ }
653
+ function isRecord(value) {
654
+ return typeof value === "object" && value !== null && !Array.isArray(value);
655
+ }
656
+
657
+ // src/hosting/routes/create-response.ts
658
+ function createResponseHandler(options) {
659
+ return async (req, res) => {
660
+ let request;
661
+ let responseId = "resp_validation";
662
+ try {
663
+ request = applyCreateResponseDefaults(normalizeCreateResponse(req.body), {
664
+ defaultModel: options.defaultModel
665
+ });
666
+ validateCreateResponseSemantics(request);
667
+ const agentReference = resolveAgentReference(request.agent_reference);
668
+ responseId = resolveResponseId(request, {
669
+ get: (name) => req.header(name)
670
+ });
671
+ const conversationId = resolveConversationId(request);
672
+ const previousResponseId = typeof request.previous_response_id === "string" ? request.previous_response_id : void 0;
673
+ if (previousResponseId) {
674
+ validateResponseId(previousResponseId);
675
+ }
676
+ const sessionId = resolveSessionId({
677
+ request,
678
+ envSessionId: options.config.sessionId,
679
+ ...agentReference ? { agentReference } : {}
680
+ });
681
+ addProtectedHeaders(res, { [SESSION_ID_HEADER]: sessionId });
682
+ const isolation = resolveAgentIsolation2((name) => req.header(name));
683
+ const modeFlags = {
684
+ stream: request.stream === true,
685
+ background: request.background === true,
686
+ store: request.store !== false
687
+ };
688
+ const inputItems = getInputExpanded(request, responseId);
689
+ const historyItemIds = modeFlags.store ? await options.provider.getHistoryItemIds(
690
+ previousResponseId,
691
+ conversationId,
692
+ options.defaultHistoryLimit,
693
+ { isolation }
694
+ ) : [];
695
+ const context = new ResponseContext({
696
+ responseId,
697
+ request,
698
+ modeFlags,
699
+ sessionId,
700
+ provider: options.provider,
701
+ inputItems,
702
+ ...previousResponseId ? { previousResponseId } : {},
703
+ ...conversationId ? { conversationId } : {},
704
+ historyLimit: options.defaultHistoryLimit,
705
+ clientHeaders: collectClientHeaders(req.headers),
706
+ queryParameters: queryParameters(req),
707
+ isolation
708
+ });
709
+ const requestSpan = startResponseRequestSpan({
710
+ req,
711
+ res,
712
+ config: options.config,
713
+ responseId,
714
+ sessionId,
715
+ conversationId,
716
+ streaming: modeFlags.stream
717
+ });
718
+ const handler = options.handlerProvider();
719
+ const sseState = createSseEncoderState();
720
+ if (modeFlags.stream) {
721
+ startResponsesSseResponse(res);
722
+ }
723
+ const execution = createExecution({
724
+ request,
725
+ responseId,
726
+ inputItems,
727
+ historyItemIds,
728
+ modeFlags,
729
+ context,
730
+ handler,
731
+ provider: options.provider,
732
+ streamProvider: options.streamProvider,
733
+ active: options.active,
734
+ isolation,
735
+ requestSpan,
736
+ onEvent: modeFlags.stream ? (event) => {
737
+ res.write(encodeSseEvent(event, sseState));
738
+ } : void 0
739
+ });
740
+ if (modeFlags.background && !modeFlags.stream) {
741
+ void execution.done.catch((error) => {
742
+ options.logger.error("Background response execution failed", {
743
+ responseId,
744
+ error: formatError(error)
745
+ });
746
+ });
747
+ res.status(200).json(execution.response);
748
+ return;
749
+ }
750
+ if (modeFlags.stream) {
751
+ try {
752
+ await execution.done;
753
+ } finally {
754
+ res.end();
755
+ }
756
+ return;
757
+ }
758
+ const response = await execution.done;
759
+ res.status(200).json(response);
760
+ } catch (error) {
761
+ options.logger.error("Failed to create response", {
762
+ responseId,
763
+ error: formatError(error)
764
+ });
765
+ const validationError = error instanceof ResponseValidationError ? error : void 0;
766
+ res.status(400).json(
767
+ createErrorBody4(
768
+ validationError?.code ?? "invalid_request",
769
+ errorMessage(error),
770
+ {
771
+ type: "invalid_request_error",
772
+ requestId: req.agentServerRequestId,
773
+ additionalInfo: validationError?.param ? { param: validationError.param } : void 0
774
+ }
775
+ )
776
+ );
777
+ }
778
+ };
779
+ }
780
+
781
+ // src/hosting/routes/delete-response.ts
782
+ import {
783
+ createErrorBody as createErrorBody5,
784
+ resolveAgentIsolation as resolveAgentIsolation3
785
+ } from "@cuylabs/agent-foundry-agentserver-core";
786
+ function deleteResponseHandler(options) {
787
+ return async (req, res) => {
788
+ const responseId = routeResponseId(req);
789
+ if (!validateRouteResponseId(responseId, res)) {
790
+ return;
791
+ }
792
+ const isolation = resolveAgentIsolation3((name) => req.header(name));
793
+ const activeExecution = options.active.get(
794
+ scopedActiveKey(responseId, isolation)
795
+ );
796
+ if (activeExecution?.modeFlags.store === false) {
797
+ res.status(404).json(createErrorBody5("not_found", "Response not found"));
798
+ return;
799
+ }
800
+ if (activeExecution?.modeFlags.background && ["queued", "in_progress"].includes(activeExecution.response.status)) {
801
+ res.status(400).json(
802
+ createErrorBody5(
803
+ "invalid_request",
804
+ "Cannot delete an in-flight response."
805
+ )
806
+ );
807
+ return;
808
+ }
809
+ activeExecution?.controller.abort();
810
+ const deleted = await options.provider.deleteResponse(responseId, {
811
+ isolation
812
+ });
813
+ await options.streamProvider.deleteStreamEvents(responseId, { isolation });
814
+ if (!deleted && !activeExecution) {
815
+ res.status(404).json(createErrorBody5("not_found", "Response not found"));
816
+ return;
817
+ }
818
+ options.active.delete(scopedActiveKey(responseId, isolation));
819
+ res.status(200).json({ id: responseId, object: "response", deleted: true });
820
+ };
821
+ }
822
+
823
+ // src/hosting/routes/get-response.ts
824
+ import {
825
+ createErrorBody as createErrorBody6,
826
+ resolveAgentIsolation as resolveAgentIsolation4
827
+ } from "@cuylabs/agent-foundry-agentserver-core";
828
+ function getResponseHandler(options) {
829
+ return async (req, res) => {
830
+ const responseId = routeResponseId(req);
831
+ if (!validateRouteResponseId(responseId, res)) {
832
+ return;
833
+ }
834
+ const isolation = resolveAgentIsolation4((name) => req.header(name));
835
+ const activeExecution = options.active.get(
836
+ scopedActiveKey(responseId, isolation)
837
+ );
838
+ const streamReplay = String(req.query.stream ?? "false").toLowerCase() === "true";
839
+ if (streamReplay) {
840
+ const startingAfter = parseStartingAfter(req, res);
841
+ if (startingAfter === false) {
842
+ return;
843
+ }
844
+ await handleStreamReplay({
845
+ activeExecution,
846
+ isolation,
847
+ routeOptions: options,
848
+ responseId,
849
+ res,
850
+ startingAfter
851
+ });
852
+ return;
853
+ }
854
+ const response = activeExecution?.response ?? await options.provider.getResponse(responseId, { isolation });
855
+ if (!response || activeExecution?.modeFlags.store === false) {
856
+ res.status(404).json(createErrorBody6("not_found", "Response not found"));
857
+ return;
858
+ }
859
+ res.status(200).json(response);
860
+ };
861
+ }
862
+ async function handleStreamReplay(options) {
863
+ const response = options.activeExecution?.response ?? await options.routeOptions.provider.getResponse(options.responseId, {
864
+ isolation: options.isolation
865
+ });
866
+ if (!response) {
867
+ options.res.status(404).json(createErrorBody6("not_found", "Response not found"));
868
+ return;
869
+ }
870
+ const flags = options.activeExecution?.modeFlags ?? modeFlagsFromResponse(response);
871
+ if (!flags.store) {
872
+ options.res.status(404).json(createErrorBody6("not_found", "Response not found"));
873
+ return;
874
+ }
875
+ if (!flags.background) {
876
+ options.res.status(400).json(
877
+ createErrorBody6(
878
+ "invalid_mode",
879
+ "This response cannot be streamed because it was not created with background=true."
880
+ )
881
+ );
882
+ return;
883
+ }
884
+ if (!flags.stream) {
885
+ options.res.status(400).json(
886
+ createErrorBody6(
887
+ "invalid_mode",
888
+ "This response cannot be streamed because it was not created with stream=true."
889
+ )
890
+ );
891
+ return;
892
+ }
893
+ const storedEvents = await options.routeOptions.streamProvider.getStreamEvents(
894
+ options.responseId,
895
+ { isolation: options.isolation }
896
+ ) ?? [];
897
+ const events = storedEvents.map((event, index) => ({
898
+ ...event,
899
+ sequence_number: typeof event.sequence_number === "number" ? event.sequence_number : index
900
+ })).filter(
901
+ (event) => options.startingAfter === void 0 || event.sequence_number > options.startingAfter
902
+ );
903
+ startResponsesSseResponse(options.res);
904
+ const sseState = createSseEncoderState();
905
+ for (const event of events) {
906
+ options.res.write(encodeSseEvent(event, sseState));
907
+ }
908
+ options.res.end();
909
+ }
910
+ function parseStartingAfter(req, res) {
911
+ const value = req.query.starting_after;
912
+ if (value === void 0) {
913
+ return void 0;
914
+ }
915
+ if (Array.isArray(value) || typeof value !== "string" || !value.trim()) {
916
+ res.status(400).json(
917
+ createErrorBody6("invalid_request", "starting_after must be an integer", {
918
+ type: "invalid_request_error",
919
+ additionalInfo: { param: "starting_after" }
920
+ })
921
+ );
922
+ return false;
923
+ }
924
+ const parsed = Number.parseInt(value, 10);
925
+ if (!Number.isInteger(parsed) || String(parsed) !== value.trim()) {
926
+ res.status(400).json(
927
+ createErrorBody6("invalid_request", "starting_after must be an integer", {
928
+ type: "invalid_request_error",
929
+ additionalInfo: { param: "starting_after" }
930
+ })
931
+ );
932
+ return false;
933
+ }
934
+ return parsed;
935
+ }
936
+
937
+ // src/hosting/routes/list-input-items.ts
938
+ import {
939
+ createErrorBody as createErrorBody7,
940
+ resolveAgentIsolation as resolveAgentIsolation5
941
+ } from "@cuylabs/agent-foundry-agentserver-core";
942
+ function listInputItemsHandler(options) {
943
+ return async (req, res) => {
944
+ const responseId = routeResponseId(req);
945
+ if (!validateRouteResponseId(responseId, res)) {
946
+ return;
947
+ }
948
+ const isolation = resolveAgentIsolation5((name) => req.header(name));
949
+ const limit = parseLimit(req.query.limit);
950
+ const items = await options.provider.getInputItems(responseId, {
951
+ after: queryString(req.query.after),
952
+ ascending: String(req.query.order ?? "desc").toLowerCase() === "asc",
953
+ before: queryString(req.query.before),
954
+ isolation,
955
+ limit: Math.min(limit + 1, 100)
956
+ });
957
+ if (!items) {
958
+ res.status(404).json(createErrorBody7("not_found", "Response not found"));
959
+ return;
960
+ }
961
+ const page = items.slice(0, limit);
962
+ res.status(200).json({
963
+ object: "list",
964
+ data: page,
965
+ first_id: page[0]?.id ?? null,
966
+ last_id: page[page.length - 1]?.id ?? null,
967
+ has_more: items.length > limit
968
+ });
969
+ };
970
+ }
971
+
972
+ // src/hosting/routes.ts
973
+ function registerResponsesRoutes(app, options) {
974
+ app.get("/", (_req, res) => {
975
+ res.json({
976
+ app: options.appName,
977
+ host: "foundry-responses",
978
+ protocol: "responses",
979
+ protocolVersion: "1.0.0",
980
+ endpoint: "/responses",
981
+ readiness: "/readiness",
982
+ liveness: "/healthz",
983
+ openapi: "/responses/docs/openapi.json"
984
+ });
985
+ });
986
+ app.get("/healthz", healthzHandler);
987
+ app.get("/readiness", readinessHandler);
988
+ app.get("/readyz", readinessHandler);
989
+ app.get("/responses/docs/openapi.json", (_req, res) => {
990
+ const handler = options.handlerProvider();
991
+ res.json(
992
+ handler.getOpenApi?.() ?? buildResponsesOpenApiSpec({ appName: options.appName })
993
+ );
994
+ });
995
+ app.post("/responses", createResponseHandler(options));
996
+ app.get("/responses/:responseId", getResponseHandler(options));
997
+ app.delete("/responses/:responseId", deleteResponseHandler(options));
998
+ app.post("/responses/:responseId/cancel", cancelResponseHandler(options));
999
+ app.get("/responses/:responseId/input_items", listInputItemsHandler(options));
1000
+ app.all("/responses", methodNotAllowed(["POST"]));
1001
+ app.all("/responses/docs/openapi.json", methodNotAllowed(["GET"]));
1002
+ app.all("/responses/:responseId/cancel", methodNotAllowed(["POST"]));
1003
+ app.all("/responses/:responseId/input_items", methodNotAllowed(["GET"]));
1004
+ app.all("/responses/:responseId", methodNotAllowed(["GET", "DELETE"]));
1005
+ app.all("/readiness", methodNotAllowed(["GET"]));
1006
+ app.all("/readyz", methodNotAllowed(["GET"]));
1007
+ app.all("/healthz", methodNotAllowed(["GET"]));
1008
+ }
1009
+
1010
+ // src/host.ts
1011
+ var DEFAULT_HOST = "0.0.0.0";
1012
+ var DEFAULT_BODY_LIMIT = "1mb";
1013
+ var DEFAULT_HISTORY_LIMIT = 100;
1014
+ function createResponsesApp(options) {
1015
+ const logger = options.logger ?? defaultAgentServerLogger("responses");
1016
+ const config = resolveAgentServerConfig({
1017
+ port: options.port,
1018
+ gracefulShutdownTimeoutSeconds: options.gracefulShutdownTimeoutSeconds
1019
+ });
1020
+ const appName = options.appName ?? "foundry-responses-host";
1021
+ const trustProxy = options.trustProxy ?? 1;
1022
+ const bodyLimit = options.bodyLimit ?? DEFAULT_BODY_LIMIT;
1023
+ const defaultHistoryLimit = resolveDefaultHistoryLimit(
1024
+ options.defaultFetchHistoryCount
1025
+ );
1026
+ const requestLogging = options.requestLogging !== false;
1027
+ const provider = options.store ?? new InMemoryResponseProvider();
1028
+ const streamProvider = supportsStreamProvider(provider) ? provider : new InMemoryResponseProvider();
1029
+ const active = /* @__PURE__ */ new Map();
1030
+ const platformServer = responsesPlatformServerHeader();
1031
+ const observabilityReady = options.configureObservability === false ? Promise.resolve(void 0) : configureAgentServerObservability({ config, logger });
1032
+ const app = express();
1033
+ configureExpressAgentServerApp(app, { trustProxy });
1034
+ app.use(agentServerRequestHeadersMiddleware({ platformServer }));
1035
+ app.use(express.json({ type: "*/*", limit: bodyLimit }));
1036
+ app.use(jsonParseErrorMiddleware);
1037
+ app.use(async (_req, _res, next) => {
1038
+ try {
1039
+ await observabilityReady;
1040
+ next();
1041
+ } catch (error) {
1042
+ logger.warn("Foundry Agent Server observability initialization failed", {
1043
+ error: formatError2(error)
1044
+ });
1045
+ next();
1046
+ }
1047
+ });
1048
+ if (requestLogging) {
1049
+ app.use(agentServerLoggingMiddleware(logger));
1050
+ }
1051
+ registerResponsesRoutes(app, {
1052
+ active,
1053
+ appName,
1054
+ config,
1055
+ defaultHistoryLimit,
1056
+ defaultModel: options.defaultModel,
1057
+ handlerProvider: makeHandlerProvider(options.handler),
1058
+ logger,
1059
+ provider,
1060
+ streamProvider
1061
+ });
1062
+ app.use(notFoundMiddleware());
1063
+ app.use(errorMiddleware(logger));
1064
+ return app;
1065
+ }
1066
+ async function runResponsesServer(options) {
1067
+ const app = createResponsesApp(options);
1068
+ const config = resolveAgentServerConfig({
1069
+ port: options.port,
1070
+ gracefulShutdownTimeoutSeconds: options.gracefulShutdownTimeoutSeconds
1071
+ });
1072
+ const port = config.port;
1073
+ const host = options.host ?? DEFAULT_HOST;
1074
+ const logger = options.logger ?? defaultAgentServerLogger("responses");
1075
+ const appName = options.appName ?? "foundry-responses-host";
1076
+ const platformServer = responsesPlatformServerHeader();
1077
+ const server = await new Promise((resolve, reject) => {
1078
+ const httpServer = app.listen(port, host, () => {
1079
+ logAgentServerStartupConfiguration(logger, config, platformServer);
1080
+ logger.info(`${appName} listening on http://${host}:${port}/responses`);
1081
+ resolve(httpServer);
1082
+ }).on("error", reject);
1083
+ });
1084
+ return {
1085
+ app,
1086
+ server,
1087
+ port,
1088
+ host,
1089
+ async close() {
1090
+ await closeServerWithTimeout(
1091
+ server,
1092
+ config.gracefulShutdownTimeoutSeconds
1093
+ );
1094
+ const handler = options.handler;
1095
+ if (typeof handler === "object" && "close" in handler) {
1096
+ await handler.close?.();
1097
+ }
1098
+ }
1099
+ };
1100
+ }
1101
+ function resolveDefaultHistoryLimit(value) {
1102
+ if (value === void 0) {
1103
+ return DEFAULT_HISTORY_LIMIT;
1104
+ }
1105
+ if (!Number.isInteger(value) || value <= 0) {
1106
+ throw new Error("defaultFetchHistoryCount must be a positive integer");
1107
+ }
1108
+ return value;
1109
+ }
1110
+ function responsesPlatformServerHeader() {
1111
+ return buildPlatformServerHeader([
1112
+ {
1113
+ name: "azure-ai-agentserver-core",
1114
+ version: AGENTSERVER_CORE_PACKAGE_VERSION
1115
+ },
1116
+ {
1117
+ name: "azure-ai-agentserver-responses",
1118
+ version: AGENTSERVER_RESPONSES_PACKAGE_VERSION
1119
+ }
1120
+ ]);
1121
+ }
1122
+
1123
+ // src/types.ts
1124
+ var ResponsesHandler = class {
1125
+ };
1126
+ export {
1127
+ AGENTSERVER_RESPONSES_PACKAGE_VERSION,
1128
+ FoundryStorageError,
1129
+ FoundryStorageNotFoundError,
1130
+ FoundryStorageProvider,
1131
+ FoundryStorageSettings,
1132
+ InMemoryResponseProvider,
1133
+ RESPONSE_ID_HEADER,
1134
+ ResponseContext,
1135
+ ResponsesHandler,
1136
+ SESSION_ID_HEADER,
1137
+ TextResponse,
1138
+ buildResponsesOpenApiSpec,
1139
+ createResponseObject,
1140
+ createResponsesApp,
1141
+ createSseEncoderState,
1142
+ deriveSessionId,
1143
+ encodeKeepAliveComment,
1144
+ encodeSseEvent,
1145
+ extractPartitionKey,
1146
+ extractTextFromItems,
1147
+ getInputExpanded,
1148
+ isValidId,
1149
+ newId,
1150
+ newMessageItemId,
1151
+ newOutputMessageItemId,
1152
+ newResponseId,
1153
+ normalizeCreateResponse,
1154
+ normalizeResponseInput,
1155
+ resolveAgentReference,
1156
+ resolveConversationId,
1157
+ resolveResponseId,
1158
+ resolveSessionId,
1159
+ runResponsesServer,
1160
+ startResponsesSseResponse,
1161
+ toOutputItem,
1162
+ validateResponseId
1163
+ };