@wrenai/genbi 0.0.5 → 0.0.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 +1 -1
- package/dist-server/harness/route/codex-ask.d.ts +1 -1
- package/dist-server/harness/route/codex-ask.js +5 -46
- package/dist-server/harness/route/dispatched.d.ts +20 -0
- package/dist-server/harness/route/dispatched.js +122 -24
- package/dist-server/harness/route/host-query.d.ts +93 -0
- package/dist-server/harness/route/{codex-host-query.js → host-query.js} +120 -54
- package/dist-server/harness/route/route.js +3 -0
- package/dist-server/harness/route/types.d.ts +18 -1
- package/dist-server/harness/route/{codex-wren-proxy.js → wren-host-proxy.js} +6 -5
- package/dist-server/harness/setup/runner.d.ts +15 -3
- package/dist-server/harness/setup/runner.js +234 -72
- package/dist-server/harness/tools/setup-native.d.ts +7 -0
- package/dist-server/harness/tools/setup-native.js +7 -4
- package/dist-server/server/app.js +13 -4
- package/dist-server/server/bin.js +8 -0
- package/dist-server/server/fold.d.ts +9 -8
- package/dist-server/server/fold.js +17 -12
- package/dist-server/server/host-evidence-grounding.d.ts +21 -0
- package/dist-server/server/{codex-local-grounding.js → host-evidence-grounding.js} +15 -14
- package/dist-server/server/native-artifacts.d.ts +93 -4
- package/dist-server/server/native-artifacts.js +153 -4
- package/dist-server/server/turn.js +4 -1
- package/package.json +2 -2
- package/dist-server/harness/route/codex-host-query.d.ts +0 -52
- package/dist-server/server/codex-local-grounding.d.ts +0 -18
- /package/dist-server/harness/route/{codex-wren-proxy.d.ts → wren-host-proxy.d.ts} +0 -0
package/README.md
CHANGED
|
@@ -25,7 +25,7 @@ the rest:
|
|
|
25
25
|
|
|
26
26
|
## Developer preview
|
|
27
27
|
|
|
28
|
-
`@wrenai/genbi` is published at `0.0.
|
|
28
|
+
`@wrenai/genbi` is published at `0.0.5`. Nothing here is under a compatibility
|
|
29
29
|
commitment yet — commands, environment variables and interfaces move without
|
|
30
30
|
notice. Runtime support is limited to the exact versions and execution scopes in the
|
|
31
31
|
packaged certification records; a supported protocol alone does not enable a runtime. See
|
|
@@ -8,7 +8,7 @@ export declare function buildCodexAskArgs(cli: ResolvedCli, options: {
|
|
|
8
8
|
readonly codexHome: string;
|
|
9
9
|
readonly models: CodexManifestModels;
|
|
10
10
|
readonly mcpServer: ResolvedCli;
|
|
11
|
-
/** The genbi stdio proxy Codex starts as its `wren` MCP server (see `
|
|
11
|
+
/** The genbi stdio proxy Codex starts as its `wren` MCP server (see `wren-host-proxy.ts`). */
|
|
12
12
|
readonly proxy: ResolvedCli;
|
|
13
13
|
/** This turn's host query credential file; the proxy reads it, its path carries no secret. */
|
|
14
14
|
readonly credentialFile: string;
|
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
|
-
import { accessSync, existsSync, constants as fsConstants } from "node:fs";
|
|
3
2
|
import { createInterface } from "node:readline";
|
|
4
3
|
import path from "node:path";
|
|
5
|
-
import { fileURLToPath } from "node:url";
|
|
6
4
|
import { hashDirectory } from "../compile/fingerprint.js";
|
|
7
5
|
import { openWrenComponentAccess } from "../components/wren-access.js";
|
|
8
6
|
import { enforceCompliance } from "../compliance/index.js";
|
|
9
7
|
import { compileProfile } from "../compile/pipeline.js";
|
|
10
8
|
import { createAgentEventEmitter } from "../events/index.js";
|
|
11
9
|
import { CodexAskEventMapper } from "./codex-ask-events.js";
|
|
12
|
-
import {
|
|
10
|
+
import { defaultWrenHostProxy, openHostQueryService, resolveExecutableOnPath, singleWrenExecutable, wrenHostProxyArgs, } from "./host-query.js";
|
|
13
11
|
import { resolveCodexLocalCli } from "./codex-local-cli.js";
|
|
14
12
|
import { CODEX_ASK_COMPONENTS, codexAnalysisToolArgs, } from "./codex-local-manifest.js";
|
|
15
13
|
const CODEX_BILLING_ENV_KEYS = new Set([
|
|
@@ -25,19 +23,7 @@ const DEFAULT_CODEX_ASK_TIMEOUT_MS = 10 * 60 * 1000;
|
|
|
25
23
|
export function buildCodexAskArgs(cli, options) {
|
|
26
24
|
if (!options.question.trim())
|
|
27
25
|
throw new Error("Codex Ask question must not be empty");
|
|
28
|
-
const serverArgs =
|
|
29
|
-
...options.proxy.prefixArgs,
|
|
30
|
-
"--credential-file",
|
|
31
|
-
options.credentialFile,
|
|
32
|
-
"--",
|
|
33
|
-
options.mcpServer.command,
|
|
34
|
-
...options.mcpServer.prefixArgs,
|
|
35
|
-
"serve",
|
|
36
|
-
"mcp",
|
|
37
|
-
"--project",
|
|
38
|
-
options.userProject,
|
|
39
|
-
"--quiet",
|
|
40
|
-
];
|
|
26
|
+
const serverArgs = wrenHostProxyArgs(options);
|
|
41
27
|
return [
|
|
42
28
|
...cli.prefixArgs,
|
|
43
29
|
"dispatch",
|
|
@@ -107,9 +93,9 @@ export async function runCodexAskDefault(options) {
|
|
|
107
93
|
// This turn's host query service: the only place codex:local's run_sql executes and the
|
|
108
94
|
// only evidence its answer can be grounded on. Closed (and its observations discarded)
|
|
109
95
|
// when the turn ends, whatever the outcome.
|
|
110
|
-
const hostQueries = await
|
|
96
|
+
const hostQueries = await openHostQueryService({
|
|
111
97
|
openAccess: async (signal) => openWrenComponentAccess({
|
|
112
|
-
executable:
|
|
98
|
+
executable: singleWrenExecutable(mcpServer),
|
|
113
99
|
project: userProject,
|
|
114
100
|
fingerprint: await hashDirectory(userProject),
|
|
115
101
|
signal,
|
|
@@ -124,7 +110,7 @@ export async function runCodexAskDefault(options) {
|
|
|
124
110
|
codexHome,
|
|
125
111
|
models,
|
|
126
112
|
mcpServer,
|
|
127
|
-
proxy:
|
|
113
|
+
proxy: defaultWrenHostProxy(),
|
|
128
114
|
credentialFile: hostQueries.credentialFile,
|
|
129
115
|
timeoutMs,
|
|
130
116
|
component,
|
|
@@ -154,33 +140,6 @@ function resolveModels(value) {
|
|
|
154
140
|
}
|
|
155
141
|
return models;
|
|
156
142
|
}
|
|
157
|
-
/** Governed access spawns one executable with fixed arguments; a prefixed command cannot be bound. */
|
|
158
|
-
function singleExecutable(cli) {
|
|
159
|
-
if (cli.prefixArgs.length > 0)
|
|
160
|
-
throw new Error("codex:local host queries need a standalone wren executable");
|
|
161
|
-
return cli.command;
|
|
162
|
-
}
|
|
163
|
-
/** The built proxy next to this module, else its source (Node strips its erasable types). */
|
|
164
|
-
function defaultWrenProxy() {
|
|
165
|
-
const built = fileURLToPath(new URL("./codex-wren-proxy.js", import.meta.url));
|
|
166
|
-
const script = existsSync(built) ? built : fileURLToPath(new URL("./codex-wren-proxy.ts", import.meta.url));
|
|
167
|
-
return { command: process.execPath, prefixArgs: [script] };
|
|
168
|
-
}
|
|
169
|
-
function resolveExecutableOnPath(name) {
|
|
170
|
-
for (const directory of (process.env.PATH ?? "").split(path.delimiter)) {
|
|
171
|
-
if (!directory)
|
|
172
|
-
continue;
|
|
173
|
-
const candidate = path.join(directory, name);
|
|
174
|
-
try {
|
|
175
|
-
accessSync(candidate, fsConstants.X_OK);
|
|
176
|
-
return candidate;
|
|
177
|
-
}
|
|
178
|
-
catch {
|
|
179
|
-
// Keep searching.
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
throw new Error(`could not find the "${name}" executable on PATH`);
|
|
183
|
-
}
|
|
184
143
|
function sanitizedCodexEnvironment() {
|
|
185
144
|
return Object.fromEntries(Object.entries(process.env).filter(([key, value]) => value !== undefined && !CODEX_BILLING_ENV_KEYS.has(key.toUpperCase())));
|
|
186
145
|
}
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import type { ResolvedCli } from "./agent-sdk-cli.js";
|
|
2
|
+
import { type HostQueryService } from "./host-query.js";
|
|
2
3
|
import type { DispatchedOptions, DispatchedResult } from "./types.js";
|
|
4
|
+
/** The host MCP server's name; warble allows exactly `mcp__wren_host__run_sql` from it. */
|
|
5
|
+
export declare const HOST_MCP_SERVER_NAME = "wren_host";
|
|
6
|
+
export declare const HOST_MCP_TOOLS: readonly ["run_sql"];
|
|
7
|
+
/** Appended once to the dispatched system prompt by warble (single line, at most 300 characters). */
|
|
8
|
+
export declare const HOST_MCP_INSTRUCTION: string;
|
|
3
9
|
export interface AgentSdkChatCommand {
|
|
4
10
|
readonly command: string;
|
|
5
11
|
readonly args: readonly string[];
|
|
@@ -40,6 +46,8 @@ export interface BuildAgentSdkChatArgsOptions {
|
|
|
40
46
|
* `DispatchedOptions.resumeSessionId`.
|
|
41
47
|
*/
|
|
42
48
|
readonly resume?: string;
|
|
49
|
+
/** This turn's host MCP config file (see `writeHostMcpConfig`), forwarded as `--host-mcp-config <path>`. */
|
|
50
|
+
readonly hostMcpConfig?: string;
|
|
43
51
|
}
|
|
44
52
|
/**
|
|
45
53
|
* Builds the argv (and stdin input) for a `warble-agent-sdk chat` invocation
|
|
@@ -113,6 +121,18 @@ export declare function buildAgentSdkChatArgs(cli: ResolvedCli, options: BuildAg
|
|
|
113
121
|
* conversation, not a disk-state prompt rebuild.
|
|
114
122
|
*/
|
|
115
123
|
export declare function runDispatchedDefault(options: DispatchedOptions): Promise<DispatchedResult>;
|
|
124
|
+
/**
|
|
125
|
+
* Writes the turn's `--host-mcp-config` file into the host query service's private 0700
|
|
126
|
+
* directory, as warble 0.16 reads it: a 0600 file owned by this user holding exactly
|
|
127
|
+
* `{name, command, args, tools, instruction}`, `command` absolute. The server is the Wren host
|
|
128
|
+
* proxy in front of `wren serve mcp`; the file names the credential file by path only, never its
|
|
129
|
+
* token. It is removed with the directory when the service closes.
|
|
130
|
+
*/
|
|
131
|
+
export declare function writeHostMcpConfig(service: Pick<HostQueryService, "directory" | "credentialFile">, options: {
|
|
132
|
+
readonly proxy: ResolvedCli;
|
|
133
|
+
readonly mcpServer: ResolvedCli;
|
|
134
|
+
readonly userProject: string;
|
|
135
|
+
}): Promise<string>;
|
|
116
136
|
/**
|
|
117
137
|
* A `spawnChat` turn that failed (non-zero exit, timeout, or spawn error) but still surfaced a
|
|
118
138
|
* resumable SDK session id along the way (the dispatcher's `{t:"session",id}` NDJSON line — see
|
|
@@ -1,14 +1,24 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
|
-
import { mkdtemp } from "node:fs/promises";
|
|
2
|
+
import { mkdtemp, readFile, writeFile } from "node:fs/promises";
|
|
3
3
|
import { createInterface } from "node:readline";
|
|
4
4
|
import os from "node:os";
|
|
5
5
|
import path from "node:path";
|
|
6
|
+
import { ModelConfig } from "@warble/claude-agent-sdk";
|
|
6
7
|
import { enforceCompliance } from "../compliance/index.js";
|
|
8
|
+
import { hashDirectory } from "../compile/fingerprint.js";
|
|
7
9
|
import { compileProfile } from "../compile/pipeline.js";
|
|
10
|
+
import { openWrenComponentAccess } from "../components/wren-access.js";
|
|
8
11
|
import { resolveWarbleBinary } from "../compile/resolve-binary.js";
|
|
9
12
|
import { createAgentEventEmitter } from "../events/index.js";
|
|
10
13
|
import { createChatEventMapperState, mapChatEventToAgentEvent, parseWarbleChatEventLine, } from "./chat-event-mapper.js";
|
|
11
14
|
import { resolveAgentSdkCli } from "./agent-sdk-cli.js";
|
|
15
|
+
import { defaultWrenHostProxy, openHostQueryService, resolveExecutableOnPath, wrenHostProxyArgs, } from "./host-query.js";
|
|
16
|
+
/** The host MCP server's name; warble allows exactly `mcp__wren_host__run_sql` from it. */
|
|
17
|
+
export const HOST_MCP_SERVER_NAME = "wren_host";
|
|
18
|
+
export const HOST_MCP_TOOLS = Object.freeze(["run_sql"]);
|
|
19
|
+
/** Appended once to the dispatched system prompt by warble (single line, at most 300 characters). */
|
|
20
|
+
export const HOST_MCP_INSTRUCTION = "Run data queries with mcp__wren_host__run_sql (sql, optional limit): it returns the rows plus a query_id; " +
|
|
21
|
+
"cite that query_id in your answer's definition.";
|
|
12
22
|
const MODE_B_AGENT_ID = "answer_query";
|
|
13
23
|
/**
|
|
14
24
|
* Builds the argv (and stdin input) for a `warble-agent-sdk chat` invocation
|
|
@@ -56,6 +66,7 @@ export function buildAgentSdkChatArgs(cli, options) {
|
|
|
56
66
|
...(options.modelsConfig !== undefined ? ["--models-config", options.modelsConfig] : []),
|
|
57
67
|
...(options.maxTurns !== undefined ? ["--max-turns", String(options.maxTurns)] : []),
|
|
58
68
|
...(options.resume !== undefined ? ["--resume", options.resume] : []),
|
|
69
|
+
...(options.hostMcpConfig !== undefined ? ["--host-mcp-config", options.hostMcpConfig] : []),
|
|
59
70
|
"--stream-json",
|
|
60
71
|
],
|
|
61
72
|
input: `${encodeQuestionForStdin(options.question)}\n`,
|
|
@@ -169,30 +180,45 @@ export async function runDispatchedDefault(options) {
|
|
|
169
180
|
const outDir = options.outDir ?? (await mkdtemp(path.join(os.tmpdir(), "wren-harness-agent-sdk-")));
|
|
170
181
|
if (options.signal?.aborted)
|
|
171
182
|
throw new Error("warble-agent-sdk chat was cancelled during preparation");
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
183
|
+
// This turn's host query service, when the caller asked for one: the only place the host MCP's
|
|
184
|
+
// run_sql executes and the only evidence the answer can be grounded on. Closed (and its
|
|
185
|
+
// observations discarded) when the turn ends, whatever the outcome.
|
|
186
|
+
const host = await openAskHostQueries(options, irPath);
|
|
187
|
+
try {
|
|
188
|
+
const command = buildAgentSdkChatArgs(cli, {
|
|
189
|
+
irPath,
|
|
190
|
+
userProject: options.userProject,
|
|
191
|
+
question: options.question,
|
|
192
|
+
outDir,
|
|
193
|
+
warbleBin,
|
|
194
|
+
agentId,
|
|
195
|
+
...(options.modelsConfig !== undefined ? { modelsConfig: options.modelsConfig } : {}),
|
|
196
|
+
...(options.maxTurns !== undefined ? { maxTurns: options.maxTurns } : {}),
|
|
197
|
+
...(options.resumeSessionId !== undefined ? { resume: options.resumeSessionId } : {}),
|
|
198
|
+
...(host !== undefined ? { hostMcpConfig: host.configFile } : {}),
|
|
199
|
+
});
|
|
200
|
+
const { finalText, sessionId } = await spawnChat(command, (event) => emitter.emit(event), options.chatTimeoutMs ?? CHAT_TIMEOUT_MS, options.signal);
|
|
201
|
+
// `spawnChat` resolves with whatever trimmed final-answer text
|
|
202
|
+
// the process produced, even if that's empty — e.g. the CLI exits 0 but
|
|
203
|
+
// never printed a terminal answer line (a dispatcher bug, an unexpected
|
|
204
|
+
// output-format change, or a turn that produced no text). Treat that as
|
|
205
|
+
// a failure rather than a silent empty "answer".
|
|
206
|
+
if (finalText.length === 0) {
|
|
207
|
+
throw new Error("warble-agent-sdk chat exited successfully but produced no stdout — expected the turn's " +
|
|
208
|
+
"final answer text; got empty output");
|
|
209
|
+
}
|
|
210
|
+
const hostObservations = host?.service.observations();
|
|
211
|
+
emitter.emit({ kind: "answer", text: finalText });
|
|
212
|
+
emitter.emit({ kind: "run.finish", status: "answer" });
|
|
213
|
+
return {
|
|
214
|
+
finalText,
|
|
215
|
+
...(sessionId !== undefined ? { sessionId } : {}),
|
|
216
|
+
...(hostObservations !== undefined ? { hostObservations } : {}),
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
finally {
|
|
220
|
+
await host?.service.close();
|
|
192
221
|
}
|
|
193
|
-
emitter.emit({ kind: "answer", text: finalText });
|
|
194
|
-
emitter.emit({ kind: "run.finish", status: "answer" });
|
|
195
|
-
return { finalText, ...(sessionId !== undefined ? { sessionId } : {}) };
|
|
196
222
|
}
|
|
197
223
|
catch (error) {
|
|
198
224
|
emitter.emit({ kind: "error", message: describeDispatchedError(error) });
|
|
@@ -200,6 +226,78 @@ export async function runDispatchedDefault(options) {
|
|
|
200
226
|
throw error;
|
|
201
227
|
}
|
|
202
228
|
}
|
|
229
|
+
/**
|
|
230
|
+
* Opens an Ask turn's host query service and writes its host MCP config, or returns `undefined`
|
|
231
|
+
* when the turn runs without one (not requested, a hybrid models config, or no `wren` on PATH).
|
|
232
|
+
*/
|
|
233
|
+
async function openAskHostQueries(options, irPath) {
|
|
234
|
+
if (options.hostQueries === undefined)
|
|
235
|
+
return undefined;
|
|
236
|
+
if (options.modelsConfig !== undefined && await bindsNonAnthropicTier(irPath, options.modelsConfig))
|
|
237
|
+
return undefined;
|
|
238
|
+
let wren;
|
|
239
|
+
try {
|
|
240
|
+
wren = options.hostQueries.wren ?? resolveExecutableOnPath("wren");
|
|
241
|
+
}
|
|
242
|
+
catch {
|
|
243
|
+
return undefined;
|
|
244
|
+
}
|
|
245
|
+
const userProject = options.userProject;
|
|
246
|
+
const service = await openHostQueryService({
|
|
247
|
+
openAccess: async (signal) => openWrenComponentAccess({
|
|
248
|
+
executable: wren,
|
|
249
|
+
project: userProject,
|
|
250
|
+
fingerprint: await hashDirectory(userProject),
|
|
251
|
+
signal,
|
|
252
|
+
}),
|
|
253
|
+
...(options.signal !== undefined ? { signal: options.signal } : {}),
|
|
254
|
+
});
|
|
255
|
+
try {
|
|
256
|
+
const configFile = await writeHostMcpConfig(service, {
|
|
257
|
+
proxy: defaultWrenHostProxy(),
|
|
258
|
+
mcpServer: { command: wren, prefixArgs: [] },
|
|
259
|
+
userProject,
|
|
260
|
+
});
|
|
261
|
+
return { service, configFile };
|
|
262
|
+
}
|
|
263
|
+
catch (error) {
|
|
264
|
+
await service.close();
|
|
265
|
+
throw error;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* True when the models config binds a tier this IR's steps use to a non-Anthropic provider. Warble
|
|
270
|
+
* runs such a plan hybrid-staged and refuses `--host-mcp-config` for it; an all-Anthropic config (the
|
|
271
|
+
* only kind Setup writes for Claude subscription) keeps the host MCP. Every IR tier is checked, not
|
|
272
|
+
* just the dispatched component's, so a hybrid tier anywhere in the profile also skips it.
|
|
273
|
+
*/
|
|
274
|
+
async function bindsNonAnthropicTier(irPath, modelsConfig) {
|
|
275
|
+
const ir = JSON.parse(await readFile(irPath, "utf8"));
|
|
276
|
+
const models = ModelConfig.fromYaml(await readFile(modelsConfig, "utf8"));
|
|
277
|
+
const tiers = new Set(ir.components.flatMap((component) => component.llm_calls.map((step) => step.tier)));
|
|
278
|
+
return [...tiers].some((tier) => models.binding(tier).provider !== "anthropic");
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Writes the turn's `--host-mcp-config` file into the host query service's private 0700
|
|
282
|
+
* directory, as warble 0.16 reads it: a 0600 file owned by this user holding exactly
|
|
283
|
+
* `{name, command, args, tools, instruction}`, `command` absolute. The server is the Wren host
|
|
284
|
+
* proxy in front of `wren serve mcp`; the file names the credential file by path only, never its
|
|
285
|
+
* token. It is removed with the directory when the service closes.
|
|
286
|
+
*/
|
|
287
|
+
export async function writeHostMcpConfig(service, options) {
|
|
288
|
+
if (!path.isAbsolute(options.proxy.command))
|
|
289
|
+
throw new Error("the host MCP command must be an absolute path");
|
|
290
|
+
const file = path.join(service.directory, "host-mcp.json");
|
|
291
|
+
const config = {
|
|
292
|
+
name: HOST_MCP_SERVER_NAME,
|
|
293
|
+
command: options.proxy.command,
|
|
294
|
+
args: wrenHostProxyArgs({ ...options, credentialFile: service.credentialFile }),
|
|
295
|
+
tools: [...HOST_MCP_TOOLS],
|
|
296
|
+
instruction: HOST_MCP_INSTRUCTION,
|
|
297
|
+
};
|
|
298
|
+
await writeFile(file, JSON.stringify(config), { encoding: "utf8", mode: 0o600, flag: "wx" });
|
|
299
|
+
return file;
|
|
300
|
+
}
|
|
203
301
|
function describeDispatchedError(error) {
|
|
204
302
|
if (error instanceof Error)
|
|
205
303
|
return error.message;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import type { ComponentAccess } from "../components/broker.js";
|
|
2
|
+
import { type GovernedErrorClass } from "../components/wren-access.js";
|
|
3
|
+
import type { ResolvedCli } from "./agent-sdk-cli.js";
|
|
4
|
+
/**
|
|
5
|
+
* One query the host executed for a subscription turn or a native session, in the component
|
|
6
|
+
* runner's observation shape (`input.sql`, `output` carrying a host `query_id`), so the shared
|
|
7
|
+
* grounding predicate reads it unchanged.
|
|
8
|
+
*/
|
|
9
|
+
export interface HostQueryObservation {
|
|
10
|
+
readonly tool: "run_sql";
|
|
11
|
+
readonly input: {
|
|
12
|
+
readonly sql: string;
|
|
13
|
+
readonly limit: number;
|
|
14
|
+
};
|
|
15
|
+
readonly output: unknown;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Per-scope bounds (one Ask turn, or one native session). Count, request and per-result bytes
|
|
19
|
+
* reuse the component runner's limits; the total caps what the host keeps in memory for one scope
|
|
20
|
+
* until its answer is grounded. Rows default to 1000 like `run_sql`. Like `run_sql`, the host
|
|
21
|
+
* probes one row past the limit to report truncation; the cap is 9999 so the probe stays within
|
|
22
|
+
* governed-stdio's 10000-row bound.
|
|
23
|
+
*/
|
|
24
|
+
export declare const HOST_QUERY_LIMITS: Readonly<{
|
|
25
|
+
queries: 32;
|
|
26
|
+
requestBytes: 65536;
|
|
27
|
+
resultBytes: 1048576;
|
|
28
|
+
totalResultBytes: number;
|
|
29
|
+
defaultRows: 1000;
|
|
30
|
+
maxRows: 9999;
|
|
31
|
+
timeoutMs: 120000;
|
|
32
|
+
}>;
|
|
33
|
+
/** Error classes a host query may return; each transport owns the sentence the model sees. */
|
|
34
|
+
export type HostQueryErrorClass = GovernedErrorClass | "unauthorized" | "budget_exhausted";
|
|
35
|
+
export type HostQueryOutcome = {
|
|
36
|
+
readonly result: unknown;
|
|
37
|
+
} | {
|
|
38
|
+
readonly error: HostQueryErrorClass;
|
|
39
|
+
};
|
|
40
|
+
export interface HostQueryRecorderOptions {
|
|
41
|
+
/** Opens governed access for this scope; called at most once, on the first query. */
|
|
42
|
+
readonly openAccess: (signal: AbortSignal) => Promise<ComponentAccess>;
|
|
43
|
+
readonly signal?: AbortSignal;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Executes and records one scope's queries in process. The scope is the recorder: its
|
|
47
|
+
* observations are never shared, and its `query_id`s carry a tag no other recorder has.
|
|
48
|
+
*/
|
|
49
|
+
export interface HostQueryRecorder {
|
|
50
|
+
execute(sql: unknown, limit: unknown): Promise<HostQueryOutcome>;
|
|
51
|
+
/** This scope's recorded observations, in execution order. Empty after `close()`. */
|
|
52
|
+
observations(): readonly HostQueryObservation[];
|
|
53
|
+
/** Stops accepting queries, discards observations, closes governed access. Idempotent. */
|
|
54
|
+
close(): Promise<void>;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Every query runs through governed access (`wren governed-stdio`), gets a scope-unique host
|
|
58
|
+
* `query_id` (`q<N>-<tag>`), and is recorded as an observation of this scope only.
|
|
59
|
+
*/
|
|
60
|
+
export declare function createHostQueryRecorder(options: HostQueryRecorderOptions): HostQueryRecorder;
|
|
61
|
+
export interface HostQueryService {
|
|
62
|
+
/** This service's private 0700 directory; it is removed on `close()`. */
|
|
63
|
+
readonly directory: string;
|
|
64
|
+
/** A 0600 file holding this turn's socket path and token. Its own path carries no secret. */
|
|
65
|
+
readonly credentialFile: string;
|
|
66
|
+
/** This turn's recorded observations, in execution order. Empty after `close()`. */
|
|
67
|
+
observations(): readonly HostQueryObservation[];
|
|
68
|
+
/** Stops accepting queries, discards observations, closes governed access, removes the directory. */
|
|
69
|
+
close(): Promise<void>;
|
|
70
|
+
}
|
|
71
|
+
export type HostQueryServiceOptions = HostQueryRecorderOptions;
|
|
72
|
+
/**
|
|
73
|
+
* The host side of a subscription turn's `run_sql` (codex:local and the Claude agent-sdk Ask):
|
|
74
|
+
* a per-turn unix socket in a fresh 0700 directory that answers only requests carrying this
|
|
75
|
+
* turn's random token, in front of one {@link createHostQueryRecorder} for the turn.
|
|
76
|
+
*/
|
|
77
|
+
export declare function openHostQueryService(options: HostQueryServiceOptions): Promise<HostQueryService>;
|
|
78
|
+
/**
|
|
79
|
+
* The argv after the proxy command: the proxy script, this turn's credential file, then the
|
|
80
|
+
* `wren serve mcp` command it fronts. Shared by codex:local's `--server-arg`s and the agent-sdk
|
|
81
|
+
* host MCP config so the two cannot drift.
|
|
82
|
+
*/
|
|
83
|
+
export declare function wrenHostProxyArgs(options: {
|
|
84
|
+
readonly proxy: ResolvedCli;
|
|
85
|
+
readonly credentialFile: string;
|
|
86
|
+
readonly mcpServer: ResolvedCli;
|
|
87
|
+
readonly userProject: string;
|
|
88
|
+
}): string[];
|
|
89
|
+
/** The built proxy next to this module, else its source (Node strips its erasable types). */
|
|
90
|
+
export declare function defaultWrenHostProxy(): ResolvedCli;
|
|
91
|
+
/** Governed access spawns one executable with fixed arguments; a prefixed command cannot be bound. */
|
|
92
|
+
export declare function singleWrenExecutable(cli: ResolvedCli): string;
|
|
93
|
+
export declare function resolveExecutableOnPath(name: string): string;
|