@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.
Files changed (230) hide show
  1. package/README.md +64 -2
  2. package/dist/assets/{echarts-LTFC-ysd.js → echarts-DYM4hjvk.js} +2 -2
  3. package/dist/assets/index-A_Wrq7Ey.css +1 -0
  4. package/dist/assets/{index-Bo--QMUN.js → index-DsH129tV.js} +18 -17
  5. package/dist/index.html +2 -2
  6. package/dist-server/harness/bundle/schema.d.ts +38 -0
  7. package/dist-server/harness/bundle/schema.js +18 -10
  8. package/dist-server/harness/bundle/version.js +1 -1
  9. package/dist-server/harness/cli-args.d.ts +10 -2
  10. package/dist-server/harness/cli-args.js +24 -8
  11. package/dist-server/harness/cli.js +75 -9
  12. package/dist-server/harness/compile/cache.js +4 -4
  13. package/dist-server/harness/compile/compose-profile.d.ts +51 -14
  14. package/dist-server/harness/compile/compose-profile.js +85 -19
  15. package/dist-server/harness/compile/context-loader.d.ts +56 -0
  16. package/dist-server/harness/compile/context-loader.js +237 -0
  17. package/dist-server/harness/compile/errors.d.ts +19 -0
  18. package/dist-server/harness/compile/errors.js +35 -0
  19. package/dist-server/harness/compile/index.d.ts +4 -3
  20. package/dist-server/harness/compile/index.js +4 -3
  21. package/dist-server/harness/compile/pipeline.d.ts +15 -7
  22. package/dist-server/harness/compile/pipeline.js +62 -14
  23. package/dist-server/harness/compile/resolve-binary.d.ts +2 -2
  24. package/dist-server/harness/compile/resolve-binary.js +3 -3
  25. package/dist-server/harness/compile/types.d.ts +29 -0
  26. package/dist-server/harness/compile/warble-identity.d.ts +11 -0
  27. package/dist-server/harness/compile/warble-identity.js +13 -0
  28. package/dist-server/harness/components/ai-step.d.ts +4 -0
  29. package/dist-server/harness/components/ai-step.js +40 -0
  30. package/dist-server/harness/components/broker.d.ts +60 -0
  31. package/dist-server/harness/components/broker.js +117 -0
  32. package/dist-server/harness/components/capability-card.d.ts +98 -0
  33. package/dist-server/harness/components/capability-card.js +169 -0
  34. package/dist-server/harness/components/context.d.ts +2 -0
  35. package/dist-server/harness/components/context.js +30 -0
  36. package/dist-server/harness/components/display.d.ts +6 -0
  37. package/dist-server/harness/components/display.js +29 -0
  38. package/dist-server/harness/components/egress-judge.d.ts +12 -0
  39. package/dist-server/harness/components/egress-judge.js +27 -0
  40. package/dist-server/harness/components/egress.d.ts +142 -0
  41. package/dist-server/harness/components/egress.js +469 -0
  42. package/dist-server/harness/components/in-process.d.ts +7 -0
  43. package/dist-server/harness/components/in-process.js +245 -0
  44. package/dist-server/harness/components/normalize.d.ts +35 -0
  45. package/dist-server/harness/components/normalize.js +245 -0
  46. package/dist-server/harness/components/plan.d.ts +31 -0
  47. package/dist-server/harness/components/plan.js +184 -0
  48. package/dist-server/harness/components/report.d.ts +207 -0
  49. package/dist-server/harness/components/report.js +309 -0
  50. package/dist-server/harness/components/runner.d.ts +193 -0
  51. package/dist-server/harness/components/runner.js +492 -0
  52. package/dist-server/harness/components/semantic-guards.d.ts +20 -0
  53. package/dist-server/harness/components/semantic-guards.js +42 -0
  54. package/dist-server/harness/components/terminal-json.d.ts +18 -0
  55. package/dist-server/harness/components/terminal-json.js +82 -0
  56. package/dist-server/harness/components/usage.d.ts +14 -0
  57. package/dist-server/harness/components/usage.js +19 -0
  58. package/dist-server/harness/components/wren-access.d.ts +29 -0
  59. package/dist-server/harness/components/wren-access.js +233 -0
  60. package/dist-server/harness/components/zone-dry-run.d.ts +43 -0
  61. package/dist-server/harness/components/zone-dry-run.js +107 -0
  62. package/dist-server/harness/components/zone-gate.d.ts +76 -0
  63. package/dist-server/harness/components/zone-gate.js +144 -0
  64. package/dist-server/harness/events/index.d.ts +1 -1
  65. package/dist-server/harness/events/types.d.ts +52 -0
  66. package/dist-server/harness/index.d.ts +9 -2
  67. package/dist-server/harness/index.js +5 -1
  68. package/dist-server/harness/loop/executor.d.ts +1 -0
  69. package/dist-server/harness/loop/executor.js +37 -15
  70. package/dist-server/harness/npm-package-resolve.d.ts +2 -0
  71. package/dist-server/harness/npm-package-resolve.js +16 -4
  72. package/dist-server/harness/providers/adapters/openai-compatible.d.ts +13 -0
  73. package/dist-server/harness/providers/adapters/openai-compatible.js +17 -0
  74. package/dist-server/harness/providers/adapters/openai.d.ts +7 -0
  75. package/dist-server/harness/providers/adapters/openai.js +6 -0
  76. package/dist-server/harness/providers/adapters/pi-ai.d.ts +48 -0
  77. package/dist-server/harness/providers/adapters/pi-ai.js +518 -0
  78. package/dist-server/harness/providers/binding.d.ts +45 -4
  79. package/dist-server/harness/providers/binding.js +44 -8
  80. package/dist-server/harness/providers/index.d.ts +9 -3
  81. package/dist-server/harness/providers/index.js +10 -3
  82. package/dist-server/harness/providers/zone.d.ts +96 -0
  83. package/dist-server/harness/providers/zone.js +112 -0
  84. package/dist-server/harness/render/answering-query.d.ts +29 -0
  85. package/dist-server/harness/render/answering-query.js +62 -0
  86. package/dist-server/harness/render/envelope.d.ts +6 -0
  87. package/dist-server/harness/render/envelope.js +10 -3
  88. package/dist-server/harness/route/adapter-spec.d.ts +8 -9
  89. package/dist-server/harness/route/adapter-spec.js +13 -14
  90. package/dist-server/harness/route/codex-ask-events.js +2 -1
  91. package/dist-server/harness/route/codex-ask.d.ts +4 -0
  92. package/dist-server/harness/route/codex-ask.js +69 -30
  93. package/dist-server/harness/route/codex-host-query.d.ts +52 -0
  94. package/dist-server/harness/route/codex-host-query.js +191 -0
  95. package/dist-server/harness/route/codex-local-manifest.d.ts +9 -1
  96. package/dist-server/harness/route/codex-local-manifest.js +96 -15
  97. package/dist-server/harness/route/codex-wren-proxy.d.ts +33 -0
  98. package/dist-server/harness/route/codex-wren-proxy.js +227 -0
  99. package/dist-server/harness/route/describe.js +45 -6
  100. package/dist-server/harness/route/in-process.d.ts +22 -1
  101. package/dist-server/harness/route/in-process.js +54 -4
  102. package/dist-server/harness/route/route.js +5 -2
  103. package/dist-server/harness/route/types.d.ts +30 -1
  104. package/dist-server/harness/session/run.js +30 -10
  105. package/dist-server/harness/setup/runner.d.ts +2 -0
  106. package/dist-server/harness/setup/runner.js +38 -17
  107. package/dist-server/harness/setup/yaml-scalar.d.ts +23 -0
  108. package/dist-server/harness/setup/yaml-scalar.js +62 -0
  109. package/dist-server/server/adopt.js +1 -17
  110. package/dist-server/server/app.d.ts +1 -0
  111. package/dist-server/server/app.js +163 -14
  112. package/dist-server/server/auth-choice.d.ts +13 -1
  113. package/dist-server/server/auth-choice.js +66 -1
  114. package/dist-server/server/bin.js +177 -25
  115. package/dist-server/server/codex-local-grounding.d.ts +18 -0
  116. package/dist-server/server/codex-local-grounding.js +92 -0
  117. package/dist-server/server/codex-native-session.d.ts +25 -0
  118. package/dist-server/server/codex-native-session.js +1 -0
  119. package/dist-server/server/codex-runtime-composition.d.ts +34 -0
  120. package/dist-server/server/codex-runtime-composition.js +250 -0
  121. package/dist-server/server/codex-structured-ask.d.ts +23 -0
  122. package/dist-server/server/codex-structured-ask.js +102 -0
  123. package/dist-server/server/conn-config.js +4 -16
  124. package/dist-server/server/conversation-socket.d.ts +16 -0
  125. package/dist-server/server/conversation-socket.js +65 -0
  126. package/dist-server/server/db.d.ts +46 -0
  127. package/dist-server/server/db.js +88 -9
  128. package/dist-server/server/env-detect.d.ts +6 -0
  129. package/dist-server/server/env-detect.js +7 -0
  130. package/dist-server/server/fold.d.ts +22 -4
  131. package/dist-server/server/fold.js +37 -5
  132. package/dist-server/server/harness.d.ts +2 -2
  133. package/dist-server/server/harness.js +33 -7
  134. package/dist-server/server/interactive-terminal.d.ts +10 -6
  135. package/dist-server/server/interactive-terminal.js +33 -8
  136. package/dist-server/server/managed-wren-runtime.d.ts +116 -0
  137. package/dist-server/server/managed-wren-runtime.js +670 -0
  138. package/dist-server/server/native-artifacts.d.ts +23 -7
  139. package/dist-server/server/native-artifacts.js +43 -12
  140. package/dist-server/server/native-component-context.d.ts +19 -0
  141. package/dist-server/server/native-component-context.js +76 -0
  142. package/dist-server/server/native-component-preparation.d.ts +20 -0
  143. package/dist-server/server/native-component-preparation.js +129 -0
  144. package/dist-server/server/native-component-runtime.d.ts +36 -0
  145. package/dist-server/server/native-component-runtime.js +92 -0
  146. package/dist-server/server/native-components.d.ts +84 -0
  147. package/dist-server/server/native-components.js +160 -0
  148. package/dist-server/server/native-dispatch-process.d.ts +8 -0
  149. package/dist-server/server/native-dispatch-process.js +65 -0
  150. package/dist-server/server/native-runtime-spec.d.ts +89 -0
  151. package/dist-server/server/native-runtime-spec.js +169 -0
  152. package/dist-server/server/native-sessions.d.ts +172 -19
  153. package/dist-server/server/native-sessions.js +839 -244
  154. package/dist-server/server/native-shutdown.d.ts +6 -0
  155. package/dist-server/server/native-shutdown.js +28 -0
  156. package/dist-server/server/native-wren-home.d.ts +18 -0
  157. package/dist-server/server/native-wren-home.js +262 -0
  158. package/dist-server/server/node-pty-host.d.ts +2 -1
  159. package/dist-server/server/node-pty-host.js +3 -2
  160. package/dist-server/server/profile-registry.d.ts +139 -0
  161. package/dist-server/server/profile-registry.js +620 -0
  162. package/dist-server/server/runtime-binding.d.ts +4 -1
  163. package/dist-server/server/runtime-binding.js +50 -2
  164. package/dist-server/server/runtime-host/claude-component-step.d.ts +25 -0
  165. package/dist-server/server/runtime-host/claude-component-step.js +186 -0
  166. package/dist-server/server/runtime-host/codex-app-server.d.ts +75 -0
  167. package/dist-server/server/runtime-host/codex-app-server.js +239 -0
  168. package/dist-server/server/runtime-host/codex-compatibility.d.ts +110 -0
  169. package/dist-server/server/runtime-host/codex-compatibility.js +162 -0
  170. package/dist-server/server/runtime-host/codex-component-step.d.ts +19 -0
  171. package/dist-server/server/runtime-host/codex-component-step.js +308 -0
  172. package/dist-server/server/runtime-host/codex-conversation.d.ts +109 -0
  173. package/dist-server/server/runtime-host/codex-conversation.js +295 -0
  174. package/dist-server/server/runtime-host/codex-direct-tools.d.ts +23 -0
  175. package/dist-server/server/runtime-host/codex-direct-tools.js +59 -0
  176. package/dist-server/server/runtime-host/codex-events.d.ts +647 -0
  177. package/dist-server/server/runtime-host/codex-events.js +66 -0
  178. package/dist-server/server/runtime-host/codex-policy.d.ts +16 -0
  179. package/dist-server/server/runtime-host/codex-policy.js +151 -0
  180. package/dist-server/server/runtime-host/codex-process.d.ts +8 -0
  181. package/dist-server/server/runtime-host/codex-process.js +96 -0
  182. package/dist-server/server/runtime-host/codex-rpc.d.ts +47 -0
  183. package/dist-server/server/runtime-host/codex-rpc.js +182 -0
  184. package/dist-server/server/runtime-host/codex-session.d.ts +74 -0
  185. package/dist-server/server/runtime-host/codex-session.js +492 -0
  186. package/dist-server/server/runtime-host/codex-terminal-protocol.d.ts +35 -0
  187. package/dist-server/server/runtime-host/codex-terminal-protocol.js +243 -0
  188. package/dist-server/server/runtime-host/codex-terminal.d.ts +16 -0
  189. package/dist-server/server/runtime-host/codex-terminal.js +120 -0
  190. package/dist-server/server/runtime-host/index.d.ts +5 -0
  191. package/dist-server/server/runtime-host/index.js +3 -0
  192. package/dist-server/server/runtime-host/local.d.ts +29 -0
  193. package/dist-server/server/runtime-host/local.js +90 -0
  194. package/dist-server/server/runtime-host/policy.d.ts +8 -0
  195. package/dist-server/server/runtime-host/policy.js +78 -0
  196. package/dist-server/server/runtime-host/types.d.ts +52 -0
  197. package/dist-server/server/runtime-host/types.js +62 -0
  198. package/dist-server/server/structured-query-evidence.d.ts +7 -0
  199. package/dist-server/server/structured-query-evidence.js +47 -0
  200. package/dist-server/server/structured-runtime.d.ts +52 -0
  201. package/dist-server/server/structured-runtime.js +130 -0
  202. package/dist-server/server/structured-scope.d.ts +4 -0
  203. package/dist-server/server/structured-scope.js +12 -0
  204. package/dist-server/server/turn.d.ts +6 -1
  205. package/dist-server/server/turn.js +82 -25
  206. package/dist-server/server/wire-types.d.ts +67 -3
  207. package/dist-server/server/wren-profiles.js +1 -1
  208. package/managed-wren/README.md +60 -0
  209. package/managed-wren/dependency-wheels.json +282 -0
  210. package/managed-wren/manifest.json +373 -0
  211. package/managed-wren/release-inputs.json +45 -0
  212. package/managed-wren/runtime-tree.cjs +115 -0
  213. package/managed-wren/source-inputs.json +40 -0
  214. package/package.json +15 -6
  215. package/profiles/genbi-default/context/binding.yml +17 -7
  216. package/profiles/genbi-default/context/context.json +146 -0
  217. package/profiles/genbi-default/ir.golden.json +39 -43
  218. package/profiles/genbi-default/profile.yml +31 -0
  219. package/profiles/genbi-enrich-context/context/binding.yml +14 -3
  220. package/profiles/genbi-enrich-context/context/context.json +146 -0
  221. package/profiles/genbi-enrich-context/ir.golden.json +4 -5
  222. package/profiles/genbi-monitor/context/binding.yml +13 -5
  223. package/profiles/genbi-monitor/context/context.json +146 -0
  224. package/profiles/genbi-monitor/ir.golden.json +13 -7
  225. package/profiles/genbi-report/context/binding.yml +19 -0
  226. package/profiles/genbi-report/context/context.json +223 -0
  227. package/profiles/genbi-report/ir.golden.json +328 -0
  228. package/profiles/genbi-report/profile.yml +38 -0
  229. package/profiles/genbi-setup/ir.golden.json +3 -1
  230. package/dist/assets/index-Xs4q7pNo.css +0 -1
@@ -0,0 +1,227 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * codex:local's Wren MCP server: a stdio proxy in front of `wren serve mcp`.
4
+ *
5
+ * node codex-wren-proxy.js --credential-file <abs path> -- <wren> serve mcp --project <p> --quiet
6
+ *
7
+ * Every JSON-RPC line passes through to the child `wren serve mcp` unchanged, so the model sees
8
+ * the same tool names and schemas, except a `tools/call` of `run_sql`: that goes to this turn's
9
+ * host query service (see `codex-host-query.ts`), which executes it through governed access and
10
+ * records it as host evidence. The credential file (0600, owned by this user) names the turn's
11
+ * unix socket and token; without a valid one, `run_sql` is refused rather than run elsewhere.
12
+ *
13
+ * Node builtins only, and erasable TypeScript only, so it also runs from source.
14
+ */
15
+ import { spawn } from "node:child_process";
16
+ import { closeSync, constants as fsConstants, fstatSync, openSync, readFileSync, realpathSync } from "node:fs";
17
+ import { connect } from "node:net";
18
+ import path from "node:path";
19
+ import { createInterface } from "node:readline";
20
+ import { fileURLToPath } from "node:url";
21
+ const RUN_SQL = "run_sql";
22
+ const MAX_SQL_BYTES = 65_536;
23
+ /** The sentence the model sees per host error class; nothing from the host's wire is forwarded. */
24
+ const ERROR_TEXT = {
25
+ invalid_request: "The request did not match the governed run_sql contract: pass a non-empty sql string and an optional positive integer limit.",
26
+ model_not_found: "The query references a table that is not a model in the bound semantic context; query only the models it defines.",
27
+ policy_rejected: "The read-only analytical policy rejected the query: use one SELECT that reads at least one bound model.",
28
+ invalid_sql: "The SQL could not be parsed or planned against the semantic context.",
29
+ execution_failed: "The data source rejected the query at execution time, for example an unknown column or a type mismatch.",
30
+ timeout: "The query exceeded the statement time limit.",
31
+ datasource_unavailable: "The bound data source could not be reached.",
32
+ result_too_large: "The result exceeded the governed byte limit; narrow the query.",
33
+ budget_exhausted: "This turn's query budget is exhausted; answer from the results you already have.",
34
+ unauthorized: "run_sql is unavailable: the host query service rejected this session.",
35
+ internal: "The governed query failed for an unclassified reason.",
36
+ transport: "The host query service is unavailable.",
37
+ };
38
+ const NO_CREDENTIAL = "run_sql is unavailable: no valid host query credential was provided for this turn.";
39
+ /**
40
+ * Accepts only an absolute, regular, user-owned file no one else can read or write. The file is
41
+ * opened without following a symlink and checked and read through that one descriptor, so what
42
+ * is checked is what is read.
43
+ */
44
+ export function readCredential(file) {
45
+ if (!file || !path.isAbsolute(file))
46
+ return undefined;
47
+ let fd;
48
+ try {
49
+ fd = openSync(file, fsConstants.O_RDONLY | fsConstants.O_NOFOLLOW);
50
+ const stat = fstatSync(fd);
51
+ if (!stat.isFile() || (stat.mode & 0o077) !== 0)
52
+ return undefined;
53
+ if (typeof process.getuid === "function" && stat.uid !== process.getuid())
54
+ return undefined;
55
+ const value = JSON.parse(readFileSync(fd, "utf8"));
56
+ if (typeof value.socket !== "string" || !path.isAbsolute(value.socket))
57
+ return undefined;
58
+ if (typeof value.token !== "string" || value.token.length < 32)
59
+ return undefined;
60
+ return { socket: value.socket, token: value.token };
61
+ }
62
+ catch {
63
+ return undefined;
64
+ }
65
+ finally {
66
+ if (fd !== undefined)
67
+ closeSync(fd);
68
+ }
69
+ }
70
+ /**
71
+ * One connection at a time to the turn's host query service, reconnecting after a drop. Only
72
+ * the current socket's failure fails the pending requests (all of which were sent on it): a
73
+ * stale socket closing late never fails a request pending on its replacement. Each socket has
74
+ * its own read buffer.
75
+ */
76
+ export class HostConnection {
77
+ credential;
78
+ open;
79
+ pending = new Map();
80
+ socket;
81
+ nextId = 1;
82
+ constructor(credential, open = connect) {
83
+ this.credential = credential;
84
+ this.open = open;
85
+ }
86
+ request(sql, limit) {
87
+ return new Promise((resolve) => {
88
+ const id = this.nextId++;
89
+ try {
90
+ const socket = this.current();
91
+ this.pending.set(id, resolve);
92
+ socket.write(`${JSON.stringify({ token: this.credential.token, id, sql, ...(limit !== undefined ? { limit } : {}) })}\n`);
93
+ }
94
+ catch {
95
+ this.pending.delete(id);
96
+ resolve({ error: "transport" });
97
+ }
98
+ });
99
+ }
100
+ close() {
101
+ this.socket?.end();
102
+ }
103
+ current() {
104
+ if (this.socket && !this.socket.destroyed)
105
+ return this.socket;
106
+ const socket = this.open(this.credential.socket);
107
+ let buffer = "";
108
+ const fail = () => {
109
+ if (this.socket !== socket)
110
+ return;
111
+ this.socket = undefined;
112
+ for (const waiter of this.pending.values())
113
+ waiter({ error: "transport" });
114
+ this.pending.clear();
115
+ };
116
+ socket.setEncoding("utf8");
117
+ socket.on("data", (chunk) => {
118
+ buffer += chunk;
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
+ try {
125
+ const frame = JSON.parse(line);
126
+ const waiter = typeof frame.id === "number" ? this.pending.get(frame.id) : undefined;
127
+ if (!waiter || typeof frame.id !== "number")
128
+ continue;
129
+ this.pending.delete(frame.id);
130
+ if (frame.error !== undefined)
131
+ waiter({ error: typeof frame.error?.class === "string" ? frame.error.class : "internal" });
132
+ else
133
+ waiter({ result: frame.result });
134
+ }
135
+ catch {
136
+ socket.destroy();
137
+ }
138
+ }
139
+ });
140
+ socket.on("error", fail);
141
+ socket.on("close", fail);
142
+ this.socket = socket;
143
+ return socket;
144
+ }
145
+ }
146
+ function parseArgs(argv) {
147
+ const separator = argv.indexOf("--");
148
+ if (separator < 0 || separator === argv.length - 1) {
149
+ process.stderr.write("codex wren proxy: usage: --credential-file <path> -- <command> [args...]\n");
150
+ process.exit(2);
151
+ }
152
+ const own = argv.slice(0, separator);
153
+ const index = own.indexOf("--credential-file");
154
+ const credentialFile = index >= 0 ? own[index + 1] : undefined;
155
+ return { ...(credentialFile !== undefined ? { credentialFile } : {}), command: argv[separator + 1], args: argv.slice(separator + 2) };
156
+ }
157
+ function main() {
158
+ const options = parseArgs(process.argv.slice(2));
159
+ const credential = readCredential(options.credentialFile);
160
+ const host = credential ? new HostConnection(credential) : undefined;
161
+ const child = spawn(options.command, options.args, { stdio: ["pipe", "pipe", "inherit"] });
162
+ child.on("error", (error) => {
163
+ process.stderr.write(`codex wren proxy: could not start the Wren MCP server: ${error.message}\n`);
164
+ process.exit(1);
165
+ });
166
+ child.on("exit", (code, signal) => process.exit(code ?? (signal ? 1 : 0)));
167
+ const send = (line) => { process.stdout.write(`${line}\n`); };
168
+ createInterface({ input: child.stdout, crlfDelay: Infinity }).on("line", send);
169
+ const toolResult = (id, text, isError, structured) => {
170
+ send(JSON.stringify({ jsonrpc: "2.0", id, result: {
171
+ content: [{ type: "text", text }],
172
+ ...(structured !== undefined ? { structuredContent: structured } : {}),
173
+ isError,
174
+ } }));
175
+ };
176
+ const runSql = async (id, input) => {
177
+ if (!host) {
178
+ toolResult(id, NO_CREDENTIAL, true);
179
+ return;
180
+ }
181
+ const args = typeof input === "object" && input !== null && !Array.isArray(input) ? input : {};
182
+ const { sql, limit } = args;
183
+ if (typeof sql !== "string" || !sql.trim() || Buffer.byteLength(sql) > MAX_SQL_BYTES
184
+ || (limit !== undefined && limit !== null && (typeof limit !== "number" || !Number.isInteger(limit) || limit < 1))) {
185
+ toolResult(id, ERROR_TEXT.invalid_request, true);
186
+ return;
187
+ }
188
+ const outcome = await host.request(sql, typeof limit === "number" ? limit : undefined);
189
+ if (outcome.error !== undefined) {
190
+ const known = outcome.error in ERROR_TEXT ? outcome.error : "internal";
191
+ toolResult(id, `Governed Wren query failed [${known}]: ${ERROR_TEXT[known]}`, true);
192
+ return;
193
+ }
194
+ toolResult(id, JSON.stringify(outcome.result), false, outcome.result);
195
+ };
196
+ createInterface({ input: process.stdin, crlfDelay: Infinity })
197
+ .on("line", (line) => {
198
+ let message;
199
+ try {
200
+ message = JSON.parse(line);
201
+ }
202
+ catch {
203
+ message = undefined;
204
+ }
205
+ if (message && message.method === "tools/call" && message.id !== undefined && message.params?.name === RUN_SQL) {
206
+ void runSql(message.id, message.params.arguments);
207
+ return;
208
+ }
209
+ child.stdin.write(`${line}\n`);
210
+ })
211
+ .on("close", () => {
212
+ child.stdin.end();
213
+ host?.close();
214
+ });
215
+ }
216
+ function invokedDirectly() {
217
+ if (process.argv[1] === undefined)
218
+ return false;
219
+ try {
220
+ return realpathSync(process.argv[1]) === realpathSync(fileURLToPath(import.meta.url));
221
+ }
222
+ catch {
223
+ return false;
224
+ }
225
+ }
226
+ if (invokedDirectly())
227
+ main();
@@ -1,6 +1,12 @@
1
- import { readFile } from "node:fs/promises";
1
+ import { mkdtemp, readFile, realpath, rm } from "node:fs/promises";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ import { generatePreparedContext, resolveContextLoaderBinary } from "../compile/context-loader.js";
5
+ import { resolveWarbleBinary } from "../compile/resolve-binary.js";
2
6
  import { loadBundleWithProvenance } from "../bundle/loader.js";
3
7
  import { compileProfile, compileRawProfile } from "../compile/pipeline.js";
8
+ import { planDigest, readExecutionPlan } from "../components/plan.js";
9
+ import { describeComponentPlan } from "../components/display.js";
4
10
  import { buildAgentSdkManifestArgs, runAgentSdkManifest } from "./agent-sdk-manifest.js";
5
11
  import { resolveAgentSdkCli } from "./agent-sdk-cli.js";
6
12
  import { resolveCodexLocalCli } from "./codex-local-cli.js";
@@ -45,14 +51,44 @@ async function describeCodexManifest(options) {
45
51
  throw new Error("Codex manifest requires orchestrator, cheap, and strong model bindings");
46
52
  }
47
53
  const cli = await resolveCodexLocalCli(options.codexLocalBin);
48
- return purpose === "context_enrichment"
49
- ? describeCodexEnrichmentManifest(cli, compiled.irPath, models)
50
- : describeCodexAskManifest(cli, compiled.irPath, models);
54
+ if (purpose === "context_enrichment")
55
+ return describeCodexEnrichmentManifest(cli, compiled.irPath, models);
56
+ const ir = JSON.parse(await readFile(compiled.irPath, "utf8"));
57
+ if (!ir.components.some((node) => node.llm_calls.some((step) => step.component_calls?.length))) {
58
+ return describeCodexAskManifest(cli, compiled.irPath, models);
59
+ }
60
+ const dir = await mkdtemp(path.join(os.tmpdir(), "genbi-manifest-context-"));
61
+ try {
62
+ if (options.context === "bootstrap")
63
+ throw new Error("Composed analysis requires a bound project");
64
+ const project = await realpath(options.userProject);
65
+ for (const id of ["answer_query", "generate_dashboard"]) {
66
+ const binding = ir.components.find((node) => node.id === id)?.context_binding;
67
+ if (!binding || typeof binding.project !== "string" || !path.isAbsolute(binding.project) || await realpath(binding.project) !== project) {
68
+ throw new Error(`Codex manifest cannot capture context for ${id} from a different project`);
69
+ }
70
+ }
71
+ const file = path.join(dir, "context.json");
72
+ await generatePreparedContext(resolveContextLoaderBinary(), project, file);
73
+ const snapshot = JSON.parse(await readFile(file, "utf8"));
74
+ return await describeCodexAskManifest(cli, compiled.irPath, models, {
75
+ warbleBin: await resolveWarbleBinary(options.warbleBin),
76
+ contexts: Object.fromEntries(ir.components.map((node) => [node.id, { binding: node.context_binding, snapshot }])),
77
+ });
78
+ }
79
+ finally {
80
+ await rm(dir, { recursive: true, force: true });
81
+ }
51
82
  }
52
83
  async function describeVercelBundle(options) {
53
84
  const compiled = await compileForDescription(options, "agnostic");
54
85
  // `mode: "agnostic"` always produces a bundle — see `compileProfile`'s doc comment.
55
86
  const bundleJson = JSON.parse(await readFile(compiled.bundlePath, "utf-8"));
87
+ if (bundleJson.vercel_bundle_version === "0.2") {
88
+ const ir = JSON.parse(await readFile(compiled.irPath, "utf8"));
89
+ const plan = readExecutionPlan(JSON.stringify(bundleJson), { digest: bundleJson.bundle_sha256, inputIrDigest: planDigest(ir), declarations: Object.fromEntries(ir.components.map((node) => [node.id, node])), contextBinding: ir.context_binding });
90
+ return describeComponentPlan(plan, bundleJson.profile);
91
+ }
56
92
  return loadBundleWithProvenance(bundleJson, {
57
93
  ...(compiled.warbleBin !== undefined ? { warbleBin: compiled.warbleBin } : {}),
58
94
  profileSource: options.profileSource,
@@ -66,10 +102,13 @@ async function describeAgentSdkManifest(options) {
66
102
  ...(options.context !== "bootstrap" ? { userProject: options.userProject } : {}),
67
103
  });
68
104
  const stdout = await runAgentSdkManifest(command, args);
69
- return loadBundleWithProvenance(JSON.parse(stdout), {
105
+ const manifest = JSON.parse(stdout);
106
+ if (manifest.target !== "claude-agent-sdk:local")
107
+ throw new Error("Unexpected SDK manifest target");
108
+ return loadBundleWithProvenance(manifest, {
70
109
  ...(compiled.warbleBin !== undefined ? { warbleBin: compiled.warbleBin } : {}),
71
110
  profileSource: options.profileSource,
72
- });
111
+ }, { irVersion: "0.9", bundleVersions: ["0.1", "0.3"] });
73
112
  }
74
113
  function compileForDescription(options, mode) {
75
114
  const shared = {
@@ -1,11 +1,32 @@
1
+ import { type TierBinding } from "../providers/index.js";
1
2
  import type { RunAgentResult } from "../session/index.js";
2
3
  import type { InProcessOptions } from "./types.js";
3
- /** Projects a bundle-wide validated map onto the component selected for one turn. */
4
+ /**
5
+ * Projects a bundle-wide validated map onto the component selected for one
6
+ * turn, keyed on (mount, tier): an `<agent.id>/<tier>` entry wins over the
7
+ * bare tier entry, so the same tier name can bind a different model per mount.
8
+ */
4
9
  export declare function filterTierBindingForAgent(agent: {
10
+ readonly id?: string;
5
11
  readonly steps: readonly {
6
12
  readonly tier: string;
7
13
  }[];
8
14
  }, tierBinding: Readonly<Record<string, import("../providers/index.js").AdapterSpec>>): Readonly<Record<string, import("../providers/index.js").AdapterSpec>>;
15
+ /**
16
+ * The legacy (single-agent, IR 0.6) path has no caller/callee split: every
17
+ * step, and the render stage, sees rows. So a zone-aware binding on this path
18
+ * must be all-private; a `public` or unset zone on any tier the agent (or its
19
+ * render stage) uses is a loud failure, the same posture as the composed gate.
20
+ */
21
+ export declare function assertLegacyZoneBinding(agent: {
22
+ readonly id: string;
23
+ readonly steps: readonly {
24
+ readonly tier: string;
25
+ readonly realization: {
26
+ readonly kind: string;
27
+ };
28
+ }[];
29
+ }, binding: TierBinding, renderTier: string | undefined): void;
9
30
  /**
10
31
  * Resolves the `rootDir` the native `write_artifact` tool's
11
32
  * `createLocalExecutionEnv` scope is rooted at. Precedence: an explicit
@@ -3,20 +3,57 @@ import path from "node:path";
3
3
  import { createDefaultCapabilityRegistry } from "../capability/registry.js";
4
4
  import { createLocalExecutionEnv } from "../exec/index.js";
5
5
  import { deriveEnforcement } from "../guardrails/index.js";
6
- import { createDefaultProviderRegistry } from "../providers/index.js";
6
+ import { createDefaultProviderRegistry, isZoneAwareBinding, resolveTierSpec } from "../providers/index.js";
7
+ import { ZoneGateError } from "../components/zone-gate.js";
7
8
  import { runAgent } from "../session/index.js";
8
9
  import { createWrenNativeToolRegistry, resolveWrenBinary } from "../tools/index.js";
9
10
  import { deriveAdapterSpec } from "./adapter-spec.js";
10
11
  import { describeBundle } from "./describe.js";
11
12
  import { buildHybridTierBinding, buildUniformTierBinding } from "./tier-binding.js";
13
+ import { executionPlanFor } from "../components/display.js";
14
+ import { runInProcessComponents } from "../components/in-process.js";
12
15
  const ANSWER_QUERY_AGENT_ID = "answer_query";
13
16
  /** Operator-wide override for `resolveArtifactsDir`'s default, e.g. for a long-running BFF process. */
14
17
  const ARTIFACTS_DIR_ENV_VAR = "WREN_HARNESS_ARTIFACTS_DIR";
15
- /** Projects a bundle-wide validated map onto the component selected for one turn. */
18
+ /**
19
+ * Projects a bundle-wide validated map onto the component selected for one
20
+ * turn, keyed on (mount, tier): an `<agent.id>/<tier>` entry wins over the
21
+ * bare tier entry, so the same tier name can bind a different model per mount.
22
+ */
16
23
  export function filterTierBindingForAgent(agent, tierBinding) {
24
+ const binding = { tiers: tierBinding };
17
25
  return Object.fromEntries([...new Set(agent.steps.map((step) => step.tier))]
18
- .filter((tier) => tierBinding[tier] !== undefined)
19
- .map((tier) => [tier, tierBinding[tier]]));
26
+ .map((tier) => [tier, resolveTierSpec(binding, tier, agent.id)?.spec])
27
+ .filter((entry) => entry[1] !== undefined));
28
+ }
29
+ /**
30
+ * The legacy (single-agent, IR 0.6) path has no caller/callee split: every
31
+ * step, and the render stage, sees rows. So a zone-aware binding on this path
32
+ * must be all-private; a `public` or unset zone on any tier the agent (or its
33
+ * render stage) uses is a loud failure, the same posture as the composed gate.
34
+ */
35
+ export function assertLegacyZoneBinding(agent, binding, renderTier) {
36
+ if (!isZoneAwareBinding(binding))
37
+ return;
38
+ const violations = [];
39
+ const check = (tier, what) => {
40
+ const resolved = resolveTierSpec(binding, tier, agent.id);
41
+ const zone = resolved?.spec.zone ?? "unbound";
42
+ if (!resolved)
43
+ violations.push({ code: "unbound_tier", component: agent.id, tier, zone, message: `${what} (tier ${tier}) has no binding entry` });
44
+ else if (zone === "unbound")
45
+ violations.push({ code: "missing_zone", component: agent.id, tier, zone, message: `${what} (tier ${tier}) sees project data but binding entry "${resolved.key}" declares no zone` });
46
+ else if (zone !== "private")
47
+ violations.push({ code: "data_step_public", component: agent.id, tier, zone, message: `${what} (tier ${tier}) sees project data but binding entry "${resolved.key}" is zone: ${zone}; it must be private` });
48
+ };
49
+ for (const step of agent.steps)
50
+ check(step.tier, `step ${agent.id}.${step.tier}`);
51
+ const independent = agent.steps.filter((step) => step.realization.kind === "independent");
52
+ const render = renderTier ?? independent[independent.length - 1]?.tier;
53
+ if (render !== undefined)
54
+ check(render, `render stage of ${agent.id}`);
55
+ if (violations.length > 0)
56
+ throw new ZoneGateError(violations);
20
57
  }
21
58
  /**
22
59
  * Resolves the `rootDir` the native `write_artifact` tool's
@@ -56,6 +93,12 @@ export function resolveArtifactsDir(outDir) {
56
93
  */
57
94
  export async function runInProcessDefault(options) {
58
95
  const bundle = options.bundle ?? (await describeBundle(options));
96
+ if (bundle.vercel_bundle_version === "0.2") {
97
+ const plan = executionPlanFor(bundle);
98
+ if (!plan)
99
+ throw new Error("Composed display has no trusted execution plan");
100
+ return runInProcessComponents(plan, options);
101
+ }
59
102
  // Intent routing (server/turn.ts) picks the agent id when present; falls back to answer_query,
60
103
  // the original default from before intent routing existed.
61
104
  const agentId = options.agentId ?? ANSWER_QUERY_AGENT_ID;
@@ -69,6 +112,13 @@ export async function runInProcessDefault(options) {
69
112
  // here; `buildHybridTierBinding` intentionally rejects tiers used solely by
70
113
  // another component.
71
114
  const agentTierBinding = options.tierBinding === undefined ? undefined : filterTierBindingForAgent(agent, options.tierBinding);
115
+ if (options.tierBinding !== undefined || options.disclosurePolicy !== undefined || options.zoneRoles !== undefined) {
116
+ assertLegacyZoneBinding(agent, {
117
+ tiers: options.tierBinding ?? {},
118
+ ...(options.disclosurePolicy !== undefined ? { disclosurePolicy: options.disclosurePolicy } : {}),
119
+ ...(options.zoneRoles !== undefined ? { roles: options.zoneRoles } : {}),
120
+ }, undefined);
121
+ }
72
122
  const binding = options.tierBinding !== undefined
73
123
  ? buildHybridTierBinding(agent, agentTierBinding)
74
124
  : buildUniformTierBinding(agent, deriveAdapterSpec(options.authChoice, options.model !== undefined ? { model: options.model } : {}));
@@ -41,8 +41,8 @@ export async function route(options) {
41
41
  const deployment = options.deployment ?? "personal";
42
42
  const { warnings } = enforceCompliance(authChoice, { deployment });
43
43
  if (authChoice.mode === "subscription") {
44
- if (options.tierBinding !== undefined) {
45
- throw new Error("tierBinding (hybrid in-process per-tier routing) has no effect under a subscription " +
44
+ if (options.tierBinding !== undefined || options.disclosurePolicy !== undefined || options.zoneRoles !== undefined) {
45
+ throw new Error("tierBinding (with disclosurePolicy/zoneRoles: hybrid in-process per-tier routing) has no effect under a subscription " +
46
46
  "authChoice — dispatched has no adapter/tier binding of its own; use modelsConfig instead " +
47
47
  "(warble-agent-sdk's own --models-config per-step routing)");
48
48
  }
@@ -96,6 +96,7 @@ export async function route(options) {
96
96
  "per-tier adapter map)");
97
97
  }
98
98
  const result = await runInProcess({
99
+ ...(options.signal !== undefined ? { signal: options.signal } : {}),
99
100
  authChoice,
100
101
  profileSource: options.profileSource,
101
102
  userProject: options.userProject,
@@ -107,6 +108,8 @@ export async function route(options) {
107
108
  ...(options.bundle !== undefined ? { bundle: options.bundle } : {}),
108
109
  ...(options.mcpServers !== undefined ? { mcpServers: options.mcpServers } : {}),
109
110
  ...(options.tierBinding !== undefined ? { tierBinding: options.tierBinding } : {}),
111
+ ...(options.disclosurePolicy !== undefined ? { disclosurePolicy: options.disclosurePolicy } : {}),
112
+ ...(options.zoneRoles !== undefined ? { zoneRoles: options.zoneRoles } : {}),
110
113
  ...(options.onEvent !== undefined ? { onEvent: options.onEvent } : {}),
111
114
  ...(options.agentId !== undefined ? { agentId: options.agentId } : {}),
112
115
  });
@@ -2,7 +2,7 @@ import type { AuthChoice } from "../auth/index.js";
2
2
  import type { Bundle } from "../bundle/schema.js";
3
3
  import type { Deployment } from "../compliance/index.js";
4
4
  import type { AgentEvent } from "../events/index.js";
5
- import type { AdapterSpec } from "../providers/index.js";
5
+ import type { AdapterSpec, DisclosurePolicy, ZoneRoles } from "../providers/index.js";
6
6
  import type { RunAgentResult } from "../session/index.js";
7
7
  import type { McpServerConfigMap } from "../tools/index.js";
8
8
  import type { ResolvedCli } from "./agent-sdk-cli.js";
@@ -15,6 +15,7 @@ import type { CodexManifestModels } from "./codex-local-manifest.js";
15
15
  * tool through the real `wren` CLI (native tools) against `userProject`.
16
16
  */
17
17
  export interface InProcessOptions {
18
+ readonly signal?: AbortSignal;
18
19
  readonly authChoice: Extract<AuthChoice, {
19
20
  mode: "api-key" | "local" | "gateway";
20
21
  }>;
@@ -55,6 +56,19 @@ export interface InProcessOptions {
55
56
  * coverage is total-or-loud-fail.
56
57
  */
57
58
  readonly tierBinding?: Readonly<Record<string, AdapterSpec>>;
59
+ /**
60
+ * Hybrid privacy split: the egress disclosure policy the verification step
61
+ * enforces on every callee result before a caller sees it. Its presence
62
+ * arms the zone gate (see `components/zone-gate.ts`) and requires
63
+ * `zoneRoles.judge`.
64
+ */
65
+ readonly disclosurePolicy?: DisclosurePolicy;
66
+ /** Hybrid privacy split: binding keys of the judge / render tiers the zone gate checks by role. */
67
+ readonly zoneRoles?: ZoneRoles;
68
+ /** Hybrid privacy split: size bound for the capability card handed to public-zone steps (default 32 KiB). */
69
+ readonly capabilityCard?: {
70
+ readonly maxBytes?: number;
71
+ };
58
72
  /** Live-event layer: forwarded to `RunAgentContext.onEvent`. See that field's doc comment. */
59
73
  readonly onEvent?: (event: AgentEvent) => void;
60
74
  /**
@@ -209,6 +223,11 @@ export interface CodexAskOptions {
209
223
  }
210
224
  export interface CodexAskResult {
211
225
  readonly finalText: string;
226
+ /**
227
+ * codex:local only: the queries this turn's host query service executed for the model's
228
+ * `run_sql`, the only evidence its answer may be grounded on. Never persisted.
229
+ */
230
+ readonly hostObservations?: readonly import("./codex-host-query.js").HostQueryObservation[];
212
231
  }
213
232
  export type CodexAskExecutor = (options: CodexAskOptions) => Promise<CodexAskResult>;
214
233
  /**
@@ -243,6 +262,10 @@ export interface RouteOptions {
243
262
  readonly mcpServers?: McpServerConfigMap;
244
263
  /** In-process only (hybrid mode); see `InProcessOptions.tierBinding`. `route()` rejects this alongside a `subscription` authChoice. */
245
264
  readonly tierBinding?: Readonly<Record<string, AdapterSpec>>;
265
+ /** In-process only; see `InProcessOptions.disclosurePolicy`. */
266
+ readonly disclosurePolicy?: DisclosurePolicy;
267
+ /** In-process only; see `InProcessOptions.zoneRoles`. */
268
+ readonly zoneRoles?: ZoneRoles;
246
269
  /** Dispatched only (hybrid mode); see `DispatchedOptions.modelsConfig`. `route()` rejects this alongside a non-`subscription` authChoice. */
247
270
  readonly modelsConfig?: string;
248
271
  /** Live-event layer: forwarded to whichever mode executor runs. See `InProcessOptions.onEvent`/`DispatchedOptions.onEvent`. */
@@ -283,4 +306,10 @@ export type RouteResult = ({
283
306
  } & DispatchedResult) | ({
284
307
  readonly backend: "codex-local";
285
308
  readonly warnings: readonly string[];
309
+ } & CodexAskResult) | ({
310
+ readonly backend: "codex-app-server";
311
+ readonly warnings: readonly string[];
312
+ /** Host-captured normalized component evidence; never recovered from model prose. */
313
+ readonly envelope?: import("../render/envelope.js").RenderEnvelope;
314
+ readonly dataAttempted?: boolean;
286
315
  } & CodexAskResult);
@@ -4,6 +4,7 @@ import { createLocalExecutionEnv } from "../exec/index.js";
4
4
  import { deriveEnforcement } from "../guardrails/index.js";
5
5
  import { executeAgent, summarizeToolOutput } from "../loop/index.js";
6
6
  import { getAllowedBlockTypes, renderEnvelope } from "../render/index.js";
7
+ import { selectAnsweringCandidate } from "../render/answering-query.js";
7
8
  import { BUILD_DASHBOARD_TOOL_NAME, WREN_QUERY_TOOL_NAME, withResolvedTools } from "../tools/index.js";
8
9
  import { UnknownAgentError } from "./errors.js";
9
10
  import { findLockedGatedCheck } from "./gate.js";
@@ -23,11 +24,16 @@ import { findLockedGatedCheck } from "./gate.js";
23
24
  * becomes a `RefusalResult`.
24
25
  */
25
26
  export async function runAgent(bundle, agentId, userInput, ctx) {
27
+ if (bundle.vercel_bundle_version === "0.2")
28
+ throw new Error("Composed plans require the component host runner");
26
29
  assertCapabilities(bundle, ctx.capabilityRegistry);
27
30
  const agent = bundle.agents.find((candidate) => candidate.id === agentId);
28
31
  if (!agent) {
29
32
  throw new UnknownAgentError(agentId);
30
33
  }
34
+ if (agent.entrypoint === false || (agent.dependencies?.length ?? 0) > 0 || bundle.manifest_version === "0.3") {
35
+ throw new Error("This manifest requires its owning component runtime");
36
+ }
31
37
  const emitter = createAgentEventEmitter(ctx.onEvent);
32
38
  emitter.emit({ kind: "run.start", mode: "A", agentId });
33
39
  try {
@@ -50,15 +56,16 @@ export async function runAgent(bundle, agentId, userInput, ctx) {
50
56
  // forced to a refusal by this check.
51
57
  let sawSuccessfulDataAccessCall = false;
52
58
  const requiresDataAccess = agent.capabilities.some((entry) => entry.capability.startsWith("sql_execution:"));
53
- // Deterministic table capture: the most recent *successful* `query`
54
- // tool result, kept as a flat table payload (columns + positional
55
- // rows + the SQL that produced it). This is the agent's real data —
56
- // `renderEnvelope` builds the table/definition blocks straight from
57
- // it instead of asking the render model to re-emit structure it tends
58
- // to summarize away (leaving a bare `{type:"table"}`). The last
59
- // success wins because, in the answer_query flow, it is the query
60
- // whose rows are the answer.
61
- let toolTable;
59
+ // Deterministic table capture: every *successful* `query` tool result,
60
+ // kept as a flat table payload (columns + positional rows + the SQL that
61
+ // produced it). This is the agent's real data — `renderEnvelope` builds
62
+ // the table/definition blocks straight from it instead of asking the
63
+ // render model to re-emit structure it tends to summarize away (leaving
64
+ // a bare `{type:"table"}`). Which capture is the answer is decided after
65
+ // the loop (see `selectAnsweringCandidate`): the one whose SQL the
66
+ // terminal artifact names, because a model that runs a stray check after
67
+ // the right query must not turn that check into a verified answer.
68
+ const toolTables = [];
62
69
  // Deterministic dashboard-envelope capture: the most recent
63
70
  // *successful* `build_dashboard` tool result, kept as-is (it's
64
71
  // already a complete, ordered `{blocks}` envelope — see
@@ -110,7 +117,7 @@ export async function runAgent(bundle, agentId, userInput, ctx) {
110
117
  sawSuccessfulDataAccessCall = true;
111
118
  const seed = toToolTableSeed(outcome.output, outcome.input);
112
119
  if (seed !== undefined)
113
- toolTable = seed;
120
+ toolTables.push(seed);
114
121
  }
115
122
  if (outcome.tool === BUILD_DASHBOARD_TOOL_NAME && outcome.outcome === "success") {
116
123
  const seed = toDashboardSeed(outcome.output);
@@ -124,6 +131,7 @@ export async function runAgent(bundle, agentId, userInput, ctx) {
124
131
  },
125
132
  onEvent: emitter.emit,
126
133
  });
134
+ const toolTable = selectAnsweringCandidate(toolTables, terminalArtifactValue(artifacts));
127
135
  const envelope = await renderEnvelope(agent, artifacts, {
128
136
  binding: ctx.binding,
129
137
  registry: ctx.registry,
@@ -278,6 +286,18 @@ export async function runAgent(bundle, agentId, userInput, ctx) {
278
286
  throw error;
279
287
  }
280
288
  }
289
+ /** The last dataflow artifact as a value: JSON text is parsed so a named `definition.sql` is visible. */
290
+ function terminalArtifactValue(artifacts) {
291
+ const last = [...artifacts.values()].at(-1);
292
+ if (typeof last !== "string")
293
+ return last;
294
+ try {
295
+ return JSON.parse(last);
296
+ }
297
+ catch {
298
+ return last;
299
+ }
300
+ }
281
301
  function describeRunError(error) {
282
302
  if (error instanceof Error)
283
303
  return error.message;
@@ -18,6 +18,8 @@ export declare const BUILD_CONTEXT_AGENT_ID = "build_context";
18
18
  */
19
19
  export declare const DEFAULT_SETUP_MAX_TURNS = 120;
20
20
  export interface SetupStepRunOptions {
21
+ /** Owned turn cancellation, forwarded to the selected runtime. */
22
+ readonly signal?: AbortSignal;
21
23
  /** The composed single-line prompt (see `composeSetupPrompt`). */
22
24
  readonly prompt: string;
23
25
  /** The workspace root the initial connect turn scaffolds `<name>/` into. */