@sovara/runner 0.4.4 → 0.4.6
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 +97 -61
- package/dist/apiTypes.d.ts +1 -0
- package/dist/apiTypes.d.ts.map +1 -1
- package/dist/apiTypes.js +1 -0
- package/dist/apiTypes.js.map +1 -1
- package/dist/client.d.ts +35 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +52 -0
- package/dist/client.js.map +1 -0
- package/dist/context.d.ts +6 -4
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +72 -24
- package/dist/context.js.map +1 -1
- package/dist/endpointPatterns.json +4 -0
- package/dist/failsafe.d.ts.map +1 -1
- package/dist/failsafe.js +9 -2
- package/dist/failsafe.js.map +1 -1
- package/dist/index.d.ts +2 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -2
- package/dist/index.js.map +1 -1
- package/dist/lessons.js +1 -1
- package/dist/lessons.js.map +1 -1
- package/dist/openaiAgentsPatch.d.ts +2 -0
- package/dist/openaiAgentsPatch.d.ts.map +1 -0
- package/dist/openaiAgentsPatch.js +112 -0
- package/dist/openaiAgentsPatch.js.map +1 -0
- package/dist/payload/editIoExcludePatterns.json +24 -0
- package/dist/payload/wrap.d.ts.map +1 -1
- package/dist/payload/wrap.js +3 -1
- package/dist/payload/wrap.js.map +1 -1
- package/dist/runtimeClient.d.ts +2 -0
- package/dist/runtimeClient.d.ts.map +1 -1
- package/dist/runtimeClient.js +6 -1
- package/dist/runtimeClient.js.map +1 -1
- package/package.json +9 -3
- package/dist/agentCredentials.d.ts +0 -13
- package/dist/agentCredentials.d.ts.map +0 -1
- package/dist/agentCredentials.js +0 -90
- package/dist/agentCredentials.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# @sovara/runner
|
|
2
2
|
|
|
3
3
|
TypeScript runner for [Sovara](https://docs.sovara-labs.com) — wrap your script in a
|
|
4
|
-
single function and Sovara records
|
|
5
|
-
|
|
4
|
+
single function and Sovara records supported LLM, MCP, and framework tool
|
|
5
|
+
calls, plus log lines, as structured run steps.
|
|
6
6
|
|
|
7
7
|
The runner is the TS counterpart to the Python `sovara` runner. It targets
|
|
8
8
|
the same Sovara server, produces the same step shapes, and is safe to mix
|
|
@@ -14,8 +14,8 @@ Documentation: https://docs.sovara-labs.com
|
|
|
14
14
|
|
|
15
15
|
- Node 18 or newer (built-in `fetch`).
|
|
16
16
|
- A reachable Sovara backend. For local use, open the Sovara desktop app.
|
|
17
|
-
|
|
18
|
-
- A Sovara
|
|
17
|
+
Remote agent hosts use a project-scoped agent token.
|
|
18
|
+
- A Sovara project name for `new SovaraClient({ projectName })`.
|
|
19
19
|
|
|
20
20
|
## Install
|
|
21
21
|
|
|
@@ -37,13 +37,14 @@ npm install @anthropic-ai/claude-agent-sdk
|
|
|
37
37
|
## Quick start
|
|
38
38
|
|
|
39
39
|
```ts
|
|
40
|
-
import { withSovaraRun } from "@sovara/runner";
|
|
41
40
|
import OpenAI from "openai";
|
|
41
|
+
import { SovaraClient } from "@sovara/runner";
|
|
42
42
|
|
|
43
|
-
const
|
|
43
|
+
const openai_client = new OpenAI();
|
|
44
|
+
const sovara_client = new SovaraClient({ projectName: "My Project" });
|
|
44
45
|
|
|
45
|
-
await
|
|
46
|
-
const response = await
|
|
46
|
+
await sovara_client.run("hello-world", async () => {
|
|
47
|
+
const response = await openai_client.responses.create({
|
|
47
48
|
model: "gpt-5",
|
|
48
49
|
input: "Say hello.",
|
|
49
50
|
});
|
|
@@ -56,16 +57,21 @@ That's the full integration. The call to OpenAI is captured automatically;
|
|
|
56
57
|
`console.log` output is captured as run logs; the run shows up in the
|
|
57
58
|
Sovara UI.
|
|
58
59
|
|
|
60
|
+
OpenAI Agents function tools created with `tool()` and MCP calls are also
|
|
61
|
+
captured automatically inside a run. Use `trace` only for important custom
|
|
62
|
+
operations that are not already captured.
|
|
63
|
+
|
|
59
64
|
## How it works
|
|
60
65
|
|
|
61
|
-
Importing `@sovara/runner` patches
|
|
66
|
+
Importing `@sovara/runner` patches the supported provider transports at module
|
|
67
|
+
load, including:
|
|
62
68
|
|
|
63
69
|
- `globalThis.fetch` — wraps every `fetch` call.
|
|
64
70
|
- `node:http` and `node:https` — wraps `http.request` / `http.get` and
|
|
65
71
|
their HTTPS counterparts. This covers `axios` and any library that uses
|
|
66
72
|
Node's HTTP modules directly.
|
|
67
73
|
|
|
68
|
-
Inside `
|
|
74
|
+
Inside `sovara_client.run(...)`, calls to whitelisted endpoints are observed:
|
|
69
75
|
the runner builds a request envelope, posts it to the Sovara server's
|
|
70
76
|
`/internal/runner/llm/prepare` endpoint (which optionally applies
|
|
71
77
|
runtime preparation such as lessons injection), executes the prepared
|
|
@@ -90,84 +96,99 @@ completes.
|
|
|
90
96
|
|
|
91
97
|
## API
|
|
92
98
|
|
|
93
|
-
### `
|
|
99
|
+
### `new SovaraClient(options)`
|
|
94
100
|
|
|
95
101
|
```ts
|
|
96
|
-
export type
|
|
97
|
-
|
|
102
|
+
export type SovaraClientOptions = {
|
|
103
|
+
projectName: string; // required Sovara project name
|
|
104
|
+
url?: string; // exec server URL (default: http://127.0.0.1:5960)
|
|
105
|
+
agentToken?: string; // optional project-scoped agent token
|
|
106
|
+
fetch?: typeof globalThis.fetch; // optional caller-owned transport
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
export type SovaraRunOptions = {
|
|
98
110
|
clientRunId?: string; // optional project-scoped correlation id
|
|
99
|
-
url?: string; // server URL (default: http://127.0.0.1:5959)
|
|
100
111
|
captureLogs?: boolean; // default: true
|
|
101
|
-
|
|
112
|
+
lessonScope?: string | string[] | null; // optional lesson folder scope
|
|
102
113
|
};
|
|
103
114
|
|
|
104
|
-
export
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
115
|
+
export class SovaraClient {
|
|
116
|
+
run<T>(
|
|
117
|
+
name: string,
|
|
118
|
+
fn: () => Promise<T> | T,
|
|
119
|
+
options?: SovaraRunOptions,
|
|
120
|
+
): Promise<T>;
|
|
121
|
+
}
|
|
108
122
|
```
|
|
109
123
|
|
|
110
|
-
The
|
|
124
|
+
The client owns project identity and connection configuration. `run()` returns
|
|
111
125
|
whatever `fn` returns.
|
|
112
126
|
|
|
113
127
|
**Top-level call** (no parent run on the stack):
|
|
114
128
|
|
|
115
129
|
1. Health-checks the configured server and fails fast if it is unreachable.
|
|
116
|
-
2.
|
|
117
|
-
|
|
118
|
-
3. Resolves the project for `process.cwd()`. Fails hard with a `sovara init`
|
|
119
|
-
hint if the cwd isn't part of a known Sovara project.
|
|
120
|
-
4. Registers the run, runs `fn` inside an `AsyncLocalStorage` scope, and
|
|
130
|
+
2. Uses the client's `projectName` and `process.cwd()` metadata for the run.
|
|
131
|
+
3. Registers the run, runs `fn` inside an `AsyncLocalStorage` scope, and
|
|
121
132
|
deregisters in `finally`.
|
|
122
133
|
|
|
123
|
-
**Nested top-level call** (called from inside another `
|
|
134
|
+
**Nested top-level call** (called from inside another `sovara_client.run()`):
|
|
124
135
|
|
|
125
|
-
The nested
|
|
126
|
-
|
|
136
|
+
The nested run is ignored with a warning and `fn` runs inside the existing
|
|
137
|
+
active scope. Use `sovara_client.subrun(...)` for child execution.
|
|
127
138
|
|
|
128
|
-
`fn` exceptions propagate untouched. The run is deregistered in either
|
|
129
|
-
case.
|
|
139
|
+
`fn` exceptions propagate untouched. The run is deregistered in either case.
|
|
130
140
|
|
|
131
141
|
### Subruns
|
|
132
142
|
|
|
133
143
|
```ts
|
|
134
|
-
await
|
|
135
|
-
const docs = await
|
|
136
|
-
await
|
|
144
|
+
await sovara_client.run("pipeline", async () => {
|
|
145
|
+
const docs = await sovara_client.subrun("retrieve", async () => loadDocs());
|
|
146
|
+
await sovara_client.subrun("summarize", async () => summarize(docs));
|
|
137
147
|
});
|
|
138
148
|
```
|
|
139
149
|
|
|
140
|
-
Every explicit `
|
|
150
|
+
Every explicit `sovara_client.subrun(...)` becomes a child run under the active
|
|
151
|
+
scope.
|
|
141
152
|
|
|
142
153
|
### Logs
|
|
143
154
|
|
|
144
155
|
Standard `console.log`, `console.error`, and any direct writes to
|
|
145
|
-
`process.stdout` / `process.stderr` are captured into the run log
|
|
146
|
-
|
|
156
|
+
`process.stdout` / `process.stderr` are captured into the run log buffer and
|
|
157
|
+
flushed to the server every 250ms.
|
|
147
158
|
|
|
148
159
|
Disable with `captureLogs: false`:
|
|
149
160
|
|
|
150
161
|
```ts
|
|
151
|
-
await
|
|
152
|
-
|
|
153
|
-
|
|
162
|
+
await sovara_client.run(
|
|
163
|
+
"noisy",
|
|
164
|
+
async () => {
|
|
165
|
+
// stdout/stderr will not be tee'd to the server
|
|
166
|
+
},
|
|
167
|
+
{ captureLogs: false },
|
|
168
|
+
);
|
|
154
169
|
```
|
|
155
170
|
|
|
156
|
-
###
|
|
171
|
+
### Project and server
|
|
157
172
|
|
|
158
173
|
```ts
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
name: "billing-job",
|
|
162
|
-
user: { fullName: "CI Bot", email: "ci@example.com" },
|
|
163
|
-
project: { name: "Billing", description: "Nightly invoice run" },
|
|
164
|
-
},
|
|
165
|
-
async () => doWork(),
|
|
166
|
-
);
|
|
174
|
+
const sovara_client = new SovaraClient({ projectName: "Billing" });
|
|
175
|
+
await sovara_client.run("billing-job", async () => doWork());
|
|
167
176
|
```
|
|
168
177
|
|
|
169
|
-
|
|
170
|
-
|
|
178
|
+
Pass `projectName` once when constructing the client. A remote agent host can
|
|
179
|
+
also configure its exec URL and project-scoped token there. The custom `fetch`
|
|
180
|
+
is optional:
|
|
181
|
+
|
|
182
|
+
```ts
|
|
183
|
+
const sovara_client = new SovaraClient({
|
|
184
|
+
projectName: "Billing",
|
|
185
|
+
url: "https://exec.example.com",
|
|
186
|
+
agentToken: process.env.SOVARA_AGENT_TOKEN,
|
|
187
|
+
fetch: customFetch,
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
await sovara_client.run("billing-job", async () => doWork());
|
|
191
|
+
```
|
|
171
192
|
|
|
172
193
|
### `trace(fn, options?)`
|
|
173
194
|
|
|
@@ -176,7 +197,9 @@ HTTP call on its own, but should still appear as a tool step in the Sovara
|
|
|
176
197
|
run steps.
|
|
177
198
|
|
|
178
199
|
```ts
|
|
179
|
-
import {
|
|
200
|
+
import { SovaraClient, trace } from "@sovara/runner";
|
|
201
|
+
|
|
202
|
+
const sovara_client = new SovaraClient({ projectName: "My Project" });
|
|
180
203
|
|
|
181
204
|
const lookupCustomer = trace(
|
|
182
205
|
async function lookupCustomer(customerId: string) {
|
|
@@ -185,7 +208,7 @@ const lookupCustomer = trace(
|
|
|
185
208
|
{ meta: { system: "crm" } },
|
|
186
209
|
);
|
|
187
210
|
|
|
188
|
-
await
|
|
211
|
+
await sovara_client.run("support-agent", async () => {
|
|
189
212
|
const customer = await lookupCustomer("cust_123");
|
|
190
213
|
// Continue your agent flow with customer.
|
|
191
214
|
});
|
|
@@ -194,8 +217,9 @@ await withSovaraRun("support-agent", async () => {
|
|
|
194
217
|
`trace` records function arguments as the step input and the return value as
|
|
195
218
|
the step output. If the wrapped function throws, the error type and message
|
|
196
219
|
are recorded and the original exception is re-thrown. Outside an active
|
|
197
|
-
`
|
|
198
|
-
recording anything.
|
|
220
|
+
`sovara_client.run(...)` scope, the wrapper calls the function normally without
|
|
221
|
+
recording anything. Do not also wrap OpenAI Agents `tool()` functions or MCP
|
|
222
|
+
tools; Sovara captures those automatically.
|
|
199
223
|
|
|
200
224
|
```ts
|
|
201
225
|
export type TraceOptions = {
|
|
@@ -246,16 +270,18 @@ That single import change is the entire integration. The wrapper:
|
|
|
246
270
|
matches the Python runner's behavior.
|
|
247
271
|
- Throws if `options.env.ANTHROPIC_BASE_URL` already points at the
|
|
248
272
|
Sovara proxy, to prevent infinite proxy loops.
|
|
249
|
-
- Outside a `
|
|
273
|
+
- Outside a `sovara_client.run(...)` scope, `query()` and `startup()` are
|
|
250
274
|
pass-throughs to the real SDK with zero overhead.
|
|
251
275
|
|
|
252
276
|
### Example
|
|
253
277
|
|
|
254
278
|
```ts
|
|
255
|
-
import {
|
|
279
|
+
import { SovaraClient } from "@sovara/runner";
|
|
256
280
|
import { query } from "@sovara/runner/claude";
|
|
257
281
|
|
|
258
|
-
|
|
282
|
+
const sovara_client = new SovaraClient({ projectName: "My Project" });
|
|
283
|
+
|
|
284
|
+
await sovara_client.run("agent-task", async () => {
|
|
259
285
|
for await (const message of query({
|
|
260
286
|
prompt: "List the largest files in the current directory.",
|
|
261
287
|
})) {
|
|
@@ -287,16 +313,26 @@ for await (const message of handle.query("What did I install last?")) {
|
|
|
287
313
|
| ---------------------------- | -------------------------------------------------------------------------- | ----------------------- |
|
|
288
314
|
| `SOVARA_EXEC_SERVER_PORT` | Exec server port (used to compute the default URL) | `5960` |
|
|
289
315
|
| `SOVARA_EXEC_SERVER_URL` | Explicit exec server URL | `http://127.0.0.1:5960` |
|
|
316
|
+
| `SOVARA_AGENT_TOKEN` | Project-scoped token for a remote/headless agent | unset |
|
|
290
317
|
| `SOVARA_CLAUDE_PROXY_ACTIVE` | Set automatically by `@sovara/runner/claude` to prevent nested proxy loops | unset |
|
|
291
318
|
|
|
292
319
|
### Server URL
|
|
293
320
|
|
|
294
|
-
The TypeScript runner uses the local exec server by default. Pass `url`
|
|
321
|
+
The TypeScript runner uses the local exec server by default. Pass `url` when
|
|
322
|
+
constructing the client if the caller manages a different exec-server connection:
|
|
295
323
|
|
|
296
324
|
```ts
|
|
297
|
-
|
|
325
|
+
const sovara_client = new SovaraClient({
|
|
326
|
+
projectName: "My Project",
|
|
327
|
+
url: "http://localhost:6000",
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
await sovara_client.run("demo", fn);
|
|
298
331
|
```
|
|
299
332
|
|
|
333
|
+
Pass `agentToken` for remote project authentication. It falls back to
|
|
334
|
+
`SOVARA_AGENT_TOKEN`. Pass `fetch` only when a caller-owned transport is needed.
|
|
335
|
+
|
|
300
336
|
## Error messages
|
|
301
337
|
|
|
302
338
|
The runner fails fast for misconfiguration. Common ones:
|
|
@@ -304,8 +340,8 @@ The runner fails fast for misconfiguration. Common ones:
|
|
|
304
340
|
- **`Sovara exec server is not reachable at <url>. Run sovara-exec-server start or use the Sovara CLI package that bundles it.`**
|
|
305
341
|
Start the exec server or pass `url` when the caller owns exec-server setup.
|
|
306
342
|
|
|
307
|
-
-
|
|
308
|
-
|
|
343
|
+
- **`projectName is required.`**
|
|
344
|
+
Pass `projectName` to `new SovaraClient(...)`.
|
|
309
345
|
|
|
310
346
|
- **`Custom Claude Agent SDK transports are not supported yet under sovara.`**
|
|
311
347
|
Remove `options.transport` from your `query()` / `startup()` call.
|
package/dist/apiTypes.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export declare const API_TYPE_HTTPX_CLIENT_SEND = "httpx.Client.send";
|
|
2
2
|
export declare const API_TYPE_HTTPX_ASYNC_CLIENT_SEND = "httpx.AsyncClient.send";
|
|
3
3
|
export declare const API_TYPE_MCP_CLIENT_SESSION_SEND_REQUEST = "MCP.ClientSession.send_request";
|
|
4
|
+
export declare const API_TYPE_OPENAI_AGENTS_FUNCTION_TOOL_RUN = "openai_agents.FunctionTool.run";
|
|
4
5
|
//# sourceMappingURL=apiTypes.d.ts.map
|
package/dist/apiTypes.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apiTypes.d.ts","sourceRoot":"","sources":["../src/apiTypes.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,0BAA0B,sBAAsB,CAAC;AAC9D,eAAO,MAAM,gCAAgC,2BAA2B,CAAC;AACzE,eAAO,MAAM,wCAAwC,mCAAmC,CAAC"}
|
|
1
|
+
{"version":3,"file":"apiTypes.d.ts","sourceRoot":"","sources":["../src/apiTypes.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,0BAA0B,sBAAsB,CAAC;AAC9D,eAAO,MAAM,gCAAgC,2BAA2B,CAAC;AACzE,eAAO,MAAM,wCAAwC,mCAAmC,CAAC;AACzF,eAAO,MAAM,wCAAwC,mCAAmC,CAAC"}
|
package/dist/apiTypes.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export const API_TYPE_HTTPX_CLIENT_SEND = "httpx.Client.send";
|
|
2
2
|
export const API_TYPE_HTTPX_ASYNC_CLIENT_SEND = "httpx.AsyncClient.send";
|
|
3
3
|
export const API_TYPE_MCP_CLIENT_SESSION_SEND_REQUEST = "MCP.ClientSession.send_request";
|
|
4
|
+
export const API_TYPE_OPENAI_AGENTS_FUNCTION_TOOL_RUN = "openai_agents.FunctionTool.run";
|
|
4
5
|
//# sourceMappingURL=apiTypes.js.map
|
package/dist/apiTypes.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apiTypes.js","sourceRoot":"","sources":["../src/apiTypes.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,0BAA0B,GAAG,mBAAmB,CAAC;AAC9D,MAAM,CAAC,MAAM,gCAAgC,GAAG,wBAAwB,CAAC;AACzE,MAAM,CAAC,MAAM,wCAAwC,GAAG,gCAAgC,CAAC"}
|
|
1
|
+
{"version":3,"file":"apiTypes.js","sourceRoot":"","sources":["../src/apiTypes.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,0BAA0B,GAAG,mBAAmB,CAAC;AAC9D,MAAM,CAAC,MAAM,gCAAgC,GAAG,wBAAwB,CAAC;AACzE,MAAM,CAAC,MAAM,wCAAwC,GAAG,gCAAgC,CAAC;AACzF,MAAM,CAAC,MAAM,wCAAwC,GAAG,gCAAgC,CAAC"}
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { type LessonScopeInput } from "./context.js";
|
|
2
|
+
import type { MetricValue } from "./runtimeClient.js";
|
|
3
|
+
export type SovaraClientOptions = {
|
|
4
|
+
projectName: string;
|
|
5
|
+
url?: string;
|
|
6
|
+
agentToken?: string;
|
|
7
|
+
fetch?: typeof globalThis.fetch;
|
|
8
|
+
};
|
|
9
|
+
export type SovaraRunOptions = {
|
|
10
|
+
clientRunId?: string;
|
|
11
|
+
captureLogs?: boolean;
|
|
12
|
+
lessonScope?: LessonScopeInput;
|
|
13
|
+
};
|
|
14
|
+
export type SovaraSubrunOptions = {
|
|
15
|
+
lessonScope?: LessonScopeInput;
|
|
16
|
+
};
|
|
17
|
+
type RunFunction<T> = () => Promise<T> | T;
|
|
18
|
+
export declare class SovaraClient {
|
|
19
|
+
readonly projectName: string;
|
|
20
|
+
private readonly url?;
|
|
21
|
+
private readonly agentToken?;
|
|
22
|
+
private readonly fetch?;
|
|
23
|
+
constructor(options: SovaraClientOptions);
|
|
24
|
+
run<T>(name: string, fn: RunFunction<T>, options?: SovaraRunOptions): Promise<T>;
|
|
25
|
+
subrun<T>(name: string, fn: RunFunction<T>, options?: SovaraSubrunOptions): Promise<T>;
|
|
26
|
+
lessonScope<T>(scope: LessonScopeInput, fn: RunFunction<T>): Promise<T>;
|
|
27
|
+
logInput(input: unknown): Promise<void>;
|
|
28
|
+
logOutput(output: unknown): Promise<void>;
|
|
29
|
+
logMetrics(metrics: Record<string, MetricValue>): Promise<void>;
|
|
30
|
+
injectLessons(context: unknown): Promise<string>;
|
|
31
|
+
queueForAnnotation(): Promise<void>;
|
|
32
|
+
getRunId(): string | undefined;
|
|
33
|
+
}
|
|
34
|
+
export {};
|
|
35
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAUL,KAAK,gBAAgB,EACtB,MAAM,cAAc,CAAC;AAEtB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEtD,MAAM,MAAM,mBAAmB,GAAG;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;CACjC,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,WAAW,CAAC,EAAE,gBAAgB,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,WAAW,CAAC,EAAE,gBAAgB,CAAC;CAChC,CAAC;AAEF,KAAK,WAAW,CAAC,CAAC,IAAI,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;AAE3C,qBAAa,YAAY;IACvB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAS;IAC9B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,CAA0B;gBAErC,OAAO,EAAE,mBAAmB;IAUxC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,OAAO,GAAE,gBAAqB,GAAG,OAAO,CAAC,CAAC,CAAC;IAcpF,MAAM,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,OAAO,GAAE,mBAAwB,GAAG,OAAO,CAAC,CAAC,CAAC;IAI1F,WAAW,CAAC,CAAC,EAAE,KAAK,EAAE,gBAAgB,EAAE,EAAE,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAIvE,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAIvC,SAAS,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAIzC,UAAU,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAI/D,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC;IAIhD,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC;IAInC,QAAQ,IAAI,MAAM,GAAG,SAAS;CAG/B"}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { getRunId, logInput, logMetrics, logOutput, normalizeProjectName, queueForAnnotation, withLessonScope, withSovaraRun, withSovaraSubrun, } from "./context.js";
|
|
2
|
+
import { injectLessons } from "./lessons.js";
|
|
3
|
+
export class SovaraClient {
|
|
4
|
+
projectName;
|
|
5
|
+
url;
|
|
6
|
+
agentToken;
|
|
7
|
+
fetch;
|
|
8
|
+
constructor(options) {
|
|
9
|
+
if (!options || typeof options !== "object") {
|
|
10
|
+
throw new TypeError("SovaraClient requires an options object with projectName.");
|
|
11
|
+
}
|
|
12
|
+
this.projectName = normalizeProjectName(options.projectName);
|
|
13
|
+
this.url = options.url;
|
|
14
|
+
this.agentToken = options.agentToken;
|
|
15
|
+
this.fetch = options.fetch;
|
|
16
|
+
}
|
|
17
|
+
run(name, fn, options = {}) {
|
|
18
|
+
return withSovaraRun({
|
|
19
|
+
...options,
|
|
20
|
+
name,
|
|
21
|
+
projectName: this.projectName,
|
|
22
|
+
url: this.url,
|
|
23
|
+
agentToken: this.agentToken,
|
|
24
|
+
fetch: this.fetch,
|
|
25
|
+
}, fn);
|
|
26
|
+
}
|
|
27
|
+
subrun(name, fn, options = {}) {
|
|
28
|
+
return withSovaraSubrun({ name, ...options }, fn);
|
|
29
|
+
}
|
|
30
|
+
lessonScope(scope, fn) {
|
|
31
|
+
return withLessonScope(scope, fn);
|
|
32
|
+
}
|
|
33
|
+
logInput(input) {
|
|
34
|
+
return logInput(input);
|
|
35
|
+
}
|
|
36
|
+
logOutput(output) {
|
|
37
|
+
return logOutput(output);
|
|
38
|
+
}
|
|
39
|
+
logMetrics(metrics) {
|
|
40
|
+
return logMetrics(metrics);
|
|
41
|
+
}
|
|
42
|
+
injectLessons(context) {
|
|
43
|
+
return injectLessons(context);
|
|
44
|
+
}
|
|
45
|
+
queueForAnnotation() {
|
|
46
|
+
return queueForAnnotation();
|
|
47
|
+
}
|
|
48
|
+
getRunId() {
|
|
49
|
+
return getRunId();
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,SAAS,EACT,oBAAoB,EACpB,kBAAkB,EAClB,eAAe,EACf,aAAa,EACb,gBAAgB,GAEjB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAsB7C,MAAM,OAAO,YAAY;IACd,WAAW,CAAS;IACZ,GAAG,CAAU;IACb,UAAU,CAAU;IACpB,KAAK,CAA2B;IAEjD,YAAY,OAA4B;QACtC,IAAI,CAAC,OAAO,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;YAC5C,MAAM,IAAI,SAAS,CAAC,2DAA2D,CAAC,CAAC;QACnF,CAAC;QACD,IAAI,CAAC,WAAW,GAAG,oBAAoB,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;QAC7D,IAAI,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC;QACvB,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;QACrC,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;IAC7B,CAAC;IAED,GAAG,CAAI,IAAY,EAAE,EAAkB,EAAE,UAA4B,EAAE;QACrE,OAAO,aAAa,CAClB;YACE,GAAG,OAAO;YACV,IAAI;YACJ,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,GAAG,EAAE,IAAI,CAAC,GAAG;YACb,UAAU,EAAE,IAAI,CAAC,UAAU;YAC3B,KAAK,EAAE,IAAI,CAAC,KAAK;SAClB,EACD,EAAE,CACH,CAAC;IACJ,CAAC;IAED,MAAM,CAAI,IAAY,EAAE,EAAkB,EAAE,UAA+B,EAAE;QAC3E,OAAO,gBAAgB,CAAC,EAAE,IAAI,EAAE,GAAG,OAAO,EAAE,EAAE,EAAE,CAAC,CAAC;IACpD,CAAC;IAED,WAAW,CAAI,KAAuB,EAAE,EAAkB;QACxD,OAAO,eAAe,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACpC,CAAC;IAED,QAAQ,CAAC,KAAc;QACrB,OAAO,QAAQ,CAAC,KAAK,CAAC,CAAC;IACzB,CAAC;IAED,SAAS,CAAC,MAAe;QACvB,OAAO,SAAS,CAAC,MAAM,CAAC,CAAC;IAC3B,CAAC;IAED,UAAU,CAAC,OAAoC;QAC7C,OAAO,UAAU,CAAC,OAAO,CAAC,CAAC;IAC7B,CAAC;IAED,aAAa,CAAC,OAAgB;QAC5B,OAAO,aAAa,CAAC,OAAO,CAAC,CAAC;IAChC,CAAC;IAED,kBAAkB;QAChB,OAAO,kBAAkB,EAAE,CAAC;IAC9B,CAAC;IAED,QAAQ;QACN,OAAO,QAAQ,EAAE,CAAC;IACpB,CAAC;CACF"}
|
package/dist/context.d.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { RuntimeClient, type MetricValue } from "./runtimeClient.js";
|
|
2
2
|
export type WithSovaraRunOptions = {
|
|
3
|
-
name
|
|
3
|
+
name?: string;
|
|
4
4
|
clientRunId?: string;
|
|
5
5
|
url?: string;
|
|
6
|
+
agentToken?: string;
|
|
7
|
+
fetch?: typeof globalThis.fetch;
|
|
6
8
|
captureLogs?: boolean;
|
|
7
|
-
projectName
|
|
9
|
+
projectName: string;
|
|
8
10
|
lessonScope?: LessonScopeInput;
|
|
9
11
|
__testing?: {
|
|
10
12
|
client?: RunScopeClient;
|
|
@@ -23,12 +25,12 @@ export type RunScope = {
|
|
|
23
25
|
client: RunScopeClient;
|
|
24
26
|
lessonScopePaths: string[] | null;
|
|
25
27
|
};
|
|
26
|
-
export declare const client: RuntimeClient;
|
|
27
28
|
export type WithSovaraSubrunOptions = {
|
|
28
29
|
name: string;
|
|
29
30
|
lessonScope?: LessonScopeInput;
|
|
30
31
|
};
|
|
31
32
|
export type LessonScopeInput = string | string[] | null | undefined;
|
|
33
|
+
export declare function normalizeProjectName(projectName: unknown): string;
|
|
32
34
|
export declare function normalizeLessonScope(scope: LessonScopeInput): string[] | null;
|
|
33
35
|
export declare function getCurrentRunScope(): RunScope | undefined;
|
|
34
36
|
export declare function getRunId(): string | undefined;
|
|
@@ -36,7 +38,7 @@ export declare function logMetrics(metrics: Record<string, MetricValue>): Promis
|
|
|
36
38
|
export declare function logInput(input: unknown): Promise<void>;
|
|
37
39
|
export declare function logOutput(output: unknown): Promise<void>;
|
|
38
40
|
export declare function queueForAnnotation(): Promise<void>;
|
|
39
|
-
export declare function withSovaraRun<T>(input:
|
|
41
|
+
export declare function withSovaraRun<T>(input: WithSovaraRunOptions, fn: () => Promise<T> | T): Promise<T>;
|
|
40
42
|
export declare function withSovaraSubrun<T>(input: string | WithSovaraSubrunOptions, fn: () => Promise<T> | T): Promise<T>;
|
|
41
43
|
export declare function withLessonScope<T>(lessonScope: LessonScopeInput, fn: () => Promise<T> | T): Promise<T>;
|
|
42
44
|
export {};
|
package/dist/context.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"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"}
|
package/dist/context.js
CHANGED
|
@@ -1,30 +1,79 @@
|
|
|
1
1
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
|
-
import {
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
import { renameSync, unlinkSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { basename, dirname, resolve } from "node:path";
|
|
3
5
|
import { collectCodeVersion } from "./codeVersion.js";
|
|
4
6
|
import { readRunnerConfig } from "./env.js";
|
|
5
7
|
import { installRunLogCapture } from "./runLogCapture.js";
|
|
6
8
|
import { RuntimeClient, RuntimeClientError } from "./runtimeClient.js";
|
|
7
9
|
import { ensureServerRunning } from "./serverLifecycle.js";
|
|
8
|
-
import { isDegraded, markDegraded, raiseIfConfigError
|
|
10
|
+
import { isDegraded, markDegraded, raiseIfConfigError } from "./failsafe.js";
|
|
9
11
|
import { ensureMcpPatchInstalled } from "./mcpPatch.js";
|
|
12
|
+
import { ensureOpenAIAgentsPatchInstalled } from "./openaiAgentsPatch.js";
|
|
10
13
|
import { warnOnce } from "./util/logger.js";
|
|
11
14
|
const scopeStorage = new AsyncLocalStorage();
|
|
12
15
|
const lessonScopeStorage = new AsyncLocalStorage();
|
|
13
|
-
export const client = new RuntimeClient(readRunnerConfig().url);
|
|
14
16
|
const ROOT_SCOPE_MARKERS = new Set(["", ".", "/"]);
|
|
17
|
+
export function normalizeProjectName(projectName) {
|
|
18
|
+
if (projectName === null || projectName === undefined) {
|
|
19
|
+
throw new Error("projectName is required.");
|
|
20
|
+
}
|
|
21
|
+
if (typeof projectName !== "string") {
|
|
22
|
+
throw new TypeError("projectName must be a string.");
|
|
23
|
+
}
|
|
24
|
+
const normalized = projectName.trim();
|
|
25
|
+
if (!normalized) {
|
|
26
|
+
throw new Error("projectName is required.");
|
|
27
|
+
}
|
|
28
|
+
return normalized;
|
|
29
|
+
}
|
|
15
30
|
function normalizeRunOptions(input) {
|
|
16
|
-
|
|
17
|
-
|
|
31
|
+
if (typeof input === "string") {
|
|
32
|
+
throw new Error("SovaraClient.run() requires a client-bound project name.");
|
|
33
|
+
}
|
|
34
|
+
const options = input;
|
|
35
|
+
if (options.name !== undefined && typeof options.name !== "string") {
|
|
36
|
+
throw new Error("SovaraClient.run() name must be a string.");
|
|
37
|
+
}
|
|
38
|
+
const name = options.name === undefined ? defaultTopLevelRunName() : options.name.trim();
|
|
18
39
|
if (!name) {
|
|
19
|
-
throw new Error("
|
|
40
|
+
throw new Error("SovaraClient.run() name must be a non-empty string.");
|
|
20
41
|
}
|
|
21
42
|
return { ...options, name };
|
|
22
43
|
}
|
|
44
|
+
function defaultTopLevelRunName() {
|
|
45
|
+
return `Run ${randomUUID().replace(/-/g, "").slice(0, 4)}`;
|
|
46
|
+
}
|
|
47
|
+
function writeRunResult(result) {
|
|
48
|
+
const path = process.env.SOVARA_RUN_FILE;
|
|
49
|
+
if (!path)
|
|
50
|
+
return;
|
|
51
|
+
const absolutePath = resolve(path);
|
|
52
|
+
const tempPath = `${dirname(absolutePath)}/.${basename(absolutePath)}.${randomUUID()}`;
|
|
53
|
+
try {
|
|
54
|
+
writeFileSync(tempPath, JSON.stringify({
|
|
55
|
+
run_id: result.runId,
|
|
56
|
+
project_id: result.projectId,
|
|
57
|
+
project_name: result.projectName,
|
|
58
|
+
appended: result.appended,
|
|
59
|
+
}), { encoding: "utf8", flag: "wx", mode: 0o600 });
|
|
60
|
+
renameSync(tempPath, absolutePath);
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
try {
|
|
64
|
+
unlinkSync(tempPath);
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
// The temporary file may not have been created.
|
|
68
|
+
}
|
|
69
|
+
warnOnce("publish-run-result", "[sovara] Failed to publish run result.");
|
|
70
|
+
}
|
|
71
|
+
}
|
|
23
72
|
function normalizeSubrunOptions(input) {
|
|
24
73
|
const options = typeof input === "string" ? { name: input } : input;
|
|
25
74
|
const name = options.name.trim();
|
|
26
75
|
if (!name) {
|
|
27
|
-
throw new Error("
|
|
76
|
+
throw new Error("SovaraClient.subrun() name must be a non-empty string.");
|
|
28
77
|
}
|
|
29
78
|
return { ...options, name };
|
|
30
79
|
}
|
|
@@ -87,15 +136,7 @@ function scopeWithLessonScope(parent, lessonScopePaths) {
|
|
|
87
136
|
};
|
|
88
137
|
}
|
|
89
138
|
function resolveRunProjectConfig(cwd, options) {
|
|
90
|
-
|
|
91
|
-
return resolveProjectConfig(cwd, options.projectName);
|
|
92
|
-
}
|
|
93
|
-
catch (error) {
|
|
94
|
-
if (options.__testing?.client && options.__testing.skipLifecycle) {
|
|
95
|
-
return { projectName: "Project", projectRoot: cwd };
|
|
96
|
-
}
|
|
97
|
-
throw new SovaraConfigError(error instanceof Error ? error.message : String(error));
|
|
98
|
-
}
|
|
139
|
+
return { projectName: normalizeProjectName(options.projectName), projectRoot: cwd };
|
|
99
140
|
}
|
|
100
141
|
export function getCurrentRunScope() {
|
|
101
142
|
return scopeStorage.getStore();
|
|
@@ -143,35 +184,34 @@ export async function queueForAnnotation() {
|
|
|
143
184
|
await scope.client.requestAnnotationEvaluation({ runId: scope.runId });
|
|
144
185
|
}
|
|
145
186
|
export async function withSovaraRun(input, fn) {
|
|
146
|
-
await ensureMcpPatchInstalled();
|
|
187
|
+
await Promise.all([ensureMcpPatchInstalled(), ensureOpenAIAgentsPatchInstalled()]);
|
|
147
188
|
const options = normalizeRunOptions(input);
|
|
148
189
|
const parent = getCurrentRunScope();
|
|
149
190
|
if (!parent && isDegraded()) {
|
|
150
191
|
return await fn();
|
|
151
192
|
}
|
|
152
193
|
if (parent) {
|
|
153
|
-
console.warn("
|
|
194
|
+
console.warn("SovaraClient.run() was called inside an active run; ignoring nested run. Use SovaraClient.subrun() for child agent execution.");
|
|
154
195
|
return await fn();
|
|
155
196
|
}
|
|
156
197
|
const config = readRunnerConfig();
|
|
157
198
|
const project = resolveRunProjectConfig(config.cwd, options);
|
|
158
|
-
const
|
|
159
|
-
const url = options.url ?? connection?.serverUrl ?? config.url;
|
|
199
|
+
const url = options.url ?? config.url;
|
|
160
200
|
const skipLifecycle = options.__testing?.skipLifecycle ?? false;
|
|
161
201
|
if (!skipLifecycle) {
|
|
162
|
-
const reachable = await ensureServerRunning(url);
|
|
202
|
+
const reachable = await ensureServerRunning(url, options.fetch ? { fetchFn: options.fetch } : undefined);
|
|
163
203
|
if (!reachable) {
|
|
164
204
|
return await fn(); // no-op: run user code without a scope
|
|
165
205
|
}
|
|
166
206
|
}
|
|
167
|
-
const client = options.__testing?.client
|
|
207
|
+
const client = options.__testing?.client
|
|
208
|
+
?? new RuntimeClient(url, options.fetch, options.agentToken);
|
|
168
209
|
const codeVersion = collectCodeVersion(project.projectRoot);
|
|
169
210
|
let registration;
|
|
170
211
|
try {
|
|
171
212
|
registration = await client.registerRun({
|
|
172
213
|
name: options.name,
|
|
173
214
|
clientRunId: options.clientRunId,
|
|
174
|
-
projectId: connection?.projectId,
|
|
175
215
|
projectName: project.projectName,
|
|
176
216
|
projectRoot: project.projectRoot,
|
|
177
217
|
codeVersion,
|
|
@@ -234,13 +274,21 @@ export async function withSovaraRun(input, fn) {
|
|
|
234
274
|
throw error;
|
|
235
275
|
}
|
|
236
276
|
}
|
|
277
|
+
finally {
|
|
278
|
+
writeRunResult({
|
|
279
|
+
runId: registration.runId,
|
|
280
|
+
projectId: registration.projectId,
|
|
281
|
+
projectName: project.projectName,
|
|
282
|
+
appended: registration.appended ?? false,
|
|
283
|
+
});
|
|
284
|
+
}
|
|
237
285
|
}
|
|
238
286
|
}
|
|
239
287
|
export async function withSovaraSubrun(input, fn) {
|
|
240
288
|
const options = normalizeSubrunOptions(input);
|
|
241
289
|
const parent = getCurrentRunScope();
|
|
242
290
|
if (!parent) {
|
|
243
|
-
throw new Error("
|
|
291
|
+
throw new Error("SovaraClient.subrun() requires an active Sovara run.");
|
|
244
292
|
}
|
|
245
293
|
const lessonScopePaths = resolveLessonScope(options.lessonScope, parent.lessonScopePaths);
|
|
246
294
|
const subrun = await parent.client.registerSubrun({
|