@rcrsr/rill-agent-foundry 0.18.4

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 (47) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +36 -0
  3. package/dist/conversations.d.ts +23 -0
  4. package/dist/conversations.d.ts.map +1 -0
  5. package/dist/conversations.js +91 -0
  6. package/dist/conversations.js.map +1 -0
  7. package/dist/errors.d.ts +26 -0
  8. package/dist/errors.d.ts.map +1 -0
  9. package/dist/errors.js +40 -0
  10. package/dist/errors.js.map +1 -0
  11. package/dist/extract.d.ts +19 -0
  12. package/dist/extract.d.ts.map +1 -0
  13. package/dist/extract.js +109 -0
  14. package/dist/extract.js.map +1 -0
  15. package/dist/harness.d.ts +21 -0
  16. package/dist/harness.d.ts.map +1 -0
  17. package/dist/harness.js +481 -0
  18. package/dist/harness.js.map +1 -0
  19. package/dist/id.d.ts +26 -0
  20. package/dist/id.d.ts.map +1 -0
  21. package/dist/id.js +83 -0
  22. package/dist/id.js.map +1 -0
  23. package/dist/index.d.ts +17 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +22 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/response.d.ts +25 -0
  28. package/dist/response.d.ts.map +1 -0
  29. package/dist/response.js +154 -0
  30. package/dist/response.js.map +1 -0
  31. package/dist/session.d.ts +24 -0
  32. package/dist/session.d.ts.map +1 -0
  33. package/dist/session.js +34 -0
  34. package/dist/session.js.map +1 -0
  35. package/dist/stream.d.ts +47 -0
  36. package/dist/stream.d.ts.map +1 -0
  37. package/dist/stream.js +189 -0
  38. package/dist/stream.js.map +1 -0
  39. package/dist/telemetry.d.ts +21 -0
  40. package/dist/telemetry.d.ts.map +1 -0
  41. package/dist/telemetry.js +53 -0
  42. package/dist/telemetry.js.map +1 -0
  43. package/dist/types.d.ts +161 -0
  44. package/dist/types.d.ts.map +1 -0
  45. package/dist/types.js +5 -0
  46. package/dist/types.js.map +1 -0
  47. package/package.json +56 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Andre Bremer
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,36 @@
1
+ # @rcrsr/rill-agent-foundry
2
+
3
+ Azure-hosted harness factory for the [rill](https://github.com/rcrsr/rill) agent framework. Wraps an `AgentRouter` in a Hono server that implements the Foundry Responses API protocol with SSE streaming, session persistence, and OpenTelemetry tracing. Outbound calls to Azure AI Foundry Conversations authenticate via `DefaultAzureCredential` (Entra ID).
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @rcrsr/rill-agent-foundry @rcrsr/rill-agent @opentelemetry/api
9
+ ```
10
+
11
+ `@opentelemetry/api` is a required peer dependency.
12
+
13
+ ## Quick Start
14
+
15
+ ```typescript
16
+ import { loadManifest, createRouter } from '@rcrsr/rill-agent';
17
+ import { createFoundryHarness } from '@rcrsr/rill-agent-foundry';
18
+
19
+ const manifest = await loadManifest('./build');
20
+ const router = await createRouter(manifest);
21
+
22
+ const harness = createFoundryHarness(router, { port: 8080 });
23
+
24
+ await harness.listen();
25
+ ```
26
+
27
+ The harness exposes `POST /responses` and `POST /runs` for synchronous and streaming responses, plus `/liveness`, `/readiness`, and `/metrics` for operations.
28
+
29
+ ## Documentation
30
+
31
+ - [Reference](docs/agent-foundry.md) — configuration, routes, streaming events, session storage, telemetry, error codes
32
+ - [How to deploy a rill agent to Azure AI Foundry](docs/deploy-foundry-agent.md) — end-to-end packaging, Dockerfile, ACR push, and registration script
33
+
34
+ ## License
35
+
36
+ MIT
@@ -0,0 +1,23 @@
1
+ import type { TokenCredential } from '@azure/identity';
2
+ export interface ConversationsClient {
3
+ saveItems(conversationId: string, items: ReadonlyArray<unknown>): Promise<void>;
4
+ }
5
+ /**
6
+ * Error thrown when the Conversations API returns a non-success status
7
+ * or when a network timeout occurs.
8
+ * Maps to HTTP 502. Error code: SERVER_ERROR.
9
+ */
10
+ export declare class PersistenceError extends Error {
11
+ readonly statusCode: 502;
12
+ constructor(message: string);
13
+ }
14
+ /**
15
+ * Create a client for the Azure AI Foundry Conversations API.
16
+ *
17
+ * Auth: DefaultAzureCredential with scope https://ai.azure.com/.default
18
+ * API version: 2025-11-15-preview
19
+ *
20
+ * saveItems() throws PersistenceError on 4xx/5xx or network timeout.
21
+ */
22
+ export declare function createConversationsClient(projectEndpoint: string, credential: TokenCredential): ConversationsClient;
23
+ //# sourceMappingURL=conversations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conversations.d.ts","sourceRoot":"","sources":["../src/conversations.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAOvD,MAAM,WAAW,mBAAmB;IAClC,SAAS,CACP,cAAc,EAAE,MAAM,EACtB,KAAK,EAAE,aAAa,CAAC,OAAO,CAAC,GAC5B,OAAO,CAAC,IAAI,CAAC,CAAC;CAClB;AAMD;;;;GAIG;AACH,qBAAa,gBAAiB,SAAQ,KAAK;IACzC,QAAQ,CAAC,UAAU,EAAE,GAAG,CAAC;gBAEb,OAAO,EAAE,MAAM;CAK5B;AAcD;;;;;;;GAOG;AACH,wBAAgB,yBAAyB,CACvC,eAAe,EAAE,MAAM,EACvB,UAAU,EAAE,eAAe,GAC1B,mBAAmB,CAiErB"}
@@ -0,0 +1,91 @@
1
+ import { CredentialError } from './errors.js';
2
+ // ============================================================
3
+ // ERRORS
4
+ // ============================================================
5
+ /**
6
+ * Error thrown when the Conversations API returns a non-success status
7
+ * or when a network timeout occurs.
8
+ * Maps to HTTP 502. Error code: SERVER_ERROR.
9
+ */
10
+ export class PersistenceError extends Error {
11
+ statusCode;
12
+ constructor(message) {
13
+ super(message);
14
+ this.name = 'PersistenceError';
15
+ this.statusCode = 502;
16
+ }
17
+ }
18
+ // ============================================================
19
+ // CONSTANTS
20
+ // ============================================================
21
+ const API_VERSION = '2025-11-15-preview';
22
+ const TOKEN_SCOPE = 'https://ai.azure.com/.default';
23
+ const REQUEST_TIMEOUT_MS = 30_000;
24
+ // ============================================================
25
+ // FACTORY
26
+ // ============================================================
27
+ /**
28
+ * Create a client for the Azure AI Foundry Conversations API.
29
+ *
30
+ * Auth: DefaultAzureCredential with scope https://ai.azure.com/.default
31
+ * API version: 2025-11-15-preview
32
+ *
33
+ * saveItems() throws PersistenceError on 4xx/5xx or network timeout.
34
+ */
35
+ export function createConversationsClient(projectEndpoint, credential) {
36
+ return {
37
+ async saveItems(conversationId, items) {
38
+ // Acquire token
39
+ let token;
40
+ try {
41
+ const accessToken = await credential.getToken(TOKEN_SCOPE);
42
+ if (accessToken === null) {
43
+ throw new Error(`Failed to acquire token for scope ${TOKEN_SCOPE}`);
44
+ }
45
+ token = accessToken.token;
46
+ }
47
+ catch (err) {
48
+ if (err instanceof CredentialError) {
49
+ throw err;
50
+ }
51
+ const message = err instanceof Error ? err.message : String(err);
52
+ throw new CredentialError(message);
53
+ }
54
+ // Encode conversationId as a path segment: it originates from the
55
+ // request body and must be treated as untrusted to prevent path
56
+ // injection when IDs contain '/', '..', or other reserved chars.
57
+ const encodedConversationId = encodeURIComponent(conversationId);
58
+ const url = new URL(`${projectEndpoint}/openai/conversations/${encodedConversationId}/items`);
59
+ url.searchParams.set('api-version', API_VERSION);
60
+ const controller = new AbortController();
61
+ const timer = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
62
+ try {
63
+ let response;
64
+ try {
65
+ response = await fetch(url.toString(), {
66
+ method: 'POST',
67
+ headers: {
68
+ 'Content-Type': 'application/json',
69
+ Authorization: `Bearer ${token}`,
70
+ },
71
+ body: JSON.stringify({ items }),
72
+ signal: controller.signal,
73
+ });
74
+ }
75
+ catch (err) {
76
+ const isTimeout = err instanceof Error && err.name === 'AbortError';
77
+ throw new PersistenceError(isTimeout
78
+ ? 'Conversations API timeout'
79
+ : `Conversations API error: ${err instanceof Error ? err.message : String(err)}`);
80
+ }
81
+ if (!response.ok) {
82
+ throw new PersistenceError(`Conversations API error: ${response.status}`);
83
+ }
84
+ }
85
+ finally {
86
+ clearTimeout(timer);
87
+ }
88
+ },
89
+ };
90
+ }
91
+ //# sourceMappingURL=conversations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conversations.js","sourceRoot":"","sources":["../src/conversations.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Error thrown when the request input is missing, empty, or unparseable.
3
+ * Maps to HTTP 400. Error code: INVALID_REQUEST.
4
+ */
5
+ export declare class InputError extends Error {
6
+ /** HTTP status code for this error. */
7
+ readonly statusCode: 400;
8
+ constructor(message: string);
9
+ }
10
+ /**
11
+ * Error thrown when managed identity credential acquisition fails at startup.
12
+ * Triggers a non-zero process exit.
13
+ */
14
+ export declare class CredentialError extends Error {
15
+ constructor(message: string);
16
+ }
17
+ /**
18
+ * Error thrown when the session pool is at maximum capacity.
19
+ * Maps to HTTP 429. Error code: RATE_LIMITED.
20
+ */
21
+ export declare class CapacityError extends Error {
22
+ /** HTTP status code for this error. */
23
+ readonly statusCode: 429;
24
+ constructor(max: number);
25
+ }
26
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAIA;;;GAGG;AACH,qBAAa,UAAW,SAAQ,KAAK;IACnC,uCAAuC;IACvC,QAAQ,CAAC,UAAU,EAAE,GAAG,CAAC;gBAEb,OAAO,EAAE,MAAM;CAK5B;AAED;;;GAGG;AACH,qBAAa,eAAgB,SAAQ,KAAK;gBAC5B,OAAO,EAAE,MAAM;CAI5B;AAED;;;GAGG;AACH,qBAAa,aAAc,SAAQ,KAAK;IACtC,uCAAuC;IACvC,QAAQ,CAAC,UAAU,EAAE,GAAG,CAAC;gBAEb,GAAG,EAAE,MAAM;CAKxB"}
package/dist/errors.js ADDED
@@ -0,0 +1,40 @@
1
+ // ============================================================
2
+ // ERROR CLASSES
3
+ // ============================================================
4
+ /**
5
+ * Error thrown when the request input is missing, empty, or unparseable.
6
+ * Maps to HTTP 400. Error code: INVALID_REQUEST.
7
+ */
8
+ export class InputError extends Error {
9
+ /** HTTP status code for this error. */
10
+ statusCode;
11
+ constructor(message) {
12
+ super(message);
13
+ this.name = 'InputError';
14
+ this.statusCode = 400;
15
+ }
16
+ }
17
+ /**
18
+ * Error thrown when managed identity credential acquisition fails at startup.
19
+ * Triggers a non-zero process exit.
20
+ */
21
+ export class CredentialError extends Error {
22
+ constructor(message) {
23
+ super(message);
24
+ this.name = 'CredentialError';
25
+ }
26
+ }
27
+ /**
28
+ * Error thrown when the session pool is at maximum capacity.
29
+ * Maps to HTTP 429. Error code: RATE_LIMITED.
30
+ */
31
+ export class CapacityError extends Error {
32
+ /** HTTP status code for this error. */
33
+ statusCode;
34
+ constructor(max) {
35
+ super(`Maximum concurrent sessions (${max}) reached`);
36
+ this.name = 'CapacityError';
37
+ this.statusCode = 429;
38
+ }
39
+ }
40
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,gBAAgB;AAChB,+DAA+D;AAE/D;;;GAGG;AACH,MAAM,OAAO,UAAW,SAAQ,KAAK;IACnC,uCAAuC;IAC9B,UAAU,CAAM;IAEzB,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,YAAY,CAAC;QACzB,IAAI,CAAC,UAAU,GAAG,GAAG,CAAC;IACxB,CAAC;CACF;AAED;;;GAGG;AACH,MAAM,OAAO,eAAgB,SAAQ,KAAK;IACxC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;IAChC,CAAC;CACF;AAED;;;GAGG;AACH,MAAM,OAAO,aAAc,SAAQ,KAAK;IACtC,uCAAuC;IAC9B,UAAU,CAAM;IAEzB,YAAY,GAAW;QACrB,KAAK,CAAC,gCAAgC,GAAG,WAAW,CAAC,CAAC;QACtD,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;QAC5B,IAAI,CAAC,UAAU,GAAG,GAAG,CAAC;IACxB,CAAC;CACF"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Result of extractInput: the extracted params and an optional target agent.
3
+ */
4
+ export interface ExtractedInput {
5
+ readonly params: Record<string, unknown>;
6
+ readonly targetAgent?: string | undefined;
7
+ }
8
+ /**
9
+ * Extract normalized params and optional target agent from a polymorphic input.
10
+ *
11
+ * Variants:
12
+ * - string → { params: { input: string } }
13
+ * - array with user message → { params: { input: lastUserText } }
14
+ * - array with function_call_output → { params: { tool_results: [...] }, targetAgent }
15
+ *
16
+ * Throws InputError for missing, empty, or unactionable input.
17
+ */
18
+ export declare function extractInput(input: unknown): ExtractedInput;
19
+ //# sourceMappingURL=extract.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extract.d.ts","sourceRoot":"","sources":["../src/extract.ts"],"names":[],"mappings":"AAOA;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACzC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC3C;AAsFD;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,cAAc,CAiC3D"}
@@ -0,0 +1,109 @@
1
+ import { InputError } from './errors.js';
2
+ // ============================================================
3
+ // HELPERS
4
+ // ============================================================
5
+ /**
6
+ * Concatenate text from all `input_text` ContentPart entries.
7
+ */
8
+ function concatenateTextParts(parts) {
9
+ return parts
10
+ .filter((p) => p.type === 'input_text')
11
+ .map((p) => p.text)
12
+ .join('');
13
+ }
14
+ /**
15
+ * Extract text from a user message content field (string or ContentPart[]).
16
+ */
17
+ function extractUserText(content) {
18
+ if (typeof content === 'string') {
19
+ return content;
20
+ }
21
+ return concatenateTextParts(content);
22
+ }
23
+ /**
24
+ * Find the last user message item in an input array and return its text.
25
+ * Returns undefined when no user message is present.
26
+ */
27
+ function findLastUserText(items) {
28
+ let text;
29
+ for (const item of items) {
30
+ if (item.type === 'message' && item.role === 'user') {
31
+ text = extractUserText(item.content);
32
+ }
33
+ }
34
+ return text;
35
+ }
36
+ /**
37
+ * Find all function_call_output items paired with their function_call.
38
+ * Returns extracted params and target agent name.
39
+ * Throws InputError when a function_call_output has no paired function_call.
40
+ */
41
+ function extractFunctionCallOutputs(items) {
42
+ // Build a map of call_id → handler name from function_call items.
43
+ const callMap = new Map();
44
+ for (const item of items) {
45
+ if (item.type === 'function_call') {
46
+ callMap.set(item.call_id, item.name);
47
+ }
48
+ }
49
+ // Collect all function_call_output items.
50
+ const outputs = items.filter((item) => item.type === 'function_call_output');
51
+ // Resolve each output to a handler name and validate pairing.
52
+ let targetAgent;
53
+ const toolResults = [];
54
+ for (const output of outputs) {
55
+ const handlerName = callMap.get(output.call_id);
56
+ if (handlerName === undefined) {
57
+ throw new InputError('function_call_output missing paired function_call');
58
+ }
59
+ // Use the first resolved handler name as the target agent.
60
+ if (targetAgent === undefined) {
61
+ targetAgent = handlerName;
62
+ }
63
+ toolResults.push({ call_id: output.call_id, output: output.output });
64
+ }
65
+ return {
66
+ params: { tool_results: toolResults },
67
+ targetAgent,
68
+ };
69
+ }
70
+ // ============================================================
71
+ // EXTRACT INPUT
72
+ // ============================================================
73
+ /**
74
+ * Extract normalized params and optional target agent from a polymorphic input.
75
+ *
76
+ * Variants:
77
+ * - string → { params: { input: string } }
78
+ * - array with user message → { params: { input: lastUserText } }
79
+ * - array with function_call_output → { params: { tool_results: [...] }, targetAgent }
80
+ *
81
+ * Throws InputError for missing, empty, or unactionable input.
82
+ */
83
+ export function extractInput(input) {
84
+ if (input === undefined || input === null || input === '') {
85
+ throw new InputError('Missing required field: input');
86
+ }
87
+ if (typeof input === 'string') {
88
+ if (input.trim() === '') {
89
+ throw new InputError('Missing required field: input');
90
+ }
91
+ return { params: { input } };
92
+ }
93
+ if (!Array.isArray(input) || input.length === 0) {
94
+ throw new InputError('Missing required field: input');
95
+ }
96
+ const items = input;
97
+ // Check for function_call_output items first.
98
+ const hasFunctionCallOutput = items.some((item) => item.type === 'function_call_output');
99
+ if (hasFunctionCallOutput) {
100
+ return extractFunctionCallOutputs(items);
101
+ }
102
+ // Look for a user message.
103
+ const userText = findLastUserText(items);
104
+ if (userText !== undefined) {
105
+ return { params: { input: userText } };
106
+ }
107
+ throw new InputError('No actionable input items found');
108
+ }
109
+ //# sourceMappingURL=extract.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extract.js","sourceRoot":"","sources":["../src/extract.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import { Hono } from 'hono';
2
+ import type { AgentRouter } from '@rcrsr/rill-agent';
3
+ import type { FoundryHarnessOptions, FoundryMetrics } from './types.js';
4
+ export interface FoundryHarness {
5
+ listen(): Promise<void>;
6
+ close(): Promise<void>;
7
+ readonly app: Hono;
8
+ metrics(): FoundryMetrics;
9
+ }
10
+ /**
11
+ * Create a Foundry harness wrapping an AgentRouter.
12
+ *
13
+ * Routes:
14
+ * POST /responses — main Foundry Responses endpoint
15
+ * POST /runs — alias for /responses
16
+ * GET /readiness — 503 before init, 200 after
17
+ * GET /liveness — always 200
18
+ * GET /metrics — FoundryMetrics JSON
19
+ */
20
+ export declare function createFoundryHarness(router: AgentRouter, options?: FoundryHarnessOptions): FoundryHarness;
21
+ //# sourceMappingURL=harness.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"harness.d.ts","sourceRoot":"","sources":["../src/harness.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAK5B,OAAO,KAAK,EAAE,WAAW,EAA0B,MAAM,mBAAmB,CAAC;AAC7E,OAAO,KAAK,EAEV,qBAAqB,EACrB,cAAc,EACf,MAAM,YAAY,CAAC;AAiBpB,MAAM,WAAW,cAAc;IAC7B,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACxB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,QAAQ,CAAC,GAAG,EAAE,IAAI,CAAC;IACnB,OAAO,IAAI,cAAc,CAAC;CAC3B;AAgGD;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,WAAW,EACnB,OAAO,CAAC,EAAE,qBAAqB,GAC9B,cAAc,CAyehB"}