@hue-run/sdk 0.1.4 → 0.2.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 (58) hide show
  1. package/ENVIRONMENTS.md +182 -0
  2. package/EVALUATIONS.md +12 -0
  3. package/README.md +204 -21
  4. package/dist/ai-sdk.d.ts +9 -1
  5. package/dist/ai-sdk.js +37 -2
  6. package/dist/client.d.ts +130 -5
  7. package/dist/client.js +518 -110
  8. package/dist/config.d.ts +11 -2
  9. package/dist/config.js +50 -4
  10. package/dist/environment/client.d.ts +73 -0
  11. package/dist/environment/client.js +209 -0
  12. package/dist/environment/tools.d.ts +30 -0
  13. package/dist/environment/tools.js +24 -0
  14. package/dist/environment/types.d.ts +429 -0
  15. package/dist/environment/types.js +1 -0
  16. package/dist/environment.d.ts +5 -0
  17. package/dist/environment.js +2 -0
  18. package/dist/evals/attempt.d.ts +454 -0
  19. package/dist/evals/attempt.js +687 -0
  20. package/dist/evals/client.d.ts +99 -5
  21. package/dist/evals/client.js +136 -7
  22. package/dist/evals/environment-evidence.d.ts +6 -0
  23. package/dist/evals/environment-evidence.js +123 -0
  24. package/dist/evals/environment-json.d.ts +3 -0
  25. package/dist/evals/environment-json.js +76 -0
  26. package/dist/evals/json.d.ts +9 -1
  27. package/dist/evals/json.js +14 -6
  28. package/dist/evals/runner.d.ts +61 -2
  29. package/dist/evals/runner.js +71 -9
  30. package/dist/evals/scorer-publication.d.ts +2 -0
  31. package/dist/evals/scorer-publication.js +84 -0
  32. package/dist/evals/scorers.d.ts +11 -0
  33. package/dist/evals/scorers.js +56 -5
  34. package/dist/evals/simulation.d.ts +184 -0
  35. package/dist/evals/simulation.js +603 -0
  36. package/dist/evals/types.d.ts +304 -0
  37. package/dist/evals.d.ts +5 -1
  38. package/dist/evals.js +3 -1
  39. package/dist/experimental-telemetry.d.ts +8 -0
  40. package/dist/experimental-telemetry.js +13 -0
  41. package/dist/index.d.ts +4 -1
  42. package/dist/index.js +3 -1
  43. package/dist/managed.d.ts +51 -1
  44. package/dist/managed.js +11 -1
  45. package/dist/privacy.d.ts +2 -0
  46. package/dist/privacy.js +54 -21
  47. package/dist/receipt.d.ts +12 -1
  48. package/dist/receipt.js +10 -1
  49. package/dist/safety.d.ts +7 -0
  50. package/dist/safety.js +179 -0
  51. package/dist/snapshot.d.ts +12 -0
  52. package/dist/snapshot.js +200 -0
  53. package/dist/transport.d.ts +46 -8
  54. package/dist/transport.js +266 -48
  55. package/dist/types.d.ts +167 -8
  56. package/dist/version.d.ts +2 -0
  57. package/dist/version.js +3 -0
  58. package/package.json +51 -15
package/dist/config.d.ts CHANGED
@@ -1,4 +1,13 @@
1
- import type { HueOptions } from "./types.js";
1
+ import type { HueOptions, SharedHueOptions } from "./types.js";
2
2
  export declare const MAX_BODY_BYTES: number;
3
3
  export declare const MAX_CONTENT_BYTES: number;
4
- export declare function validateOptions(options: HueOptions): Required<Pick<HueOptions, "apiKey" | "serviceName" | "baseUrl" | "captureContent" | "timeoutMillis">> & HueOptions;
4
+ /** Loopback hostnames that may use plain HTTP without opting in. */
5
+ export declare function isLoopbackHost(hostname: string): boolean;
6
+ /** True when a validated origin exports over plain HTTP to a host other than loopback. */
7
+ export declare function isInsecureOrigin(baseUrl: string): boolean;
8
+ export declare function validateOptions(options: HueOptions): HueOptions & Required<Pick<SharedHueOptions, "captureContent" | "baseUrl" | "timeoutMillis" | "maxQueueBytes">> & {
9
+ /** Project key after validation; empty for a disabled client. */
10
+ apiKey: string;
11
+ /** Service name after validation; `hue-disabled` for a disabled client. */
12
+ serviceName: string;
13
+ };
package/dist/config.js CHANGED
@@ -1,6 +1,37 @@
1
1
  export const MAX_BODY_BYTES = 1024 * 1024;
2
2
  export const MAX_CONTENT_BYTES = 256 * 1024;
3
+ /** Loopback hostnames that may use plain HTTP without opting in. */
4
+ export function isLoopbackHost(hostname) {
5
+ return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "[::1]";
6
+ }
7
+ /** True when a validated origin exports over plain HTTP to a host other than loopback. */
8
+ export function isInsecureOrigin(baseUrl) {
9
+ const url = new URL(baseUrl);
10
+ return url.protocol === "http:" && !isLoopbackHost(url.hostname);
11
+ }
12
+ // The return type stays anonymous: HueTransport.options exposes it through ReturnType, and the
13
+ // API reference must not reference a name that is not part of the public entry points.
3
14
  export function validateOptions(options) {
15
+ if (options.enabled !== undefined && typeof options.enabled !== "boolean")
16
+ throw new TypeError("enabled must be a boolean");
17
+ if (options.enabled === false) {
18
+ // The kill switch exports nothing, so the content decision defaults to metadata-only. The
19
+ // diagnostics hook stays attached so a disabled client can still report why it is off.
20
+ if (options.captureContent !== undefined && typeof options.captureContent !== "boolean")
21
+ throw new TypeError("captureContent must be a boolean");
22
+ return {
23
+ ...(typeof options.onExportIssue === "function"
24
+ ? { onExportIssue: options.onExportIssue }
25
+ : {}),
26
+ captureContent: options.captureContent ?? false,
27
+ enabled: false,
28
+ apiKey: "",
29
+ serviceName: "hue-disabled",
30
+ baseUrl: "https://app.hue.run",
31
+ timeoutMillis: 10000,
32
+ maxQueueBytes: 8 * 1024 * 1024,
33
+ };
34
+ }
4
35
  if (typeof options.captureContent !== "boolean")
5
36
  throw new TypeError("Choose captureContent explicitly: true or false");
6
37
  if (typeof options.apiKey !== "string" ||
@@ -13,6 +44,13 @@ export function validateOptions(options) {
13
44
  !options.serviceName.trim() ||
14
45
  options.serviceName.length > 256)
15
46
  throw new TypeError("A serviceName of 1–256 characters is required");
47
+ if (options.allowInsecureHttp !== undefined && typeof options.allowInsecureHttp !== "boolean")
48
+ throw new TypeError("allowInsecureHttp must be a boolean");
49
+ if (options.resourceAttributes !== undefined &&
50
+ (options.resourceAttributes === null ||
51
+ typeof options.resourceAttributes !== "object" ||
52
+ Array.isArray(options.resourceAttributes)))
53
+ throw new TypeError("resourceAttributes must be an object of attribute values");
16
54
  let url;
17
55
  try {
18
56
  url = new URL(options.baseUrl ?? "https://app.hue.run");
@@ -20,13 +58,21 @@ export function validateOptions(options) {
20
58
  catch {
21
59
  throw new TypeError("Invalid Hue baseUrl");
22
60
  }
23
- const loopback = url.hostname === "localhost" || url.hostname === "127.0.0.1" || url.hostname === "[::1]";
24
- if (url.protocol !== "https:" && !(url.protocol === "http:" && loopback))
25
- throw new TypeError("Hue requires HTTPS except for a loopback development server");
61
+ if (url.protocol !== "https:" && url.protocol !== "http:")
62
+ throw new TypeError("Hue baseUrl must use https, or http for a loopback development server");
63
+ if (url.protocol === "http:" &&
64
+ !isLoopbackHost(url.hostname) &&
65
+ options.allowInsecureHttp !== true)
66
+ throw new TypeError("Hue requires HTTPS except for a loopback development server; set allowInsecureHttp: true to export over plain HTTP to another host");
26
67
  if (url.username || url.password || url.pathname !== "/" || url.search || url.hash)
27
68
  throw new TypeError("Hue baseUrl must be an origin without credentials, a path, query parameters or fragments");
28
69
  const timeoutMillis = options.timeoutMillis ?? 10000;
29
70
  if (!Number.isInteger(timeoutMillis) || timeoutMillis < 100 || timeoutMillis > 60000)
30
71
  throw new TypeError("timeoutMillis must be 100–60000");
31
- return { ...options, baseUrl: url.origin, timeoutMillis };
72
+ const maxQueueBytes = options.maxQueueBytes ?? 8 * 1024 * 1024;
73
+ if (!Number.isSafeInteger(maxQueueBytes) ||
74
+ maxQueueBytes < 1024 ||
75
+ maxQueueBytes > 64 * 1024 * 1024)
76
+ throw new TypeError("maxQueueBytes must be 1024–67108864");
77
+ return { ...options, baseUrl: url.origin, timeoutMillis, maxQueueBytes };
32
78
  }
@@ -0,0 +1,73 @@
1
+ import type { ActionInput, ActionResult, CoverageGapInput, CoverageGapResult, CreateRunInput, Environment, EnvironmentDefinition, EnvironmentIdentity, EnvironmentPage, EnvironmentPageOptions, EnvironmentRun, EnvironmentSummary, EnvironmentVersion, EnvironmentVersionSummary, FinishRunInput, SealedRun, StepPage, StepPageOptions } from "./types.js";
2
+ /** Connection and retry options for {@link createEnvironmentClient}. */
3
+ export interface EnvironmentClientOptions {
4
+ /** Project service key sent as a bearer token; server-side only. */
5
+ apiKey: string;
6
+ /** Hue origin; defaults to `https://app.hue.run`. */
7
+ baseUrl?: string;
8
+ /** Per-request deadline in milliseconds. */
9
+ timeoutMillis?: number;
10
+ /** Attempts for idempotent run mutations, 1–10; defaults to 4. */
11
+ maxAttempts?: number;
12
+ }
13
+ /** Sanitized environment API failure that never includes response text or credentials. */
14
+ export declare class HueEnvironmentError extends Error {
15
+ /** HTTP status when Hue answered; absent for transport, timeout or parse failure. */
16
+ readonly status?: number | undefined;
17
+ constructor(
18
+ /** HTTP status when Hue answered; absent for transport, timeout or parse failure. */
19
+ status?: number | undefined);
20
+ }
21
+ /** Typed client for authored environments, isolated runs and immutable journals. */
22
+ export declare class EnvironmentClient {
23
+ /** Validated Hue origin. */
24
+ readonly baseUrl: string;
25
+ private readonly apiKey;
26
+ private readonly timeoutMillis;
27
+ private readonly maxAttempts;
28
+ constructor(options: EnvironmentClientOptions);
29
+ private send;
30
+ private request;
31
+ private requestOnce;
32
+ private page;
33
+ /** Creates an environment identity; this non-idempotent registry write is not retried. */
34
+ createEnvironment(input: EnvironmentIdentity): Promise<EnvironmentSummary>;
35
+ /** Lists active environment identities. */
36
+ listEnvironments(page?: EnvironmentPageOptions): Promise<EnvironmentPage>;
37
+ /** Reads one environment and its immutable version summaries. */
38
+ getEnvironment(id: string): Promise<Environment>;
39
+ /** Publishes an immutable definition; this non-idempotent registry write is not retried. */
40
+ publishVersion(environmentId: string, definition: EnvironmentDefinition): Promise<EnvironmentVersionSummary>;
41
+ /** Reads a full immutable environment version and generated action catalog. */
42
+ getVersion(id: string): Promise<EnvironmentVersion>;
43
+ /** Creates or recovers one fresh isolated world using a stable idempotency key. */
44
+ createRun(input: CreateRunInput): Promise<EnvironmentRun>;
45
+ /** Reads authoritative current or sealed world state. */
46
+ getRun(runId: string): Promise<{
47
+ id: string;
48
+ environmentVersionId: string;
49
+ executionId: string | null;
50
+ seed: string;
51
+ status: "open" | "completed" | "abandoned" | "expired";
52
+ stepCount: number;
53
+ maxSteps: number;
54
+ clockNs: string;
55
+ expiresAt: string;
56
+ createdAt: string;
57
+ sealedAt: string | null;
58
+ stateDigest: string;
59
+ finalState?: import("./types.js").JsonValue;
60
+ validity: "environment_incomplete" | "not_assessed";
61
+ coverageGap: import("./types.js").CoverageGap | null;
62
+ }>;
63
+ /** Record a known coverage gap with durable identity; retries reuse the exact request. */
64
+ recordCoverageGap(runId: string, input: CoverageGapInput): Promise<CoverageGapResult>;
65
+ /** Invokes an action; repeating an invocation identity replays its recorded result. */
66
+ act(runId: string, input: ActionInput): Promise<ActionResult>;
67
+ /** Pages the immutable journal by step ordinal. */
68
+ listSteps(runId: string, page?: StepPageOptions): Promise<StepPage>;
69
+ /** Seals a world as completed or abandoned and freezes its evidence. */
70
+ finishRun(runId: string, input: FinishRunInput): Promise<SealedRun>;
71
+ }
72
+ /** Creates a typed simulated-environment client. */
73
+ export declare function createEnvironmentClient(options: EnvironmentClientOptions): EnvironmentClient;
@@ -0,0 +1,209 @@
1
+ import { validateOptions } from "../config.js";
2
+ import { aggregateBounds, json, uuid, valueBounds } from "../evals/json.js";
3
+ /** Sanitized environment API failure that never includes response text or credentials. */
4
+ export class HueEnvironmentError extends Error {
5
+ status;
6
+ constructor(
7
+ /** HTTP status when Hue answered; absent for transport, timeout or parse failure. */
8
+ status) {
9
+ super(status
10
+ ? `Hue environment request failed (HTTP ${status})`
11
+ : "Hue environment connection or response failed");
12
+ this.status = status;
13
+ this.name = "HueEnvironmentError";
14
+ }
15
+ }
16
+ const RETRYABLE = new Set([408, 429, 500, 502, 503, 504]);
17
+ const MAX_RESPONSE_BYTES = 4 * 1024 * 1024;
18
+ const REQUEST_BOUNDS = { ...valueBounds, bytes: 1024 * 1024 };
19
+ /** The server bounds JSON inside each entity independently, then permits the parsed
20
+ * definition to contain up to 240 KB. The publication wrapper adds one node/depth
21
+ * level and a small fixed byte prefix. Aggregate bounds cover every server-valid
22
+ * definition without relaxing action/run request envelopes.
23
+ */
24
+ const ENVIRONMENT_PUBLICATION_BOUNDS = aggregateBounds(240_000 + 32);
25
+ /** Typed client for authored environments, isolated runs and immutable journals. */
26
+ export class EnvironmentClient {
27
+ /** Validated Hue origin. */
28
+ baseUrl;
29
+ apiKey;
30
+ timeoutMillis;
31
+ maxAttempts;
32
+ constructor(options) {
33
+ const validated = validateOptions({
34
+ ...options,
35
+ serviceName: "hue-environments",
36
+ captureContent: false,
37
+ });
38
+ this.baseUrl = validated.baseUrl;
39
+ this.apiKey = validated.apiKey;
40
+ this.timeoutMillis = validated.timeoutMillis;
41
+ const attempts = options.maxAttempts ?? 4;
42
+ if (!Number.isInteger(attempts) || attempts < 1 || attempts > 10)
43
+ throw new RangeError("maxAttempts must be 1–10");
44
+ this.maxAttempts = attempts;
45
+ }
46
+ async send(method, path, payload) {
47
+ let response;
48
+ try {
49
+ response = await fetch(`${this.baseUrl}/api/v1${path}`, {
50
+ method,
51
+ headers: {
52
+ Authorization: `Bearer ${this.apiKey}`,
53
+ ...(payload ? { "Content-Type": "application/json" } : {}),
54
+ },
55
+ body: payload,
56
+ redirect: "error",
57
+ signal: AbortSignal.timeout(this.timeoutMillis),
58
+ });
59
+ }
60
+ catch {
61
+ throw new HueEnvironmentError();
62
+ }
63
+ if (!response.ok) {
64
+ await response.body?.cancel();
65
+ throw new HueEnvironmentError(response.status);
66
+ }
67
+ try {
68
+ const reader = response.body?.getReader();
69
+ if (!reader)
70
+ throw new Error("Missing response");
71
+ const chunks = [];
72
+ let size = 0;
73
+ try {
74
+ for (;;) {
75
+ const { done, value } = await reader.read();
76
+ if (done)
77
+ break;
78
+ size += value.byteLength;
79
+ if (size > MAX_RESPONSE_BYTES)
80
+ throw new Error("Oversized response");
81
+ chunks.push(value);
82
+ }
83
+ }
84
+ finally {
85
+ await reader.cancel();
86
+ }
87
+ return JSON.parse(Buffer.concat(chunks).toString("utf8"));
88
+ }
89
+ catch {
90
+ throw new HueEnvironmentError();
91
+ }
92
+ }
93
+ async request(method, path, body) {
94
+ // Serialize once: a body this client cannot encode is a caller error that no retry fixes.
95
+ const payload = body === undefined
96
+ ? undefined
97
+ : JSON.stringify(json(Object.fromEntries(Object.entries(body).filter(([, value]) => value !== undefined)), REQUEST_BOUNDS));
98
+ for (let attempt = 1;; attempt++) {
99
+ try {
100
+ return await this.send(method, path, payload);
101
+ }
102
+ catch (error) {
103
+ if (!(error instanceof HueEnvironmentError))
104
+ throw error;
105
+ const recoverable = error.status === undefined || RETRYABLE.has(error.status);
106
+ if (!recoverable || attempt >= this.maxAttempts)
107
+ throw error;
108
+ const backoff = Math.min(100 * 2 ** (attempt - 1), 2000);
109
+ await new Promise((resolve) => setTimeout(resolve, backoff + Math.random() * backoff));
110
+ }
111
+ }
112
+ }
113
+ requestOnce(method, path, body, bounds = REQUEST_BOUNDS) {
114
+ const payload = body === undefined
115
+ ? undefined
116
+ : JSON.stringify(json(Object.fromEntries(Object.entries(body).filter(([, value]) => value !== undefined)), bounds));
117
+ return this.send(method, path, payload);
118
+ }
119
+ page(options = {}) {
120
+ const query = new URLSearchParams();
121
+ if (options.after)
122
+ query.set("after", uuid(options.after));
123
+ if (options.limit !== undefined) {
124
+ if (!Number.isInteger(options.limit) || options.limit < 1 || options.limit > 100)
125
+ throw new RangeError("Page limit must be 1–100");
126
+ query.set("limit", String(options.limit));
127
+ }
128
+ return query.size ? `?${query}` : "";
129
+ }
130
+ /** Creates an environment identity; this non-idempotent registry write is not retried. */
131
+ createEnvironment(input) {
132
+ return this.requestOnce("POST", "/environments", input);
133
+ }
134
+ /** Lists active environment identities. */
135
+ listEnvironments(page) {
136
+ return this.request("GET", `/environments${this.page(page)}`);
137
+ }
138
+ /** Reads one environment and its immutable version summaries. */
139
+ getEnvironment(id) {
140
+ return this.request("GET", `/environments/${uuid(id)}`);
141
+ }
142
+ /** Publishes an immutable definition; this non-idempotent registry write is not retried. */
143
+ publishVersion(environmentId, definition) {
144
+ return this.requestOnce("POST", `/environments/${uuid(environmentId)}/versions`, { definition }, ENVIRONMENT_PUBLICATION_BOUNDS);
145
+ }
146
+ /** Reads a full immutable environment version and generated action catalog. */
147
+ getVersion(id) {
148
+ return this.request("GET", `/environment-versions/${uuid(id)}`);
149
+ }
150
+ /** Creates or recovers one fresh isolated world using a stable idempotency key. */
151
+ createRun(input) {
152
+ if (input.maxSteps !== undefined &&
153
+ (!Number.isInteger(input.maxSteps) || input.maxSteps < 1 || input.maxSteps > 500))
154
+ throw new RangeError("maxSteps must be 1–500");
155
+ if (input.ttlSeconds !== undefined &&
156
+ (!Number.isInteger(input.ttlSeconds) || input.ttlSeconds < 1 || input.ttlSeconds > 86_400))
157
+ throw new RangeError("ttlSeconds must be 1–86400");
158
+ if (input.seed !== undefined && !/^[a-f0-9]{32}$/.test(input.seed))
159
+ throw new TypeError("Seed must be 32 lowercase hexadecimal characters");
160
+ return this.request("POST", "/environment-runs", {
161
+ ...input,
162
+ environmentVersionId: uuid(input.environmentVersionId),
163
+ ...(input.executionId === undefined ? {} : { executionId: uuid(input.executionId) }),
164
+ });
165
+ }
166
+ /** Reads authoritative current or sealed world state. */
167
+ async getRun(runId) {
168
+ const run = await this.request("GET", `/environment-runs/${uuid(runId)}`);
169
+ return { validity: "not_assessed", coverageGap: null, ...run };
170
+ }
171
+ /** Record a known coverage gap with durable identity; retries reuse the exact request. */
172
+ recordCoverageGap(runId, input) {
173
+ uuid(input.idempotencyKey);
174
+ if (!input.args || typeof input.args !== "object" || Array.isArray(input.args))
175
+ throw new TypeError("Coverage gap arguments must be a JSON object");
176
+ json(input.args, { ...valueBounds, bytes: 16_000 });
177
+ return this.request("POST", `/environment-runs/${uuid(runId)}/coverage-gap`, input);
178
+ }
179
+ /** Invokes an action; repeating an invocation identity replays its recorded result. */
180
+ act(runId, input) {
181
+ return this.request("POST", `/environment-runs/${uuid(runId)}/actions`, {
182
+ ...input,
183
+ args: input.args ?? {},
184
+ });
185
+ }
186
+ /** Pages the immutable journal by step ordinal. */
187
+ listSteps(runId, page = {}) {
188
+ const query = new URLSearchParams();
189
+ if (page.after !== undefined) {
190
+ if (!Number.isInteger(page.after) || page.after < -1)
191
+ throw new RangeError("Step cursor must be an ordinal");
192
+ query.set("after", String(page.after));
193
+ }
194
+ if (page.limit !== undefined) {
195
+ if (!Number.isInteger(page.limit) || page.limit < 1 || page.limit > 100)
196
+ throw new RangeError("Page limit must be 1–100");
197
+ query.set("limit", String(page.limit));
198
+ }
199
+ return this.request("GET", `/environment-runs/${uuid(runId)}/steps${query.size ? `?${query}` : ""}`);
200
+ }
201
+ /** Seals a world as completed or abandoned and freezes its evidence. */
202
+ finishRun(runId, input) {
203
+ return this.request("POST", `/environment-runs/${uuid(runId)}/finish`, input);
204
+ }
205
+ }
206
+ /** Creates a typed simulated-environment client. */
207
+ export function createEnvironmentClient(options) {
208
+ return new EnvironmentClient(options);
209
+ }
@@ -0,0 +1,30 @@
1
+ import type { Context } from "@opentelemetry/api";
2
+ import type { HueClient } from "../client.js";
3
+ import type { EnvironmentClient } from "./client.js";
4
+ import type { ActionSchema, EnvironmentRun, JsonValue, Observation } from "./types.js";
5
+ /** Framework-neutral callable generated from one environment action. */
6
+ export interface EnvironmentTool {
7
+ /** Tool name. */
8
+ name: string;
9
+ /** Agent-visible description. */
10
+ description?: string;
11
+ /** JSON Schema input contract. */
12
+ inputSchema: ActionSchema;
13
+ /** Executes the action and resolves with the world's observation. */
14
+ execute(args?: Record<string, JsonValue>): Promise<Observation>;
15
+ }
16
+ /** Dependencies and tracing context for {@link bindEnvironmentTools}. */
17
+ export interface BindEnvironmentToolsOptions {
18
+ /** Hue client used to record each action as tool telemetry. */
19
+ hue: HueClient;
20
+ /** Environment client bound to the same Hue origin. */
21
+ client: Pick<EnvironmentClient, "act">;
22
+ /** Fresh environment run whose closed catalog becomes callables. */
23
+ run: EnvironmentRun;
24
+ /** Optional parent span context for generated tool spans. */
25
+ parentContext?: Context;
26
+ /** Optional durable invocation-ID factory for caller-owned resume state. */
27
+ invocationId?(action: string): string;
28
+ }
29
+ /** Binds a run's generated catalog to plain local callables without changing the agent framework. */
30
+ export declare function bindEnvironmentTools(options: BindEnvironmentToolsOptions): Record<string, EnvironmentTool>;
@@ -0,0 +1,24 @@
1
+ import { randomUUID } from "node:crypto";
2
+ /** Binds a run's generated catalog to plain local callables without changing the agent framework. */
3
+ export function bindEnvironmentTools(options) {
4
+ const tools = {};
5
+ for (const action of options.run.actions) {
6
+ tools[action.name] = {
7
+ name: action.name,
8
+ ...(action.description === undefined ? {} : { description: action.description }),
9
+ inputSchema: action.inputSchema,
10
+ execute: async (args = {}) => {
11
+ const invocationId = options.invocationId?.(action.name) ?? randomUUID();
12
+ const execute = async () => (await options.client.act(options.run.id, {
13
+ invocationId,
14
+ action: action.name,
15
+ args,
16
+ })).observation;
17
+ return options.hue.tool(action.name, args, execute, {
18
+ parentContext: options.parentContext,
19
+ });
20
+ },
21
+ };
22
+ }
23
+ return tools;
24
+ }