@wrenai/genbi 0.0.4 → 0.0.5
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 +64 -2
- package/dist/assets/{echarts-LTFC-ysd.js → echarts-DYM4hjvk.js} +2 -2
- package/dist/assets/index-A_Wrq7Ey.css +1 -0
- package/dist/assets/{index-Bo--QMUN.js → index-DsH129tV.js} +18 -17
- package/dist/index.html +2 -2
- package/dist-server/harness/bundle/schema.d.ts +38 -0
- package/dist-server/harness/bundle/schema.js +18 -10
- package/dist-server/harness/bundle/version.js +1 -1
- package/dist-server/harness/cli-args.d.ts +10 -2
- package/dist-server/harness/cli-args.js +24 -8
- package/dist-server/harness/cli.js +75 -9
- package/dist-server/harness/compile/cache.js +4 -4
- package/dist-server/harness/compile/compose-profile.d.ts +51 -14
- package/dist-server/harness/compile/compose-profile.js +85 -19
- package/dist-server/harness/compile/context-loader.d.ts +56 -0
- package/dist-server/harness/compile/context-loader.js +237 -0
- package/dist-server/harness/compile/errors.d.ts +19 -0
- package/dist-server/harness/compile/errors.js +35 -0
- package/dist-server/harness/compile/index.d.ts +4 -3
- package/dist-server/harness/compile/index.js +4 -3
- package/dist-server/harness/compile/pipeline.d.ts +15 -7
- package/dist-server/harness/compile/pipeline.js +62 -14
- package/dist-server/harness/compile/resolve-binary.d.ts +2 -2
- package/dist-server/harness/compile/resolve-binary.js +3 -3
- package/dist-server/harness/compile/types.d.ts +29 -0
- package/dist-server/harness/compile/warble-identity.d.ts +11 -0
- package/dist-server/harness/compile/warble-identity.js +13 -0
- package/dist-server/harness/components/ai-step.d.ts +4 -0
- package/dist-server/harness/components/ai-step.js +40 -0
- package/dist-server/harness/components/broker.d.ts +60 -0
- package/dist-server/harness/components/broker.js +117 -0
- package/dist-server/harness/components/capability-card.d.ts +98 -0
- package/dist-server/harness/components/capability-card.js +169 -0
- package/dist-server/harness/components/context.d.ts +2 -0
- package/dist-server/harness/components/context.js +30 -0
- package/dist-server/harness/components/display.d.ts +6 -0
- package/dist-server/harness/components/display.js +29 -0
- package/dist-server/harness/components/egress-judge.d.ts +12 -0
- package/dist-server/harness/components/egress-judge.js +27 -0
- package/dist-server/harness/components/egress.d.ts +142 -0
- package/dist-server/harness/components/egress.js +469 -0
- package/dist-server/harness/components/in-process.d.ts +7 -0
- package/dist-server/harness/components/in-process.js +245 -0
- package/dist-server/harness/components/normalize.d.ts +35 -0
- package/dist-server/harness/components/normalize.js +245 -0
- package/dist-server/harness/components/plan.d.ts +31 -0
- package/dist-server/harness/components/plan.js +184 -0
- package/dist-server/harness/components/report.d.ts +207 -0
- package/dist-server/harness/components/report.js +309 -0
- package/dist-server/harness/components/runner.d.ts +193 -0
- package/dist-server/harness/components/runner.js +492 -0
- package/dist-server/harness/components/semantic-guards.d.ts +20 -0
- package/dist-server/harness/components/semantic-guards.js +42 -0
- package/dist-server/harness/components/terminal-json.d.ts +18 -0
- package/dist-server/harness/components/terminal-json.js +82 -0
- package/dist-server/harness/components/usage.d.ts +14 -0
- package/dist-server/harness/components/usage.js +19 -0
- package/dist-server/harness/components/wren-access.d.ts +29 -0
- package/dist-server/harness/components/wren-access.js +233 -0
- package/dist-server/harness/components/zone-dry-run.d.ts +43 -0
- package/dist-server/harness/components/zone-dry-run.js +107 -0
- package/dist-server/harness/components/zone-gate.d.ts +76 -0
- package/dist-server/harness/components/zone-gate.js +144 -0
- package/dist-server/harness/events/index.d.ts +1 -1
- package/dist-server/harness/events/types.d.ts +52 -0
- package/dist-server/harness/index.d.ts +9 -2
- package/dist-server/harness/index.js +5 -1
- package/dist-server/harness/loop/executor.d.ts +1 -0
- package/dist-server/harness/loop/executor.js +37 -15
- package/dist-server/harness/npm-package-resolve.d.ts +2 -0
- package/dist-server/harness/npm-package-resolve.js +16 -4
- package/dist-server/harness/providers/adapters/openai-compatible.d.ts +13 -0
- package/dist-server/harness/providers/adapters/openai-compatible.js +17 -0
- package/dist-server/harness/providers/adapters/openai.d.ts +7 -0
- package/dist-server/harness/providers/adapters/openai.js +6 -0
- package/dist-server/harness/providers/adapters/pi-ai.d.ts +48 -0
- package/dist-server/harness/providers/adapters/pi-ai.js +518 -0
- package/dist-server/harness/providers/binding.d.ts +45 -4
- package/dist-server/harness/providers/binding.js +44 -8
- package/dist-server/harness/providers/index.d.ts +9 -3
- package/dist-server/harness/providers/index.js +10 -3
- package/dist-server/harness/providers/zone.d.ts +96 -0
- package/dist-server/harness/providers/zone.js +112 -0
- package/dist-server/harness/render/answering-query.d.ts +29 -0
- package/dist-server/harness/render/answering-query.js +62 -0
- package/dist-server/harness/render/envelope.d.ts +6 -0
- package/dist-server/harness/render/envelope.js +10 -3
- package/dist-server/harness/route/adapter-spec.d.ts +8 -9
- package/dist-server/harness/route/adapter-spec.js +13 -14
- package/dist-server/harness/route/codex-ask-events.js +2 -1
- package/dist-server/harness/route/codex-ask.d.ts +4 -0
- package/dist-server/harness/route/codex-ask.js +69 -30
- package/dist-server/harness/route/codex-host-query.d.ts +52 -0
- package/dist-server/harness/route/codex-host-query.js +191 -0
- package/dist-server/harness/route/codex-local-manifest.d.ts +9 -1
- package/dist-server/harness/route/codex-local-manifest.js +96 -15
- package/dist-server/harness/route/codex-wren-proxy.d.ts +33 -0
- package/dist-server/harness/route/codex-wren-proxy.js +227 -0
- package/dist-server/harness/route/describe.js +45 -6
- package/dist-server/harness/route/in-process.d.ts +22 -1
- package/dist-server/harness/route/in-process.js +54 -4
- package/dist-server/harness/route/route.js +5 -2
- package/dist-server/harness/route/types.d.ts +30 -1
- package/dist-server/harness/session/run.js +30 -10
- package/dist-server/harness/setup/runner.d.ts +2 -0
- package/dist-server/harness/setup/runner.js +38 -17
- package/dist-server/harness/setup/yaml-scalar.d.ts +23 -0
- package/dist-server/harness/setup/yaml-scalar.js +62 -0
- package/dist-server/server/adopt.js +1 -17
- package/dist-server/server/app.d.ts +1 -0
- package/dist-server/server/app.js +163 -14
- package/dist-server/server/auth-choice.d.ts +13 -1
- package/dist-server/server/auth-choice.js +66 -1
- package/dist-server/server/bin.js +177 -25
- package/dist-server/server/codex-local-grounding.d.ts +18 -0
- package/dist-server/server/codex-local-grounding.js +92 -0
- package/dist-server/server/codex-native-session.d.ts +25 -0
- package/dist-server/server/codex-native-session.js +1 -0
- package/dist-server/server/codex-runtime-composition.d.ts +34 -0
- package/dist-server/server/codex-runtime-composition.js +250 -0
- package/dist-server/server/codex-structured-ask.d.ts +23 -0
- package/dist-server/server/codex-structured-ask.js +102 -0
- package/dist-server/server/conn-config.js +4 -16
- package/dist-server/server/conversation-socket.d.ts +16 -0
- package/dist-server/server/conversation-socket.js +65 -0
- package/dist-server/server/db.d.ts +46 -0
- package/dist-server/server/db.js +88 -9
- package/dist-server/server/env-detect.d.ts +6 -0
- package/dist-server/server/env-detect.js +7 -0
- package/dist-server/server/fold.d.ts +22 -4
- package/dist-server/server/fold.js +37 -5
- package/dist-server/server/harness.d.ts +2 -2
- package/dist-server/server/harness.js +33 -7
- package/dist-server/server/interactive-terminal.d.ts +10 -6
- package/dist-server/server/interactive-terminal.js +33 -8
- package/dist-server/server/managed-wren-runtime.d.ts +116 -0
- package/dist-server/server/managed-wren-runtime.js +670 -0
- package/dist-server/server/native-artifacts.d.ts +23 -7
- package/dist-server/server/native-artifacts.js +43 -12
- package/dist-server/server/native-component-context.d.ts +19 -0
- package/dist-server/server/native-component-context.js +76 -0
- package/dist-server/server/native-component-preparation.d.ts +20 -0
- package/dist-server/server/native-component-preparation.js +129 -0
- package/dist-server/server/native-component-runtime.d.ts +36 -0
- package/dist-server/server/native-component-runtime.js +92 -0
- package/dist-server/server/native-components.d.ts +84 -0
- package/dist-server/server/native-components.js +160 -0
- package/dist-server/server/native-dispatch-process.d.ts +8 -0
- package/dist-server/server/native-dispatch-process.js +65 -0
- package/dist-server/server/native-runtime-spec.d.ts +89 -0
- package/dist-server/server/native-runtime-spec.js +169 -0
- package/dist-server/server/native-sessions.d.ts +172 -19
- package/dist-server/server/native-sessions.js +839 -244
- package/dist-server/server/native-shutdown.d.ts +6 -0
- package/dist-server/server/native-shutdown.js +28 -0
- package/dist-server/server/native-wren-home.d.ts +18 -0
- package/dist-server/server/native-wren-home.js +262 -0
- package/dist-server/server/node-pty-host.d.ts +2 -1
- package/dist-server/server/node-pty-host.js +3 -2
- package/dist-server/server/profile-registry.d.ts +139 -0
- package/dist-server/server/profile-registry.js +620 -0
- package/dist-server/server/runtime-binding.d.ts +4 -1
- package/dist-server/server/runtime-binding.js +50 -2
- package/dist-server/server/runtime-host/claude-component-step.d.ts +25 -0
- package/dist-server/server/runtime-host/claude-component-step.js +186 -0
- package/dist-server/server/runtime-host/codex-app-server.d.ts +75 -0
- package/dist-server/server/runtime-host/codex-app-server.js +239 -0
- package/dist-server/server/runtime-host/codex-compatibility.d.ts +110 -0
- package/dist-server/server/runtime-host/codex-compatibility.js +162 -0
- package/dist-server/server/runtime-host/codex-component-step.d.ts +19 -0
- package/dist-server/server/runtime-host/codex-component-step.js +308 -0
- package/dist-server/server/runtime-host/codex-conversation.d.ts +109 -0
- package/dist-server/server/runtime-host/codex-conversation.js +295 -0
- package/dist-server/server/runtime-host/codex-direct-tools.d.ts +23 -0
- package/dist-server/server/runtime-host/codex-direct-tools.js +59 -0
- package/dist-server/server/runtime-host/codex-events.d.ts +647 -0
- package/dist-server/server/runtime-host/codex-events.js +66 -0
- package/dist-server/server/runtime-host/codex-policy.d.ts +16 -0
- package/dist-server/server/runtime-host/codex-policy.js +151 -0
- package/dist-server/server/runtime-host/codex-process.d.ts +8 -0
- package/dist-server/server/runtime-host/codex-process.js +96 -0
- package/dist-server/server/runtime-host/codex-rpc.d.ts +47 -0
- package/dist-server/server/runtime-host/codex-rpc.js +182 -0
- package/dist-server/server/runtime-host/codex-session.d.ts +74 -0
- package/dist-server/server/runtime-host/codex-session.js +492 -0
- package/dist-server/server/runtime-host/codex-terminal-protocol.d.ts +35 -0
- package/dist-server/server/runtime-host/codex-terminal-protocol.js +243 -0
- package/dist-server/server/runtime-host/codex-terminal.d.ts +16 -0
- package/dist-server/server/runtime-host/codex-terminal.js +120 -0
- package/dist-server/server/runtime-host/index.d.ts +5 -0
- package/dist-server/server/runtime-host/index.js +3 -0
- package/dist-server/server/runtime-host/local.d.ts +29 -0
- package/dist-server/server/runtime-host/local.js +90 -0
- package/dist-server/server/runtime-host/policy.d.ts +8 -0
- package/dist-server/server/runtime-host/policy.js +78 -0
- package/dist-server/server/runtime-host/types.d.ts +52 -0
- package/dist-server/server/runtime-host/types.js +62 -0
- package/dist-server/server/structured-query-evidence.d.ts +7 -0
- package/dist-server/server/structured-query-evidence.js +47 -0
- package/dist-server/server/structured-runtime.d.ts +52 -0
- package/dist-server/server/structured-runtime.js +130 -0
- package/dist-server/server/structured-scope.d.ts +4 -0
- package/dist-server/server/structured-scope.js +12 -0
- package/dist-server/server/turn.d.ts +6 -1
- package/dist-server/server/turn.js +82 -25
- package/dist-server/server/wire-types.d.ts +67 -3
- package/dist-server/server/wren-profiles.js +1 -1
- package/managed-wren/README.md +60 -0
- package/managed-wren/dependency-wheels.json +282 -0
- package/managed-wren/manifest.json +373 -0
- package/managed-wren/release-inputs.json +45 -0
- package/managed-wren/runtime-tree.cjs +115 -0
- package/managed-wren/source-inputs.json +40 -0
- package/package.json +15 -6
- package/profiles/genbi-default/context/binding.yml +17 -7
- package/profiles/genbi-default/context/context.json +146 -0
- package/profiles/genbi-default/ir.golden.json +39 -43
- package/profiles/genbi-default/profile.yml +31 -0
- package/profiles/genbi-enrich-context/context/binding.yml +14 -3
- package/profiles/genbi-enrich-context/context/context.json +146 -0
- package/profiles/genbi-enrich-context/ir.golden.json +4 -5
- package/profiles/genbi-monitor/context/binding.yml +13 -5
- package/profiles/genbi-monitor/context/context.json +146 -0
- package/profiles/genbi-monitor/ir.golden.json +13 -7
- package/profiles/genbi-report/context/binding.yml +19 -0
- package/profiles/genbi-report/context/context.json +223 -0
- package/profiles/genbi-report/ir.golden.json +328 -0
- package/profiles/genbi-report/profile.yml +38 -0
- package/profiles/genbi-setup/ir.golden.json +3 -1
- package/dist/assets/index-Xs4q7pNo.css +0 -1
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { ComponentAccess } from "../components/broker.js";
|
|
2
|
+
import { type GovernedErrorClass } from "../components/wren-access.js";
|
|
3
|
+
/**
|
|
4
|
+
* One query the host executed for a codex:local turn, in the component runner's observation
|
|
5
|
+
* shape (`input.sql`, `output` carrying a host `query_id`), so the shared grounding predicate
|
|
6
|
+
* reads it unchanged.
|
|
7
|
+
*/
|
|
8
|
+
export interface HostQueryObservation {
|
|
9
|
+
readonly tool: "run_sql";
|
|
10
|
+
readonly input: {
|
|
11
|
+
readonly sql: string;
|
|
12
|
+
readonly limit: number;
|
|
13
|
+
};
|
|
14
|
+
readonly output: unknown;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Per-turn bounds. Count, request and per-result bytes reuse the component runner's limits;
|
|
18
|
+
* the total caps what the host keeps in memory for one turn until its answer is grounded.
|
|
19
|
+
* Rows default to 1000 like `run_sql`. Like `run_sql`, the host probes one row past the limit to
|
|
20
|
+
* report truncation; the cap is 9999 so the probe stays within governed-stdio's 10000-row bound.
|
|
21
|
+
*/
|
|
22
|
+
export declare const CODEX_HOST_QUERY_LIMITS: Readonly<{
|
|
23
|
+
queries: 32;
|
|
24
|
+
requestBytes: 65536;
|
|
25
|
+
resultBytes: 1048576;
|
|
26
|
+
totalResultBytes: number;
|
|
27
|
+
defaultRows: 1000;
|
|
28
|
+
maxRows: 9999;
|
|
29
|
+
timeoutMs: 120000;
|
|
30
|
+
}>;
|
|
31
|
+
/** Error classes the socket may return; the proxy owns the sentence the model sees for each. */
|
|
32
|
+
export type HostQueryErrorClass = GovernedErrorClass | "unauthorized" | "budget_exhausted";
|
|
33
|
+
export interface CodexHostQueryService {
|
|
34
|
+
/** A 0600 file holding this turn's socket path and token. Its own path carries no secret. */
|
|
35
|
+
readonly credentialFile: string;
|
|
36
|
+
/** This turn's recorded observations, in execution order. Empty after `close()`. */
|
|
37
|
+
observations(): readonly HostQueryObservation[];
|
|
38
|
+
/** Stops accepting queries, discards observations, closes governed access, removes the credential. */
|
|
39
|
+
close(): Promise<void>;
|
|
40
|
+
}
|
|
41
|
+
export interface CodexHostQueryServiceOptions {
|
|
42
|
+
/** Opens governed access for this turn; called at most once, on the first query. */
|
|
43
|
+
readonly openAccess: (signal: AbortSignal) => Promise<ComponentAccess>;
|
|
44
|
+
readonly signal?: AbortSignal;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* The host side of codex:local's `run_sql`: a per-turn unix socket in a fresh 0700 directory
|
|
48
|
+
* that answers only requests carrying this turn's random token. Every query runs through
|
|
49
|
+
* governed access (`wren governed-stdio`), gets a turn-unique host `query_id`, and is recorded
|
|
50
|
+
* as an observation of this turn only.
|
|
51
|
+
*/
|
|
52
|
+
export declare function openCodexHostQueryService(options: CodexHostQueryServiceOptions): Promise<CodexHostQueryService>;
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { randomBytes, timingSafeEqual } from "node:crypto";
|
|
2
|
+
import { chmod, mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
3
|
+
import { createServer } from "node:net";
|
|
4
|
+
import os from "node:os";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { COMPONENT_LIMITS } from "../components/runner.js";
|
|
7
|
+
import { GovernedWrenError } from "../components/wren-access.js";
|
|
8
|
+
/**
|
|
9
|
+
* Per-turn bounds. Count, request and per-result bytes reuse the component runner's limits;
|
|
10
|
+
* the total caps what the host keeps in memory for one turn until its answer is grounded.
|
|
11
|
+
* Rows default to 1000 like `run_sql`. Like `run_sql`, the host probes one row past the limit to
|
|
12
|
+
* report truncation; the cap is 9999 so the probe stays within governed-stdio's 10000-row bound.
|
|
13
|
+
*/
|
|
14
|
+
export const CODEX_HOST_QUERY_LIMITS = Object.freeze({
|
|
15
|
+
queries: COMPONENT_LIMITS.calls,
|
|
16
|
+
requestBytes: COMPONENT_LIMITS.requestBytes,
|
|
17
|
+
resultBytes: COMPONENT_LIMITS.resultBytes,
|
|
18
|
+
totalResultBytes: 8 * COMPONENT_LIMITS.resultBytes,
|
|
19
|
+
defaultRows: 1000,
|
|
20
|
+
maxRows: 9_999,
|
|
21
|
+
timeoutMs: COMPONENT_LIMITS.timeoutMs,
|
|
22
|
+
});
|
|
23
|
+
function requestFrame(value) {
|
|
24
|
+
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
25
|
+
return undefined;
|
|
26
|
+
const record = value;
|
|
27
|
+
if (typeof record.id !== "number" || !Number.isSafeInteger(record.id))
|
|
28
|
+
return undefined;
|
|
29
|
+
return { token: record.token, id: record.id, sql: record.sql, limit: record.limit };
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* The host side of codex:local's `run_sql`: a per-turn unix socket in a fresh 0700 directory
|
|
33
|
+
* that answers only requests carrying this turn's random token. Every query runs through
|
|
34
|
+
* governed access (`wren governed-stdio`), gets a turn-unique host `query_id`, and is recorded
|
|
35
|
+
* as an observation of this turn only.
|
|
36
|
+
*/
|
|
37
|
+
export async function openCodexHostQueryService(options) {
|
|
38
|
+
if (process.platform === "win32")
|
|
39
|
+
throw new Error("codex:local host query service requires unix sockets");
|
|
40
|
+
const directory = await mkdtemp(path.join(os.tmpdir(), "genbi-hq-"));
|
|
41
|
+
const token = Buffer.from(randomBytes(32).toString("base64url"), "utf8");
|
|
42
|
+
const turnTag = randomBytes(4).toString("hex");
|
|
43
|
+
const socketPath = path.join(directory, "s");
|
|
44
|
+
const credentialFile = path.join(directory, "credential.json");
|
|
45
|
+
const controller = new AbortController();
|
|
46
|
+
const signal = options.signal ? AbortSignal.any([options.signal, controller.signal]) : controller.signal;
|
|
47
|
+
const recorded = [];
|
|
48
|
+
const sockets = new Set();
|
|
49
|
+
let attempts = 0;
|
|
50
|
+
let recordedBytes = 0;
|
|
51
|
+
let closed = false;
|
|
52
|
+
let access;
|
|
53
|
+
const authorized = (candidate) => {
|
|
54
|
+
if (typeof candidate !== "string")
|
|
55
|
+
return false;
|
|
56
|
+
const presented = Buffer.from(candidate, "utf8");
|
|
57
|
+
return presented.length === token.length && timingSafeEqual(presented, token);
|
|
58
|
+
};
|
|
59
|
+
const execute = async (sql, limit) => {
|
|
60
|
+
if (closed || signal.aborted)
|
|
61
|
+
return { error: "transport" };
|
|
62
|
+
if (typeof sql !== "string" || !sql.trim() || Buffer.byteLength(sql) > CODEX_HOST_QUERY_LIMITS.requestBytes)
|
|
63
|
+
return { error: "invalid_request" };
|
|
64
|
+
if (limit !== undefined && limit !== null && (typeof limit !== "number" || !Number.isInteger(limit) || limit < 1))
|
|
65
|
+
return { error: "invalid_request" };
|
|
66
|
+
if (attempts >= CODEX_HOST_QUERY_LIMITS.queries)
|
|
67
|
+
return { error: "budget_exhausted" };
|
|
68
|
+
attempts += 1;
|
|
69
|
+
const rows = Math.min(CODEX_HOST_QUERY_LIMITS.maxRows, typeof limit === "number" ? limit : CODEX_HOST_QUERY_LIMITS.defaultRows);
|
|
70
|
+
let result;
|
|
71
|
+
try {
|
|
72
|
+
access ??= options.openAccess(signal);
|
|
73
|
+
const governed = await access;
|
|
74
|
+
result = await governed.query({ sql, limit: rows + 1 }, AbortSignal.any([signal, AbortSignal.timeout(CODEX_HOST_QUERY_LIMITS.timeoutMs)]));
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
return { error: error instanceof GovernedWrenError ? error.errorClass : "transport" };
|
|
78
|
+
}
|
|
79
|
+
if (closed)
|
|
80
|
+
return { error: "transport" };
|
|
81
|
+
if (typeof result !== "object" || result === null || Array.isArray(result))
|
|
82
|
+
return { error: "internal" };
|
|
83
|
+
const probed = result.rows;
|
|
84
|
+
if (!Array.isArray(probed))
|
|
85
|
+
return { error: "internal" };
|
|
86
|
+
// The N+1 probe, as plain `run_sql` reports it: more rows than asked means the shown rows
|
|
87
|
+
// are not the whole result. Only `rows` rows are returned and recorded.
|
|
88
|
+
const truncated = probed.length > rows;
|
|
89
|
+
const shown = truncated ? probed.slice(0, rows) : probed;
|
|
90
|
+
// The id is assigned here, once, so the observation and the model's copy carry the same id. The
|
|
91
|
+
// tag makes ids unique per turn: an id copied from an earlier turn never names this turn's query.
|
|
92
|
+
const output = { ...result, rows: shown, row_count: shown.length, truncated, query_id: `q${attempts}-${turnTag}` };
|
|
93
|
+
const bytes = Buffer.byteLength(JSON.stringify(output));
|
|
94
|
+
if (bytes > CODEX_HOST_QUERY_LIMITS.resultBytes)
|
|
95
|
+
return { error: "result_too_large" };
|
|
96
|
+
if (recordedBytes + bytes > CODEX_HOST_QUERY_LIMITS.totalResultBytes)
|
|
97
|
+
return { error: "budget_exhausted" };
|
|
98
|
+
recordedBytes += bytes;
|
|
99
|
+
recorded.push(Object.freeze({ tool: "run_sql", input: Object.freeze({ sql, limit: rows }), output: structuredClone(output) }));
|
|
100
|
+
return { result: output };
|
|
101
|
+
};
|
|
102
|
+
const serve = (socket) => {
|
|
103
|
+
if (closed) {
|
|
104
|
+
socket.destroy();
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
sockets.add(socket);
|
|
108
|
+
socket.on("close", () => sockets.delete(socket));
|
|
109
|
+
socket.on("error", () => socket.destroy());
|
|
110
|
+
let buffer = "";
|
|
111
|
+
let tail = Promise.resolve();
|
|
112
|
+
socket.setEncoding("utf8");
|
|
113
|
+
socket.on("data", (chunk) => {
|
|
114
|
+
buffer += chunk;
|
|
115
|
+
if (buffer.length > 2 * CODEX_HOST_QUERY_LIMITS.requestBytes) {
|
|
116
|
+
socket.destroy();
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
let newline = buffer.indexOf("\n");
|
|
120
|
+
while (newline >= 0) {
|
|
121
|
+
const line = buffer.slice(0, newline);
|
|
122
|
+
buffer = buffer.slice(newline + 1);
|
|
123
|
+
newline = buffer.indexOf("\n");
|
|
124
|
+
let frame;
|
|
125
|
+
try {
|
|
126
|
+
frame = requestFrame(JSON.parse(line));
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
frame = undefined;
|
|
130
|
+
}
|
|
131
|
+
if (!frame) {
|
|
132
|
+
socket.destroy();
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
const { id, sql, limit } = frame;
|
|
136
|
+
if (!authorized(frame.token)) {
|
|
137
|
+
socket.end(`${JSON.stringify({ id, error: { class: "unauthorized" } })}\n`);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
tail = tail.then(async () => {
|
|
141
|
+
const outcome = await execute(sql, limit);
|
|
142
|
+
if (socket.destroyed)
|
|
143
|
+
return;
|
|
144
|
+
socket.write(`${JSON.stringify("result" in outcome ? { id, result: outcome.result } : { id, error: { class: outcome.error } })}\n`);
|
|
145
|
+
}).catch(() => { socket.destroy(); });
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
};
|
|
149
|
+
const server = createServer(serve);
|
|
150
|
+
try {
|
|
151
|
+
await new Promise((resolve, reject) => {
|
|
152
|
+
server.once("error", reject);
|
|
153
|
+
server.listen(socketPath, () => { server.off("error", reject); resolve(); });
|
|
154
|
+
});
|
|
155
|
+
await chmod(socketPath, 0o600);
|
|
156
|
+
await writeFile(credentialFile, JSON.stringify({ socket: socketPath, token: token.toString("utf8") }), { encoding: "utf8", mode: 0o600, flag: "wx" });
|
|
157
|
+
}
|
|
158
|
+
catch (error) {
|
|
159
|
+
server.close();
|
|
160
|
+
await rm(directory, { recursive: true, force: true });
|
|
161
|
+
throw error;
|
|
162
|
+
}
|
|
163
|
+
let closing;
|
|
164
|
+
const close = () => {
|
|
165
|
+
closing ??= (async () => {
|
|
166
|
+
closed = true;
|
|
167
|
+
recorded.length = 0;
|
|
168
|
+
controller.abort();
|
|
169
|
+
for (const socket of sockets)
|
|
170
|
+
socket.destroy();
|
|
171
|
+
await new Promise((resolve) => server.close(() => resolve()));
|
|
172
|
+
try {
|
|
173
|
+
if (access)
|
|
174
|
+
await (await access).close();
|
|
175
|
+
}
|
|
176
|
+
catch {
|
|
177
|
+
// Access that never opened, or already closed, has nothing left to release.
|
|
178
|
+
}
|
|
179
|
+
finally {
|
|
180
|
+
await rm(directory, { recursive: true, force: true });
|
|
181
|
+
}
|
|
182
|
+
})();
|
|
183
|
+
return closing;
|
|
184
|
+
};
|
|
185
|
+
options.signal?.addEventListener("abort", () => { void close(); }, { once: true });
|
|
186
|
+
return {
|
|
187
|
+
credentialFile,
|
|
188
|
+
observations: () => Object.freeze([...recorded]),
|
|
189
|
+
close,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
@@ -9,10 +9,18 @@ export declare const CODEX_ASK_COMPONENTS: readonly ["answer_query", "generate_d
|
|
|
9
9
|
export type CodexAskComponent = (typeof CODEX_ASK_COMPONENTS)[number];
|
|
10
10
|
export declare const CODEX_ENRICH_COMPONENTS: readonly ["inspect_context", "draft_enrichment"];
|
|
11
11
|
export type CodexManifestPurpose = "analysis" | "setup" | "context_enrichment";
|
|
12
|
+
export declare function codexAnalysisToolArgs(component: CodexAskComponent): string[];
|
|
12
13
|
export declare function buildCodexAskManifestArgs(cli: ResolvedCli, irPath: string, models: CodexManifestModels, component?: CodexAskComponent): readonly string[];
|
|
13
14
|
export declare function buildCodexBootstrapManifestArgs(cli: ResolvedCli, irPath: string): readonly string[];
|
|
14
15
|
export declare function buildCodexEnrichmentManifestArgs(cli: ResolvedCli, irPath: string, models: CodexManifestModels, component: (typeof CODEX_ENRICH_COMPONENTS)[number]): readonly string[];
|
|
15
|
-
export declare function describeCodexAskManifest(cli: ResolvedCli, irPath: string, models: CodexManifestModels): Promise<Bundle>;
|
|
16
|
+
export declare function describeCodexAskManifest(cli: ResolvedCli, irPath: string, models: CodexManifestModels, context?: CodexManifestContext): Promise<Bundle>;
|
|
17
|
+
export interface CodexManifestContext {
|
|
18
|
+
readonly warbleBin: string;
|
|
19
|
+
readonly contexts: Readonly<Record<string, {
|
|
20
|
+
readonly binding: unknown;
|
|
21
|
+
readonly snapshot: unknown;
|
|
22
|
+
}>>;
|
|
23
|
+
}
|
|
16
24
|
/**
|
|
17
25
|
* Setup is a raw/bootstrap profile, not an Ask profile. Its dispatcher has a
|
|
18
26
|
* dedicated whole-profile `manifest` command that neither needs nor accepts a
|
|
@@ -1,8 +1,21 @@
|
|
|
1
1
|
import { execFile } from "node:child_process";
|
|
2
|
-
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { planDigest } from "../components/plan.js";
|
|
3
6
|
import { loadBundle } from "../bundle/loader.js";
|
|
4
7
|
export const CODEX_ASK_COMPONENTS = ["answer_query", "generate_dashboard"];
|
|
5
8
|
export const CODEX_ENRICH_COMPONENTS = ["inspect_context", "draft_enrichment"];
|
|
9
|
+
export function codexAnalysisToolArgs(component) {
|
|
10
|
+
const bindings = component === "answer_query"
|
|
11
|
+
? ["generate_sql=run_sql", "repair_sql=run_sql"]
|
|
12
|
+
: ["plan_dashboard=get_context", "compose_layout=run_sql"];
|
|
13
|
+
const required = component === "answer_query"
|
|
14
|
+
? ["generate_sql", "repair_sql"]
|
|
15
|
+
: ["plan_dashboard", "compose_layout"];
|
|
16
|
+
return ["--transport", "orchestrate", ...bindings.flatMap((binding) => ["--step-tool", binding]),
|
|
17
|
+
...required.flatMap((step) => ["--require-tool", step])];
|
|
18
|
+
}
|
|
6
19
|
export function buildCodexAskManifestArgs(cli, irPath, models, component = "answer_query") {
|
|
7
20
|
return [
|
|
8
21
|
...cli.prefixArgs,
|
|
@@ -20,10 +33,7 @@ export function buildCodexAskManifestArgs(cli, irPath, models, component = "answ
|
|
|
20
33
|
// spawn it; the runtime binds the real Wren MCP command separately.
|
|
21
34
|
"--server-command",
|
|
22
35
|
process.execPath,
|
|
23
|
-
|
|
24
|
-
"get_context",
|
|
25
|
-
"--query-tool",
|
|
26
|
-
"run_sql",
|
|
36
|
+
...codexAnalysisToolArgs(component),
|
|
27
37
|
];
|
|
28
38
|
}
|
|
29
39
|
export function buildCodexBootstrapManifestArgs(cli, irPath) {
|
|
@@ -33,10 +43,11 @@ export function buildCodexBootstrapManifestArgs(cli, irPath) {
|
|
|
33
43
|
irPath,
|
|
34
44
|
"--server-command",
|
|
35
45
|
process.execPath,
|
|
36
|
-
"--
|
|
37
|
-
"setup_execution",
|
|
38
|
-
"--
|
|
39
|
-
"
|
|
46
|
+
"--transport", "exec",
|
|
47
|
+
"--step-tool", "connect=setup_execution",
|
|
48
|
+
"--step-tool", "build=setup_execution",
|
|
49
|
+
"--require-tool", "connect",
|
|
50
|
+
"--require-tool", "build",
|
|
40
51
|
];
|
|
41
52
|
}
|
|
42
53
|
export function buildCodexEnrichmentManifestArgs(cli, irPath, models, component) {
|
|
@@ -50,10 +61,9 @@ export function buildCodexEnrichmentManifestArgs(cli, irPath, models, component)
|
|
|
50
61
|
component === "inspect_context" ? models.cheap : models.strong,
|
|
51
62
|
"--server-command",
|
|
52
63
|
process.execPath,
|
|
53
|
-
"--
|
|
54
|
-
"
|
|
55
|
-
"--
|
|
56
|
-
"get_context",
|
|
64
|
+
"--transport", "turn",
|
|
65
|
+
"--step-tool", `${component === "inspect_context" ? "inspect" : "draft"}=get_context`,
|
|
66
|
+
"--require-tool", component === "inspect_context" ? "inspect" : "draft",
|
|
57
67
|
];
|
|
58
68
|
}
|
|
59
69
|
async function describeCodexComponentManifests(cli, irPath, components, argsFor) {
|
|
@@ -73,9 +83,80 @@ async function describeCodexComponentManifests(cli, irPath, components, argsFor)
|
|
|
73
83
|
}
|
|
74
84
|
return loadBundle({ ...primary, agents: bundles.flatMap((bundle) => bundle.agents) });
|
|
75
85
|
}
|
|
76
|
-
export async function describeCodexAskManifest(cli, irPath, models) {
|
|
86
|
+
export async function describeCodexAskManifest(cli, irPath, models, context) {
|
|
87
|
+
const ir = JSON.parse(await readFile(irPath, "utf8"));
|
|
88
|
+
if (records(ir.components).some((node) => records(node.llm_calls).some((step) => records(step.component_calls).length))) {
|
|
89
|
+
if (!context)
|
|
90
|
+
throw new Error("Codex composed manifest requires component-owned prepared context");
|
|
91
|
+
return describeComposedCodexManifest(cli, irPath, ir, models, context);
|
|
92
|
+
}
|
|
77
93
|
return describeCodexComponentManifests(cli, irPath, CODEX_ASK_COMPONENTS, (component) => buildCodexAskManifestArgs(cli, irPath, models, component));
|
|
78
94
|
}
|
|
95
|
+
async function describeComposedCodexManifest(cli, irPath, ir, models, context) {
|
|
96
|
+
const dir = await mkdtemp(path.join(os.tmpdir(), "genbi-codex-manifest-"));
|
|
97
|
+
try {
|
|
98
|
+
const bindings = Object.fromEntries(CODEX_ASK_COMPONENTS.map((id) => {
|
|
99
|
+
const node = records(ir.components).find((component) => component.id === id);
|
|
100
|
+
const prepared = context.contexts[id];
|
|
101
|
+
if (!node || !prepared || planDigest(prepared.binding) !== planDigest(node.context_binding)) {
|
|
102
|
+
throw new Error(`Codex manifest context binding mismatch for ${id}`);
|
|
103
|
+
}
|
|
104
|
+
// Display preparation never launches this MCP command. Tools remain owned
|
|
105
|
+
// by the answer component; the dashboard only receives its call alias.
|
|
106
|
+
return [id, { transport: "orchestrate", models, context: JSON.stringify(prepared.snapshot), mcp: {
|
|
107
|
+
name: "wren", command: process.execPath,
|
|
108
|
+
toolsByStep: id === "answer_query" ? { resolve_intent: [], generate_sql: ["run_sql"], repair_sql: ["run_sql"] }
|
|
109
|
+
: { plan_dashboard: [], compose_layout: [] },
|
|
110
|
+
requireTool: id === "answer_query" ? ["generate_sql", "repair_sql"] : [],
|
|
111
|
+
} }];
|
|
112
|
+
}));
|
|
113
|
+
const file = path.join(dir, "bindings.json");
|
|
114
|
+
await writeFile(file, JSON.stringify({ components: bindings }), { mode: 0o600 });
|
|
115
|
+
const bundles = [];
|
|
116
|
+
for (const id of CODEX_ASK_COMPONENTS) {
|
|
117
|
+
const manifest = JSON.parse(await execCodexManifest(cli.command, [...cli.prefixArgs, "manifest", irPath,
|
|
118
|
+
"--component", id, "--transport", "orchestrate", "--component-bindings", file, "--warble-bin", context.warbleBin]));
|
|
119
|
+
if (manifest.version !== "0.3" || manifest.target !== "codex:local" || manifest.profile !== ir.profile || manifest.entry !== id || manifest.transport !== "orchestrate") {
|
|
120
|
+
throw new Error("Unexpected composed Codex manifest identity");
|
|
121
|
+
}
|
|
122
|
+
const nodes = records(manifest.components);
|
|
123
|
+
const expectedIds = id === "answer_query" ? [id] : [...CODEX_ASK_COMPONENTS];
|
|
124
|
+
if (nodes.length !== expectedIds.length || new Set(nodes.map((node) => node.id)).size !== nodes.length || nodes.some((node) => !expectedIds.includes(String(node.id)))) {
|
|
125
|
+
throw new Error("Unexpected composed Codex manifest closure");
|
|
126
|
+
}
|
|
127
|
+
for (const node of nodes) {
|
|
128
|
+
const source = records(ir.components).find((candidate) => candidate.id === node.id);
|
|
129
|
+
const emitted = records(node.steps);
|
|
130
|
+
const declared = records(source.llm_calls);
|
|
131
|
+
if (emitted.length !== declared.length)
|
|
132
|
+
throw new Error("Unexpected composed Codex manifest steps");
|
|
133
|
+
for (let index = 0; index < declared.length; index++) {
|
|
134
|
+
const step = declared[index];
|
|
135
|
+
const actual = emitted[index];
|
|
136
|
+
const aliases = Object.fromEntries(records(step.component_calls).map((edge) => [String(edge.alias), edge.component]));
|
|
137
|
+
const tools = bindings[String(node.id)].mcp.toolsByStep[String(step.name)] ?? [];
|
|
138
|
+
if (actual.name !== step.name || planDigest(actual.componentCalls) !== planDigest(aliases) || planDigest(actual.enabledTools) !== planDigest(tools)) {
|
|
139
|
+
throw new Error("Unexpected composed Codex manifest tool authority");
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
const entries = records(manifest.components).filter((node) => node.role === "entry");
|
|
144
|
+
if (entries.length !== 1 || entries[0].id !== id)
|
|
145
|
+
throw new Error("Unexpected composed Codex manifest entry");
|
|
146
|
+
const entry = entries[0];
|
|
147
|
+
const steps = records(entry.steps);
|
|
148
|
+
bundles.push(normalizeCodexManifest({ manifest_version: "0.1", target: manifest.target, profile: manifest.profile,
|
|
149
|
+
compat: { min_ir_version: ir.warble_ir_version, max_ir_version: ir.warble_ir_version },
|
|
150
|
+
agents: [{ ...entry, steps: steps.map((step) => ({ name: step.name })),
|
|
151
|
+
tools: [...new Set(steps.flatMap((step) => Array.isArray(step.enabledTools) ? step.enabledTools.map(String) : []))].map((name) => ({ name, source: "mcp:wren" })) }],
|
|
152
|
+
}, ir));
|
|
153
|
+
}
|
|
154
|
+
return loadBundle({ ...bundles[0], agents: bundles.flatMap((bundle) => bundle.agents) });
|
|
155
|
+
}
|
|
156
|
+
finally {
|
|
157
|
+
await rm(dir, { recursive: true, force: true });
|
|
158
|
+
}
|
|
159
|
+
}
|
|
79
160
|
/**
|
|
80
161
|
* Setup is a raw/bootstrap profile, not an Ask profile. Its dispatcher has a
|
|
81
162
|
* dedicated whole-profile `manifest` command that neither needs nor accepts a
|
|
@@ -130,7 +211,7 @@ function normalizeCodexManifest(manifest, ir) {
|
|
|
130
211
|
return {
|
|
131
212
|
name,
|
|
132
213
|
tier: String(step["tier"] ?? source["tier"] ?? "strong"),
|
|
133
|
-
consumes: Array.isArray(step["consumes"]) ? step["consumes"].map(String) : [],
|
|
214
|
+
consumes: Array.isArray(step["consumes"]) ? step["consumes"].map(String) : Array.isArray(source["consumes"]) ? source["consumes"].map(String) : [],
|
|
134
215
|
produces: String(step["produces"] ?? source["produces"] ?? "result"),
|
|
135
216
|
prompt: String(source["prompt"] ?? ""),
|
|
136
217
|
realization: when
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { type Socket } from "node:net";
|
|
3
|
+
export interface Credential {
|
|
4
|
+
readonly socket: string;
|
|
5
|
+
readonly token: string;
|
|
6
|
+
}
|
|
7
|
+
export interface HostOutcome {
|
|
8
|
+
readonly result?: unknown;
|
|
9
|
+
readonly error?: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Accepts only an absolute, regular, user-owned file no one else can read or write. The file is
|
|
13
|
+
* opened without following a symlink and checked and read through that one descriptor, so what
|
|
14
|
+
* is checked is what is read.
|
|
15
|
+
*/
|
|
16
|
+
export declare function readCredential(file: string | undefined): Credential | undefined;
|
|
17
|
+
/**
|
|
18
|
+
* One connection at a time to the turn's host query service, reconnecting after a drop. Only
|
|
19
|
+
* the current socket's failure fails the pending requests (all of which were sent on it): a
|
|
20
|
+
* stale socket closing late never fails a request pending on its replacement. Each socket has
|
|
21
|
+
* its own read buffer.
|
|
22
|
+
*/
|
|
23
|
+
export declare class HostConnection {
|
|
24
|
+
private readonly credential;
|
|
25
|
+
private readonly open;
|
|
26
|
+
private readonly pending;
|
|
27
|
+
private socket;
|
|
28
|
+
private nextId;
|
|
29
|
+
constructor(credential: Credential, open?: (socketPath: string) => Socket);
|
|
30
|
+
request(sql: string, limit: number | undefined): Promise<HostOutcome>;
|
|
31
|
+
close(): void;
|
|
32
|
+
private current;
|
|
33
|
+
}
|