@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,6 @@
1
+ /** Drain every owned resource and report cleanup failures without process details. */
2
+ export declare function shutdownNativeResources(options: {
3
+ shutdownSessions(): Promise<void>;
4
+ closeTerminals(): void;
5
+ closeServer(): void;
6
+ }): Promise<0 | 1>;
@@ -0,0 +1,28 @@
1
+ /** Drain every owned resource and report cleanup failures without process details. */
2
+ export async function shutdownNativeResources(options) {
3
+ let failed = false;
4
+ // Calling the session fence before other cleanup closes new admission immediately.
5
+ let cleanup = Promise.resolve();
6
+ try {
7
+ cleanup = options.shutdownSessions();
8
+ }
9
+ catch {
10
+ failed = true;
11
+ }
12
+ // Attach the rejection observer before synchronous cleanup can throw.
13
+ const settled = cleanup.catch(() => { failed = true; });
14
+ try {
15
+ options.closeTerminals();
16
+ }
17
+ catch {
18
+ failed = true;
19
+ }
20
+ try {
21
+ options.closeServer();
22
+ }
23
+ catch {
24
+ failed = true;
25
+ }
26
+ await settled;
27
+ return failed ? 1 : 0;
28
+ }
@@ -0,0 +1,18 @@
1
+ import type { NativeWrenRuntime } from "./native-wren-runtime.js";
2
+ export interface CodexWrenHome {
3
+ readonly home: string;
4
+ readonly dataRoots: readonly string[];
5
+ readonly active?: () => boolean;
6
+ readonly assertActive?: () => void;
7
+ readonly cleanup?: () => void;
8
+ }
9
+ /** Setup has no bound profile yet, but still receives a fresh private WREN_HOME. */
10
+ export declare function createEmptyCodexWrenHome(cwd: string): CodexWrenHome;
11
+ export declare function materializeCodexWrenHome(input: {
12
+ readonly runtime: NativeWrenRuntime;
13
+ readonly projectPath: string;
14
+ readonly cwd: string;
15
+ readonly sourceWrenHome: string;
16
+ readonly home: string;
17
+ readonly toolDirectories: readonly string[];
18
+ }): CodexWrenHome;
@@ -0,0 +1,262 @@
1
+ import { execFileSync } from "node:child_process";
2
+ import { createHash } from "node:crypto";
3
+ import { chmodSync, existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, realpathSync, rmSync, writeFileSync } from "node:fs";
4
+ import path from "node:path";
5
+ import { buildNativeChildEnvironment, nativeProcessEnvironment } from "./native-runtime-spec.js";
6
+ import { InteractiveLaunchError } from "./native-session-workspace.js";
7
+ const MATERIALIZE_WREN_HOME_SCRIPT = String.raw `
8
+ import json
9
+ import os
10
+ import pathlib
11
+ import re
12
+ import sys
13
+ import yaml
14
+ from dotenv import dotenv_values
15
+ from wren.profile import resolve_profile_for_project
16
+
17
+ project = pathlib.Path(sys.argv[1]).resolve(strict=True)
18
+ destination = pathlib.Path(sys.argv[2])
19
+ source_home = pathlib.Path(sys.argv[3]).resolve(strict=True)
20
+
21
+ def regular(path, required=True):
22
+ if not path.exists():
23
+ if required:
24
+ raise SystemExit(2)
25
+ return None
26
+ if path.is_symlink() or not path.is_file() or path.resolve(strict=True) != path:
27
+ raise SystemExit(3)
28
+ return path
29
+
30
+ regular(project / "wren_project.yml")
31
+ regular(source_home / "profiles.yml")
32
+ name, profile = resolve_profile_for_project(project, strict=True)
33
+ if not name or not isinstance(profile, dict):
34
+ raise SystemExit(4)
35
+
36
+ pattern = re.compile(r"\$\{([_A-Z][_A-Z0-9]*)\}")
37
+ keys = set()
38
+ def collect(value):
39
+ if isinstance(value, str):
40
+ keys.update(pattern.findall(value))
41
+ elif isinstance(value, dict):
42
+ for item in value.values(): collect(item)
43
+ elif isinstance(value, list):
44
+ for item in value: collect(item)
45
+ collect(profile)
46
+
47
+ project_env_path = regular(project / ".env", required=False)
48
+ home_env_path = regular(source_home / ".env", required=False)
49
+ project_values = dotenv_values(project_env_path) if project_env_path else {}
50
+ home_values = dotenv_values(home_env_path) if home_env_path else {}
51
+ selected = {}
52
+ for key in sorted(keys):
53
+ value = project_values.get(key)
54
+ if value is None:
55
+ value = home_values.get(key)
56
+ if value is None:
57
+ raise SystemExit(5)
58
+ selected[key] = str(value)
59
+
60
+ def expand(value):
61
+ if isinstance(value, str):
62
+ return pattern.sub(lambda match: selected[match.group(1)], value)
63
+ if isinstance(value, dict):
64
+ return {key: expand(item) for key, item in value.items()}
65
+ if isinstance(value, list):
66
+ return [expand(item) for item in value]
67
+ return value
68
+
69
+ destination.mkdir(mode=0o700, parents=False, exist_ok=False)
70
+ os.chmod(destination, 0o700)
71
+ def private_write(path, content):
72
+ descriptor = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
73
+ with os.fdopen(descriptor, "w", encoding="utf-8") as output:
74
+ output.write(content)
75
+ os.chmod(path, 0o600)
76
+
77
+ profile_path = destination / "profiles.yml"
78
+ private_write(profile_path, yaml.safe_dump({"active": name, "profiles": {name: profile}}, sort_keys=False))
79
+ if selected:
80
+ session_env = destination / ".env"
81
+ private_write(session_env, "".join(f"{key}={json.dumps(value)}\n" for key, value in selected.items()))
82
+
83
+ expanded = expand(profile)
84
+ data_source = expanded.get("datasource")
85
+ data_root = None
86
+ if data_source in ("duckdb", "local_file"):
87
+ data_root = expanded.get("url")
88
+ elif data_source == "datafusion":
89
+ data_root = expanded.get("source")
90
+ if data_root:
91
+ root = pathlib.Path(data_root).expanduser()
92
+ if not root.is_absolute():
93
+ root = project / root
94
+ print(root.resolve(strict=True))
95
+ `;
96
+ function unavailable() {
97
+ throw new InteractiveLaunchError("native Wren session home is unavailable");
98
+ }
99
+ function privateDirectoryIdentity(directory) {
100
+ try {
101
+ const metadata = lstatSync(directory);
102
+ const canonical = realpathSync(directory);
103
+ if (!metadata.isDirectory() || metadata.isSymbolicLink() || canonical !== directory || (metadata.mode & 0o777) !== 0o700)
104
+ return unavailable();
105
+ return { canonical, dev: metadata.dev, ino: metadata.ino, mode: metadata.mode & 0o777 };
106
+ }
107
+ catch (error) {
108
+ if (error instanceof InteractiveLaunchError)
109
+ throw error;
110
+ return unavailable();
111
+ }
112
+ }
113
+ function privateFileIdentity(file) {
114
+ try {
115
+ const metadata = lstatSync(file);
116
+ const canonical = realpathSync(file);
117
+ if (!metadata.isFile() || metadata.isSymbolicLink() || canonical !== file || (metadata.mode & 0o777) !== 0o600)
118
+ return unavailable();
119
+ return {
120
+ canonical,
121
+ dev: metadata.dev,
122
+ ino: metadata.ino,
123
+ mode: metadata.mode & 0o777,
124
+ size: metadata.size,
125
+ mtimeMs: metadata.mtimeMs,
126
+ ctimeMs: metadata.ctimeMs,
127
+ digest: createHash("sha256").update(readFileSync(file)).digest("hex"),
128
+ };
129
+ }
130
+ catch (error) {
131
+ if (error instanceof InteractiveLaunchError)
132
+ throw error;
133
+ return unavailable();
134
+ }
135
+ }
136
+ function samePathIdentity(left, right) {
137
+ return left.canonical === right.canonical && left.dev === right.dev && left.ino === right.ino && left.mode === right.mode;
138
+ }
139
+ function sameFileIdentity(left, right) {
140
+ return samePathIdentity(left, right)
141
+ && left.size === right.size
142
+ && left.mtimeMs === right.mtimeMs
143
+ && left.ctimeMs === right.ctimeMs
144
+ && left.digest === right.digest;
145
+ }
146
+ function captureWrenHome(destination, fileNames) {
147
+ try {
148
+ const actualNames = readdirSync(destination).sort();
149
+ const expectedNames = [...fileNames].sort();
150
+ if (JSON.stringify(actualNames) !== JSON.stringify(expectedNames))
151
+ return unavailable();
152
+ return {
153
+ directory: privateDirectoryIdentity(destination),
154
+ files: Object.freeze(Object.fromEntries(expectedNames.map((name) => [name, privateFileIdentity(path.join(destination, name))]))),
155
+ };
156
+ }
157
+ catch (error) {
158
+ if (error instanceof InteractiveLaunchError)
159
+ throw error;
160
+ return unavailable();
161
+ }
162
+ }
163
+ function assertWrenHome(destination, expected) {
164
+ const current = captureWrenHome(destination, Object.keys(expected.files));
165
+ if (!samePathIdentity(current.directory, expected.directory))
166
+ return unavailable();
167
+ for (const [name, identity] of Object.entries(expected.files)) {
168
+ const observed = current.files[name];
169
+ if (!observed || !sameFileIdentity(observed, identity))
170
+ return unavailable();
171
+ }
172
+ }
173
+ function retirementMarker(cwd) {
174
+ return path.join(cwd, ".wren-retired");
175
+ }
176
+ function retire(destination, marker) {
177
+ rmSync(destination, { recursive: true, force: true });
178
+ writeFileSync(marker, "retired\n", { encoding: "utf8", mode: 0o600, flag: "wx" });
179
+ privateFileIdentity(marker);
180
+ }
181
+ /** Setup has no bound profile yet, but still receives a fresh private WREN_HOME. */
182
+ export function createEmptyCodexWrenHome(cwd) {
183
+ const destination = path.join(cwd, ".wren");
184
+ const marker = retirementMarker(cwd);
185
+ let active = false;
186
+ try {
187
+ if (existsSync(marker))
188
+ throw new Error("retired");
189
+ mkdirSync(destination, { mode: 0o700 });
190
+ chmodSync(destination, 0o700);
191
+ const identity = captureWrenHome(destination, []);
192
+ active = true;
193
+ return Object.freeze({
194
+ home: destination,
195
+ dataRoots: Object.freeze([]),
196
+ active: () => active,
197
+ assertActive: () => {
198
+ if (!active)
199
+ return unavailable();
200
+ assertWrenHome(destination, identity);
201
+ },
202
+ cleanup: () => {
203
+ if (!active)
204
+ return;
205
+ active = false;
206
+ retire(destination, marker);
207
+ },
208
+ });
209
+ }
210
+ catch {
211
+ rmSync(destination, { recursive: true, force: true });
212
+ throw new InteractiveLaunchError("native Wren session home is unavailable");
213
+ }
214
+ }
215
+ export function materializeCodexWrenHome(input) {
216
+ const destination = path.join(input.cwd, ".wren");
217
+ const marker = retirementMarker(input.cwd);
218
+ let active = false;
219
+ try {
220
+ if (existsSync(marker))
221
+ throw new Error("retired");
222
+ const environment = buildNativeChildEnvironment({
223
+ toolDirectories: input.toolDirectories,
224
+ home: input.home,
225
+ projectPath: input.projectPath,
226
+ wrenHome: input.sourceWrenHome,
227
+ python: true,
228
+ });
229
+ const output = execFileSync(input.runtime.venv_python, ["-c", MATERIALIZE_WREN_HOME_SCRIPT, input.projectPath, destination, input.sourceWrenHome], {
230
+ cwd: input.projectPath,
231
+ env: nativeProcessEnvironment(environment),
232
+ encoding: "utf8",
233
+ stdio: ["ignore", "pipe", "ignore"],
234
+ timeout: 10_000,
235
+ });
236
+ const envFile = path.join(destination, ".env");
237
+ const identity = captureWrenHome(destination, ["profiles.yml", ...(existsSync(envFile) ? [".env"] : [])]);
238
+ active = true;
239
+ const assertActive = () => {
240
+ if (!active)
241
+ return unavailable();
242
+ assertWrenHome(destination, identity);
243
+ };
244
+ return Object.freeze({
245
+ home: destination,
246
+ dataRoots: Object.freeze(output.trim() ? [output.trim()] : []),
247
+ active: () => active,
248
+ assertActive,
249
+ cleanup: () => {
250
+ if (!active)
251
+ return;
252
+ active = false;
253
+ retire(destination, marker);
254
+ },
255
+ });
256
+ }
257
+ catch {
258
+ active = false;
259
+ rmSync(destination, { recursive: true, force: true });
260
+ throw new InteractiveLaunchError("native Wren project profile is unavailable");
261
+ }
262
+ }
@@ -1,4 +1,5 @@
1
1
  import { type PtyFactory, type PtyProcess } from "./interactive-terminal.js";
2
+ import { type NativeChildEnvironment } from "./native-runtime-spec.js";
2
3
  interface NodePtyModule {
3
4
  spawn(file: string, args: string[], options: {
4
5
  cwd: string;
@@ -10,7 +11,7 @@ interface NodePtyModule {
10
11
  }
11
12
  export interface PtyHostOptions {
12
13
  readonly cwd?: string;
13
- readonly env?: NodeJS.ProcessEnv;
14
+ readonly env?: NodeJS.ProcessEnv | NativeChildEnvironment;
14
15
  readonly probeExecutable?: string;
15
16
  readonly probeTimeoutMs?: number;
16
17
  }
@@ -1,6 +1,7 @@
1
1
  import { closeSync, constants, fchmodSync, fstatSync, lstatSync, openSync } from "node:fs";
2
2
  import path from "node:path";
3
3
  import { nativeTerminalEnvironment } from "./interactive-terminal.js";
4
+ import { nativeProcessEnvironment } from "./native-runtime-spec.js";
4
5
  /**
5
6
  * node-pty 1.1 ships a small spawn-helper for prebuilt macOS binaries. Some
6
7
  * package managers restore that regular file without its executable mode,
@@ -62,7 +63,7 @@ export async function createProbedPtyFactory(nodePty, options = {}) {
62
63
  cols: 80,
63
64
  rows: 24,
64
65
  name: "xterm-256color",
65
- env,
66
+ env: nativeProcessEnvironment(env),
66
67
  });
67
68
  }
68
69
  catch {
@@ -94,7 +95,7 @@ export async function createProbedPtyFactory(nodePty, options = {}) {
94
95
  name: "xterm-256color",
95
96
  // Defend this lower adapter too: future PTY callers cannot bypass the
96
97
  // server-owned native-terminal color contract by supplying an env.
97
- env: nativeTerminalEnvironment(spawnOptions.env ?? env),
98
+ env: nativeProcessEnvironment(nativeTerminalEnvironment(spawnOptions.env ?? env)),
98
99
  }),
99
100
  };
100
101
  }
@@ -0,0 +1,139 @@
1
+ import type { Store, WarbleProfileAdmissionStatus, WarbleProfileEntryKind, WarbleProfileKind, WarbleProfileRole, WarbleProfileRow } from "./db.js";
2
+ import type { WarbleProfileDto } from "./wire-types.js";
3
+ export type { WarbleProfileAdmissionStatus, WarbleProfileEntryKind, WarbleProfileKind, WarbleProfileRole, WarbleProfileRow };
4
+ /** The shipped profiles and the role each plays. Anything else under `profiles/` is ignored. */
5
+ export declare const BUILTIN_PROFILE_ROLES: Readonly<Record<string, WarbleProfileRole>>;
6
+ /** The profile whose compiled IR defines the host ceiling. */
7
+ export declare const CEILING_PROFILE_ID = "genbi-default";
8
+ /** The persisted runtime tier rows come from the analysis profile and name exactly these two tiers. */
9
+ export declare const HOST_TIERS: ReadonlySet<string>;
10
+ /**
11
+ * Bump when an admission rule is added, tightened or withdrawn. A stored verdict computed under an
12
+ * older rule set is stale even when the profile bytes and the warble binary are unchanged — the
13
+ * rules are the third input to the verdict. 1: rules 1–6. 2: a pinned entry must not compose.
14
+ * 3: that rule withdrawn (the dispatcher refuses every composing profile without a host, so it
15
+ * never separated anything); composition is recorded on the component summary instead.
16
+ */
17
+ export declare const ADMISSION_RULES_VERSION = 3;
18
+ export declare class ProfileRegistryError extends Error {
19
+ readonly status: 400 | 404 | 409 | 503;
20
+ constructor(status: 400 | 404 | 409 | 503, message: string);
21
+ }
22
+ export interface HostCeiling {
23
+ readonly capabilities: ReadonlySet<string>;
24
+ readonly tiers: ReadonlySet<string>;
25
+ }
26
+ export interface AdmissionComponentSummary {
27
+ readonly id: string;
28
+ readonly type: string;
29
+ readonly trigger: string;
30
+ readonly outcome: string;
31
+ readonly realizationKind: string;
32
+ readonly entrypoint: boolean;
33
+ readonly tiers: readonly string[];
34
+ readonly capabilities: readonly string[];
35
+ readonly hasDescription: boolean;
36
+ readonly nativeEligible: boolean;
37
+ /** `alias → component` edges this component's steps authorize; non-empty means it composes. */
38
+ readonly composes: readonly string[];
39
+ }
40
+ export interface AdmissionVerdict {
41
+ readonly status: WarbleProfileAdmissionStatus;
42
+ /** Every failed rule, in rule order; empty when admitted. */
43
+ readonly reasons: readonly string[];
44
+ readonly entry?: {
45
+ readonly kind: WarbleProfileEntryKind;
46
+ readonly verb?: string;
47
+ };
48
+ readonly irVersion?: string;
49
+ readonly components: readonly AdmissionComponentSummary[];
50
+ }
51
+ /**
52
+ * The capability ceiling is whatever the shipped analysis profile already requires, read from its
53
+ * compiled IR. Listing the names by hand would let the ceiling drift from the profile it describes.
54
+ */
55
+ export declare function deriveHostCeiling(ceilingIr: unknown): HostCeiling;
56
+ /** Rules 2–5 over a compiled IR. Pure: rule 1 (compiling) and rule 6 (staleness) live in the registry. */
57
+ export declare function admitCompiledProfile(ir: unknown, options: {
58
+ readonly expectedId: string;
59
+ readonly ceiling: HostCeiling;
60
+ }): AdmissionVerdict;
61
+ /** The `profile:` scalar of a profile.yml. Flat YAML, read the same way `composeUserProfile` does. */
62
+ export declare function readProfileId(profileYamlText: string): string | undefined;
63
+ export type SourcePathCheck = {
64
+ readonly ok: true;
65
+ readonly resolved: string;
66
+ readonly real: string;
67
+ } | {
68
+ readonly ok: false;
69
+ readonly message: string;
70
+ };
71
+ /**
72
+ * A user profile is handed to the server as a directory path on this machine, the same way the
73
+ * adopt flow receives a project path. The path is checked, not trusted: it must be absolute, be a
74
+ * real directory rather than a link, hold a `profile.yml`, and sit outside the roots the registry
75
+ * itself owns, so a request can never register the package tree or the user-data tree as "new".
76
+ */
77
+ export declare function validateUserProfileSourcePath(sourcePath: unknown, options: {
78
+ readonly forbiddenRoots: readonly string[];
79
+ }): SourcePathCheck;
80
+ export declare function toWarbleProfileDto(row: WarbleProfileRow): WarbleProfileDto;
81
+ export interface ProfileRegistryOptions {
82
+ readonly store: Store;
83
+ /** The package's own `profiles/` tree (the directory the analysis profile source lives in). */
84
+ readonly builtinProfilesDir: string;
85
+ /** Where user-added profiles are copied: `<workspaceRoot>/profiles`. Created on first add. */
86
+ readonly userProfilesDir: string;
87
+ /** Rule 1: compile a raw profile directory with the pinned warble. Throws on failure. */
88
+ readonly compileRaw: (profileSource: string) => Promise<{
89
+ readonly irPath: string;
90
+ }>;
91
+ /** Identity of the warble binary admission ran against; part of the staleness key (rule 6). */
92
+ readonly warbleIdentity: () => Promise<string>;
93
+ readonly now?: () => Date;
94
+ }
95
+ export declare class ProfileRegistry {
96
+ private readonly options;
97
+ private ready;
98
+ private ceiling;
99
+ private ceilingFailure;
100
+ private readonly now;
101
+ constructor(options: ProfileRegistryOptions);
102
+ /** Registers the shipped profiles once and re-checks stored rows whose inputs changed. Idempotent. */
103
+ ensureReady(): Promise<void>;
104
+ private initialize;
105
+ private registerBuiltins;
106
+ private registerBuiltin;
107
+ private readmitIfStale;
108
+ /**
109
+ * Runs rules 1–5 for one directory and persists the verdict. The ceiling profile's own verdict
110
+ * also fixes the ceiling; while it fails to compile no conversation profile can be admitted,
111
+ * and each row says so rather than silently inheriting an empty ceiling.
112
+ */
113
+ private admitAndStore;
114
+ /**
115
+ * `warble compile` re-derives its fetched Hub cache on every run and documents that two
116
+ * concurrent runs can collide on the rename, failing one of them loudly and transiently. A BFF
117
+ * compiles for readiness and sessions while this registry is admitting, so one bounded retry
118
+ * keeps a shipped profile from being recorded as unavailable over that collision. A profile that
119
+ * fails twice is recorded with the second failure.
120
+ */
121
+ private compileOnceWithRetry;
122
+ list(): Promise<readonly WarbleProfileRow[]>;
123
+ get(id: string): Promise<WarbleProfileRow | undefined>;
124
+ /**
125
+ * Copies a profile directory from `sourcePath` into the user-data tree and admits it. The copy is
126
+ * what the registry owns from then on; the original is never read again.
127
+ */
128
+ add(sourcePath: unknown): Promise<WarbleProfileRow>;
129
+ /** Removes a user profile and its copy. A profile any native session has run inside stays. */
130
+ remove(id: string): Promise<void>;
131
+ /**
132
+ * The directory a conversation session compiles from, for an id the browser selected. Refuses
133
+ * anything that is not an admitted conversation profile, with the stored reason.
134
+ */
135
+ resolveConversationProfileSource(id: string): Promise<{
136
+ readonly sourceDir: string;
137
+ readonly row: WarbleProfileRow;
138
+ }>;
139
+ }