@wrenai/genbi 0.0.3 → 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 +65 -3
- 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 +13 -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 +197 -26
- 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 +870 -251
- 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
|
@@ -1,16 +1,25 @@
|
|
|
1
1
|
import { execFile } from "node:child_process";
|
|
2
|
-
import { mkdtemp, rm } from "node:fs/promises";
|
|
2
|
+
import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
|
3
3
|
import os from "node:os";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
import { createFileSystemCompileCache } from "./cache.js";
|
|
7
7
|
import { composeUserProfile } from "./compose-profile.js";
|
|
8
|
+
import { getContextLoaderIdentity, resolveContextLoader } from "./context-loader.js";
|
|
8
9
|
import { WarbleCommandFailedError } from "./errors.js";
|
|
9
10
|
import { hashDirectory, hashFiles } from "./fingerprint.js";
|
|
10
11
|
import { resolveHubDir, resolveWarbleBinary } from "./resolve-binary.js";
|
|
11
12
|
import { getWarbleIdentity } from "./warble-identity.js";
|
|
13
|
+
import { planDigest, VERCEL_HOST_CONTRACT } from "../components/plan.js";
|
|
12
14
|
/** Fixed `providerFragmentHash` for `"native"` mode, which never reads `--provider` fragments at all. */
|
|
13
15
|
const NATIVE_MODE_PROVIDER_HASH = "native:no-providers";
|
|
16
|
+
/**
|
|
17
|
+
* Fixed `contextLoaderIdentity` for `compileRawProfile`, which compiles a raw-source binding and so
|
|
18
|
+
* never runs the `wren-context-loader` generator. A sentinel rather than an empty string, for the
|
|
19
|
+
* same reason `NATIVE_MODE_PROVIDER_HASH` is one: "the generator was not involved" is a distinct
|
|
20
|
+
* fact, not an alias for some generator that happened to hash to nothing.
|
|
21
|
+
*/
|
|
22
|
+
const RAW_PROFILE_CONTEXT_LOADER_IDENTITY = "raw-profile:no-context-loader";
|
|
14
23
|
/**
|
|
15
24
|
* This package's bundled wren capability-provider fragment (`providers/wren.provider.yaml`) — the
|
|
16
25
|
* default `--provider` for in-process dispatch, matching how `fixtures/genbi-default.native.bundle.json`
|
|
@@ -29,6 +38,13 @@ const WARBLE_COMMAND_TIMEOUT_MS = 2 * 60 * 1000;
|
|
|
29
38
|
* mode — additionally `warble dispatch --target vercel` for the vercel bundle this package's
|
|
30
39
|
* `runAgent()` consumes.
|
|
31
40
|
*
|
|
41
|
+
* The rebind runs WrenAI's own `wren-context-loader` generator over `userProject` and binds its
|
|
42
|
+
* output as a `kind: prepared` document, so Warble reads a projection WrenAI produced rather than
|
|
43
|
+
* introspecting the semantic layer itself. Resolving that generator loud-fails
|
|
44
|
+
* ({@link ContextLoaderNotFoundError}) — there is no fallback to Warble's built-in `wren_project`
|
|
45
|
+
* adapter, because the compiled IR is byte-identical either way and a silent degrade would be
|
|
46
|
+
* undetectable from the artifact.
|
|
47
|
+
*
|
|
32
48
|
* A cache hit (see below) never *executes* `warble`, but by default DOES need to resolve + read
|
|
33
49
|
* the binary — its content hash and the Hub root derived from its location are both cache-key
|
|
34
50
|
* inputs (see `warbleIdentity` and `hubDir` below), so a hit can't be trusted without knowing which
|
|
@@ -43,13 +59,14 @@ const WARBLE_COMMAND_TIMEOUT_MS = 2 * 60 * 1000;
|
|
|
43
59
|
* the same no-shell, structured-result `execFile` convention `harness/exec/local.ts` already uses.
|
|
44
60
|
*
|
|
45
61
|
* Results are cached on (profile content hash x user-context fingerprint x mode x resolved
|
|
46
|
-
* provider-fragment content hash x warble binary identity x
|
|
47
|
-
* so an unchanged profile/context/providers/compiler/
|
|
48
|
-
* instead of recompiling. Folding in the
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
62
|
+
* provider-fragment content hash x warble binary identity x `wren-context-loader` binary identity x
|
|
63
|
+
* resolved Hub root) — see `./cache.js` — so an unchanged profile/context/providers/compiler/
|
|
64
|
+
* generator/Hub tuple returns the previous artifact instead of recompiling. Folding in the
|
|
65
|
+
* provider-fragment hash, warble identity, generator identity and Hub root (rather than just
|
|
66
|
+
* profile+context+mode) matters: otherwise a custom `options.providers` and this package's default
|
|
67
|
+
* fragment collide on the same key, and editing a provider fragment, rebuilding `warble`, rebuilding
|
|
68
|
+
* the generator, or compiling against a different component library would keep serving a stale
|
|
69
|
+
* artifact forever, since there's no TTL/eviction.
|
|
53
70
|
*
|
|
54
71
|
* The `warble` invocations run inside a temporary scratch directory (the composed profile + raw
|
|
55
72
|
* compiler output). When the cache relocates the artifacts to a durable home outside that scratch
|
|
@@ -60,10 +77,21 @@ const WARBLE_COMMAND_TIMEOUT_MS = 2 * 60 * 1000;
|
|
|
60
77
|
* a caller-supplied `workDir` is never deleted.
|
|
61
78
|
*/
|
|
62
79
|
export async function compileProfile(options) {
|
|
63
|
-
|
|
80
|
+
// Resolving the generator is memoized here the same way `ensureWarbleBin` memoizes warble below:
|
|
81
|
+
// both the identity computation (a cache-key input) and the compose step (on a miss) need it, and
|
|
82
|
+
// resolution loud-fails rather than degrading to Warble's built-in adapter.
|
|
83
|
+
let resolvedLoader;
|
|
84
|
+
const ensureContextLoader = () => {
|
|
85
|
+
resolvedLoader ??= resolveContextLoader(options.contextLoaderBin);
|
|
86
|
+
return resolvedLoader;
|
|
87
|
+
};
|
|
88
|
+
return compileProfileSource(options,
|
|
89
|
+
// The compiled binding embeds the absolute project path, even for identical bytes.
|
|
90
|
+
planDigest({ project: path.resolve(options.userProject), content: await hashDirectory(path.resolve(options.userProject)) }), async () => options.contextLoaderIdentity ?? getContextLoaderIdentity(ensureContextLoader()), (workDir) => composeUserProfile({
|
|
64
91
|
profileSource: options.profileSource,
|
|
65
92
|
userProject: options.userProject,
|
|
66
93
|
destDir: workDir,
|
|
94
|
+
contextLoaderBin: ensureContextLoader().bin,
|
|
67
95
|
}));
|
|
68
96
|
}
|
|
69
97
|
/**
|
|
@@ -73,13 +101,18 @@ export async function compileProfile(options) {
|
|
|
73
101
|
* the entry whenever the source profile changes.
|
|
74
102
|
*/
|
|
75
103
|
export async function compileRawProfile(options) {
|
|
76
|
-
return compileProfileSource(options, "raw-profile-context", async () => path.resolve(options.profileSource));
|
|
104
|
+
return compileProfileSource(options, "raw-profile-context", async () => RAW_PROFILE_CONTEXT_LOADER_IDENTITY, async () => path.resolve(options.profileSource));
|
|
77
105
|
}
|
|
78
|
-
async function compileProfileSource(options, contextFingerprint,
|
|
106
|
+
async function compileProfileSource(options, contextFingerprint,
|
|
107
|
+
/**
|
|
108
|
+
* Deferred so `compileRawProfile` never resolves a generator it has no use for, and so the
|
|
109
|
+
* `contextLoaderIdentity` escape hatch can still skip touching the binary on a hit.
|
|
110
|
+
*/
|
|
111
|
+
contextLoaderIdentityOf, prepareProfile) {
|
|
79
112
|
const cache = options.cache ?? createFileSystemCompileCache();
|
|
80
113
|
const profileHash = await hashDirectory(path.resolve(options.profileSource));
|
|
81
114
|
const providerPaths = options.providers ?? [DEFAULT_WREN_PROVIDER_PATH];
|
|
82
|
-
const providerFragmentHash = options.mode === "agnostic" ? await hashFiles(providerPaths) : NATIVE_MODE_PROVIDER_HASH;
|
|
115
|
+
const providerFragmentHash = options.mode === "agnostic" ? `${await hashFiles(providerPaths)}:${planDigest(VERCEL_HOST_CONTRACT)}` : NATIVE_MODE_PROVIDER_HASH;
|
|
83
116
|
// Resolving `warble` is memoized here so it happens at most once per call no matter how many of
|
|
84
117
|
// the two spots below need it (identity computation, and — on a miss — actually running it).
|
|
85
118
|
let resolvedWarbleBin;
|
|
@@ -101,7 +134,16 @@ async function compileProfileSource(options, contextFingerprint, prepareProfile)
|
|
|
101
134
|
// without resolving the binary. `undefined` here means no `--hub-dir` is passed at all and
|
|
102
135
|
// warble's compiled-in default applies — today's behaviour, preserved.
|
|
103
136
|
const hubDir = options.hubDir ?? resolveHubDir(await ensureWarbleBin());
|
|
104
|
-
const
|
|
137
|
+
const contextLoaderIdentity = await contextLoaderIdentityOf();
|
|
138
|
+
const cacheKey = {
|
|
139
|
+
profileHash,
|
|
140
|
+
contextFingerprint,
|
|
141
|
+
mode: options.mode,
|
|
142
|
+
providerFragmentHash,
|
|
143
|
+
warbleIdentity,
|
|
144
|
+
contextLoaderIdentity,
|
|
145
|
+
hubDir,
|
|
146
|
+
};
|
|
105
147
|
const cached = await cache.get(cacheKey);
|
|
106
148
|
if (cached !== undefined) {
|
|
107
149
|
return { ...cached, cacheHit: true, ...(resolvedWarbleBin !== undefined ? { warbleBin: resolvedWarbleBin } : {}) };
|
|
@@ -126,7 +168,13 @@ async function compileProfileSource(options, contextFingerprint, prepareProfile)
|
|
|
126
168
|
if (options.mode === "agnostic") {
|
|
127
169
|
const bundleOutDir = path.join(workDir, "bundle");
|
|
128
170
|
const providerArgs = providerPaths.flatMap((provider) => ["--provider", provider]);
|
|
129
|
-
|
|
171
|
+
const ir = JSON.parse(await readFile(irPath, "utf8"));
|
|
172
|
+
const composed = ir.components.some((component) => component.llm_calls.some((step) => (step.component_calls?.length ?? 0) > 0));
|
|
173
|
+
const hostPath = path.join(workDir, "component-host.json");
|
|
174
|
+
if (composed)
|
|
175
|
+
await writeFile(hostPath, JSON.stringify(VERCEL_HOST_CONTRACT), { mode: 0o600 });
|
|
176
|
+
await runWarble(warbleBin, ["dispatch", "--target", "vercel", ...providerArgs,
|
|
177
|
+
...(composed ? ["--host-contract", hostPath] : []), irPath, "--out", bundleOutDir]);
|
|
130
178
|
bundlePath = path.join(bundleOutDir, "bundle.json");
|
|
131
179
|
}
|
|
132
180
|
const freshEntry = { irPath, ...(bundlePath !== undefined ? { bundlePath } : {}) };
|
|
@@ -46,8 +46,8 @@ export declare function resolveWarbleBinary(explicit?: string): Promise<string>;
|
|
|
46
46
|
* 1. The binary's own checkout — a `<root>/target/release/warble` puts the Hub at
|
|
47
47
|
* `<root>/hub/components`. Skipped for a bare `"warble"` resolved off `PATH`, which names no
|
|
48
48
|
* directory to walk up from.
|
|
49
|
-
* 2. A sibling `warble` checkout's `hub/components`,
|
|
50
|
-
*
|
|
49
|
+
* 2. A sibling `warble` checkout's `hub/components`, only when the explicit sibling-checkout
|
|
50
|
+
* development mode is enabled. An installed release must use its own default Hub.
|
|
51
51
|
*
|
|
52
52
|
* Returns `undefined` when neither resolves; callers then pass no `--hub-dir` at all, leaving
|
|
53
53
|
* today's compiled-in default in charge rather than pointing warble at a directory we guessed.
|
|
@@ -31,6 +31,16 @@ import { isWarbleSiblingCheckoutDevModeEnabled, resolveInstalledPackageBin } fro
|
|
|
31
31
|
*/
|
|
32
32
|
export async function resolveWarbleBinary(explicit) {
|
|
33
33
|
if (explicit !== undefined) {
|
|
34
|
+
// A value with no path separator is a command name, not a file path, and is
|
|
35
|
+
// resolved on PATH — the same reading the native-session preflight gives it.
|
|
36
|
+
// Requiring it to exist relative to the working directory made `"warble"` an
|
|
37
|
+
// explicit value that could never resolve, while the preflight found it and
|
|
38
|
+
// reported the producer healthy: one string, two answers.
|
|
39
|
+
if (!explicit.includes(path.sep) && !path.isAbsolute(explicit)) {
|
|
40
|
+
if (await isExecutableOnPath(explicit))
|
|
41
|
+
return explicit;
|
|
42
|
+
throw new WarbleBinaryNotFoundError([`explicit warbleBin "${explicit}" is not on PATH`]);
|
|
43
|
+
}
|
|
34
44
|
if (!existsSync(explicit)) {
|
|
35
45
|
throw new WarbleBinaryNotFoundError([`explicit warbleBin "${explicit}" does not exist`]);
|
|
36
46
|
}
|
|
@@ -88,8 +98,8 @@ function isExecutableOnPath(bin) {
|
|
|
88
98
|
* 1. The binary's own checkout — a `<root>/target/release/warble` puts the Hub at
|
|
89
99
|
* `<root>/hub/components`. Skipped for a bare `"warble"` resolved off `PATH`, which names no
|
|
90
100
|
* directory to walk up from.
|
|
91
|
-
* 2. A sibling `warble` checkout's `hub/components`,
|
|
92
|
-
*
|
|
101
|
+
* 2. A sibling `warble` checkout's `hub/components`, only when the explicit sibling-checkout
|
|
102
|
+
* development mode is enabled. An installed release must use its own default Hub.
|
|
93
103
|
*
|
|
94
104
|
* Returns `undefined` when neither resolves; callers then pass no `--hub-dir` at all, leaving
|
|
95
105
|
* today's compiled-in default in charge rather than pointing warble at a directory we guessed.
|
|
@@ -103,7 +113,7 @@ export function resolveHubDir(warbleBin) {
|
|
|
103
113
|
if (existsSync(candidate))
|
|
104
114
|
return candidate;
|
|
105
115
|
}
|
|
106
|
-
return findSiblingWarbleEntry("hub", "components");
|
|
116
|
+
return isWarbleSiblingCheckoutDevModeEnabled() ? findSiblingWarbleEntry("hub", "components") : undefined;
|
|
107
117
|
}
|
|
108
118
|
/**
|
|
109
119
|
* Walks up from this package's own directory (`harness/compile/` in source, `dist/compile/` once
|
|
@@ -26,6 +26,20 @@ export interface CompileCacheKey {
|
|
|
26
26
|
* compiled, instead of silently serving an artifact built by a now-stale `warble`.
|
|
27
27
|
*/
|
|
28
28
|
readonly warbleIdentity: string;
|
|
29
|
+
/**
|
|
30
|
+
* Content identity of the resolved `wren-context-loader` binary — the generator that renders the
|
|
31
|
+
* user's semantic layer into the prepared-context document a `kind: prepared` binding reads (see
|
|
32
|
+
* `getBinaryIdentity`). Fixed sentinel for `compileRawProfile`, which binds a raw source and never
|
|
33
|
+
* runs the generator at all.
|
|
34
|
+
*
|
|
35
|
+
* This is here for the *generator's* identity, not the project's: `contextFingerprint` already
|
|
36
|
+
* content-hashes the user project's semantic-layer files, so folding in the generated document
|
|
37
|
+
* would say nothing new about the project. What it would miss is a changed generator — a new
|
|
38
|
+
* version that projects the same layer differently (or extracts a facet the old one dropped)
|
|
39
|
+
* produces a different document, and therefore a different compiled artifact, from byte-identical
|
|
40
|
+
* project inputs. Same reason `warbleIdentity` is here, one stage earlier in the pipeline.
|
|
41
|
+
*/
|
|
42
|
+
readonly contextLoaderIdentity: string;
|
|
29
43
|
/**
|
|
30
44
|
* The Hub component-library root passed to `warble compile --hub-dir` (see `resolveHubDir`), or
|
|
31
45
|
* `undefined` when none could be derived and the compile therefore ran against warble's own
|
|
@@ -75,6 +89,21 @@ export interface CompileProfileOptions {
|
|
|
75
89
|
* hit skip touching the binary at all.
|
|
76
90
|
*/
|
|
77
91
|
readonly warbleIdentity?: string;
|
|
92
|
+
/**
|
|
93
|
+
* Explicit path to the `wren-context-loader` binary. See `resolveContextLoaderBinary` for the
|
|
94
|
+
* resolution order when omitted (explicit → `WREN_HARNESS_CONTEXT_LOADER_BIN` → in-repo build →
|
|
95
|
+
* verified exact `@wrenai/context-loader` package → loud failure; there is no fallback to
|
|
96
|
+
* Warble's built-in `wren_project` adapter).
|
|
97
|
+
*/
|
|
98
|
+
readonly contextLoaderBin?: string;
|
|
99
|
+
/**
|
|
100
|
+
* Precomputed identity for the resolved `wren-context-loader` binary (see
|
|
101
|
+
* `CompileCacheKey.contextLoaderIdentity`). Same purpose as `warbleIdentity`: without it,
|
|
102
|
+
* computing the cache key has to resolve and read the generator even for what would otherwise be
|
|
103
|
+
* a cache hit. Pass this, `warbleIdentity` and `hubDir` together to make a hit touch no binary at
|
|
104
|
+
* all. Ignored by `compileRawProfile`, which never runs the generator.
|
|
105
|
+
*/
|
|
106
|
+
readonly contextLoaderIdentity?: string;
|
|
78
107
|
/**
|
|
79
108
|
* Explicit Hub component-library root for `warble compile --hub-dir`. When omitted it is derived
|
|
80
109
|
* from the resolved `warble` binary (see `resolveHubDir`) so the compiler and its Hub always come
|
|
@@ -10,3 +10,14 @@
|
|
|
10
10
|
* override), a cache hit still never *executes* warble, only resolves + reads its file.
|
|
11
11
|
*/
|
|
12
12
|
export declare function getWarbleIdentity(binPath: string): Promise<string>;
|
|
13
|
+
/**
|
|
14
|
+
* The generic form of {@link getWarbleIdentity}: a memoized content hash of any binary whose
|
|
15
|
+
* identity belongs in the compile cache key. Also used for the `wren-context-loader` generator (see
|
|
16
|
+
* `CompileCacheKey.contextLoaderIdentity`), which is a compiler of the same kind one stage earlier
|
|
17
|
+
* — it turns the user's semantic layer into the prepared-context document Warble then compiles.
|
|
18
|
+
*
|
|
19
|
+
* Hashes content rather than shelling out for a version string: it needs no subprocess, so folding
|
|
20
|
+
* a binary's identity into the key never costs an execution, and it catches a rebuild that keeps
|
|
21
|
+
* the same version number.
|
|
22
|
+
*/
|
|
23
|
+
export declare function getBinaryIdentity(binPath: string): Promise<string>;
|
|
@@ -19,6 +19,19 @@ const identityCache = new Map();
|
|
|
19
19
|
* override), a cache hit still never *executes* warble, only resolves + reads its file.
|
|
20
20
|
*/
|
|
21
21
|
export async function getWarbleIdentity(binPath) {
|
|
22
|
+
return getBinaryIdentity(binPath);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* The generic form of {@link getWarbleIdentity}: a memoized content hash of any binary whose
|
|
26
|
+
* identity belongs in the compile cache key. Also used for the `wren-context-loader` generator (see
|
|
27
|
+
* `CompileCacheKey.contextLoaderIdentity`), which is a compiler of the same kind one stage earlier
|
|
28
|
+
* — it turns the user's semantic layer into the prepared-context document Warble then compiles.
|
|
29
|
+
*
|
|
30
|
+
* Hashes content rather than shelling out for a version string: it needs no subprocess, so folding
|
|
31
|
+
* a binary's identity into the key never costs an execution, and it catches a rebuild that keeps
|
|
32
|
+
* the same version number.
|
|
33
|
+
*/
|
|
34
|
+
export async function getBinaryIdentity(binPath) {
|
|
22
35
|
const resolved = path.resolve(binPath);
|
|
23
36
|
let cached = identityCache.get(resolved);
|
|
24
37
|
if (cached === undefined) {
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { type LanguageModel } from "ai";
|
|
2
|
+
import type { StepResponse, StepRun } from "./runner.js";
|
|
3
|
+
/** A fresh AI SDK loop for each host-owned step. No caller history or ambient tools. */
|
|
4
|
+
export declare function runAiComponentStep(run: StepRun, model: LanguageModel): Promise<StepResponse>;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { ToolLoopAgent, isStepCount, jsonSchema, tool } from "ai";
|
|
2
|
+
import { readSlots } from "./egress.js";
|
|
3
|
+
const SINGLE_ANSWER_STEPS = 12;
|
|
4
|
+
/**
|
|
5
|
+
* Model turns one step may take. A per-slot step gets two turns per slot (a query and one
|
|
6
|
+
* retry, for a model that issues one tool call per turn) plus four for lookups and the answer.
|
|
7
|
+
*/
|
|
8
|
+
function stepCap(run) {
|
|
9
|
+
const read = run.perSlot ? readSlots(run) : undefined;
|
|
10
|
+
const slots = read && !("error" in read) ? read.slots.length : 0;
|
|
11
|
+
return Math.max(SINGLE_ANSWER_STEPS, 2 * slots + 4);
|
|
12
|
+
}
|
|
13
|
+
/** A fresh AI SDK loop for each host-owned step. No caller history or ambient tools. */
|
|
14
|
+
export async function runAiComponentStep(run, model) {
|
|
15
|
+
const tools = {};
|
|
16
|
+
for (const [name, execute] of Object.entries(run.tools)) {
|
|
17
|
+
const inputSchema = run.toolSchemas[name];
|
|
18
|
+
if (!inputSchema)
|
|
19
|
+
throw new Error("Component tool has no closed input schema");
|
|
20
|
+
tools[name] = tool({ description: run.toolDescriptions[name] ?? name,
|
|
21
|
+
inputSchema: jsonSchema(inputSchema),
|
|
22
|
+
execute: async (input) => execute(input), });
|
|
23
|
+
}
|
|
24
|
+
const prompt = JSON.stringify({ request: run.request, input: run.input, consumes: run.consumes });
|
|
25
|
+
const agent = new ToolLoopAgent({ model, tools, stopWhen: isStepCount(stepCap(run)),
|
|
26
|
+
instructions: [run.brief, run.prompt].filter(Boolean).join("\n\n"),
|
|
27
|
+
});
|
|
28
|
+
const result = await agent.generate({ prompt, abortSignal: run.signal });
|
|
29
|
+
const toolErrors = result.steps.some((step) => step.content.some((part) => part.type === "tool-error"));
|
|
30
|
+
// A single-answer step fails on any tool error, so the declared repair step runs and an
|
|
31
|
+
// unrepaired failure ends the child. In a per-slot invocation a tool error is one slot's
|
|
32
|
+
// outcome (the model reports it as `{slot_id, status: "unanswerable"}` and answers the rest);
|
|
33
|
+
// the step itself fails only when the loop did not complete. Normalization still grounds
|
|
34
|
+
// every answered slot in an observed query, so the leniency cannot promote a claim to data.
|
|
35
|
+
return {
|
|
36
|
+
value: result.text,
|
|
37
|
+
failed: result.finishReason !== "stop" || (!run.perSlot && toolErrors),
|
|
38
|
+
usage: { inputTokens: result.totalUsage.inputTokens ?? 0, outputTokens: result.totalUsage.outputTokens ?? 0 },
|
|
39
|
+
};
|
|
40
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { ComponentEvidence, ComponentPlan, ExecutionPlan, RunnerHost, StepResponse, StepRun } from "./runner.js";
|
|
2
|
+
import type { ComponentInvocationResult } from "@warble/claude-agent-sdk";
|
|
3
|
+
/** Captured by the host from its project, session and approved runtime records. */
|
|
4
|
+
export interface ComponentIdentity {
|
|
5
|
+
readonly session: string;
|
|
6
|
+
readonly vendor: string;
|
|
7
|
+
readonly account: string;
|
|
8
|
+
readonly generation: string;
|
|
9
|
+
readonly project: string;
|
|
10
|
+
readonly bindingRevision: string;
|
|
11
|
+
readonly contextDigest: string;
|
|
12
|
+
readonly planDigest: string;
|
|
13
|
+
}
|
|
14
|
+
export interface ComponentAccess {
|
|
15
|
+
/** Must execute against the captured project and enforce read-only access. */
|
|
16
|
+
query(input: {
|
|
17
|
+
sql: string;
|
|
18
|
+
limit: number;
|
|
19
|
+
}, signal: AbortSignal): Promise<unknown>;
|
|
20
|
+
inspect(signal: AbortSignal): Promise<unknown>;
|
|
21
|
+
close(): Promise<void>;
|
|
22
|
+
}
|
|
23
|
+
/** Which host context a step receives: the full prepared snapshot (private zone) or the capability card (public zone). */
|
|
24
|
+
export interface ContextSurface {
|
|
25
|
+
readonly kind: "snapshot" | "card";
|
|
26
|
+
readonly text: string;
|
|
27
|
+
}
|
|
28
|
+
/** The step run as the host's `step` sees it: the assembled prompt plus its named parts for fingerprinting. */
|
|
29
|
+
export type BrokeredStepRun = StepRun & {
|
|
30
|
+
/** Named prompt parts exactly as assembled into `prompt`, for the per-surface fingerprint (per surface and in total). Absent when a host bypasses the broker. */
|
|
31
|
+
readonly surfaces?: Readonly<Record<string, string>>;
|
|
32
|
+
readonly contextKind?: ContextSurface["kind"] | "none";
|
|
33
|
+
};
|
|
34
|
+
export interface ComponentBrokerOptions {
|
|
35
|
+
readonly plan: ExecutionPlan;
|
|
36
|
+
/** Fresh snapshots captured from the same project binding used by prepare(). */
|
|
37
|
+
readonly contexts: Readonly<Record<string, {
|
|
38
|
+
binding: Readonly<Record<string, unknown>>;
|
|
39
|
+
snapshot: unknown;
|
|
40
|
+
}>>;
|
|
41
|
+
readonly verifierBinary: string;
|
|
42
|
+
readonly identity: ComponentIdentity;
|
|
43
|
+
readonly currentIdentity: () => ComponentIdentity | undefined;
|
|
44
|
+
/** Opens the exact model/tool authority represented by the verified host context. */
|
|
45
|
+
readonly prepare: (component: ComponentPlan, identity: ComponentIdentity, signal: AbortSignal) => Promise<ComponentAccess>;
|
|
46
|
+
readonly step: (run: BrokeredStepRun, component: ComponentPlan, identity: ComponentIdentity) => Promise<StepResponse>;
|
|
47
|
+
/**
|
|
48
|
+
* Chooses the host context surface for one step. Default: the component's prepared snapshot.
|
|
49
|
+
* A zone-aware host returns the capability card for public-zone steps so the snapshot never
|
|
50
|
+
* reaches a public model.
|
|
51
|
+
*/
|
|
52
|
+
readonly contextSurface?: (component: ComponentPlan, run: StepRun) => ContextSurface | undefined;
|
|
53
|
+
readonly normalize: (component: ComponentPlan, evidence: ComponentEvidence, signal: AbortSignal, context: unknown) => Promise<ComponentInvocationResult>;
|
|
54
|
+
readonly persistRoot?: RunnerHost["persistRoot"];
|
|
55
|
+
/** The egress verification seam; see `RunnerHost.verifyChild`. */
|
|
56
|
+
readonly verifyChild?: RunnerHost["verifyChild"];
|
|
57
|
+
readonly onEvent?: RunnerHost["onEvent"];
|
|
58
|
+
}
|
|
59
|
+
/** Typed host access only: models cannot supply paths, connection config, identity or commands. */
|
|
60
|
+
export declare function createComponentBroker(options: ComponentBrokerOptions): RunnerHost;
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { isDeepStrictEqual } from "node:util";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { planDigest } from "./plan.js";
|
|
4
|
+
import { verifyComponentContext } from "./context.js";
|
|
5
|
+
const queryInput = z.object({ sql: z.string().trim().min(1).max(65_536), limit: z.number().int().positive().optional() }).strict();
|
|
6
|
+
const emptyInput = z.object({}).strict();
|
|
7
|
+
const QUERY_SCHEMA = { type: "object", additionalProperties: false, required: ["sql"], properties: {
|
|
8
|
+
sql: { type: "string", minLength: 1, maxLength: 65_536 }, limit: { type: "integer", minimum: 1 },
|
|
9
|
+
} };
|
|
10
|
+
const INSPECT_SCHEMA = { type: "object", additionalProperties: false, properties: {} };
|
|
11
|
+
/** Typed host access only: models cannot supply paths, connection config, identity or commands. */
|
|
12
|
+
export function createComponentBroker(options) {
|
|
13
|
+
const identity = Object.freeze({ ...options.identity });
|
|
14
|
+
if (Object.values(identity).some((value) => !value.trim()))
|
|
15
|
+
throw new Error("Missing component execution identity");
|
|
16
|
+
const plan = structuredClone(options.plan);
|
|
17
|
+
const contexts = structuredClone(options.contexts);
|
|
18
|
+
if (identity.planDigest !== plan.identity || identity.contextDigest !== planDigest(contexts))
|
|
19
|
+
throw new Error("Component plan/context identity mismatch");
|
|
20
|
+
const owners = new WeakMap();
|
|
21
|
+
const current = () => isDeepStrictEqual(identity, options.currentIdentity());
|
|
22
|
+
const check = (signal) => { signal.throwIfAborted(); if (!current())
|
|
23
|
+
throw new Error("Component binding expired"); };
|
|
24
|
+
return {
|
|
25
|
+
async prepare(component, signal) {
|
|
26
|
+
check(signal);
|
|
27
|
+
if (!isDeepStrictEqual(plan.components[component.id], component))
|
|
28
|
+
throw new Error("Component plan mismatch");
|
|
29
|
+
const context = contexts[component.id];
|
|
30
|
+
if (context && !isDeepStrictEqual(context.binding, component.declaration.context_binding))
|
|
31
|
+
throw new Error("Component context binding mismatch");
|
|
32
|
+
const conditions = component.declaration.context_precondition;
|
|
33
|
+
if (conditions !== undefined && !Array.isArray(conditions))
|
|
34
|
+
throw new Error("Invalid component preconditions");
|
|
35
|
+
if (Array.isArray(conditions) && conditions.length > 0) {
|
|
36
|
+
if (!context)
|
|
37
|
+
throw new Error("Missing component context");
|
|
38
|
+
await verifyComponentContext(options.verifierBinary, context.snapshot, conditions, signal);
|
|
39
|
+
}
|
|
40
|
+
check(signal);
|
|
41
|
+
const guards = z.array(z.object({ name: z.string(), locked: z.boolean(), threshold: z.number().positive().optional() }).passthrough()).parse(component.declaration.guardrails);
|
|
42
|
+
if (!guards.some((guard) => guard.name === "read_only_execution" && guard.locked))
|
|
43
|
+
throw new Error("Component read-only guard is required");
|
|
44
|
+
const rowLimit = Math.min(10_000, guards.find((guard) => guard.name === "row_limit")?.threshold ?? 1000);
|
|
45
|
+
const timeoutMs = Math.min(120_000, (guards.find((guard) => guard.name === "statement_timeout")?.threshold ?? 30) * 1000);
|
|
46
|
+
const grants = [...new Map(component.steps.flatMap((step) => step.tools).map((grant) => [`${grant.source}:${grant.name}`, grant])).values()];
|
|
47
|
+
const operations = grants.map((grant) => {
|
|
48
|
+
if ((grant.source === "native" && grant.name === "query") || grant.source === "host:sql_execution:read_only")
|
|
49
|
+
return "query";
|
|
50
|
+
if ((grant.source === "native" && grant.name === "semantic_introspect") || grant.source === "host:semantic_introspection")
|
|
51
|
+
return "inspect";
|
|
52
|
+
throw new Error("Unsupported component tool binding");
|
|
53
|
+
});
|
|
54
|
+
const access = await options.prepare(component, identity, signal);
|
|
55
|
+
let closed = false;
|
|
56
|
+
let closing;
|
|
57
|
+
const close = () => { closed = true; return closing ??= access.close(); };
|
|
58
|
+
try {
|
|
59
|
+
check(signal);
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
await close();
|
|
63
|
+
throw error;
|
|
64
|
+
}
|
|
65
|
+
const binding = {
|
|
66
|
+
isCurrent: current, close,
|
|
67
|
+
tools: grants.map((grant, index) => ({ ...grant,
|
|
68
|
+
inputSchema: operations[index] === "query" ? QUERY_SCHEMA : INSPECT_SCHEMA,
|
|
69
|
+
async execute(input, parent) {
|
|
70
|
+
check(parent);
|
|
71
|
+
if (closed)
|
|
72
|
+
throw new Error("Component access is closed");
|
|
73
|
+
const bounded = AbortSignal.any([parent, AbortSignal.timeout(timeoutMs)]);
|
|
74
|
+
let result;
|
|
75
|
+
if (operations[index] === "query") {
|
|
76
|
+
const parsed = queryInput.parse(input);
|
|
77
|
+
result = await access.query({ sql: parsed.sql, limit: Math.min(rowLimit, parsed.limit ?? rowLimit) }, bounded);
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
emptyInput.parse(input);
|
|
81
|
+
result = await access.inspect(bounded);
|
|
82
|
+
}
|
|
83
|
+
check(bounded);
|
|
84
|
+
if (closed)
|
|
85
|
+
throw new Error("Component access is closed");
|
|
86
|
+
return result;
|
|
87
|
+
},
|
|
88
|
+
})),
|
|
89
|
+
async normalize(evidence, parent) { check(parent); if (closed)
|
|
90
|
+
throw new Error("Component access is closed"); return options.normalize(component, evidence, parent, structuredClone(context?.snapshot)); },
|
|
91
|
+
};
|
|
92
|
+
owners.set(binding, component);
|
|
93
|
+
return binding;
|
|
94
|
+
},
|
|
95
|
+
async runStep(run, binding) {
|
|
96
|
+
check(run.signal);
|
|
97
|
+
const component = owners.get(binding);
|
|
98
|
+
if (!component || !binding.isCurrent())
|
|
99
|
+
throw new Error("Invalid component binding");
|
|
100
|
+
const context = contexts[component.id];
|
|
101
|
+
const render = z.object({ render_blocks: z.array(z.object({ type: z.string(), fields: z.record(z.string(), z.string()) }).strict()).optional() }).passthrough().parse(component.declaration.effect ?? {}).render_blocks;
|
|
102
|
+
const surface = options.contextSurface ? options.contextSurface(component, run)
|
|
103
|
+
: context ? { kind: "snapshot", text: `Host semantic context:\n${JSON.stringify(context.snapshot)}` } : undefined;
|
|
104
|
+
const renderText = run.terminal && render?.length ? `Render output: Return only one JSON object with a blocks array. Each block has a type and its declared fields. Use only these block contracts (a trailing ? means optional): ${JSON.stringify(render)}. Copy data and definitions only from successful host tool or child results. A KPI label must identify its returned column; do not invent units or deltas. If evidence is insufficient, return {"status":"refused","message":"Insufficient verified data"}.` : "";
|
|
105
|
+
const surfaces = { prompt: run.prompt };
|
|
106
|
+
if (surface)
|
|
107
|
+
surfaces["context"] = surface.text;
|
|
108
|
+
if (renderText)
|
|
109
|
+
surfaces["render"] = renderText;
|
|
110
|
+
const prompt = [run.prompt, surface?.text ?? "", renderText].filter(Boolean).join("\n\n");
|
|
111
|
+
return options.step({ ...run, prompt, surfaces: Object.freeze(surfaces), contextKind: surface?.kind ?? "none" }, component, identity);
|
|
112
|
+
},
|
|
113
|
+
...(options.persistRoot ? { persistRoot: options.persistRoot } : {}),
|
|
114
|
+
...(options.verifyChild ? { verifyChild: options.verifyChild } : {}),
|
|
115
|
+
...(options.onEvent ? { onEvent: options.onEvent } : {}),
|
|
116
|
+
};
|
|
117
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { DisclosurePolicy } from "../providers/zone.js";
|
|
3
|
+
/** The catalog fields the card may read. Everything else the document carries is dropped here. */
|
|
4
|
+
export declare const capabilityCatalogSchema: z.ZodObject<{
|
|
5
|
+
catalog_version: z.ZodLiteral<1>;
|
|
6
|
+
project: z.ZodDefault<z.ZodObject<{
|
|
7
|
+
name: z.ZodOptional<z.ZodString>;
|
|
8
|
+
data_source: z.ZodOptional<z.ZodString>;
|
|
9
|
+
}, z.core.$strip>>;
|
|
10
|
+
models: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
11
|
+
description: z.ZodOptional<z.ZodString>;
|
|
12
|
+
name: z.ZodString;
|
|
13
|
+
primary_key: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
14
|
+
columns: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
15
|
+
description: z.ZodOptional<z.ZodString>;
|
|
16
|
+
name: z.ZodString;
|
|
17
|
+
type: z.ZodString;
|
|
18
|
+
not_null: z.ZodOptional<z.ZodBoolean>;
|
|
19
|
+
is_calculated: z.ZodOptional<z.ZodBoolean>;
|
|
20
|
+
is_hidden: z.ZodOptional<z.ZodBoolean>;
|
|
21
|
+
relationship: z.ZodOptional<z.ZodString>;
|
|
22
|
+
enum_values: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
23
|
+
value: z.ZodString;
|
|
24
|
+
meaning: z.ZodString;
|
|
25
|
+
}, z.core.$strip>>>;
|
|
26
|
+
}, z.core.$strip>>>;
|
|
27
|
+
}, z.core.$strip>>>;
|
|
28
|
+
relationships: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
29
|
+
name: z.ZodString;
|
|
30
|
+
models: z.ZodArray<z.ZodString>;
|
|
31
|
+
join_type: z.ZodString;
|
|
32
|
+
condition: z.ZodString;
|
|
33
|
+
}, z.core.$strip>>>;
|
|
34
|
+
cubes: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
35
|
+
description: z.ZodOptional<z.ZodString>;
|
|
36
|
+
name: z.ZodString;
|
|
37
|
+
base_object: z.ZodString;
|
|
38
|
+
measures: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
39
|
+
description: z.ZodOptional<z.ZodString>;
|
|
40
|
+
name: z.ZodString;
|
|
41
|
+
type: z.ZodString;
|
|
42
|
+
granularities: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
43
|
+
date_range: z.ZodOptional<z.ZodTuple<[z.ZodString, z.ZodString], null>>;
|
|
44
|
+
}, z.core.$strip>>>;
|
|
45
|
+
dimensions: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
46
|
+
description: z.ZodOptional<z.ZodString>;
|
|
47
|
+
name: z.ZodString;
|
|
48
|
+
type: z.ZodString;
|
|
49
|
+
granularities: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
50
|
+
date_range: z.ZodOptional<z.ZodTuple<[z.ZodString, z.ZodString], null>>;
|
|
51
|
+
}, z.core.$strip>>>;
|
|
52
|
+
time_dimensions: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
53
|
+
description: z.ZodOptional<z.ZodString>;
|
|
54
|
+
name: z.ZodString;
|
|
55
|
+
type: z.ZodString;
|
|
56
|
+
granularities: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
57
|
+
date_range: z.ZodOptional<z.ZodTuple<[z.ZodString, z.ZodString], null>>;
|
|
58
|
+
}, z.core.$strip>>>;
|
|
59
|
+
}, z.core.$strip>>>;
|
|
60
|
+
views: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
61
|
+
description: z.ZodOptional<z.ZodString>;
|
|
62
|
+
name: z.ZodString;
|
|
63
|
+
}, z.core.$strip>>>;
|
|
64
|
+
}, z.core.$strip>;
|
|
65
|
+
export type CapabilityCatalog = z.infer<typeof capabilityCatalogSchema>;
|
|
66
|
+
export interface CapabilityCard {
|
|
67
|
+
readonly text: string;
|
|
68
|
+
readonly bytes: number;
|
|
69
|
+
readonly truncated: boolean;
|
|
70
|
+
/** sha256 hex over `text`; the value a public step's `context` surface fingerprint carries when it received the card. */
|
|
71
|
+
readonly digest: string;
|
|
72
|
+
/** Lines dropped by the size bound, for the trace warning. */
|
|
73
|
+
readonly omittedLines: number;
|
|
74
|
+
}
|
|
75
|
+
/** The disclosure-policy fields the card may carry. Nothing else of the policy is read. */
|
|
76
|
+
export type CardDisclosureLimits = Pick<DisclosurePolicy, "min_group_size" | "max_rows" | "allowed_shapes">;
|
|
77
|
+
export interface CapabilityCardOptions {
|
|
78
|
+
/** Upper bound on the rendered card in UTF-8 bytes. Default 32 KiB. */
|
|
79
|
+
readonly maxBytes?: number;
|
|
80
|
+
/**
|
|
81
|
+
* The bound disclosure policy, when there is one. The card states its shape limits so the
|
|
82
|
+
* planner plans within them instead of planning slots egress then refuses. Only the three
|
|
83
|
+
* fields of `CardDisclosureLimits` are read; patterns and the judge budget never reach the card.
|
|
84
|
+
*/
|
|
85
|
+
readonly disclosure?: CardDisclosureLimits;
|
|
86
|
+
}
|
|
87
|
+
export declare const DEFAULT_CARD_MAX_BYTES: number;
|
|
88
|
+
/** Renders the card's lines from an already allow-listed catalog. Pure and order-preserving. */
|
|
89
|
+
export declare function renderCapabilityCardLines(catalog: CapabilityCatalog, disclosure?: CardDisclosureLimits): string[];
|
|
90
|
+
/**
|
|
91
|
+
* Builds the card from a raw catalog document. Deterministic: the same
|
|
92
|
+
* catalog yields the same bytes. Size-bounded: when the rendering exceeds
|
|
93
|
+
* `maxBytes`, trailing lines are dropped until it fits and a marker line
|
|
94
|
+
* says how many were omitted; the caller traces that as a warning. The
|
|
95
|
+
* disclosure limits are never dropped: with a policy bound, the whole head
|
|
96
|
+
* is kept even when that alone exceeds `maxBytes`.
|
|
97
|
+
*/
|
|
98
|
+
export declare function buildCapabilityCard(catalog: unknown, options?: CapabilityCardOptions): CapabilityCard;
|