@wrenai/genbi 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (286) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +141 -0
  3. package/bin/genbi.mjs +31 -0
  4. package/dist/assets/echarts-LTFC-ysd.js +40 -0
  5. package/dist/assets/index-Bo--QMUN.js +441 -0
  6. package/dist/assets/index-Xs4q7pNo.css +1 -0
  7. package/dist/index.html +14 -0
  8. package/dist/wrenai_logo.png +0 -0
  9. package/dist/wrenai_logo_white.png +0 -0
  10. package/dist-server/harness/auth/detect.d.ts +18 -0
  11. package/dist-server/harness/auth/detect.js +37 -0
  12. package/dist-server/harness/auth/index.d.ts +4 -0
  13. package/dist-server/harness/auth/index.js +2 -0
  14. package/dist-server/harness/auth/probe.d.ts +18 -0
  15. package/dist-server/harness/auth/probe.js +52 -0
  16. package/dist-server/harness/auth/types.d.ts +42 -0
  17. package/dist-server/harness/auth/types.js +9 -0
  18. package/dist-server/harness/bundle/loader.d.ts +32 -0
  19. package/dist-server/harness/bundle/loader.js +43 -0
  20. package/dist-server/harness/bundle/schema.d.ts +199 -0
  21. package/dist-server/harness/bundle/schema.js +127 -0
  22. package/dist-server/harness/bundle/version.d.ts +10 -0
  23. package/dist-server/harness/bundle/version.js +43 -0
  24. package/dist-server/harness/capability/gate.d.ts +6 -0
  25. package/dist-server/harness/capability/gate.js +29 -0
  26. package/dist-server/harness/capability/registry.d.ts +6 -0
  27. package/dist-server/harness/capability/registry.js +17 -0
  28. package/dist-server/harness/cli-args.d.ts +123 -0
  29. package/dist-server/harness/cli-args.js +247 -0
  30. package/dist-server/harness/cli.d.ts +2 -0
  31. package/dist-server/harness/cli.js +151 -0
  32. package/dist-server/harness/compile/cache.d.ts +23 -0
  33. package/dist-server/harness/compile/cache.js +109 -0
  34. package/dist-server/harness/compile/compose-profile.d.ts +34 -0
  35. package/dist-server/harness/compile/compose-profile.js +95 -0
  36. package/dist-server/harness/compile/errors.d.ts +22 -0
  37. package/dist-server/harness/compile/errors.js +41 -0
  38. package/dist-server/harness/compile/fingerprint.d.ts +22 -0
  39. package/dist-server/harness/compile/fingerprint.js +84 -0
  40. package/dist-server/harness/compile/index.d.ts +8 -0
  41. package/dist-server/harness/compile/index.js +7 -0
  42. package/dist-server/harness/compile/pipeline.d.ts +66 -0
  43. package/dist-server/harness/compile/pipeline.js +176 -0
  44. package/dist-server/harness/compile/resolve-binary.d.ts +55 -0
  45. package/dist-server/harness/compile/resolve-binary.js +133 -0
  46. package/dist-server/harness/compile/types.d.ts +107 -0
  47. package/dist-server/harness/compile/types.js +1 -0
  48. package/dist-server/harness/compile/warble-identity.d.ts +12 -0
  49. package/dist-server/harness/compile/warble-identity.js +33 -0
  50. package/dist-server/harness/compliance/gate.d.ts +35 -0
  51. package/dist-server/harness/compliance/gate.js +48 -0
  52. package/dist-server/harness/compliance/index.d.ts +2 -0
  53. package/dist-server/harness/compliance/index.js +1 -0
  54. package/dist-server/harness/compliance/types.d.ts +16 -0
  55. package/dist-server/harness/compliance/types.js +1 -0
  56. package/dist-server/harness/events/emitter.d.ts +34 -0
  57. package/dist-server/harness/events/emitter.js +25 -0
  58. package/dist-server/harness/events/index.d.ts +4 -0
  59. package/dist-server/harness/events/index.js +2 -0
  60. package/dist-server/harness/events/sse.d.ts +20 -0
  61. package/dist-server/harness/events/sse.js +48 -0
  62. package/dist-server/harness/events/types.d.ts +131 -0
  63. package/dist-server/harness/events/types.js +1 -0
  64. package/dist-server/harness/exec/errors.d.ts +16 -0
  65. package/dist-server/harness/exec/errors.js +29 -0
  66. package/dist-server/harness/exec/index.d.ts +5 -0
  67. package/dist-server/harness/exec/index.js +2 -0
  68. package/dist-server/harness/exec/local.d.ts +32 -0
  69. package/dist-server/harness/exec/local.js +134 -0
  70. package/dist-server/harness/exec/policy.d.ts +12 -0
  71. package/dist-server/harness/exec/policy.js +1 -0
  72. package/dist-server/harness/exec/types.d.ts +63 -0
  73. package/dist-server/harness/exec/types.js +1 -0
  74. package/dist-server/harness/guardrails/index.d.ts +2 -0
  75. package/dist-server/harness/guardrails/index.js +1 -0
  76. package/dist-server/harness/guardrails/policy.d.ts +45 -0
  77. package/dist-server/harness/guardrails/policy.js +57 -0
  78. package/dist-server/harness/index.d.ts +37 -0
  79. package/dist-server/harness/index.js +19 -0
  80. package/dist-server/harness/loop/errors.d.ts +53 -0
  81. package/dist-server/harness/loop/errors.js +72 -0
  82. package/dist-server/harness/loop/executor.d.ts +123 -0
  83. package/dist-server/harness/loop/executor.js +380 -0
  84. package/dist-server/harness/loop/guard.d.ts +16 -0
  85. package/dist-server/harness/loop/guard.js +1 -0
  86. package/dist-server/harness/loop/index.d.ts +5 -0
  87. package/dist-server/harness/loop/index.js +3 -0
  88. package/dist-server/harness/native-producer/index.d.ts +109 -0
  89. package/dist-server/harness/native-producer/index.js +302 -0
  90. package/dist-server/harness/npm-package-resolve.d.ts +28 -0
  91. package/dist-server/harness/npm-package-resolve.js +47 -0
  92. package/dist-server/harness/providers/adapters/anthropic.d.ts +7 -0
  93. package/dist-server/harness/providers/adapters/anthropic.js +6 -0
  94. package/dist-server/harness/providers/adapters/mock.d.ts +4 -0
  95. package/dist-server/harness/providers/adapters/mock.js +5 -0
  96. package/dist-server/harness/providers/adapters/openai-compatible.d.ts +10 -0
  97. package/dist-server/harness/providers/adapters/openai-compatible.js +10 -0
  98. package/dist-server/harness/providers/binding.d.ts +23 -0
  99. package/dist-server/harness/providers/binding.js +13 -0
  100. package/dist-server/harness/providers/errors.d.ts +6 -0
  101. package/dist-server/harness/providers/errors.js +12 -0
  102. package/dist-server/harness/providers/index.d.ts +18 -0
  103. package/dist-server/harness/providers/index.js +22 -0
  104. package/dist-server/harness/providers/registry.d.ts +16 -0
  105. package/dist-server/harness/providers/registry.js +24 -0
  106. package/dist-server/harness/render/envelope.d.ts +145 -0
  107. package/dist-server/harness/render/envelope.js +428 -0
  108. package/dist-server/harness/render/errors.d.ts +33 -0
  109. package/dist-server/harness/render/errors.js +46 -0
  110. package/dist-server/harness/render/index.d.ts +5 -0
  111. package/dist-server/harness/render/index.js +3 -0
  112. package/dist-server/harness/render/validate.d.ts +13 -0
  113. package/dist-server/harness/render/validate.js +88 -0
  114. package/dist-server/harness/route/adapter-spec.d.ts +40 -0
  115. package/dist-server/harness/route/adapter-spec.js +61 -0
  116. package/dist-server/harness/route/agent-sdk-cli.d.ts +30 -0
  117. package/dist-server/harness/route/agent-sdk-cli.js +86 -0
  118. package/dist-server/harness/route/agent-sdk-manifest.d.ts +31 -0
  119. package/dist-server/harness/route/agent-sdk-manifest.js +56 -0
  120. package/dist-server/harness/route/artifact-content.d.ts +53 -0
  121. package/dist-server/harness/route/artifact-content.js +83 -0
  122. package/dist-server/harness/route/chat-event-mapper.d.ts +95 -0
  123. package/dist-server/harness/route/chat-event-mapper.js +164 -0
  124. package/dist-server/harness/route/codex-ask-events.d.ts +25 -0
  125. package/dist-server/harness/route/codex-ask-events.js +395 -0
  126. package/dist-server/harness/route/codex-ask.d.ts +15 -0
  127. package/dist-server/harness/route/codex-ask.js +242 -0
  128. package/dist-server/harness/route/codex-local-cli.d.ts +20 -0
  129. package/dist-server/harness/route/codex-local-cli.js +78 -0
  130. package/dist-server/harness/route/codex-local-manifest.d.ts +22 -0
  131. package/dist-server/harness/route/codex-local-manifest.js +180 -0
  132. package/dist-server/harness/route/describe.d.ts +54 -0
  133. package/dist-server/harness/route/describe.js +84 -0
  134. package/dist-server/harness/route/dispatched.d.ts +138 -0
  135. package/dist-server/harness/route/dispatched.js +339 -0
  136. package/dist-server/harness/route/in-process.d.ts +43 -0
  137. package/dist-server/harness/route/in-process.js +106 -0
  138. package/dist-server/harness/route/index.d.ts +19 -0
  139. package/dist-server/harness/route/index.js +12 -0
  140. package/dist-server/harness/route/profile-source.d.ts +37 -0
  141. package/dist-server/harness/route/profile-source.js +76 -0
  142. package/dist-server/harness/route/route.d.ts +33 -0
  143. package/dist-server/harness/route/route.js +114 -0
  144. package/dist-server/harness/route/tier-binding.d.ts +36 -0
  145. package/dist-server/harness/route/tier-binding.js +63 -0
  146. package/dist-server/harness/route/types.d.ts +286 -0
  147. package/dist-server/harness/route/types.js +1 -0
  148. package/dist-server/harness/session/errors.d.ts +4 -0
  149. package/dist-server/harness/session/errors.js +7 -0
  150. package/dist-server/harness/session/gate.d.ts +11 -0
  151. package/dist-server/harness/session/gate.js +13 -0
  152. package/dist-server/harness/session/index.d.ts +5 -0
  153. package/dist-server/harness/session/index.js +3 -0
  154. package/dist-server/harness/session/run.d.ts +60 -0
  155. package/dist-server/harness/session/run.js +333 -0
  156. package/dist-server/harness/session/types.d.ts +29 -0
  157. package/dist-server/harness/session/types.js +1 -0
  158. package/dist-server/harness/setup/codex-events.d.ts +12 -0
  159. package/dist-server/harness/setup/codex-events.js +108 -0
  160. package/dist-server/harness/setup/runner.d.ts +497 -0
  161. package/dist-server/harness/setup/runner.js +1314 -0
  162. package/dist-server/harness/tools/errors.d.ts +107 -0
  163. package/dist-server/harness/tools/errors.js +141 -0
  164. package/dist-server/harness/tools/index.d.ts +13 -0
  165. package/dist-server/harness/tools/index.js +8 -0
  166. package/dist-server/harness/tools/mcp.d.ts +26 -0
  167. package/dist-server/harness/tools/mcp.js +15 -0
  168. package/dist-server/harness/tools/native.d.ts +179 -0
  169. package/dist-server/harness/tools/native.js +322 -0
  170. package/dist-server/harness/tools/parse.d.ts +14 -0
  171. package/dist-server/harness/tools/parse.js +18 -0
  172. package/dist-server/harness/tools/resolve-wren-binary.d.ts +12 -0
  173. package/dist-server/harness/tools/resolve-wren-binary.js +21 -0
  174. package/dist-server/harness/tools/resolve.d.ts +51 -0
  175. package/dist-server/harness/tools/resolve.js +79 -0
  176. package/dist-server/harness/tools/setup-native.d.ts +84 -0
  177. package/dist-server/harness/tools/setup-native.js +353 -0
  178. package/dist-server/harness/tools/wiring.d.ts +10 -0
  179. package/dist-server/harness/tools/wiring.js +16 -0
  180. package/dist-server/providers/setup.provider.yaml +31 -0
  181. package/dist-server/providers/wren.provider.yaml +33 -0
  182. package/dist-server/server/adopt.d.ts +87 -0
  183. package/dist-server/server/adopt.js +263 -0
  184. package/dist-server/server/app.d.ts +6 -0
  185. package/dist-server/server/app.js +2521 -0
  186. package/dist-server/server/auth-choice.d.ts +16 -0
  187. package/dist-server/server/auth-choice.js +36 -0
  188. package/dist-server/server/bin.d.ts +2 -0
  189. package/dist-server/server/bin.js +601 -0
  190. package/dist-server/server/bootstrap-project-binding.d.ts +31 -0
  191. package/dist-server/server/bootstrap-project-binding.js +71 -0
  192. package/dist-server/server/clarify.d.ts +10 -0
  193. package/dist-server/server/clarify.js +17 -0
  194. package/dist-server/server/codex-setup-mcp.d.ts +2 -0
  195. package/dist-server/server/codex-setup-mcp.js +147 -0
  196. package/dist-server/server/compose.d.ts +89 -0
  197. package/dist-server/server/compose.js +271 -0
  198. package/dist-server/server/conn-config.d.ts +30 -0
  199. package/dist-server/server/conn-config.js +253 -0
  200. package/dist-server/server/context-files.d.ts +12 -0
  201. package/dist-server/server/context-files.js +117 -0
  202. package/dist-server/server/context-map.d.ts +30 -0
  203. package/dist-server/server/context-map.js +95 -0
  204. package/dist-server/server/context-source.d.ts +68 -0
  205. package/dist-server/server/context-source.js +85 -0
  206. package/dist-server/server/db.d.ts +862 -0
  207. package/dist-server/server/db.js +1839 -0
  208. package/dist-server/server/enrichment-runner.d.ts +184 -0
  209. package/dist-server/server/enrichment-runner.js +213 -0
  210. package/dist-server/server/enrichment-verify.d.ts +73 -0
  211. package/dist-server/server/enrichment-verify.js +290 -0
  212. package/dist-server/server/enrichment.d.ts +232 -0
  213. package/dist-server/server/enrichment.js +199 -0
  214. package/dist-server/server/env-detect.d.ts +8 -0
  215. package/dist-server/server/env-detect.js +17 -0
  216. package/dist-server/server/env-field-hints.d.ts +46 -0
  217. package/dist-server/server/env-field-hints.js +48 -0
  218. package/dist-server/server/fold.d.ts +133 -0
  219. package/dist-server/server/fold.js +407 -0
  220. package/dist-server/server/harness-profile-sources.d.ts +3 -0
  221. package/dist-server/server/harness-profile-sources.js +24 -0
  222. package/dist-server/server/harness.d.ts +19 -0
  223. package/dist-server/server/harness.js +439 -0
  224. package/dist-server/server/impact.d.ts +18 -0
  225. package/dist-server/server/impact.js +85 -0
  226. package/dist-server/server/interactive-terminal.d.ts +175 -0
  227. package/dist-server/server/interactive-terminal.js +336 -0
  228. package/dist-server/server/native-artifacts.d.ts +1004 -0
  229. package/dist-server/server/native-artifacts.js +564 -0
  230. package/dist-server/server/native-dispatch-registry.d.ts +42 -0
  231. package/dist-server/server/native-dispatch-registry.js +45 -0
  232. package/dist-server/server/native-producer-fence.d.ts +14 -0
  233. package/dist-server/server/native-producer-fence.js +48 -0
  234. package/dist-server/server/native-session-resume.d.ts +6 -0
  235. package/dist-server/server/native-session-resume.js +103 -0
  236. package/dist-server/server/native-session-workspace.d.ts +40 -0
  237. package/dist-server/server/native-session-workspace.js +175 -0
  238. package/dist-server/server/native-sessions.d.ts +374 -0
  239. package/dist-server/server/native-sessions.js +1517 -0
  240. package/dist-server/server/native-wren-runtime.d.ts +20 -0
  241. package/dist-server/server/native-wren-runtime.js +259 -0
  242. package/dist-server/server/node-pty-host.d.ts +31 -0
  243. package/dist-server/server/node-pty-host.js +100 -0
  244. package/dist-server/server/route-intent.d.ts +40 -0
  245. package/dist-server/server/route-intent.js +23 -0
  246. package/dist-server/server/runtime-binding.d.ts +52 -0
  247. package/dist-server/server/runtime-binding.js +258 -0
  248. package/dist-server/server/source-catalog.d.ts +84 -0
  249. package/dist-server/server/source-catalog.js +221 -0
  250. package/dist-server/server/source-driver.d.ts +35 -0
  251. package/dist-server/server/source-driver.js +124 -0
  252. package/dist-server/server/spa.d.ts +2 -0
  253. package/dist-server/server/spa.js +51 -0
  254. package/dist-server/server/subscription-model-catalog.d.ts +27 -0
  255. package/dist-server/server/subscription-model-catalog.js +203 -0
  256. package/dist-server/server/turn.d.ts +249 -0
  257. package/dist-server/server/turn.js +1074 -0
  258. package/dist-server/server/wire-types.d.ts +799 -0
  259. package/dist-server/server/wire-types.js +1 -0
  260. package/dist-server/server/wren-home.d.ts +19 -0
  261. package/dist-server/server/wren-home.js +33 -0
  262. package/dist-server/server/wren-profiles.d.ts +43 -0
  263. package/dist-server/server/wren-profiles.js +123 -0
  264. package/package.json +93 -0
  265. package/profiles/genbi-default/context/binding.yml +9 -0
  266. package/profiles/genbi-default/ir.golden.json +506 -0
  267. package/profiles/genbi-default/profile.yml +16 -0
  268. package/profiles/genbi-enrich-context/components/apply_enrichment/component.yml +42 -0
  269. package/profiles/genbi-enrich-context/components/draft_enrichment/component.yml +30 -0
  270. package/profiles/genbi-enrich-context/components/draft_enrichment/steps/draft.md +34 -0
  271. package/profiles/genbi-enrich-context/components/inspect_context/component.yml +32 -0
  272. package/profiles/genbi-enrich-context/components/inspect_context/steps/inspect.md +13 -0
  273. package/profiles/genbi-enrich-context/context/binding.yml +4 -0
  274. package/profiles/genbi-enrich-context/ir.golden.json +302 -0
  275. package/profiles/genbi-enrich-context/profile.yml +15 -0
  276. package/profiles/genbi-monitor/context/binding.yml +7 -0
  277. package/profiles/genbi-monitor/ir.golden.json +188 -0
  278. package/profiles/genbi-monitor/profile.yml +27 -0
  279. package/profiles/genbi-setup/components/build_context/component.yml +44 -0
  280. package/profiles/genbi-setup/components/build_context/steps/build.md +14 -0
  281. package/profiles/genbi-setup/components/connect_source/component.yml +51 -0
  282. package/profiles/genbi-setup/components/connect_source/steps/connect.md +23 -0
  283. package/profiles/genbi-setup/context/binding.yml +8 -0
  284. package/profiles/genbi-setup/ir.golden.json +140 -0
  285. package/profiles/genbi-setup/profile.yml +18 -0
  286. package/profiles/genbi-setup/raw/schema.json +5 -0
@@ -0,0 +1,353 @@
1
+ import { existsSync, realpathSync } from "node:fs";
2
+ import path from "node:path";
3
+ import { tool } from "ai";
4
+ import { z } from "zod";
5
+ import { SetupCommandDeniedError, SetupExecCwdScopeError, SetupExecutionInputError, SetupWriteScopeError } from "./errors.js";
6
+ /**
7
+ * `setup_execution` (native realization): the ONE native tool
8
+ * `connect_source` and `build_context` both bind to (see
9
+ * `providers/setup.provider.yaml`) — a scope-limited exec + write pair
10
+ * mirroring the `setup_execution` guardrail's semantics on the dispatched
11
+ * (claude-agent-sdk) side of the same onboarding flow, so an api-key/in-process
12
+ * session can drive the same setup components under an equivalent, not
13
+ * merely similarly-named, permission boundary.
14
+ *
15
+ * DESTRUCTIVE/REDIRECTION below are copied VERBATIM from warble's
16
+ * claude-agent-sdk dispatcher (`dispatcher/claude-agent-sdk/src/guardrails.ts`,
17
+ * `GuardConfig.setupScope` branch) rather than reinvented — the brief for
18
+ * this work is explicit that a hand-rolled second copy that drifts from the
19
+ * original is an exploitable gap, not a stylistic choice. All three of
20
+ * DESTRUCTIVE/REDIRECTION/the dotenv-read pair below are checked FIRST and
21
+ * unconditionally, before any other permission logic, exactly as that
22
+ * dispatcher orders its own two, and are never relaxed because the scope is a
23
+ * setup scope.
24
+ *
25
+ * The dotenv-read pair (DOTENV_READER_COMMANDS + DOTENV_PATH) is a
26
+ * genbi-side addition neither DESTRUCTIVE nor REDIRECTION covers: the setup
27
+ * credential design (see `credentialBoundary` in `server/compose.ts` and the
28
+ * onboarding skill) writes an EMPTY `.env` template and never reads it back —
29
+ * but nothing enforced that until this pair was added. Observed live (real
30
+ * `gpt-4.1`, in-process): the agent ran `cat <project>/.env` through this same
31
+ * `exec` action, it succeeded (neither DESTRUCTIVE nor REDIRECTION matches a
32
+ * plain read), and the full stdout — a connection string, or for
33
+ * postgres/snowflake/bigquery, a password/API-key/service-account value — was
34
+ * handed back to the model AND persisted verbatim into the BFF's
35
+ * `turns.trace_json` (see `redactSetupExecutionOutput` below, the persistence-side
36
+ * companion to this pair). A counterpart change belongs in warble's
37
+ * claude-agent-sdk dispatcher (same file/branch as DESTRUCTIVE/REDIRECTION
38
+ * above) so the dispatched side of this same setup scope gets the identical
39
+ * guard — kept in the same shape and same position here so that copy can
40
+ * mirror it byte-for-byte.
41
+ */
42
+ const DESTRUCTIVE = /\b(rm|sudo|dd|mkfs|shutdown|reboot|kill|chmod|chown|mv|cp)\b/;
43
+ /** Shell output redirection — an artifact/warehouse write escape (same rationale/regex as the dispatcher side). */
44
+ const REDIRECTION = /(^|[^>])>>?[^>]/;
45
+ /**
46
+ * Reader commands that can print a file's contents: `cat`, `head`, `tail`,
47
+ * `less`, `more`, `od`, `xxd`, `strings`, `grep`, `awk`, `sed`. Matched as a
48
+ * whole word (`\b`) so a lookalike substring inside another word — `cat`
49
+ * inside "concatenate", `sed` inside "used", `od` inside "produce" — never
50
+ * matches.
51
+ */
52
+ const DOTENV_READER_COMMANDS = /\b(cat|head|tail|less|more|od|xxd|strings|grep|awk|sed)\b/;
53
+ /**
54
+ * A `.env`/`.env.<suffix>` path token (`.env`, `project/.env`,
55
+ * `.env.local`, `.env.production`, …), matched precisely: the literal
56
+ * `.env` must be preceded by start-of-string/whitespace/quote/`/`/`=` and
57
+ * followed by end-of-string/whitespace/quote/`/`, with an optional
58
+ * `.<suffix>` in between. This is what keeps a file named `.environment`
59
+ * (no boundary right after `.env` — the next character is `i`, not one of
60
+ * the above) and a bare directory named `env/` (no leading dot at all) from
61
+ * tripping the match, even though both contain the substring "env" — both
62
+ * are exercised in `test/setup-native.test.ts`.
63
+ */
64
+ const DOTENV_PATH = /(^|[\s"'/=])\.env(\.[\w.-]+)?(?=$|[\s"'/])/;
65
+ // A setup exec may legitimately perform a cold schema discovery or context
66
+ // build, so this is a generous hang guard rather than a performance target.
67
+ // It matches the existing warble compile/dispatch ceiling. In-process previously
68
+ // supplied no timeout at all, allowing one interactive or deadlocked command
69
+ // to leave the setup turn (and its explicit recovery retry) running forever.
70
+ const SETUP_EXEC_TIMEOUT_MS = 2 * 60 * 1000;
71
+ /**
72
+ * Whether `command`'s TEXT references a dotenv-shaped path at all —
73
+ * independent of whether it's paired with a known reader command. Used both
74
+ * by `assertCommandAllowed` (paired with `DOTENV_READER_COMMANDS`, below) and
75
+ * by `redactSetupExecutionOutput` (unpaired — see that function's doc
76
+ * comment for why the persistence-side check is deliberately broader).
77
+ */
78
+ function commandReferencesDotenvPath(command) {
79
+ return DOTENV_PATH.test(command);
80
+ }
81
+ export const SETUP_EXECUTION_TOOL_NAME = "setup_execution";
82
+ /**
83
+ * A single flat top-level object, NOT a `z.discriminatedUnion` — a
84
+ * discriminated union serializes (via `z.toJSONSchema`) to a schema whose
85
+ * TOP level is `{"oneOf": [...]}` with no top-level `"type": "object"`, which
86
+ * both OpenAI's function-calling API and Anthropic's `input_schema` reject
87
+ * outright (confirmed against a real OpenAI `gpt-4.1` call; see
88
+ * `test/native-tool-schema-shape.test.ts` for the offline, no-API-key-spent
89
+ * regression coverage). `command`/`path`/`content` are therefore all
90
+ * optional here — the per-action required-field combination a discriminated
91
+ * union would enforce at the schema level is instead checked in
92
+ * `createSetupExecutionTool`'s `execute`, before any side effect.
93
+ *
94
+ * Flattening the schema drops the model-facing pairing a discriminated union
95
+ * used to encode for free (`exec` needs `command`; `write` needs `path` +
96
+ * `content`) — nothing about the field TYPES says which action requires
97
+ * which field. Since in-process has no turn budget (unlike dispatched's fixed
98
+ * `--max-turns` cap on setup), an under-described schema makes that pairing
99
+ * discoverable only by the model guessing wrong and reading a thrown
100
+ * `SetupExecutionInputError` back — unbounded trial-and-error on the user's
101
+ * own API spend. The `.describe()` calls below and the tool `description` in
102
+ * `createSetupExecutionTool` spell the pairing out explicitly so the model
103
+ * can satisfy it on the first call; the `execute`-time checks remain as the
104
+ * enforced (not just described) guarantee.
105
+ */
106
+ export const setupExecutionInputSchema = z.object({
107
+ action: z.enum(["exec", "write"]).describe('Which operation to perform: "exec" runs a shell command; "write" writes a file.'),
108
+ command: z.string().optional().describe('Required when action is "exec": the shell command to run. Not used for "write".'),
109
+ cwd: z
110
+ .string()
111
+ .optional()
112
+ .describe('Optional for both actions: use this directory instead of the workspace root as the command cwd or the base for a relative write path (e.g. the project directory). Absolute or relative to the workspace root, and must resolve within it. Defaults to the workspace root when omitted. For exec, use this instead of "cd <dir> && ..." chaining.'),
113
+ path: z
114
+ .string()
115
+ .optional()
116
+ .describe('Required when action is "write": the file path to write. A relative path is resolved from cwd when supplied, otherwise from the setup workspace root; an absolute path is allowed only within that root. Not used for "exec".'),
117
+ content: z.string().optional().describe('Required when action is "write": the file content to write. Not used for "exec".'),
118
+ });
119
+ /**
120
+ * Rejects a command matching the destructive/redirection/dotenv-read
121
+ * denylist BEFORE it is ever handed to `ExecutionEnv.exec` — a rejection
122
+ * here has run zero subprocess, exactly like the dispatcher-side
123
+ * `canUseTool` handler that denies before invoking the tool at all.
124
+ *
125
+ * The dotenv-read check is checked FIRST (see this module's top doc comment)
126
+ * and requires BOTH `DOTENV_READER_COMMANDS` and `commandReferencesDotenvPath`
127
+ * to match — either alone is over- or under-broad: `DOTENV_READER_COMMANDS`
128
+ * alone would deny an unrelated `cat notes.txt`; `commandReferencesDotenvPath`
129
+ * alone would deny `wren project show .env-example-model` (a path that merely
130
+ * mentions ".env" as a substring of something else, not a read of it) or a
131
+ * legitimate write-scoped `action: "write"` call — though that case can't
132
+ * actually reach here, since `assertCommandAllowed` is only ever called from
133
+ * the `exec` branch, never `write` (see `createSetupExecutionTool` below) —
134
+ * the empty `.env` template write this whole boundary exists to protect stays
135
+ * unaffected by construction, not merely by this check's shape.
136
+ *
137
+ * KNOWN GAPS (a command-text mitigation, not a shell parser — see the exec
138
+ * denylist's overall doc comment): shell indirection that never spells
139
+ * ".env" in the same command string (a variable populated from a
140
+ * base64/hex-decoded literal, or a wrapper script written in a PRIOR call
141
+ * that itself references .env); a reader not in `DOTENV_READER_COMMANDS`
142
+ * (`hexdump`, `wc`, a `python`/`node`/`perl` one-liner, `source .env`/`.
143
+ * .env` sourcing it into the shell's own environment then echoing a
144
+ * variable back); and a recursive/glob read that never names the file
145
+ * literally (`grep -r PASSWORD .`). None of these are caught here.
146
+ */
147
+ function assertCommandAllowed(command) {
148
+ if (DOTENV_READER_COMMANDS.test(command) && commandReferencesDotenvPath(command)) {
149
+ throw new SetupCommandDeniedError(command, "matches the dotenv-read denylist");
150
+ }
151
+ if (DESTRUCTIVE.test(command)) {
152
+ throw new SetupCommandDeniedError(command, "matches the destructive-command denylist");
153
+ }
154
+ if (REDIRECTION.test(command)) {
155
+ throw new SetupCommandDeniedError(command, "matches the shell-redirection denylist");
156
+ }
157
+ }
158
+ /**
159
+ * Redacts `stdout`/`stderr` from an exec result whenever `command`'s TEXT
160
+ * references a dotenv path — the persistence-side companion to
161
+ * `assertCommandAllowed`. Applied INSIDE this tool's own `execute`, not
162
+ * downstream in `harness/loop/executor.ts`, so there is exactly one choke
163
+ * point for this tool: the redacted shape is both what the model sees in the
164
+ * tool-loop AND what `executor.ts` later persists verbatim into
165
+ * `turns.trace_json` (it persists whatever this tool returns as
166
+ * `part.output`, unmodified — see `reportToolCallOutcomes`/
167
+ * `summarizeToolOutput`, which only bound length, not content). There is no
168
+ * second "does this look like a dotenv path" check downstream that could
169
+ * drift from this one.
170
+ *
171
+ * Deliberately UNPAIRED with `DOTENV_READER_COMMANDS` (unlike
172
+ * `assertCommandAllowed`'s check): every KNOWN reader is already denied
173
+ * outright before it runs, so a command only ever reaches this function
174
+ * having already executed — either because it doesn't reference a dotenv
175
+ * path at all (no-op), or because it references one via a reader NOT in
176
+ * `DOTENV_READER_COMMANDS` (the exact "KNOWN GAPS" listed on
177
+ * `assertCommandAllowed`: `hexdump`, `wc`, a `python`/`node`/`perl`
178
+ * one-liner, `source .env`/`. .env` shell-sourcing then echoing a variable,
179
+ * `grep -r` without naming the file literally). Redacting on the broader,
180
+ * unpaired check is defense in depth for exactly the readers the paired
181
+ * check in `assertCommandAllowed` can't enumerate.
182
+ *
183
+ * Deliberately NOT a blanket "omit all setup stdout": that would destroy the
184
+ * exact debuggability this defect (and others) were diagnosed with —
185
+ * reading real commands/outputs back out of `turns.trace_json` in SQLite.
186
+ * Scoping the redaction to commands whose text references a dotenv path
187
+ * keeps every other setup command's output intact for that diagnosis while
188
+ * closing the credential leak. `exitCode`/`notFound`/`timedOut`/
189
+ * `maxBufferExceeded` are left untouched too — only the two fields that can
190
+ * actually CONTAIN file contents are ever replaced, so "did the command
191
+ * succeed/timeout/get truncated" stays diagnosable even for a redacted call.
192
+ *
193
+ * KNOWN GAP: this only redacts the current call's OWN stdout/stderr. A
194
+ * command that reads `.env` indirectly and echoes its contents back through
195
+ * some other channel this tool doesn't see (e.g. a value smuggled into a
196
+ * LATER command's argument list, or into a file this tool then reads without
197
+ * the path itself mentioning "env") is not caught here — a mitigation on the
198
+ * common path, not an airtight seal.
199
+ */
200
+ function redactSetupExecutionOutput(command, result) {
201
+ if (!commandReferencesDotenvPath(command)) {
202
+ return result;
203
+ }
204
+ const REDACTED = "[redacted: command references a dotenv path]";
205
+ return {
206
+ stdout: result.stdout.length > 0 ? REDACTED : result.stdout,
207
+ stderr: result.stderr.length > 0 ? REDACTED : result.stderr,
208
+ };
209
+ }
210
+ /**
211
+ * Resolves `requestedPath` against `root` and rejects anything that escapes
212
+ * it, via `path.relative` — NEVER a bare `startsWith` (a root of `/x/a` must
213
+ * not admit a sibling `/x/a-evil`; `path.relative("/x/a", "/x/a-evil")` is
214
+ * `"../a-evil"`, which the `..`-prefix check below catches, whereas
215
+ * `"/x/a-evil".startsWith("/x/a")` would wrongly admit it). Mirrors
216
+ * `harness/route/artifact-content.ts`'s `escapesRoot` exactly.
217
+ */
218
+ function escapesRoot(root, target) {
219
+ const relative = path.relative(root, target);
220
+ return relative.startsWith("..") || path.isAbsolute(relative);
221
+ }
222
+ /**
223
+ * Realpath-resolves the nearest EXISTING ancestor of `target` and re-appends
224
+ * whatever tail components don't exist yet, rather than realpath-ing
225
+ * `target` itself — a write's target commonly doesn't exist yet, and
226
+ * `realpathSync` throws ENOENT on a nonexistent path. Only the existing
227
+ * ancestor chain can possibly BE a symlink, so resolving it (and leaving the
228
+ * not-yet-existing tail literal) is sufficient to catch a symlink escape
229
+ * without requiring the target to already exist. Mirrors
230
+ * `resolveArtifactContent`'s two-phase (nominal, then realpath) containment
231
+ * pattern in `harness/route/artifact-content.ts`, adapted for a write whose
232
+ * target is usually new.
233
+ */
234
+ function realpathNearestExisting(target) {
235
+ let candidate = target;
236
+ const tail = [];
237
+ while (!existsSync(candidate)) {
238
+ const parent = path.dirname(candidate);
239
+ if (parent === candidate)
240
+ break; // reached the filesystem root without finding anything existing
241
+ tail.unshift(path.basename(candidate));
242
+ candidate = parent;
243
+ }
244
+ const realAncestor = realpathSync(candidate);
245
+ return tail.length > 0 ? path.join(realAncestor, ...tail) : realAncestor;
246
+ }
247
+ /**
248
+ * Two-phase containment check shared by the `write` action's path scope and
249
+ * the `exec` action's optional `cwd` override (see {@link resolveExecCwd}):
250
+ * (1) a nominal, zero-filesystem-access `path.relative` check against
251
+ * `workspaceRoot`, which rejects an out-of-scope path (including the
252
+ * sibling-prefix case) before touching disk at all; (2) a realpath-based
253
+ * re-check (see {@link realpathNearestExisting}) that catches a symlink
254
+ * escape a nominal check alone would miss. Either phase failing throws
255
+ * whatever error `makeError` constructs, with zero side effects — nothing is
256
+ * written or spawned. Returns the resolved absolute path on success, so a
257
+ * caller that needs it (the exec cwd) doesn't have to resolve it a second
258
+ * time.
259
+ */
260
+ function assertWithinScope(workspaceRoot, requestedPath, makeError) {
261
+ const nominalTarget = path.isAbsolute(requestedPath) ? path.resolve(requestedPath) : path.resolve(workspaceRoot, requestedPath);
262
+ if (escapesRoot(workspaceRoot, nominalTarget)) {
263
+ throw makeError(requestedPath, workspaceRoot);
264
+ }
265
+ const realRoot = realpathNearestExisting(workspaceRoot);
266
+ const realTarget = realpathNearestExisting(nominalTarget);
267
+ if (escapesRoot(realRoot, realTarget)) {
268
+ throw makeError(requestedPath, workspaceRoot);
269
+ }
270
+ return nominalTarget;
271
+ }
272
+ /**
273
+ * Resolves a write target from the same optional cwd an exec can use. A
274
+ * relative path is based at cwd (or workspaceRoot when omitted); an absolute
275
+ * path remains absolute. Both cwd itself and the final target pass through
276
+ * the existing nominal + realpath workspace containment check, so adding a
277
+ * project-relative convenience cannot broaden the write scope.
278
+ */
279
+ function resolveWritePath(workspaceRoot, requestedPath, requestedCwd) {
280
+ const makeError = (requested, scope) => new SetupWriteScopeError(requested, scope);
281
+ const baseDir = requestedCwd === undefined ? workspaceRoot : assertWithinScope(workspaceRoot, requestedCwd, makeError);
282
+ const target = path.isAbsolute(requestedPath) ? requestedPath : path.resolve(baseDir, requestedPath);
283
+ return assertWithinScope(workspaceRoot, target, makeError);
284
+ }
285
+ /**
286
+ * Resolves the `exec` action's effective subprocess `cwd`: `workspaceRoot`
287
+ * itself when `requestedCwd` is unset (the unchanged default), otherwise
288
+ * `requestedCwd` run through the same two-phase containment check as the
289
+ * write path (see {@link assertWithinScope}), throwing
290
+ * {@link SetupExecCwdScopeError} on escape. A model can therefore narrow the
291
+ * exec cwd to any directory within `workspaceRoot` (e.g. the project
292
+ * directory), but never broaden it beyond that root.
293
+ */
294
+ function resolveExecCwd(workspaceRoot, requestedCwd) {
295
+ if (requestedCwd === undefined) {
296
+ return workspaceRoot;
297
+ }
298
+ return assertWithinScope(workspaceRoot, requestedCwd, (requestedCwd, scope) => new SetupExecCwdScopeError(requestedCwd, scope));
299
+ }
300
+ /**
301
+ * Builds the combined `setup_execution` tool: `action: "exec"` runs a shell
302
+ * command via `/bin/sh -c`, cwd defaulting to `workspaceRoot` but narrowable
303
+ * per-call via the optional `cwd` field (see {@link resolveExecCwd} — always
304
+ * contained within `workspaceRoot`, never able to escape it), broadened
305
+ * beyond a single fixed binary the way the dispatcher-side `setup_execution`
306
+ * guardrail broadens Bash — but still subject to the same denylist, checked
307
+ * first, never relaxed. `action: "write"` writes a file relative to the same
308
+ * optional cwd (workspaceRoot by default), scoped to `workspaceRoot` by the
309
+ * two-phase containment check above, then delegates the actual write to
310
+ * `env.writeFile` (which applies its OWN, nominal-only
311
+ * `policy.artifactWriteScope` check — this tool's pre-flight check is
312
+ * additional, not a replacement, since `env.writeFile` has no realpath
313
+ * re-check of its own; see `harness/exec/local.ts`).
314
+ */
315
+ export async function executeSetupExecution(input, options) {
316
+ const { env, policy, workspaceRoot } = options;
317
+ if (input.action === "exec") {
318
+ if (input.command === undefined) {
319
+ throw new SetupExecutionInputError("exec", "command");
320
+ }
321
+ assertCommandAllowed(input.command);
322
+ const cwd = resolveExecCwd(workspaceRoot, input.cwd);
323
+ const result = await env.exec({ mode: "write", command: "/bin/sh", args: ["-c", input.command], cwd, timeoutMs: SETUP_EXEC_TIMEOUT_MS }, policy);
324
+ const { stdout, stderr } = redactSetupExecutionOutput(input.command, result);
325
+ return {
326
+ exitCode: result.exitCode,
327
+ stdout,
328
+ stderr,
329
+ ...(result.notFound !== undefined ? { notFound: result.notFound } : {}),
330
+ ...(result.timedOut !== undefined ? { timedOut: result.timedOut } : {}),
331
+ ...(result.maxBufferExceeded !== undefined ? { maxBufferExceeded: result.maxBufferExceeded } : {}),
332
+ };
333
+ }
334
+ if (input.path === undefined)
335
+ throw new SetupExecutionInputError("write", "path");
336
+ if (input.content === undefined)
337
+ throw new SetupExecutionInputError("write", "content");
338
+ const writePath = resolveWritePath(workspaceRoot, input.path, input.cwd);
339
+ await env.writeFile(writePath, input.content, policy);
340
+ return { written: true, path: writePath, bytes: input.content.length };
341
+ }
342
+ export function createSetupExecutionTool(options) {
343
+ return tool({
344
+ description: "Run a shell command or write a file within the setup workspace, subject to a destructive/redirection " +
345
+ 'command denylist and a workspace-root scope. Two call shapes: {action: "exec", command, cwd?} runs a ' +
346
+ "shell command (command is required; cwd is optional, defaults to the workspace root, and must resolve " +
347
+ 'within it — path/content are unused); {action: "write", path, content, cwd?} writes a file (path and content ' +
348
+ "are both required; a relative path uses cwd when supplied, otherwise the workspace root; command is unused). A call missing the field(s) its action requires, or " +
349
+ "supplying a cwd/path that escapes the workspace root, is rejected before anything runs or is written.",
350
+ inputSchema: setupExecutionInputSchema,
351
+ execute: (input) => executeSetupExecution(input, options),
352
+ });
353
+ }
@@ -0,0 +1,10 @@
1
+ import type { ToolSet } from "ai";
2
+ import type { Agent } from "../bundle/schema.js";
3
+ import { type ResolveToolsContext } from "./resolve.js";
4
+ /**
5
+ * Thin wiring showing `resolveTools`'s output feeding a run: resolves
6
+ * `agent.tools[]` into a `ToolSet`, hands it to `fn` (typically a call into
7
+ * `executeAgent` from `../loop/index.js`), and always tears down whatever
8
+ * MCP clients were opened — even if `fn` throws.
9
+ */
10
+ export declare function withResolvedTools<T>(agent: Agent, ctx: ResolveToolsContext, fn: (tools: ToolSet) => Promise<T>): Promise<T>;
@@ -0,0 +1,16 @@
1
+ import { resolveTools } from "./resolve.js";
2
+ /**
3
+ * Thin wiring showing `resolveTools`'s output feeding a run: resolves
4
+ * `agent.tools[]` into a `ToolSet`, hands it to `fn` (typically a call into
5
+ * `executeAgent` from `../loop/index.js`), and always tears down whatever
6
+ * MCP clients were opened — even if `fn` throws.
7
+ */
8
+ export async function withResolvedTools(agent, ctx, fn) {
9
+ const resolved = await resolveTools(agent, ctx);
10
+ try {
11
+ return await fn(resolved.tools);
12
+ }
13
+ finally {
14
+ await resolved.close();
15
+ }
16
+ }
@@ -0,0 +1,31 @@
1
+ # setup capability-provider fragment (NATIVE realization) — supplies the genbi-setup profile's
2
+ # onboarding capabilities (`source_connect`, `context_build`) via the harness's scope-limited
3
+ # `setup_execution` native tool, for `warble dispatch --provider <this file> --target vercel`
4
+ # against the pre-committed `warble/genbi-setup/ir.golden.json`.
5
+ #
6
+ # Mirrors wren.provider.yaml's shape exactly. Unlike that fragment (which binds each capability to
7
+ # its own dedicated tool), both capabilities here share ONE native tool — `connect_source` and
8
+ # `build_context` are sibling halves of the same guarded-Bash-and-scoped-write lifecycle
9
+ # (`setup_execution`, see the two component.yml files), so a single tool with an
10
+ # exec/write action discriminator covers both rather than duplicating the guardrail plumbing twice.
11
+ fragment_version: "0.1"
12
+ provider: setup
13
+ engine: vercel
14
+ capabilities:
15
+ source_connect:
16
+ outcome: native
17
+ via: setup-execution
18
+ provided_by: runtime
19
+ criticality: required
20
+ context_build:
21
+ outcome: native
22
+ via: setup-execution
23
+ provided_by: runtime
24
+ criticality: required
25
+ tools:
26
+ source_connect:
27
+ name: setup_execution
28
+ source: native
29
+ context_build:
30
+ name: setup_execution
31
+ source: native
@@ -0,0 +1,33 @@
1
+ # wren capability-provider fragment (NATIVE realization) — supplies the genbi-default profile's
2
+ # analytical domain capabilities via the harness's native `wren` CLI tools (query, semantic
3
+ # introspection, dashboard build), for `warble dispatch --provider <this file> --target vercel`.
4
+ # Lives outside the warble repo — warble itself stays product-neutral / zero-wren.
5
+ fragment_version: "0.1"
6
+ provider: wren
7
+ engine: vercel
8
+ capabilities:
9
+ sql_execution:read_only:
10
+ outcome: native
11
+ via: wren-cli
12
+ provided_by: runtime
13
+ criticality: required
14
+ semantic_introspection:
15
+ outcome: realize-via
16
+ via: wren-cli
17
+ provided_by: runtime
18
+ criticality: required
19
+ genbi_build:
20
+ outcome: native
21
+ via: wren-cli
22
+ provided_by: runtime
23
+ criticality: required
24
+ tools:
25
+ sql_execution:read_only:
26
+ name: query
27
+ source: native
28
+ semantic_introspection:
29
+ name: semantic_introspect
30
+ source: native
31
+ genbi_build:
32
+ name: build_dashboard
33
+ source: native
@@ -0,0 +1,87 @@
1
+ import { type ProfileCandidate } from "./wren-profiles.js";
2
+ export type { ProfileCandidate };
3
+ export interface AdoptVerifyOk {
4
+ readonly status: "ok";
5
+ /** Whether `target/mdl.json` already exists — `false` routes the caller to `needs_decision(build_context)` instead of binding immediately. */
6
+ readonly hasMdl: boolean;
7
+ readonly sourceType: string;
8
+ }
9
+ export interface AdoptVerifyNeedsProfile {
10
+ readonly status: "needs_profile";
11
+ readonly sourceType: string;
12
+ /** At least one entry — zero candidates is reported as `AdoptVerifyError` instead. Ranked: project-dir-name match first, then the global `active` profile, then the rest. */
13
+ readonly candidates: readonly ProfileCandidate[];
14
+ }
15
+ export interface AdoptVerifyError {
16
+ readonly status: "error";
17
+ /** Names exactly which check failed — surfaced verbatim as `SetupAdoptResponse.message`. */
18
+ readonly message: string;
19
+ }
20
+ export type AdoptVerifyResult = AdoptVerifyOk | AdoptVerifyNeedsProfile | AdoptVerifyError;
21
+ /**
22
+ * Verifies an existing wren project directory is adoptable: exists, is a
23
+ * directory, has a `wren_project.yml` with a `data_source:` in
24
+ * `supportedSourceTypes`. If it also has a `profile:` pin, that profile's
25
+ * connection must pass `wren context validate`'s live smoke query. If it has
26
+ * no pin, looks for a compatible profile to propose (see the module doc
27
+ * comment's three-state contract). Never crashes the caller: a missing
28
+ * `wren` binary (ENOENT) is reported the same way as any other verification
29
+ * failure, not thrown.
30
+ */
31
+ export declare function verifyAdoptProject(projectPath: string, options: {
32
+ readonly supportedSourceTypes: ReadonlySet<string>;
33
+ }): Promise<AdoptVerifyResult>;
34
+ /**
35
+ * Writes a durable `profile:` (+ `data_source:`) pin into an adopted
36
+ * project's `wren_project.yml` by shelling out to `wren context set-profile
37
+ * <name>` in the project directory — reuses the CLI's own bind logic rather
38
+ * than hand-writing YAML here, so the pin ends up byte-for-byte what a user
39
+ * running the command themselves would get. Called by the adopt route when
40
+ * the caller resolves a `needs_profile` checkpoint by re-POSTing with a
41
+ * chosen `profile` name, before re-running `verifyAdoptProject` (which will
42
+ * now see the pin and proceed to the live smoke check).
43
+ */
44
+ export declare function runSetProfile(projectPath: string, profileName: string): Promise<{
45
+ ok: true;
46
+ } | {
47
+ ok: false;
48
+ message: string;
49
+ }>;
50
+ /**
51
+ * Confirms `profileName` is actually one of the candidates `verifyAdoptProject`
52
+ * would have offered for this project — i.e. it exists in `~/.wren/profiles.yml`
53
+ * and its `datasource:` matches the project's `data_source:`. The route must
54
+ * call this before `runSetProfile`: a client-supplied `profile` that skipped
55
+ * this check would let `wren context set-profile` durably rewrite
56
+ * `data_source:` to whatever the chosen profile declares (see
57
+ * `context_cli.py`'s `set-profile`), with no compatibility check of its own.
58
+ * Recomputes the candidate list from disk rather than trusting a list the
59
+ * caller might have seen in an earlier response, so a hand-crafted request
60
+ * naming an incompatible or nonexistent profile is rejected here with zero
61
+ * mutation.
62
+ */
63
+ export declare function validateChosenProfile(projectPath: string, profileName: string, options: {
64
+ readonly supportedSourceTypes: ReadonlySet<string>;
65
+ }): Promise<{
66
+ ok: true;
67
+ } | {
68
+ ok: false;
69
+ message: string;
70
+ }>;
71
+ /**
72
+ * Applies a client-chosen profile to an adopted project and re-verifies it,
73
+ * rolling back on any failure so the project never ends up durably worse off
74
+ * than before the call: `validateChosenProfile` rejects an incompatible or
75
+ * unknown profile before anything is written; if that passes but the
76
+ * subsequent `wren context validate` (inside `verifyAdoptProject`) still
77
+ * fails — dead host, bad credentials, a moved duckdb file — the pre-call
78
+ * `wren_project.yml` bytes are restored so the project falls back to its
79
+ * previous no-pin state. That matters because `save_project_config` drops
80
+ * YAML comments on rewrite, so the ORIGINAL bytes are restored rather than
81
+ * re-serialized, and because restoring to no-pin means the next adopt call
82
+ * naturally re-offers the candidate list (`needs_profile`) instead of
83
+ * re-validating a permanently broken pin forever.
84
+ */
85
+ export declare function adoptWithChosenProfile(projectPath: string, profileName: string, options: {
86
+ readonly supportedSourceTypes: ReadonlySet<string>;
87
+ }): Promise<AdoptVerifyResult>;