@codespring-app/use-agent 0.1.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/README.md ADDED
@@ -0,0 +1,164 @@
1
+ # `@codespring-app/use-agent`
2
+
3
+ The supported server and React SDK for CodeSpring Agents. It speaks a versioned
4
+ HTTP protocol and contains no Cloudflare runtime implementation, so the same
5
+ application can target CodeSpring-hosted or self-hosted endpoints.
6
+
7
+ ![An agent built with the use-agent React SDK](./docs/screenshots/default-agent.png)
8
+
9
+ ## Server
10
+
11
+ ```ts
12
+ import { createAgent, createClient } from "@codespring-app/use-agent";
13
+
14
+ const support = createAgent({
15
+ id: "support",
16
+ revision: "7",
17
+ instructions: "Help the customer clearly and safely.",
18
+ model: "production-default",
19
+ skills: [{ id: "returns", version: "1" }],
20
+ });
21
+
22
+ const agents = createClient({
23
+ endpoint: process.env.CODESPRING_AGENTS_ENDPOINT!,
24
+ apiKey: process.env.CODESPRING_AGENTS_API_KEY!,
25
+ });
26
+
27
+ const session = await agents.sessions.create(support);
28
+ await session.submit("Where is my order?", { idempotencyKey: crypto.randomUUID() });
29
+ ```
30
+
31
+ `production-default` is a reusable, tenant-scoped virtual model profile—not a
32
+ provider model name and not an agent/use-case configuration. Multiple agents can
33
+ reference it. The control plane maps it to encrypted BYOK connections, provider
34
+ candidates, fallback, budgets, and policy. Publishing resolves the profile to an
35
+ immutable policy revision while credential rotation remains independent.
36
+
37
+ API keys are server-only. Do not pass the server client into a browser bundle.
38
+
39
+ ## Plug-and-play React UI
40
+
41
+ ```tsx
42
+ import {
43
+ AgentChat,
44
+ AgentProvider,
45
+ createAgentAppearance,
46
+ createAgentClient,
47
+ } from "@codespring-app/use-agent/react";
48
+
49
+ const agentClient = createAgentClient({
50
+ endpoint: "https://api.agents.codespring.app/browser",
51
+ clientTokenEndpoint: "/api/agents/token",
52
+ });
53
+
54
+ const acmeAppearance = createAgentAppearance({
55
+ theme: { accent: "#2856D8" },
56
+ copy: { placeholder: "Ask us anything" },
57
+ });
58
+
59
+ export function App({ sessionId }: { sessionId: string }) {
60
+ return (
61
+ <AgentProvider client={agentClient} appearance={acmeAppearance}>
62
+ <AgentChat sessionId={sessionId} />
63
+ </AgentProvider>
64
+ );
65
+ }
66
+ ```
67
+
68
+ `/api/agents/token` returns `{ token, expiresAt }`. The SDK caches it in memory,
69
+ deduplicates concurrent refreshes, refreshes before expiry, and retries once
70
+ after a 401. It never persists the token. `createAgentAppearance` produces a
71
+ frozen, reusable preset so unrelated renders do not invalidate theme/copy
72
+ consumers; use `useMemo` when appearance must be dynamic.
73
+
74
+ The `/browser` endpoint is intentional: it accepts only short-lived client
75
+ tokens and is the only runtime surface with browser CORS. Server API keys stay
76
+ on the endpoint root and must never be shipped to a browser.
77
+
78
+ The default Paper experience renders assistant replies as document content on
79
+ an edge-to-edge canvas, user messages as quiet trailing wells, tool calls as
80
+ compact inspectable activity rows, and the live-edge composer without a shadow.
81
+ `paperLightTheme`, `paperDarkTheme`, theme/copy overrides, slots, and render
82
+ functions are available for customization.
83
+
84
+ ## CSS variables, Tailwind CSS, and StyleX
85
+
86
+ Every default component resolves theme tokens through inherited
87
+ `--codespring-agent-*` custom properties. Variables override the appearance
88
+ preset; unset variables use the selected Paper or custom appearance value as a
89
+ fallback.
90
+
91
+ ```css
92
+ .acme-agent-theme {
93
+ --codespring-agent-accent: #2856d8;
94
+ --codespring-agent-container-radius: 18px;
95
+ --codespring-agent-content-max-width: 52rem;
96
+ }
97
+ ```
98
+
99
+ The public names are also exported as `agentThemeVariables`. Supported tokens
100
+ are `canvas`, `ink`, `inkSecondary`, `inkTertiary`, `well`, `hairline`,
101
+ `statusGood`, `statusBad`, `statusWarn`, `accent`, `fontFamily`, `monoFamily`,
102
+ `contentMaxWidth`, `containerRadius`, and `wellRadius`.
103
+
104
+ Tailwind CSS can set the variables on any ancestor:
105
+
106
+ ```css
107
+ @theme {
108
+ --color-acme-primary: #2856d8;
109
+ }
110
+ ```
111
+
112
+ ```tsx
113
+ <div className="[--codespring-agent-accent:var(--color-acme-primary)] [--codespring-agent-container-radius:18px]">
114
+ <AgentProvider client={agentClient} appearance={acmeAppearance}>
115
+ <AgentChat sessionId={sessionId} />
116
+ </AgentProvider>
117
+ </div>
118
+ ```
119
+
120
+ StyleX variables can be used as appearance values and themed from an ancestor:
121
+
122
+ ```tsx
123
+ // agent-theme.stylex.ts
124
+ import * as stylex from "@stylexjs/stylex";
125
+
126
+ export const agentTokens = stylex.defineVars({ accent: "#3B6AC5" });
127
+ ```
128
+
129
+ ```tsx
130
+ import * as stylex from "@stylexjs/stylex";
131
+ import { agentTokens } from "./agent-theme.stylex";
132
+
133
+ const brandedTheme = stylex.createTheme(agentTokens, { accent: "#2856D8" });
134
+ const stylexAppearance = createAgentAppearance({
135
+ theme: { accent: agentTokens.accent },
136
+ });
137
+
138
+ <div {...stylex.props(brandedTheme)}>
139
+ <AgentProvider client={agentClient} appearance={stylexAppearance}>
140
+ <AgentChat sessionId={sessionId} />
141
+ </AgentProvider>
142
+ </div>;
143
+ ```
144
+
145
+ ## Headless React
146
+
147
+ Advanced clients can use `useAgentSession`, `useAgentMessages`,
148
+ `useAgentToolCalls`, `useAgentClient`, `useAgentTheme`, and `useAgentCopy` to
149
+ build a completely custom interface. The composable `AgentMessageList`,
150
+ `AgentMessage`, `AgentToolCall`, and `AgentComposer` primitives can also be
151
+ mixed with client-owned components.
152
+
153
+ The browser entrypoint never accepts an API key. A trusted application backend
154
+ must issue short-lived, origin-bound client tokens.
155
+
156
+ ## Local showcase
157
+
158
+ ```sh
159
+ bun run showcase
160
+ ```
161
+
162
+ Open `http://127.0.0.1:5173` for the Paper UI or append `?theme=dark` for
163
+ the dark palette. The showcase uses mocked durable events and makes no
164
+ external API calls.
@@ -0,0 +1,220 @@
1
+ // src/client.ts
2
+ var AgentError = class extends Error {
3
+ constructor(message, status, code, requestId, details) {
4
+ super(message);
5
+ this.status = status;
6
+ this.code = code;
7
+ this.requestId = requestId;
8
+ this.details = details;
9
+ this.name = "AgentError";
10
+ }
11
+ status;
12
+ code;
13
+ requestId;
14
+ details;
15
+ };
16
+ var Transport = class {
17
+ constructor(options) {
18
+ this.options = options;
19
+ this.endpoint = normalizeEndpoint(options.endpoint);
20
+ this.fetchImplementation = options.fetch ?? globalThis.fetch;
21
+ if (!this.fetchImplementation) throw new TypeError("A fetch implementation is required");
22
+ }
23
+ options;
24
+ endpoint;
25
+ fetchImplementation;
26
+ async request(path, init = {}) {
27
+ let response = await this.fetchWithToken(path, init);
28
+ if (response.status === 401 && this.options.token.invalidate()) {
29
+ response = await this.fetchWithToken(path, init);
30
+ }
31
+ const requestId = response.headers.get("x-request-id") ?? void 0;
32
+ const payload = await readJson(response);
33
+ if (!response.ok) {
34
+ const error = isObject(payload) && isObject(payload.error) ? payload.error : void 0;
35
+ throw new AgentError(
36
+ typeof error?.message === "string" ? error.message : `Use Agent request failed with ${response.status}`,
37
+ response.status,
38
+ typeof error?.code === "string" ? error.code : "request_failed",
39
+ requestId,
40
+ error?.details
41
+ );
42
+ }
43
+ return payload;
44
+ }
45
+ async fetchWithToken(path, init) {
46
+ const headers = new Headers(init.headers);
47
+ headers.set("Accept", "application/json");
48
+ headers.set("Authorization", `Bearer ${await this.options.token.get()}`);
49
+ if (init.body !== void 0) headers.set("Content-Type", "application/json");
50
+ return this.fetchImplementation(`${this.endpoint}${path}`, { ...init, headers });
51
+ }
52
+ };
53
+ function jwtExpiry(token) {
54
+ const encoded = token.split(".")[1];
55
+ if (!encoded || typeof globalThis.atob !== "function") return void 0;
56
+ try {
57
+ const normalized = encoded.replace(/-/gu, "+").replace(/_/gu, "/");
58
+ const payload = JSON.parse(globalThis.atob(normalized.padEnd(Math.ceil(normalized.length / 4) * 4, "=")));
59
+ return typeof payload.exp === "number" ? payload.exp * 1e3 : void 0;
60
+ } catch {
61
+ return void 0;
62
+ }
63
+ }
64
+ function explicitExpiry(value) {
65
+ if (typeof value === "number") return value < 1e12 ? value * 1e3 : value;
66
+ if (typeof value !== "string") return void 0;
67
+ const parsed = Date.parse(value);
68
+ return Number.isFinite(parsed) ? parsed : void 0;
69
+ }
70
+ var CachedTokenProvider = class {
71
+ constructor(load, fallbackTtlMs, refreshSkewMs) {
72
+ this.load = load;
73
+ this.fallbackTtlMs = fallbackTtlMs;
74
+ this.refreshSkewMs = refreshSkewMs;
75
+ }
76
+ load;
77
+ fallbackTtlMs;
78
+ refreshSkewMs;
79
+ cached;
80
+ inFlight;
81
+ get = async () => {
82
+ if (this.cached && Date.now() < this.cached.refreshAt) return this.cached.token;
83
+ if (this.inFlight) return this.inFlight;
84
+ this.inFlight = this.refresh();
85
+ try {
86
+ return await this.inFlight;
87
+ } finally {
88
+ this.inFlight = void 0;
89
+ }
90
+ };
91
+ invalidate = () => {
92
+ this.cached = void 0;
93
+ return true;
94
+ };
95
+ async refresh() {
96
+ const loaded = await this.load();
97
+ const token = typeof loaded === "string" ? loaded : loaded.token;
98
+ if (!token.trim()) throw new TypeError("getClientToken returned an empty token");
99
+ const now = Date.now();
100
+ const expiry = (typeof loaded === "string" ? void 0 : explicitExpiry(loaded.expiresAt)) ?? jwtExpiry(token) ?? now + this.fallbackTtlMs;
101
+ const lifetime = Math.max(1e3, expiry - now);
102
+ this.cached = {
103
+ token,
104
+ refreshAt: expiry - Math.min(this.refreshSkewMs, Math.max(500, lifetime / 2))
105
+ };
106
+ return token;
107
+ }
108
+ };
109
+ var staticTokenProvider = (token) => ({
110
+ get: async () => token,
111
+ invalidate: () => false
112
+ });
113
+ var isObject = (value) => value !== null && typeof value === "object";
114
+ async function readJson(response) {
115
+ const contentType = response.headers.get("content-type") ?? "";
116
+ if (!contentType.includes("application/json")) {
117
+ if (!response.ok) return void 0;
118
+ throw new AgentError("Runtime returned a non-JSON response", response.status, "invalid_response");
119
+ }
120
+ return response.json();
121
+ }
122
+ function normalizeEndpoint(endpoint) {
123
+ const parsed = new URL(endpoint);
124
+ if (parsed.protocol !== "https:" && parsed.hostname !== "localhost" && parsed.hostname !== "127.0.0.1") {
125
+ throw new TypeError("endpoint must use HTTPS outside local development");
126
+ }
127
+ parsed.pathname = parsed.pathname.replace(/\/$/u, "");
128
+ parsed.search = "";
129
+ parsed.hash = "";
130
+ return parsed.toString().replace(/\/$/u, "");
131
+ }
132
+ var randomIdempotencyKey = () => {
133
+ if (globalThis.crypto?.randomUUID) return globalThis.crypto.randomUUID();
134
+ throw new Error("crypto.randomUUID is required when no idempotency key is supplied");
135
+ };
136
+ var AgentSession = class {
137
+ constructor(transport, id) {
138
+ this.transport = transport;
139
+ this.id = id;
140
+ }
141
+ transport;
142
+ id;
143
+ get(options = {}) {
144
+ return this.transport.request(`/v1/sessions/${encodeURIComponent(this.id)}`, {
145
+ ...options.signal === void 0 ? {} : { signal: options.signal }
146
+ });
147
+ }
148
+ submit(content, options = {}) {
149
+ const idempotencyKey = options.idempotencyKey ?? randomIdempotencyKey();
150
+ return this.transport.request(`/v1/sessions/${encodeURIComponent(this.id)}/turns`, {
151
+ method: "POST",
152
+ headers: { "Idempotency-Key": idempotencyKey },
153
+ body: JSON.stringify({ content }),
154
+ ...options.signal === void 0 ? {} : { signal: options.signal }
155
+ });
156
+ }
157
+ events(after = 0, limit = 100, options = {}) {
158
+ const query = new URLSearchParams({ after: String(after), limit: String(limit) });
159
+ return this.transport.request(`/v1/sessions/${encodeURIComponent(this.id)}/events?${query}`, {
160
+ ...options.signal === void 0 ? {} : { signal: options.signal }
161
+ });
162
+ }
163
+ async cancel(turnId, options = {}) {
164
+ return this.transport.request(
165
+ `/v1/sessions/${encodeURIComponent(this.id)}/turns/${encodeURIComponent(turnId)}/cancel`,
166
+ {
167
+ method: "POST",
168
+ ...options.signal === void 0 ? {} : { signal: options.signal }
169
+ }
170
+ );
171
+ }
172
+ };
173
+ var AgentClient = class {
174
+ constructor(transport) {
175
+ this.transport = transport;
176
+ }
177
+ transport;
178
+ sessions = {
179
+ create: async (agent, options = {}) => {
180
+ const created = await this.transport.request("/v1/sessions", {
181
+ method: "POST",
182
+ body: JSON.stringify({ agentRevisionId: agent.revisionId }),
183
+ ...options.signal === void 0 ? {} : { signal: options.signal }
184
+ });
185
+ return new AgentSession(this.transport, created.sessionId);
186
+ },
187
+ get: (sessionId) => new AgentSession(this.transport, sessionId)
188
+ };
189
+ };
190
+ function createClient(options) {
191
+ if (!options.apiKey.trim()) throw new TypeError("apiKey is required");
192
+ return new AgentClient(
193
+ new Transport({
194
+ endpoint: options.endpoint,
195
+ token: staticTokenProvider(options.apiKey),
196
+ ...options.fetch === void 0 ? {} : { fetch: options.fetch }
197
+ })
198
+ );
199
+ }
200
+ function createBrowserClient(options) {
201
+ return new AgentClient(
202
+ new Transport({
203
+ endpoint: options.endpoint,
204
+ token: new CachedTokenProvider(
205
+ options.getClientToken,
206
+ options.clientTokenTtlMs ?? 6e4,
207
+ options.refreshSkewMs ?? 3e4
208
+ ),
209
+ ...options.fetch === void 0 ? {} : { fetch: options.fetch }
210
+ })
211
+ );
212
+ }
213
+
214
+ export {
215
+ AgentError,
216
+ AgentSession,
217
+ AgentClient,
218
+ createClient,
219
+ createBrowserClient
220
+ };
@@ -0,0 +1,156 @@
1
+ type TurnStatus = "queued" | "running" | "completed" | "failed" | "cancelled";
2
+ interface AgentEvent {
3
+ schemaVersion: 1;
4
+ id: number;
5
+ sessionId: string;
6
+ turnId?: string;
7
+ attempt: number;
8
+ type: string;
9
+ createdAt: string;
10
+ data: unknown;
11
+ }
12
+ interface SessionSnapshot {
13
+ sessionId: string;
14
+ agentRevisionId: string;
15
+ createdAt: string;
16
+ updatedAt: string;
17
+ cursor: number;
18
+ turns: Array<{
19
+ id: string;
20
+ status: TurnStatus;
21
+ attempt: number;
22
+ createdAt: string;
23
+ updatedAt: string;
24
+ }>;
25
+ }
26
+ interface CreateSessionResponse {
27
+ sessionId: string;
28
+ agentRevisionId: string;
29
+ createdAt: string;
30
+ }
31
+ interface SubmitTurnResponse {
32
+ sessionId: string;
33
+ turnId: string;
34
+ status: TurnStatus;
35
+ cursor: number;
36
+ duplicate: boolean;
37
+ }
38
+ interface ListEventsResponse {
39
+ events: AgentEvent[];
40
+ cursor: number;
41
+ hasMore: boolean;
42
+ }
43
+ /** Tenant-scoped model profile configured in the CodeSpring control plane. */
44
+ type ModelProfileId = string;
45
+ interface AgentToolReference {
46
+ name: string;
47
+ description?: string;
48
+ }
49
+ interface AgentMcpServerReference {
50
+ id: string;
51
+ allowedTools?: string[];
52
+ }
53
+ interface AgentSkillReference {
54
+ id: string;
55
+ version?: string;
56
+ }
57
+ interface AgentDefinition {
58
+ readonly id: string;
59
+ readonly revision: string;
60
+ readonly revisionId: string;
61
+ readonly instructions?: string;
62
+ readonly model?: ModelProfileId;
63
+ readonly tools: readonly AgentToolReference[];
64
+ readonly mcpServers: readonly AgentMcpServerReference[];
65
+ readonly skills: readonly AgentSkillReference[];
66
+ readonly metadata: Readonly<Record<string, string>>;
67
+ }
68
+ interface CreateAgentOptions {
69
+ id: string;
70
+ revision: string;
71
+ instructions?: string;
72
+ model?: ModelProfileId;
73
+ tools?: AgentToolReference[];
74
+ mcpServers?: AgentMcpServerReference[];
75
+ skills?: AgentSkillReference[];
76
+ metadata?: Record<string, string>;
77
+ }
78
+ interface RequestOptions {
79
+ signal?: AbortSignal;
80
+ }
81
+ interface SubmitOptions extends RequestOptions {
82
+ idempotencyKey?: string;
83
+ }
84
+ type FetchLike = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
85
+ interface AgentClientOptions {
86
+ endpoint: string;
87
+ apiKey: string;
88
+ fetch?: FetchLike;
89
+ }
90
+ interface BrowserAgentClientOptions {
91
+ endpoint: string;
92
+ getClientToken: () => Promise<ClientTokenResult>;
93
+ /** Fallback lifetime for opaque legacy tokens without an expiry. Defaults to 60 seconds. */
94
+ clientTokenTtlMs?: number;
95
+ /** Refresh before expiry. Defaults to 30 seconds and is bounded for short tokens. */
96
+ refreshSkewMs?: number;
97
+ fetch?: FetchLike;
98
+ }
99
+ type ClientTokenResult = string | {
100
+ token: string;
101
+ /** ISO timestamp or Unix time in seconds/milliseconds. */
102
+ expiresAt?: string | number;
103
+ };
104
+
105
+ declare class AgentError extends Error {
106
+ readonly status: number;
107
+ readonly code: string;
108
+ readonly requestId?: string | undefined;
109
+ readonly details?: unknown | undefined;
110
+ constructor(message: string, status: number, code: string, requestId?: string | undefined, details?: unknown | undefined);
111
+ }
112
+ interface TokenProvider {
113
+ get: () => Promise<string>;
114
+ invalidate: () => boolean;
115
+ }
116
+ interface TransportOptions {
117
+ endpoint: string;
118
+ token: TokenProvider;
119
+ fetch?: FetchLike;
120
+ }
121
+ declare class Transport {
122
+ private readonly options;
123
+ readonly endpoint: string;
124
+ readonly fetchImplementation: FetchLike;
125
+ constructor(options: TransportOptions);
126
+ request<T>(path: string, init?: RequestInit): Promise<T>;
127
+ private fetchWithToken;
128
+ }
129
+ declare class AgentSession {
130
+ private readonly transport;
131
+ readonly id: string;
132
+ constructor(transport: Transport, id: string);
133
+ get(options?: RequestOptions): Promise<SessionSnapshot>;
134
+ submit(content: string, options?: SubmitOptions): Promise<SubmitTurnResponse>;
135
+ events(after?: number, limit?: number, options?: RequestOptions): Promise<ListEventsResponse>;
136
+ cancel(turnId: string, options?: RequestOptions): Promise<TurnStatusResponse>;
137
+ }
138
+ interface TurnStatusResponse {
139
+ sessionId: string;
140
+ turnId: string;
141
+ status: string;
142
+ }
143
+ declare class AgentClient {
144
+ private readonly transport;
145
+ constructor(transport: Transport);
146
+ readonly sessions: {
147
+ create: (agent: AgentDefinition, options?: RequestOptions) => Promise<AgentSession>;
148
+ get: (sessionId: string) => AgentSession;
149
+ };
150
+ }
151
+ /** Server entrypoint. Never pass this client or its API key into a browser bundle. */
152
+ declare function createClient(options: AgentClientOptions): AgentClient;
153
+ /** Browser-safe client used by the React subpath with short-lived client tokens. */
154
+ declare function createBrowserClient(options: BrowserAgentClientOptions): AgentClient;
155
+
156
+ export { type AgentDefinition as A, type BrowserAgentClientOptions as B, type CreateAgentOptions as C, type FetchLike as F, type ListEventsResponse as L, type ModelProfileId as M, type RequestOptions as R, type SessionSnapshot as S, type TurnStatus as T, AgentClient as a, type AgentClientOptions as b, AgentError as c, type AgentEvent as d, type AgentMcpServerReference as e, AgentSession as f, type AgentSkillReference as g, type AgentToolReference as h, type ClientTokenResult as i, type CreateSessionResponse as j, type SubmitOptions as k, type SubmitTurnResponse as l, createBrowserClient as m, createClient as n };
@@ -0,0 +1,7 @@
1
+ import { C as CreateAgentOptions, A as AgentDefinition } from './client-Bz3eVQXx.js';
2
+ export { a as AgentClient, b as AgentClientOptions, c as AgentError, d as AgentEvent, e as AgentMcpServerReference, f as AgentSession, g as AgentSkillReference, h as AgentToolReference, B as BrowserAgentClientOptions, i as ClientTokenResult, j as CreateSessionResponse, F as FetchLike, L as ListEventsResponse, M as ModelProfileId, R as RequestOptions, S as SessionSnapshot, k as SubmitOptions, l as SubmitTurnResponse, T as TurnStatus, m as createBrowserClient, n as createClient } from './client-Bz3eVQXx.js';
3
+
4
+ /** Defines a portable agent revision without importing runtime implementation code. */
5
+ declare function createAgent(options: CreateAgentOptions): AgentDefinition;
6
+
7
+ export { AgentDefinition, CreateAgentOptions, createAgent };
package/dist/index.js ADDED
@@ -0,0 +1,48 @@
1
+ import {
2
+ AgentClient,
3
+ AgentError,
4
+ AgentSession,
5
+ createBrowserClient,
6
+ createClient
7
+ } from "./chunk-FAD2XMPA.js";
8
+
9
+ // src/agent.ts
10
+ var identifierPattern = /^[a-zA-Z0-9][a-zA-Z0-9._-]{0,127}$/u;
11
+ function requireIdentifier(value, name) {
12
+ const normalized = value.trim();
13
+ if (!identifierPattern.test(normalized)) {
14
+ throw new TypeError(`${name} must match ${identifierPattern.source}`);
15
+ }
16
+ return normalized;
17
+ }
18
+ function uniqueNames(values, name) {
19
+ const normalized = values.map((value) => value.name.trim());
20
+ if (new Set(normalized).size !== normalized.length) {
21
+ throw new TypeError(`${name} names must be unique`);
22
+ }
23
+ }
24
+ function createAgent(options) {
25
+ const id = requireIdentifier(options.id, "id");
26
+ const revision = requireIdentifier(options.revision, "revision");
27
+ const tools = (options.tools ?? []).map((tool) => ({ ...tool, name: tool.name.trim() }));
28
+ uniqueNames(tools, "tool");
29
+ return Object.freeze({
30
+ id,
31
+ revision,
32
+ revisionId: `${id}@${revision}`,
33
+ ...options.instructions === void 0 ? {} : { instructions: options.instructions },
34
+ ...options.model === void 0 ? {} : { model: requireIdentifier(options.model, "model") },
35
+ tools: Object.freeze(tools.map((tool) => Object.freeze(tool))),
36
+ mcpServers: Object.freeze((options.mcpServers ?? []).map((server) => Object.freeze({ ...server }))),
37
+ skills: Object.freeze((options.skills ?? []).map((skill) => Object.freeze({ ...skill }))),
38
+ metadata: Object.freeze({ ...options.metadata ?? {} })
39
+ });
40
+ }
41
+ export {
42
+ AgentClient,
43
+ AgentError,
44
+ AgentSession,
45
+ createAgent,
46
+ createBrowserClient,
47
+ createClient
48
+ };