@camelai/run 0.11.0 → 0.11.1
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 +5 -5
- package/dist/clients/agents.d.ts +1 -1
- package/dist/clients/agents.js +1 -1
- package/dist/clients/handler.d.ts +1 -1
- package/dist/clients/server.d.ts +3 -3
- package/dist/clients/server.js +12 -3
- package/dist/clients/typescript.d.ts +6 -3
- package/dist/clients/typescript.js +6 -4
- package/dist/clients/watch.js +3 -2
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ npm install @camelai/run
|
|
|
10
10
|
```
|
|
11
11
|
|
|
12
12
|
Node 22 or later, Bun, Deno or Cloudflare Workers. Get an API key from the
|
|
13
|
-
console at <https://
|
|
13
|
+
console at <https://run.camelai.com/console> and export it as
|
|
14
14
|
`CAMELAI_API_KEY`.
|
|
15
15
|
|
|
16
16
|
```ts
|
|
@@ -55,10 +55,10 @@ await agents.close();
|
|
|
55
55
|
`npm create @camelai/run-app`. `watchAgent` (`@camelai/run/watch`)
|
|
56
56
|
shows an agent live with a browser token your server mints.
|
|
57
57
|
|
|
58
|
-
Documentation: [Quickstart](https://
|
|
59
|
-
[Concepts](https://
|
|
60
|
-
[SDK reference](https://
|
|
61
|
-
and all of it as Markdown at <https://
|
|
58
|
+
Documentation: [Quickstart](https://run.camelai.com/docs/quickstart.md),
|
|
59
|
+
[Concepts](https://run.camelai.com/docs/concepts.md),
|
|
60
|
+
[SDK reference](https://run.camelai.com/docs/reference/sdk.md),
|
|
61
|
+
and all of it as Markdown at <https://run.camelai.com/llms.txt>.
|
|
62
62
|
|
|
63
63
|
`AgentRuntime` and `AgentClient`, the lower-level interface the SDK is built on,
|
|
64
64
|
remain available. See the SDK reference's "Changes in 0.9" when upgrading.
|
package/dist/clients/agents.d.ts
CHANGED
|
@@ -15,7 +15,7 @@ declare const INSPECT: unique symbol;
|
|
|
15
15
|
export interface AgentsOptions {
|
|
16
16
|
/** Your API key (the console's). Default: the CAMELAI_API_KEY environment variable. */
|
|
17
17
|
apiKey?: string;
|
|
18
|
-
/** The runtime's origin. Default: CAMELAI_BASE_URL, else https://
|
|
18
|
+
/** The runtime's origin. Default: CAMELAI_BASE_URL, else https://run.camelai.com. */
|
|
19
19
|
url?: string;
|
|
20
20
|
fetch?: typeof globalThis.fetch;
|
|
21
21
|
/** Opens a local file to attach by its path; the Node entry sets it. */
|
package/dist/clients/agents.js
CHANGED
|
@@ -36,7 +36,7 @@ export class Agents {
|
|
|
36
36
|
*/
|
|
37
37
|
async upsert(key, config = {}) {
|
|
38
38
|
if (!this.runtime.options.apiKey)
|
|
39
|
-
throw new AgentError("Set apiKey (or the CAMELAI_API_KEY environment variable): create a key in the console at https://
|
|
39
|
+
throw new AgentError("Set apiKey (or the CAMELAI_API_KEY environment variable): create a key in the console at https://run.camelai.com");
|
|
40
40
|
const options = createOptions(config);
|
|
41
41
|
const { session } = await this.runtime.upsertAgent(key, options);
|
|
42
42
|
// The upsert declared these tools already (between the agent's turns, if it runs).
|
|
@@ -46,7 +46,7 @@ export interface SendEvent<A extends AgentAuth = AgentAuth> {
|
|
|
46
46
|
export interface AgentHandlerOptions<A extends AgentAuth = AgentAuth> {
|
|
47
47
|
/** Your API key. Default: the CAMELAI_API_KEY environment variable. */
|
|
48
48
|
apiKey?: string;
|
|
49
|
-
/** The runtime's origin. Default: CAMELAI_BASE_URL, else https://
|
|
49
|
+
/** The runtime's origin. Default: CAMELAI_BASE_URL, else https://run.camelai.com. */
|
|
50
50
|
url?: string;
|
|
51
51
|
/**
|
|
52
52
|
* Your own session check, on every request. Return the user (and optionally which agent), or null to
|
package/dist/clients/server.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* whom it acts for and who is acting; these helpers verify it and hand your tools the identity.
|
|
5
5
|
* Portable: fetch-style handlers and WebCrypto (Ed25519), so it runs on Workers, Node 22+, Bun and Deno.
|
|
6
6
|
*
|
|
7
|
-
* export default { fetch: serveTools(tools, { runtime: "https://
|
|
7
|
+
* export default { fetch: serveTools(tools, { runtime: "https://run.camelai.com", tenant: "acme" }) };
|
|
8
8
|
*
|
|
9
9
|
* An identity means something only within your own tenant: other tenants' agents can be pointed at your
|
|
10
10
|
* server too, so every check here requires `tenant`, and refuses tokens made for anyone else's agents.
|
|
@@ -17,9 +17,9 @@ export interface VerifyOptions {
|
|
|
17
17
|
* refused. Required: another tenant can point its agents at your server and say they act for anyone.
|
|
18
18
|
*/
|
|
19
19
|
tenant: string | string[];
|
|
20
|
-
/** The runtime's URL (e.g. https://
|
|
20
|
+
/** The runtime's URL (e.g. https://run.camelai.com): its keys are at /.well-known/jwks.json. */
|
|
21
21
|
runtime: string;
|
|
22
|
-
/** The issuer tokens must name; the runtime's URL by default. */
|
|
22
|
+
/** The issuer tokens must name; the runtime's URL by default (camelRun's hosted runtime names itself https://agents.camelai.dev at either of its URLs). */
|
|
23
23
|
issuer?: string;
|
|
24
24
|
/** What tokens must be for: your server's URL as the runtime calls it (a definition's `url`, or its `audience`). */
|
|
25
25
|
audience: string | string[];
|
package/dist/clients/server.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* whom it acts for and who is acting; these helpers verify it and hand your tools the identity.
|
|
5
5
|
* Portable: fetch-style handlers and WebCrypto (Ed25519), so it runs on Workers, Node 22+, Bun and Deno.
|
|
6
6
|
*
|
|
7
|
-
* export default { fetch: serveTools(tools, { runtime: "https://
|
|
7
|
+
* export default { fetch: serveTools(tools, { runtime: "https://run.camelai.com", tenant: "acme" }) };
|
|
8
8
|
*
|
|
9
9
|
* An identity means something only within your own tenant: other tenants' agents can be pointed at your
|
|
10
10
|
* server too, so every check here requires `tenant`, and refuses tokens made for anyone else's agents.
|
|
@@ -15,6 +15,15 @@ export class RuntimeTokenError extends Error {
|
|
|
15
15
|
constructor(message) { super(message); this.name = "RuntimeTokenError"; }
|
|
16
16
|
}
|
|
17
17
|
const trim = (url) => url.replace(/\/+$/, "");
|
|
18
|
+
/** camelRun's hosted runtime answers at both names, and signs as the first it had, which tool servers already check. */
|
|
19
|
+
const HOSTED = ["https://run.camelai.com", "https://agents.camelai.dev"];
|
|
20
|
+
const HOSTED_ISSUER = "https://agents.camelai.dev";
|
|
21
|
+
const issuerOf = (options) => {
|
|
22
|
+
if (options.issuer)
|
|
23
|
+
return trim(options.issuer);
|
|
24
|
+
const runtime = trim(options.runtime);
|
|
25
|
+
return HOSTED.includes(runtime) ? HOSTED_ISSUER : runtime;
|
|
26
|
+
};
|
|
18
27
|
const decoder = new TextDecoder();
|
|
19
28
|
function base64url(text) {
|
|
20
29
|
const binary = atob(text.replace(/-/g, "+").replace(/_/g, "/").padEnd(Math.ceil(text.length / 4) * 4, "="));
|
|
@@ -86,7 +95,7 @@ export async function verifyRuntimeToken(token, options) {
|
|
|
86
95
|
throw new RuntimeTokenError("Token signature does not verify");
|
|
87
96
|
const claims = part(pieces[1]);
|
|
88
97
|
const now = Math.floor(Date.now() / 1000), skew = options.clockTolerance ?? 30;
|
|
89
|
-
if (claims.iss !==
|
|
98
|
+
if (claims.iss !== issuerOf(options))
|
|
90
99
|
throw new RuntimeTokenError("Token is from another issuer");
|
|
91
100
|
if (![options.tenant].flat().includes(claims.tenant))
|
|
92
101
|
throw new RuntimeTokenError("Token is for another tenant's agent");
|
|
@@ -135,7 +144,7 @@ export function runtimeIdentity(extra) {
|
|
|
135
144
|
export function serveTools(tools, options) {
|
|
136
145
|
requireTenant(options);
|
|
137
146
|
const server = typeof tools.listTools === "function" && typeof tools.callTool === "function" ? tools : toolServer(tools);
|
|
138
|
-
const issuer =
|
|
147
|
+
const issuer = issuerOf(options);
|
|
139
148
|
const json = (status, body, headers = {}) => new Response(JSON.stringify(body), { status, headers: { "Content-Type": "application/json", ...headers } });
|
|
140
149
|
const WELL_KNOWN = "/.well-known/oauth-protected-resource";
|
|
141
150
|
return async (request) => {
|
|
@@ -160,9 +160,9 @@ export declare function answerMcp(message: Record<string, any>, server: ToolServ
|
|
|
160
160
|
/** `tool({...})` definitions as an attached MCP server: JSON results become a text block (and structured content for objects). */
|
|
161
161
|
export declare function toolServer(tools: Tools): ToolServer;
|
|
162
162
|
/** The hosted runtime; `url` points elsewhere (a self-hosted runtime, or http://127.0.0.1:8790 in development). */
|
|
163
|
-
export declare const DEFAULT_URL = "https://
|
|
163
|
+
export declare const DEFAULT_URL = "https://run.camelai.com";
|
|
164
164
|
export interface RuntimeOptions {
|
|
165
|
-
/** The runtime's origin. Default https://
|
|
165
|
+
/** The runtime's origin. Default https://run.camelai.com. */
|
|
166
166
|
url?: string;
|
|
167
167
|
apiKey?: string;
|
|
168
168
|
/** Injectable for tests, observability, or an application's HTTP stack. */
|
|
@@ -183,7 +183,8 @@ export interface AgentOptions {
|
|
|
183
183
|
* throws goes to `onError`. A `message_update` is its delta alone (`assistantMessageEvent`), without the
|
|
184
184
|
* message it updates: fold that from its `message_start` and the deltas since. Where the stream
|
|
185
185
|
* cannot replay (a first connect, or a reconnect after the host's buffer moved on), the first event
|
|
186
|
-
* is a `{ type: "snapshot", turn }` of the running turn to fold from.
|
|
186
|
+
* is a `{ type: "snapshot", turn }` of the running turn to fold from. `close()` stops it: events still
|
|
187
|
+
* queued are dropped.
|
|
187
188
|
*/
|
|
188
189
|
onEvent?: (event: AgentEvent, requestId?: string) => unknown | Promise<unknown>;
|
|
189
190
|
/**
|
|
@@ -359,6 +360,8 @@ interface SourceOptions {
|
|
|
359
360
|
}
|
|
360
361
|
export interface DefinitionInput {
|
|
361
362
|
name: string;
|
|
363
|
+
/** What its agents are for: shown to models as the description of each agent's MCP tool (/v1/agents/:id/mcp). */
|
|
364
|
+
description?: string;
|
|
362
365
|
model?: string;
|
|
363
366
|
systemPrompt?: string;
|
|
364
367
|
thinkingLevel?: ThinkingLevel;
|
|
@@ -147,7 +147,7 @@ export function toolServer(tools) {
|
|
|
147
147
|
};
|
|
148
148
|
}
|
|
149
149
|
/** The hosted runtime; `url` points elsewhere (a self-hosted runtime, or http://127.0.0.1:8790 in development). */
|
|
150
|
-
export const DEFAULT_URL = "https://
|
|
150
|
+
export const DEFAULT_URL = "https://run.camelai.com";
|
|
151
151
|
export class AgentError extends Error {
|
|
152
152
|
/** The HTTP status, or 0 for a failure that is not an HTTP response's (a run's, the connection's). */
|
|
153
153
|
status;
|
|
@@ -703,7 +703,7 @@ export class AgentClient {
|
|
|
703
703
|
}
|
|
704
704
|
}
|
|
705
705
|
const onEvent = this.options.onEvent;
|
|
706
|
-
if (!onEvent)
|
|
706
|
+
if (!onEvent || this.closed)
|
|
707
707
|
return;
|
|
708
708
|
if (this.queued >= MAX_QUEUED_EVENTS && event.type === "message_update") {
|
|
709
709
|
if (this.dropped++ === 0)
|
|
@@ -712,8 +712,10 @@ export class AgentClient {
|
|
|
712
712
|
}
|
|
713
713
|
this.queued++;
|
|
714
714
|
this.dispatching = this.dispatching.then(async () => {
|
|
715
|
+
// A closed client calls onEvent no more: events still queued are dropped.
|
|
715
716
|
try {
|
|
716
|
-
|
|
717
|
+
if (!this.closed)
|
|
718
|
+
await onEvent(event, requestId);
|
|
717
719
|
}
|
|
718
720
|
catch (error) {
|
|
719
721
|
this.report(error);
|
|
@@ -1019,7 +1021,7 @@ export class AgentClient {
|
|
|
1019
1021
|
waiter.reject(new AgentError("Client closed; request may still be running", 0, id));
|
|
1020
1022
|
this.pending.clear();
|
|
1021
1023
|
await this.loop;
|
|
1022
|
-
//
|
|
1024
|
+
// onEvent is called no more; the call in progress may finish, but one that never returns cannot hang shutdown.
|
|
1023
1025
|
let timer;
|
|
1024
1026
|
await Promise.race([this.dispatching, new Promise(resolve => { timer = setTimeout(resolve, 2000); })]);
|
|
1025
1027
|
clearTimeout(timer);
|
package/dist/clients/watch.js
CHANGED
|
@@ -263,12 +263,13 @@ export function watchAgent(options) {
|
|
|
263
263
|
}
|
|
264
264
|
else
|
|
265
265
|
next = undefined;
|
|
266
|
-
|
|
266
|
+
await newest();
|
|
267
|
+
// No turn in it: none runs, or the token does not show it. Its state says which, where the token reads it: read
|
|
268
|
+
// after history, so a run that began in between is running here too, never a message in history with no turn.
|
|
267
269
|
if (!turn) {
|
|
268
270
|
const known = await json200("/state").catch(() => undefined);
|
|
269
271
|
state.running = !!known?.requests?.some(request => request.state === "running" && request.began && ["prompt", "continue", "resume"].includes(request.method));
|
|
270
272
|
}
|
|
271
|
-
await newest();
|
|
272
273
|
return;
|
|
273
274
|
}
|
|
274
275
|
if (data.type === "response") {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camelai/run",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.1",
|
|
4
4
|
"description": "SDK for camelRun: define tools in your app, and the runtime runs the model loop, history and sandbox.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
"dependencies": {
|
|
67
67
|
"typebox": "1.1.38"
|
|
68
68
|
},
|
|
69
|
-
"homepage": "https://
|
|
69
|
+
"homepage": "https://run.camelai.com",
|
|
70
70
|
"peerDependencies": {
|
|
71
71
|
"@modelcontextprotocol/sdk": "^1.30.1"
|
|
72
72
|
},
|