@vextlabs/theron-agent-sdk 0.3.0 → 0.3.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/CHANGELOG.md +16 -0
- package/dist/adapters/theron.cjs +13 -2
- package/dist/adapters/theron.js +13 -2
- package/dist/agent/index.cjs +111 -2
- package/dist/agent/index.d.cts +43 -2
- package/dist/agent/index.d.ts +43 -2
- package/dist/agent/index.js +108 -3
- package/dist/council/index.cjs +4 -0
- package/dist/council/index.d.cts +25 -1
- package/dist/council/index.d.ts +25 -1
- package/dist/council/index.js +4 -1
- package/dist/index.cjs +490 -65
- package/dist/index.d.cts +64 -5
- package/dist/index.d.ts +64 -5
- package/dist/index.js +479 -66
- package/dist/loop/index.cjs +2 -1
- package/dist/loop/index.d.cts +3 -0
- package/dist/loop/index.d.ts +3 -0
- package/dist/loop/index.js +2 -1
- package/dist/mcp/index.cjs +27 -10
- package/dist/mcp/index.d.cts +2 -1
- package/dist/mcp/index.d.ts +2 -1
- package/dist/mcp/index.js +27 -10
- package/dist/runtime/index.cjs +145 -33
- package/dist/runtime/index.d.cts +78 -3
- package/dist/runtime/index.d.ts +78 -3
- package/dist/runtime/index.js +144 -34
- package/dist/tools/index.cjs +45 -10
- package/dist/tools/index.js +45 -10
- package/package.json +1 -1
package/dist/loop/index.cjs
CHANGED
|
@@ -63,8 +63,9 @@ async function compactHistory(opts) {
|
|
|
63
63
|
const older = msgs.slice(0, msgs.length - keepRecent);
|
|
64
64
|
const recent = msgs.slice(msgs.length - keepRecent);
|
|
65
65
|
const summary = String(await opts.summarize(older));
|
|
66
|
+
const summaryRole = opts.summaryRole ?? "system";
|
|
66
67
|
return {
|
|
67
|
-
messages: [{ role:
|
|
68
|
+
messages: [{ role: summaryRole, content: `${SUMMARY_PREFIX}
|
|
68
69
|
${summary}` }, ...recent],
|
|
69
70
|
compacted: true,
|
|
70
71
|
summary,
|
package/dist/loop/index.d.cts
CHANGED
|
@@ -231,6 +231,9 @@ interface CompactHistoryOptions {
|
|
|
231
231
|
/** Only compact when total content chars exceed this (default 0 = compact
|
|
232
232
|
* whenever there is more history than keepRecent). */
|
|
233
233
|
maxChars?: number;
|
|
234
|
+
/** Role to attach the summary message under. Default 'system'. Set to 'user'
|
|
235
|
+
* for providers (e.g. Anthropic) that reject a second system message. */
|
|
236
|
+
summaryRole?: "system" | "user";
|
|
234
237
|
}
|
|
235
238
|
interface CompactHistoryResult {
|
|
236
239
|
/** The compacted message list: [summary-as-system, ...recent] when compacted. */
|
package/dist/loop/index.d.ts
CHANGED
|
@@ -231,6 +231,9 @@ interface CompactHistoryOptions {
|
|
|
231
231
|
/** Only compact when total content chars exceed this (default 0 = compact
|
|
232
232
|
* whenever there is more history than keepRecent). */
|
|
233
233
|
maxChars?: number;
|
|
234
|
+
/** Role to attach the summary message under. Default 'system'. Set to 'user'
|
|
235
|
+
* for providers (e.g. Anthropic) that reject a second system message. */
|
|
236
|
+
summaryRole?: "system" | "user";
|
|
234
237
|
}
|
|
235
238
|
interface CompactHistoryResult {
|
|
236
239
|
/** The compacted message list: [summary-as-system, ...recent] when compacted. */
|
package/dist/loop/index.js
CHANGED
|
@@ -61,8 +61,9 @@ async function compactHistory(opts) {
|
|
|
61
61
|
const older = msgs.slice(0, msgs.length - keepRecent);
|
|
62
62
|
const recent = msgs.slice(msgs.length - keepRecent);
|
|
63
63
|
const summary = String(await opts.summarize(older));
|
|
64
|
+
const summaryRole = opts.summaryRole ?? "system";
|
|
64
65
|
return {
|
|
65
|
-
messages: [{ role:
|
|
66
|
+
messages: [{ role: summaryRole, content: `${SUMMARY_PREFIX}
|
|
66
67
|
${summary}` }, ...recent],
|
|
67
68
|
compacted: true,
|
|
68
69
|
summary,
|
package/dist/mcp/index.cjs
CHANGED
|
@@ -5,7 +5,7 @@ var MCP_PROTOCOL_VERSION = "2024-11-05";
|
|
|
5
5
|
var DEFAULT_TIMEOUT_MS = 12e3;
|
|
6
6
|
var MCPClient = class {
|
|
7
7
|
config;
|
|
8
|
-
|
|
8
|
+
initPromise = null;
|
|
9
9
|
toolCache = null;
|
|
10
10
|
constructor(config) {
|
|
11
11
|
if (!/^[a-z0-9_-]+$/.test(config.slug)) {
|
|
@@ -71,7 +71,17 @@ var MCPClient = class {
|
|
|
71
71
|
};
|
|
72
72
|
}
|
|
73
73
|
async ensureInitialized(signal) {
|
|
74
|
-
if (this.
|
|
74
|
+
if (!this.initPromise) {
|
|
75
|
+
this.initPromise = this.doInitialize(signal);
|
|
76
|
+
}
|
|
77
|
+
try {
|
|
78
|
+
await this.initPromise;
|
|
79
|
+
} catch (err) {
|
|
80
|
+
this.initPromise = null;
|
|
81
|
+
throw err;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
async doInitialize(signal) {
|
|
75
85
|
await this.rpc(
|
|
76
86
|
"initialize",
|
|
77
87
|
{
|
|
@@ -82,7 +92,6 @@ var MCPClient = class {
|
|
|
82
92
|
signal
|
|
83
93
|
);
|
|
84
94
|
this.rpc("notifications/initialized", {}, signal).catch(() => void 0);
|
|
85
|
-
this.initialized = true;
|
|
86
95
|
}
|
|
87
96
|
async rpc(method, params, externalSignal) {
|
|
88
97
|
const ac = new AbortController();
|
|
@@ -96,9 +105,9 @@ var MCPClient = class {
|
|
|
96
105
|
}
|
|
97
106
|
const body = {
|
|
98
107
|
jsonrpc: "2.0",
|
|
99
|
-
id: Date.now() + Math.floor(Math.random() * 1e3),
|
|
100
108
|
method,
|
|
101
|
-
params
|
|
109
|
+
params,
|
|
110
|
+
...method.startsWith("notifications/") ? {} : { id: Date.now() + Math.floor(Math.random() * 1e3) }
|
|
102
111
|
};
|
|
103
112
|
try {
|
|
104
113
|
const r = await fetch(this.config.url, {
|
|
@@ -121,11 +130,19 @@ var MCPClient = class {
|
|
|
121
130
|
const ct = r.headers.get("content-type") || "";
|
|
122
131
|
if (ct.includes("text/event-stream")) {
|
|
123
132
|
const text = await r.text();
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
133
|
+
for (const ev of text.split(/\n\n/)) {
|
|
134
|
+
const payload = ev.split(/\r?\n/).filter((l) => l.startsWith("data:")).map((l) => l.slice(5).replace(/^ /, "")).join("\n").trim();
|
|
135
|
+
if (!payload || payload === "[DONE]") continue;
|
|
136
|
+
let env2;
|
|
137
|
+
try {
|
|
138
|
+
env2 = JSON.parse(payload);
|
|
139
|
+
} catch {
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
if (env2.error) throw new Error(`mcp error: ${env2.error.message}`);
|
|
143
|
+
return env2.result;
|
|
144
|
+
}
|
|
145
|
+
throw new Error("mcp sse stream had no data event");
|
|
129
146
|
}
|
|
130
147
|
const env = await r.json();
|
|
131
148
|
if (env.error) throw new Error(`mcp error: ${env.error.message}`);
|
package/dist/mcp/index.d.cts
CHANGED
|
@@ -34,7 +34,7 @@ interface McpTool {
|
|
|
34
34
|
*/
|
|
35
35
|
declare class MCPClient {
|
|
36
36
|
readonly config: McpServerConfig;
|
|
37
|
-
private
|
|
37
|
+
private initPromise;
|
|
38
38
|
private toolCache;
|
|
39
39
|
constructor(config: McpServerConfig);
|
|
40
40
|
/** Fetch the server's tool catalog. Cached for the lifetime of the client. */
|
|
@@ -55,6 +55,7 @@ declare class MCPClient {
|
|
|
55
55
|
}>;
|
|
56
56
|
private toSdkTool;
|
|
57
57
|
private ensureInitialized;
|
|
58
|
+
private doInitialize;
|
|
58
59
|
private rpc;
|
|
59
60
|
}
|
|
60
61
|
/**
|
package/dist/mcp/index.d.ts
CHANGED
|
@@ -34,7 +34,7 @@ interface McpTool {
|
|
|
34
34
|
*/
|
|
35
35
|
declare class MCPClient {
|
|
36
36
|
readonly config: McpServerConfig;
|
|
37
|
-
private
|
|
37
|
+
private initPromise;
|
|
38
38
|
private toolCache;
|
|
39
39
|
constructor(config: McpServerConfig);
|
|
40
40
|
/** Fetch the server's tool catalog. Cached for the lifetime of the client. */
|
|
@@ -55,6 +55,7 @@ declare class MCPClient {
|
|
|
55
55
|
}>;
|
|
56
56
|
private toSdkTool;
|
|
57
57
|
private ensureInitialized;
|
|
58
|
+
private doInitialize;
|
|
58
59
|
private rpc;
|
|
59
60
|
}
|
|
60
61
|
/**
|
package/dist/mcp/index.js
CHANGED
|
@@ -3,7 +3,7 @@ var MCP_PROTOCOL_VERSION = "2024-11-05";
|
|
|
3
3
|
var DEFAULT_TIMEOUT_MS = 12e3;
|
|
4
4
|
var MCPClient = class {
|
|
5
5
|
config;
|
|
6
|
-
|
|
6
|
+
initPromise = null;
|
|
7
7
|
toolCache = null;
|
|
8
8
|
constructor(config) {
|
|
9
9
|
if (!/^[a-z0-9_-]+$/.test(config.slug)) {
|
|
@@ -69,7 +69,17 @@ var MCPClient = class {
|
|
|
69
69
|
};
|
|
70
70
|
}
|
|
71
71
|
async ensureInitialized(signal) {
|
|
72
|
-
if (this.
|
|
72
|
+
if (!this.initPromise) {
|
|
73
|
+
this.initPromise = this.doInitialize(signal);
|
|
74
|
+
}
|
|
75
|
+
try {
|
|
76
|
+
await this.initPromise;
|
|
77
|
+
} catch (err) {
|
|
78
|
+
this.initPromise = null;
|
|
79
|
+
throw err;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
async doInitialize(signal) {
|
|
73
83
|
await this.rpc(
|
|
74
84
|
"initialize",
|
|
75
85
|
{
|
|
@@ -80,7 +90,6 @@ var MCPClient = class {
|
|
|
80
90
|
signal
|
|
81
91
|
);
|
|
82
92
|
this.rpc("notifications/initialized", {}, signal).catch(() => void 0);
|
|
83
|
-
this.initialized = true;
|
|
84
93
|
}
|
|
85
94
|
async rpc(method, params, externalSignal) {
|
|
86
95
|
const ac = new AbortController();
|
|
@@ -94,9 +103,9 @@ var MCPClient = class {
|
|
|
94
103
|
}
|
|
95
104
|
const body = {
|
|
96
105
|
jsonrpc: "2.0",
|
|
97
|
-
id: Date.now() + Math.floor(Math.random() * 1e3),
|
|
98
106
|
method,
|
|
99
|
-
params
|
|
107
|
+
params,
|
|
108
|
+
...method.startsWith("notifications/") ? {} : { id: Date.now() + Math.floor(Math.random() * 1e3) }
|
|
100
109
|
};
|
|
101
110
|
try {
|
|
102
111
|
const r = await fetch(this.config.url, {
|
|
@@ -119,11 +128,19 @@ var MCPClient = class {
|
|
|
119
128
|
const ct = r.headers.get("content-type") || "";
|
|
120
129
|
if (ct.includes("text/event-stream")) {
|
|
121
130
|
const text = await r.text();
|
|
122
|
-
const
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
131
|
+
for (const ev of text.split(/\n\n/)) {
|
|
132
|
+
const payload = ev.split(/\r?\n/).filter((l) => l.startsWith("data:")).map((l) => l.slice(5).replace(/^ /, "")).join("\n").trim();
|
|
133
|
+
if (!payload || payload === "[DONE]") continue;
|
|
134
|
+
let env2;
|
|
135
|
+
try {
|
|
136
|
+
env2 = JSON.parse(payload);
|
|
137
|
+
} catch {
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
if (env2.error) throw new Error(`mcp error: ${env2.error.message}`);
|
|
141
|
+
return env2.result;
|
|
142
|
+
}
|
|
143
|
+
throw new Error("mcp sse stream had no data event");
|
|
127
144
|
}
|
|
128
145
|
const env = await r.json();
|
|
129
146
|
if (env.error) throw new Error(`mcp error: ${env.error.message}`);
|
package/dist/runtime/index.cjs
CHANGED
|
@@ -1,5 +1,75 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var promises = require('fs/promises');
|
|
4
|
+
var os = require('os');
|
|
5
|
+
var path = require('path');
|
|
6
|
+
var child_process = require('child_process');
|
|
7
|
+
var util = require('util');
|
|
8
|
+
var crypto = require('crypto');
|
|
9
|
+
|
|
10
|
+
// src/runtime/cloud-session.ts
|
|
11
|
+
var pExecFile = util.promisify(child_process.execFile);
|
|
12
|
+
function resolveInside(root, p) {
|
|
13
|
+
const abs = path.isAbsolute(p) ? p : path.resolve(root, p);
|
|
14
|
+
const rel = path.relative(root, abs);
|
|
15
|
+
if (rel === ".." || rel.startsWith(`..${path.sep}`) || path.isAbsolute(rel)) {
|
|
16
|
+
throw new Error(`path escapes session root: ${p}`);
|
|
17
|
+
}
|
|
18
|
+
return abs;
|
|
19
|
+
}
|
|
20
|
+
var LocalCloudSession = class {
|
|
21
|
+
id;
|
|
22
|
+
root;
|
|
23
|
+
disposed = false;
|
|
24
|
+
constructor(id, root) {
|
|
25
|
+
this.id = id;
|
|
26
|
+
this.root = root;
|
|
27
|
+
}
|
|
28
|
+
async exec(command, options = {}) {
|
|
29
|
+
if (this.disposed) throw new Error("session disposed");
|
|
30
|
+
const cwd = options.cwd ? resolveInside(this.root, options.cwd) : this.root;
|
|
31
|
+
try {
|
|
32
|
+
const { stdout, stderr } = await pExecFile("/bin/sh", ["-c", command], {
|
|
33
|
+
cwd,
|
|
34
|
+
timeout: options.timeoutMs ?? 12e4,
|
|
35
|
+
env: { ...process.env, ...options.env ?? {} },
|
|
36
|
+
maxBuffer: 64 * 1024 * 1024
|
|
37
|
+
});
|
|
38
|
+
return { stdout: stdout.toString(), stderr: stderr.toString(), exitCode: 0 };
|
|
39
|
+
} catch (e) {
|
|
40
|
+
const err = e;
|
|
41
|
+
const exitCode = typeof err.code === "number" ? err.code : err.killed ? 124 : 1;
|
|
42
|
+
return {
|
|
43
|
+
stdout: (err.stdout ?? "").toString(),
|
|
44
|
+
stderr: (err.stderr ?? err.message ?? String(e)).toString(),
|
|
45
|
+
exitCode
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
async readFile(path) {
|
|
50
|
+
if (this.disposed) throw new Error("session disposed");
|
|
51
|
+
return promises.readFile(resolveInside(this.root, path), "utf8");
|
|
52
|
+
}
|
|
53
|
+
async writeFile(path$1, content) {
|
|
54
|
+
if (this.disposed) throw new Error("session disposed");
|
|
55
|
+
const abs = resolveInside(this.root, path$1);
|
|
56
|
+
await promises.mkdir(path.dirname(abs), { recursive: true });
|
|
57
|
+
await promises.writeFile(abs, content, "utf8");
|
|
58
|
+
}
|
|
59
|
+
async dispose() {
|
|
60
|
+
if (this.disposed) return;
|
|
61
|
+
this.disposed = true;
|
|
62
|
+
await promises.rm(this.root, { recursive: true, force: true });
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
var LocalCloudSessionProvider = class {
|
|
66
|
+
async provision() {
|
|
67
|
+
const id = crypto.randomUUID();
|
|
68
|
+
const root = await promises.mkdtemp(path.join(os.tmpdir(), `theron-session-${id.slice(0, 8)}-`));
|
|
69
|
+
return new LocalCloudSession(id, root);
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
|
|
3
73
|
// src/runtime/index.ts
|
|
4
74
|
var Runner = class {
|
|
5
75
|
model;
|
|
@@ -37,8 +107,9 @@ var Runner = class {
|
|
|
37
107
|
* 4. Run any registered verifier kernels on the final output
|
|
38
108
|
* 5. Return the AgentResult
|
|
39
109
|
*/
|
|
40
|
-
async run(agent, query) {
|
|
110
|
+
async run(agent, query, opts) {
|
|
41
111
|
const startedAt = Date.now();
|
|
112
|
+
const signal = opts?.signal;
|
|
42
113
|
this.emit({ type: "agent_start", agent: agent.name, query });
|
|
43
114
|
const messages = [
|
|
44
115
|
{ role: "system", content: agent.instruction.system }
|
|
@@ -51,49 +122,84 @@ var Runner = class {
|
|
|
51
122
|
const toolCalls = [];
|
|
52
123
|
let tokensIn = 0;
|
|
53
124
|
let tokensOut = 0;
|
|
125
|
+
let costUsd = 0;
|
|
54
126
|
let finalOutput = "";
|
|
127
|
+
let completed = false;
|
|
128
|
+
let aborted = false;
|
|
55
129
|
for (let turn = 0; turn < agent.max_turns; turn++) {
|
|
130
|
+
if (signal?.aborted) {
|
|
131
|
+
aborted = true;
|
|
132
|
+
this.emit({ type: "aborted", agent: agent.name });
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
56
135
|
const response = await this.model.chat({
|
|
57
136
|
model: agent.model ?? this.default_model,
|
|
58
137
|
messages,
|
|
59
138
|
tools: agent.toolSchemas(),
|
|
60
|
-
onDelta: (delta) => this.emit({ type: "agent_thinking", agent: agent.name, delta })
|
|
139
|
+
onDelta: (delta) => this.emit({ type: "agent_thinking", agent: agent.name, delta }),
|
|
140
|
+
signal
|
|
61
141
|
});
|
|
62
142
|
tokensIn += response.tokens.input;
|
|
63
143
|
tokensOut += response.tokens.output;
|
|
144
|
+
costUsd += response.cost_usd ?? 0;
|
|
64
145
|
if (response.tool_calls && response.tool_calls.length > 0) {
|
|
65
146
|
messages.push({ role: "assistant", content: response.content });
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
agent: agent.name,
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
147
|
+
const calls = response.tool_calls;
|
|
148
|
+
const results = await Promise.all(
|
|
149
|
+
calls.map(async (call) => {
|
|
150
|
+
const subAgent = agent.findSubAgent(call.name);
|
|
151
|
+
if (subAgent) {
|
|
152
|
+
this.emit({ type: "tool_call_start", agent: agent.name, tool: call.name, input: call.input });
|
|
153
|
+
const t02 = Date.now();
|
|
154
|
+
try {
|
|
155
|
+
const task = typeof call.input?.task === "string" ? call.input.task : JSON.stringify(call.input);
|
|
156
|
+
const sub = await this.run(subAgent, task, { signal });
|
|
157
|
+
const ms = Date.now() - t02;
|
|
158
|
+
this.emit({ type: "tool_call_done", agent: agent.name, tool: call.name, output: sub.output, ms });
|
|
159
|
+
return { call, output: sub.output, content: sub.output, ok: true };
|
|
160
|
+
} catch (err) {
|
|
161
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
162
|
+
this.emit({ type: "error", agent: agent.name, message: `Sub-agent ${subAgent.name} threw: ${msg}` });
|
|
163
|
+
return { call, content: `error: ${msg}`, ok: false };
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
const tool = agent.tools.find((t) => t.schema.name === call.name);
|
|
167
|
+
if (!tool) {
|
|
168
|
+
this.emit({
|
|
169
|
+
type: "error",
|
|
170
|
+
agent: agent.name,
|
|
171
|
+
message: `Model called unknown tool: ${call.name}`
|
|
172
|
+
});
|
|
173
|
+
return { call, content: `error: unknown tool ${call.name}`, ok: false };
|
|
174
|
+
}
|
|
175
|
+
this.emit({ type: "tool_call_start", agent: agent.name, tool: call.name, input: call.input });
|
|
176
|
+
const t0 = Date.now();
|
|
177
|
+
try {
|
|
178
|
+
const output = await tool.execute(call.input, this.tool_context);
|
|
179
|
+
const ms = Date.now() - t0;
|
|
180
|
+
this.emit({ type: "tool_call_done", agent: agent.name, tool: call.name, output, ms });
|
|
181
|
+
return { call, output, content: JSON.stringify(output), ok: true };
|
|
182
|
+
} catch (err) {
|
|
183
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
184
|
+
this.emit({ type: "error", agent: agent.name, message: `Tool ${call.name} threw: ${msg}` });
|
|
185
|
+
return { call, content: `error: ${msg}`, ok: false };
|
|
186
|
+
}
|
|
187
|
+
})
|
|
188
|
+
);
|
|
189
|
+
for (const r of results) {
|
|
190
|
+
if (r.ok) toolCalls.push({ name: r.call.name, input: r.call.input, output: r.output });
|
|
191
|
+
messages.push({ role: "tool", content: r.content });
|
|
90
192
|
}
|
|
91
193
|
continue;
|
|
92
194
|
}
|
|
93
195
|
finalOutput = response.content;
|
|
94
196
|
messages.push({ role: "assistant", content: finalOutput });
|
|
197
|
+
completed = true;
|
|
95
198
|
break;
|
|
96
199
|
}
|
|
200
|
+
if (!completed && !aborted) {
|
|
201
|
+
this.emit({ type: "max_turns_exhausted", agent: agent.name, turns: agent.max_turns });
|
|
202
|
+
}
|
|
97
203
|
this.emit({ type: "agent_output", agent: agent.name, output: finalOutput });
|
|
98
204
|
const verifier_results = [];
|
|
99
205
|
for (const v of agent.verifiers) {
|
|
@@ -116,8 +222,8 @@ var Runner = class {
|
|
|
116
222
|
tool_calls: toolCalls,
|
|
117
223
|
verifier_results,
|
|
118
224
|
tokens_used: { input: tokensIn, output: tokensOut },
|
|
119
|
-
cost_usd:
|
|
120
|
-
// adapter-
|
|
225
|
+
cost_usd: costUsd,
|
|
226
|
+
// summed from adapter-reported per-call cost (0 if the adapter doesn't report it)
|
|
121
227
|
latency_ms
|
|
122
228
|
};
|
|
123
229
|
}
|
|
@@ -127,17 +233,18 @@ var Runner = class {
|
|
|
127
233
|
* Fan out to all specialists in parallel (with timeout), gather outputs,
|
|
128
234
|
* run council-level verifier kernels on each, and reconcile.
|
|
129
235
|
*/
|
|
130
|
-
async runCouncil(council, query) {
|
|
236
|
+
async runCouncil(council, query, opts) {
|
|
131
237
|
const startedAt = Date.now();
|
|
238
|
+
const signal = opts?.signal;
|
|
132
239
|
this.emit({ type: "council_start", council: council.name, query });
|
|
133
240
|
const withTimeout = (p, ms) => Promise.race([
|
|
134
241
|
p,
|
|
135
|
-
new Promise((
|
|
242
|
+
new Promise((resolve2) => setTimeout(() => resolve2(null), ms))
|
|
136
243
|
]);
|
|
137
244
|
const specialistResults = await Promise.all(
|
|
138
245
|
council.specialists.map(async (spec) => {
|
|
139
246
|
try {
|
|
140
|
-
const result = await withTimeout(this.run(spec, query), council.specialist_timeout_ms);
|
|
247
|
+
const result = await withTimeout(this.run(spec, query, { signal }), council.specialist_timeout_ms);
|
|
141
248
|
if (result === null) {
|
|
142
249
|
this.emit({
|
|
143
250
|
type: "error",
|
|
@@ -163,8 +270,11 @@ var Runner = class {
|
|
|
163
270
|
const out = {
|
|
164
271
|
specialist: spec.name,
|
|
165
272
|
output: result.output,
|
|
166
|
-
claims
|
|
167
|
-
//
|
|
273
|
+
// Extract claims if the council supplies an extractor; otherwise the
|
|
274
|
+
// reconciler is responsible (the default deterministic reconciler
|
|
275
|
+
// votes over these claims, so a council that wants automatic
|
|
276
|
+
// ratification should set `claimExtractor`).
|
|
277
|
+
claims: council.claimExtractor ? council.claimExtractor(result.output) : [],
|
|
168
278
|
// AgentResult.verifier_results widens issues to unknown[]; at the
|
|
169
279
|
// runtime layer we know every entry came from a Verifier.check()
|
|
170
280
|
// call (which produces VerifierIssue[]), so the cast is sound.
|
|
@@ -202,4 +312,6 @@ var Runner = class {
|
|
|
202
312
|
}
|
|
203
313
|
};
|
|
204
314
|
|
|
315
|
+
exports.LocalCloudSession = LocalCloudSession;
|
|
316
|
+
exports.LocalCloudSessionProvider = LocalCloudSessionProvider;
|
|
205
317
|
exports.Runner = Runner;
|
package/dist/runtime/index.d.cts
CHANGED
|
@@ -6,6 +6,65 @@ import { ToolContext } from '../tools/index.cjs';
|
|
|
6
6
|
import { VerifierResult } from '../verifiers/index.cjs';
|
|
7
7
|
import 'zod';
|
|
8
8
|
|
|
9
|
+
interface CloudExecResult {
|
|
10
|
+
stdout: string;
|
|
11
|
+
stderr: string;
|
|
12
|
+
/** 0 on success. Non-zero exit is RETURNED, never thrown. 124 = timeout. */
|
|
13
|
+
exitCode: number;
|
|
14
|
+
}
|
|
15
|
+
interface CloudExecOptions {
|
|
16
|
+
/** Working directory, relative to (or inside) the session root. */
|
|
17
|
+
cwd?: string;
|
|
18
|
+
/** Hard timeout in ms (default 120000). */
|
|
19
|
+
timeoutMs?: number;
|
|
20
|
+
/** Extra environment variables for the command. */
|
|
21
|
+
env?: Record<string, string>;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* An isolated, per-session execution environment with a filesystem.
|
|
25
|
+
*
|
|
26
|
+
* Lifecycle: a {@link CloudSessionProvider} hands back a live session from
|
|
27
|
+
* `provision()`; call `dispose()` to release it (tear down the VM / delete the
|
|
28
|
+
* workspace). All file paths are resolved INSIDE the session root; a path that
|
|
29
|
+
* escapes the root is rejected.
|
|
30
|
+
*/
|
|
31
|
+
interface CloudSession {
|
|
32
|
+
/** Stable id, for receipts and logs. */
|
|
33
|
+
readonly id: string;
|
|
34
|
+
/** Absolute path to the session filesystem root (backend-specific). */
|
|
35
|
+
readonly root: string;
|
|
36
|
+
/** Run a shell command in the session. Non-zero exit is returned, not thrown. */
|
|
37
|
+
exec(command: string, options?: CloudExecOptions): Promise<CloudExecResult>;
|
|
38
|
+
/** Read a session file as UTF-8 (path resolved inside the root). */
|
|
39
|
+
readFile(path: string): Promise<string>;
|
|
40
|
+
/** Write a session file, creating parent dirs (path resolved inside the root). */
|
|
41
|
+
writeFile(path: string, content: string): Promise<void>;
|
|
42
|
+
/** Release the session. Idempotent. */
|
|
43
|
+
dispose(): Promise<void>;
|
|
44
|
+
}
|
|
45
|
+
interface CloudSessionProvider {
|
|
46
|
+
/** Provision a fresh, isolated session. */
|
|
47
|
+
provision(): Promise<CloudSession>;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* In-process {@link CloudSession} backend: a temp workspace on the host, commands
|
|
51
|
+
* via `/bin/sh -c`. For tests/CI/local dev only — NOT a security boundary.
|
|
52
|
+
*/
|
|
53
|
+
declare class LocalCloudSession implements CloudSession {
|
|
54
|
+
readonly id: string;
|
|
55
|
+
readonly root: string;
|
|
56
|
+
private disposed;
|
|
57
|
+
constructor(id: string, root: string);
|
|
58
|
+
exec(command: string, options?: CloudExecOptions): Promise<CloudExecResult>;
|
|
59
|
+
readFile(path: string): Promise<string>;
|
|
60
|
+
writeFile(path: string, content: string): Promise<void>;
|
|
61
|
+
dispose(): Promise<void>;
|
|
62
|
+
}
|
|
63
|
+
/** Provisions {@link LocalCloudSession}s in fresh OS temp dirs. */
|
|
64
|
+
declare class LocalCloudSessionProvider implements CloudSessionProvider {
|
|
65
|
+
provision(): Promise<CloudSession>;
|
|
66
|
+
}
|
|
67
|
+
|
|
9
68
|
/** Events the Runner emits as it executes. Subscribe via runner.on(). */
|
|
10
69
|
type RunnerEvent = {
|
|
11
70
|
type: "agent_start";
|
|
@@ -47,6 +106,13 @@ type RunnerEvent = {
|
|
|
47
106
|
type: "council_done";
|
|
48
107
|
council: string;
|
|
49
108
|
output: CouncilOutput;
|
|
109
|
+
} | {
|
|
110
|
+
type: "max_turns_exhausted";
|
|
111
|
+
agent: string;
|
|
112
|
+
turns: number;
|
|
113
|
+
} | {
|
|
114
|
+
type: "aborted";
|
|
115
|
+
agent: string;
|
|
50
116
|
} | {
|
|
51
117
|
type: "error";
|
|
52
118
|
agent: string;
|
|
@@ -77,6 +143,8 @@ interface ModelAdapter {
|
|
|
77
143
|
max_tokens?: number;
|
|
78
144
|
temperature?: number;
|
|
79
145
|
onDelta?: (delta: string) => void;
|
|
146
|
+
/** Cancellation signal — adapters should forward it to fetch(). */
|
|
147
|
+
signal?: AbortSignal;
|
|
80
148
|
}): Promise<{
|
|
81
149
|
content: string;
|
|
82
150
|
tool_calls?: Array<{
|
|
@@ -87,8 +155,15 @@ interface ModelAdapter {
|
|
|
87
155
|
input: number;
|
|
88
156
|
output: number;
|
|
89
157
|
};
|
|
158
|
+
/** Optional per-call cost in USD. Adapters that can compute it should set
|
|
159
|
+
* it so AgentResult.cost_usd and the costUsdAtLeast stop-predicate work. */
|
|
160
|
+
cost_usd?: number;
|
|
90
161
|
}>;
|
|
91
162
|
}
|
|
163
|
+
/** Per-run options. `signal` cancels the loop cooperatively. */
|
|
164
|
+
interface RunOptions {
|
|
165
|
+
signal?: AbortSignal;
|
|
166
|
+
}
|
|
92
167
|
interface RunnerConfig {
|
|
93
168
|
/** The model adapter to use. */
|
|
94
169
|
model: ModelAdapter;
|
|
@@ -135,14 +210,14 @@ declare class Runner {
|
|
|
135
210
|
* 4. Run any registered verifier kernels on the final output
|
|
136
211
|
* 5. Return the AgentResult
|
|
137
212
|
*/
|
|
138
|
-
run(agent: Agent, query: string): Promise<AgentResult>;
|
|
213
|
+
run(agent: Agent, query: string, opts?: RunOptions): Promise<AgentResult>;
|
|
139
214
|
/**
|
|
140
215
|
* Run a Council on a query.
|
|
141
216
|
*
|
|
142
217
|
* Fan out to all specialists in parallel (with timeout), gather outputs,
|
|
143
218
|
* run council-level verifier kernels on each, and reconcile.
|
|
144
219
|
*/
|
|
145
|
-
runCouncil(council: Council, query: string): Promise<CouncilOutput>;
|
|
220
|
+
runCouncil(council: Council, query: string, opts?: RunOptions): Promise<CouncilOutput>;
|
|
146
221
|
}
|
|
147
222
|
|
|
148
|
-
export { type ModelAdapter, Runner, type RunnerConfig, type RunnerEvent };
|
|
223
|
+
export { type CloudExecOptions, type CloudExecResult, type CloudSession, type CloudSessionProvider, LocalCloudSession, LocalCloudSessionProvider, type ModelAdapter, type RunOptions, Runner, type RunnerConfig, type RunnerEvent };
|