@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,175 @@
1
+ import type { EnrichmentBinding } from "./enrichment.js";
2
+ export { InteractiveLaunchError, legacyInteractiveWorkspace } from "./native-session-workspace.js";
3
+ import type { NativeSessionStateBase } from "./native-session-workspace.js";
4
+ export declare const INTERACTIVE_TARGETS: readonly ["claude-code:interactive", "codex:interactive"];
5
+ export type InteractiveTarget = (typeof INTERACTIVE_TARGETS)[number];
6
+ export interface InteractiveLaunchSpec {
7
+ readonly version: "1" | "2" | "3" | "4";
8
+ readonly target: InteractiveTarget;
9
+ readonly executable: "claude" | "codex";
10
+ readonly argv: readonly string[];
11
+ readonly cwd: string;
12
+ readonly artifact_root: string;
13
+ readonly handoff_path: string;
14
+ /** Host-attested absolute executable; never read from the producer file. */
15
+ readonly hostExecutable?: string;
16
+ /** Present only for native Setup after the BFF revalidates its sealed root. */
17
+ readonly bootstrap_root?: string;
18
+ }
19
+ export interface PtyProcess {
20
+ onData(listener: (data: string) => void): {
21
+ dispose(): void;
22
+ };
23
+ onExit(listener: (event: {
24
+ exitCode: number;
25
+ signal?: number;
26
+ }) => void): {
27
+ dispose(): void;
28
+ };
29
+ write(data: string): void;
30
+ resize(columns: number, rows: number): void;
31
+ kill(): void;
32
+ }
33
+ export interface PtyFactory {
34
+ spawn(file: string, args: readonly string[], options: {
35
+ cwd: string;
36
+ cols: number;
37
+ rows: number;
38
+ env?: NodeJS.ProcessEnv;
39
+ }): PtyProcess;
40
+ }
41
+ /**
42
+ * The embedded terminal is a color-capable product surface, not a reflection
43
+ * of a browser or caller preference. Keep this at the server/PTTY boundary so
44
+ * every native launch receives the same contract.
45
+ *
46
+ * `NO_COLOR` is deliberately removed. It is a useful convention for ordinary
47
+ * command output, but honoring an inherited value here would make our
48
+ * advertised xterm ANSI palette silently optional and allow a caller to
49
+ * weaken the native-terminal contract.
50
+ */
51
+ export declare function nativeTerminalEnvironment(base?: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
52
+ /**
53
+ * Mirrors the PATH lookup a shell-free PTY launch relies on. A shell alias or
54
+ * function is deliberately not accepted: node-pty cannot execute either.
55
+ */
56
+ export declare function interactiveExecutableAvailable(executable: "claude" | "codex", pathValue?: string | undefined): boolean;
57
+ export interface InteractiveTerminalSession {
58
+ readonly id: string;
59
+ readonly capability: string;
60
+ readonly target: InteractiveTarget;
61
+ readonly handoffPath: string;
62
+ readonly fallbackCommand: string;
63
+ claim(capability: string): boolean;
64
+ detach(): void;
65
+ close(): void;
66
+ write(data: string): void;
67
+ resize(columns: number, rows: number): void;
68
+ /**
69
+ * Subscribe to terminal output. The optional callback is called exactly once
70
+ * before this attachment's replay, and makes any retained-output truncation
71
+ * explicit to transports that can surface it to their users.
72
+ */
73
+ onData(listener: (data: string) => void, onReplay?: (metadata: TerminalReplayMetadata) => void): () => void;
74
+ onExit(listener: (exitCode: number) => void): () => void;
75
+ }
76
+ /** Public replay contract for a terminal attachment. */
77
+ export interface TerminalReplayMetadata {
78
+ /** True when output older than the retained tail was omitted. */
79
+ readonly truncated: boolean;
80
+ /** UTF-8 bytes available in this attachment's retained replay. */
81
+ readonly retainedBytes: number;
82
+ /** Hard upper bound for the process-local retained tail. */
83
+ readonly retentionLimitBytes: number;
84
+ }
85
+ /**
86
+ * The legacy Context terminal owns a short local lease. Durable native
87
+ * Sessions deliberately pass `NO_INTERACTIVE_TERMINAL_LEASES`: their service
88
+ * owns both the initial-attachment and post-claim lifecycle transitions.
89
+ */
90
+ export interface InteractiveTerminalLeasePolicy {
91
+ readonly initialAttachmentTimeoutMs?: number;
92
+ readonly postClaimDetachTimeoutMs?: number;
93
+ }
94
+ export declare const LEGACY_INTERACTIVE_TERMINAL_LEASES: InteractiveTerminalLeasePolicy;
95
+ export declare const NO_INTERACTIVE_TERMINAL_LEASES: InteractiveTerminalLeasePolicy;
96
+ /**
97
+ * Terminal output is process-local and deliberately bounded. Attachments get
98
+ * only this UTF-8 tail; `TerminalReplayMetadata.truncated` says when the
99
+ * beginning was omitted instead of silently presenting an incomplete log.
100
+ */
101
+ export declare const TERMINAL_OUTPUT_RETENTION_LIMIT_BYTES: number;
102
+ export interface PreparedInteractiveHandoff {
103
+ readonly target: InteractiveTarget;
104
+ readonly fallbackCommand: string;
105
+ }
106
+ export interface InteractiveTargetReadiness {
107
+ readonly copyAvailable: boolean;
108
+ readonly embeddedTerminalAvailable: boolean;
109
+ readonly copyReason?: string;
110
+ readonly embeddedTerminalReason?: string;
111
+ }
112
+ export declare function unavailableInteractiveReadiness(reason: string): Record<InteractiveTarget, InteractiveTargetReadiness>;
113
+ /** Clipboard-only text. It is never passed to a shell or subprocess. */
114
+ export declare function launchFallbackCommand(spec: Pick<InteractiveLaunchSpec, "cwd" | "executable" | "argv" | "hostExecutable">): string;
115
+ /** Parse exactly the public v1 producer contract; unknown fields fail closed. */
116
+ export declare function readInteractiveLaunchSpec(boundProject: string, requestedTarget: InteractiveTarget, materializationRoot?: string, materializationState?: NativeSessionStateBase): InteractiveLaunchSpec;
117
+ export declare function dispatchInteractiveArtifacts(input: {
118
+ readonly warbleBin: string;
119
+ readonly irPath: string;
120
+ readonly target: InteractiveTarget;
121
+ readonly cwd: string;
122
+ readonly boundProject?: string;
123
+ readonly materializationState?: NativeSessionStateBase;
124
+ }): Promise<void>;
125
+ export interface InteractivePreparationInput {
126
+ readonly target: InteractiveTarget;
127
+ readonly binding: EnrichmentBinding;
128
+ /** A BFF-owned external root, never browser-supplied. */
129
+ readonly artifactRoot?: string;
130
+ /** Required whenever artifactRoot is external to the bound project. */
131
+ readonly materializationState?: NativeSessionStateBase;
132
+ readonly materialize: () => Promise<void>;
133
+ }
134
+ export interface InteractivePreparationDeps {
135
+ readonly getCurrentBinding: () => EnrichmentBinding | undefined;
136
+ readonly executableAvailable?: (executable: "claude" | "codex") => boolean;
137
+ }
138
+ /**
139
+ * Materializes and validates the clipboard/launch contract without loading a
140
+ * PTY implementation or creating a process. This is deliberately shared by
141
+ * copy and embedded-terminal paths so their binding fence cannot drift.
142
+ */
143
+ export declare function prepareInteractiveHandoff(input: InteractivePreparationInput, deps: InteractivePreparationDeps): Promise<{
144
+ readonly spec: InteractiveLaunchSpec;
145
+ readonly handoff: PreparedInteractiveHandoff;
146
+ }>;
147
+ /**
148
+ * Evaluates copy and optional embedded-host capability separately per target.
149
+ *
150
+ * This must stay a pure probe: the producer owns a shared output root and can
151
+ * materialize only one target there at a time. Reading Context must never
152
+ * create, replace, or inspect target artifacts, because doing so would make
153
+ * one target's availability depend on whichever target readiness happened to
154
+ * probe first. Artifact materialization remains exclusively in the selected
155
+ * Copy or embedded Launch action via `prepareInteractiveHandoff`.
156
+ */
157
+ export declare function getInteractiveTerminalReadiness(input: {
158
+ readonly getCurrentBinding: () => EnrichmentBinding | undefined;
159
+ readonly terminalHostAvailable: () => Promise<boolean>;
160
+ readonly executableAvailable?: (executable: "claude" | "codex") => boolean;
161
+ /** BFF-owned base; validation is non-mutating and never reads the project root. */
162
+ readonly materializationState?: NativeSessionStateBase;
163
+ }): Promise<Record<InteractiveTarget, InteractiveTargetReadiness>>;
164
+ export declare class InteractiveTerminalManager {
165
+ private readonly pty;
166
+ private readonly sessions;
167
+ constructor(pty: PtyFactory);
168
+ start(spec: InteractiveLaunchSpec, identity?: {
169
+ readonly id: string;
170
+ readonly capability: string;
171
+ }, env?: NodeJS.ProcessEnv, leasePolicy?: InteractiveTerminalLeasePolicy): InteractiveTerminalSession;
172
+ get(id: string): InteractiveTerminalSession | undefined;
173
+ /** Runtime changes revoke every non-durable compatibility terminal immediately. */
174
+ closeAll(): void;
175
+ }
@@ -0,0 +1,336 @@
1
+ /**
2
+ * Native interactive enrichment is intentionally a very small boundary:
3
+ * Warble materializes a versioned launch file and the BFF validates it before
4
+ * a PTY starts a vendor-owned TUI. Nothing supplied by a browser becomes a
5
+ * command, argument, cwd, prompt, or environment value.
6
+ */
7
+ import { randomUUID } from "node:crypto";
8
+ import { existsSync, realpathSync, readFileSync, statSync } from "node:fs";
9
+ import path from "node:path";
10
+ import { spawn as spawnProcess } from "node:child_process";
11
+ export { InteractiveLaunchError, legacyInteractiveWorkspace } from "./native-session-workspace.js";
12
+ import { InteractiveLaunchError, nativeSessionStateBaseAvailable, validateLegacyInteractiveWorkspace } from "./native-session-workspace.js";
13
+ export const INTERACTIVE_TARGETS = ["claude-code:interactive", "codex:interactive"];
14
+ const EXECUTABLE = {
15
+ "claude-code:interactive": "claude",
16
+ "codex:interactive": "codex",
17
+ };
18
+ /**
19
+ * The embedded terminal is a color-capable product surface, not a reflection
20
+ * of a browser or caller preference. Keep this at the server/PTTY boundary so
21
+ * every native launch receives the same contract.
22
+ *
23
+ * `NO_COLOR` is deliberately removed. It is a useful convention for ordinary
24
+ * command output, but honoring an inherited value here would make our
25
+ * advertised xterm ANSI palette silently optional and allow a caller to
26
+ * weaken the native-terminal contract.
27
+ */
28
+ export function nativeTerminalEnvironment(base = process.env) {
29
+ const { NO_COLOR: _noColor, ...inherited } = base;
30
+ return { ...inherited, TERM: "xterm-256color", COLORTERM: "truecolor" };
31
+ }
32
+ /**
33
+ * Mirrors the PATH lookup a shell-free PTY launch relies on. A shell alias or
34
+ * function is deliberately not accepted: node-pty cannot execute either.
35
+ */
36
+ export function interactiveExecutableAvailable(executable, pathValue = process.env["PATH"]) {
37
+ if (!pathValue)
38
+ return false;
39
+ return pathValue.split(path.delimiter).some((directory) => {
40
+ if (!directory)
41
+ return false;
42
+ try {
43
+ const candidate = path.join(directory, executable);
44
+ const stat = statSync(candidate);
45
+ return stat.isFile() && (stat.mode & 0o111) !== 0;
46
+ }
47
+ catch {
48
+ return false;
49
+ }
50
+ });
51
+ }
52
+ export const LEGACY_INTERACTIVE_TERMINAL_LEASES = {
53
+ initialAttachmentTimeoutMs: 15_000,
54
+ postClaimDetachTimeoutMs: 15_000,
55
+ };
56
+ export const NO_INTERACTIVE_TERMINAL_LEASES = {};
57
+ const TERMINAL_EXIT_RETENTION_MS = 15_000;
58
+ /**
59
+ * Terminal output is process-local and deliberately bounded. Attachments get
60
+ * only this UTF-8 tail; `TerminalReplayMetadata.truncated` says when the
61
+ * beginning was omitted instead of silently presenting an incomplete log.
62
+ */
63
+ export const TERMINAL_OUTPUT_RETENTION_LIMIT_BYTES = 64 * 1024;
64
+ export function unavailableInteractiveReadiness(reason) {
65
+ return Object.fromEntries(INTERACTIVE_TARGETS.map((target) => [target, { copyAvailable: false, embeddedTerminalAvailable: false, copyReason: reason, embeddedTerminalReason: reason }]));
66
+ }
67
+ /** Clipboard-only text. It is never passed to a shell or subprocess. */
68
+ export function launchFallbackCommand(spec) {
69
+ const quote = (value) => `'${value.replace(/'/g, `'"'"'`)}'`;
70
+ const executable = spec.hostExecutable ? quote(spec.hostExecutable) : spec.executable;
71
+ return `cd -- ${quote(spec.cwd)} && ${executable}${spec.argv.map((arg) => ` ${quote(arg)}`).join("")}`;
72
+ }
73
+ function record(value) {
74
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : undefined;
75
+ }
76
+ function isTarget(value) {
77
+ return typeof value === "string" && INTERACTIVE_TARGETS.includes(value);
78
+ }
79
+ function contained(root, candidate) {
80
+ const relative = path.relative(root, candidate);
81
+ return relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative));
82
+ }
83
+ /** Parse exactly the public v1 producer contract; unknown fields fail closed. */
84
+ export function readInteractiveLaunchSpec(boundProject, requestedTarget, materializationRoot, materializationState) {
85
+ const canonicalProject = realpathSync(boundProject);
86
+ const canonicalArtifactRoot = materializationRoot === undefined
87
+ ? canonicalProject
88
+ : materializationState === undefined
89
+ ? realpathSync(materializationRoot)
90
+ : validateLegacyInteractiveWorkspace(materializationState, canonicalProject, materializationRoot);
91
+ if (materializationState === undefined && canonicalArtifactRoot !== canonicalProject)
92
+ throw new InteractiveLaunchError("interactive enrichment launch paths are outside the bound project");
93
+ const specPath = path.join(canonicalArtifactRoot, ".warble", "interactive-launch.json");
94
+ if (!existsSync(specPath))
95
+ throw new InteractiveLaunchError("interactive enrichment artifacts are not available yet");
96
+ let raw;
97
+ try {
98
+ raw = JSON.parse(readFileSync(specPath, "utf8"));
99
+ }
100
+ catch {
101
+ throw new InteractiveLaunchError("interactive enrichment launch specification is invalid");
102
+ }
103
+ const value = record(raw);
104
+ const expectedKeys = ["version", "target", "executable", "argv", "cwd", "artifact_root", "handoff_path"];
105
+ if (!value || Object.keys(value).length !== expectedKeys.length || !expectedKeys.every((key) => Object.hasOwn(value, key))) {
106
+ throw new InteractiveLaunchError("interactive enrichment launch specification is invalid");
107
+ }
108
+ if (value.version !== "1" || !isTarget(value.target) || value.target !== requestedTarget || value.executable !== EXECUTABLE[requestedTarget] || !Array.isArray(value.argv) || value.argv.length !== 0 || typeof value.cwd !== "string" || typeof value.artifact_root !== "string" || typeof value.handoff_path !== "string") {
109
+ throw new InteractiveLaunchError("interactive enrichment launch specification is incompatible");
110
+ }
111
+ let cwd;
112
+ let artifactRoot;
113
+ let handoffPath;
114
+ try {
115
+ cwd = realpathSync(value.cwd);
116
+ artifactRoot = realpathSync(value.artifact_root);
117
+ handoffPath = realpathSync(value.handoff_path);
118
+ }
119
+ catch {
120
+ throw new InteractiveLaunchError("interactive enrichment launch paths are unavailable");
121
+ }
122
+ if (cwd !== canonicalArtifactRoot || artifactRoot !== canonicalArtifactRoot || !contained(artifactRoot, handoffPath)) {
123
+ throw new InteractiveLaunchError("interactive enrichment launch paths are outside the bound project");
124
+ }
125
+ return { version: "1", target: requestedTarget, executable: EXECUTABLE[requestedTarget], argv: [], cwd, artifact_root: artifactRoot, handoff_path: handoffPath };
126
+ }
127
+ export async function dispatchInteractiveArtifacts(input) {
128
+ // Structured hardcoded argv only. In particular, never append browser or
129
+ // proposal text to this invocation and never use a shell.
130
+ if ((input.boundProject === undefined) !== (input.materializationState === undefined))
131
+ throw new InteractiveLaunchError("interactive enrichment materialization failed");
132
+ if (input.boundProject && input.materializationState)
133
+ validateLegacyInteractiveWorkspace(input.materializationState, input.boundProject, input.cwd);
134
+ await new Promise((resolve, reject) => {
135
+ const child = spawnProcess(input.warbleBin, ["dispatch", input.irPath, "--target", input.target, "--out", input.cwd], { cwd: input.cwd, shell: false, stdio: "ignore" });
136
+ child.once("error", () => reject(new InteractiveLaunchError("interactive enrichment materialization failed")));
137
+ child.once("exit", (code) => code === 0 ? resolve() : reject(new InteractiveLaunchError("interactive enrichment materialization failed")));
138
+ });
139
+ }
140
+ /**
141
+ * Materializes and validates the clipboard/launch contract without loading a
142
+ * PTY implementation or creating a process. This is deliberately shared by
143
+ * copy and embedded-terminal paths so their binding fence cannot drift.
144
+ */
145
+ export async function prepareInteractiveHandoff(input, deps) {
146
+ const executableAvailable = deps.executableAvailable ?? interactiveExecutableAvailable;
147
+ if (!executableAvailable(EXECUTABLE[input.target])) {
148
+ throw new InteractiveLaunchError(`the ${EXECUTABLE[input.target]} interactive CLI is not available on this machine`);
149
+ }
150
+ await input.materialize();
151
+ const current = deps.getCurrentBinding();
152
+ if (!current || current.path !== input.binding.path || current.identity !== input.binding.identity || current.generation !== input.binding.generation || current.revision !== input.binding.revision) {
153
+ throw new InteractiveLaunchError("interactive enrichment is stale because the bound project changed");
154
+ }
155
+ const spec = readInteractiveLaunchSpec(input.binding.path, input.target, input.artifactRoot, input.materializationState);
156
+ return { spec, handoff: { target: spec.target, fallbackCommand: launchFallbackCommand(spec) } };
157
+ }
158
+ /**
159
+ * Evaluates copy and optional embedded-host capability separately per target.
160
+ *
161
+ * This must stay a pure probe: the producer owns a shared output root and can
162
+ * materialize only one target there at a time. Reading Context must never
163
+ * create, replace, or inspect target artifacts, because doing so would make
164
+ * one target's availability depend on whichever target readiness happened to
165
+ * probe first. Artifact materialization remains exclusively in the selected
166
+ * Copy or embedded Launch action via `prepareInteractiveHandoff`.
167
+ */
168
+ export async function getInteractiveTerminalReadiness(input) {
169
+ const binding = input.getCurrentBinding();
170
+ if (!binding)
171
+ return unavailableInteractiveReadiness("interactive enrichment requires a current bound project");
172
+ if (input.materializationState && !nativeSessionStateBaseAvailable(input.materializationState))
173
+ return unavailableInteractiveReadiness("interactive enrichment workspace is unavailable");
174
+ const hostAvailable = await input.terminalHostAvailable();
175
+ const executableAvailable = input.executableAvailable ?? interactiveExecutableAvailable;
176
+ const result = {};
177
+ for (const target of INTERACTIVE_TARGETS) {
178
+ if (!executableAvailable(EXECUTABLE[target])) {
179
+ const reason = `the ${EXECUTABLE[target]} interactive CLI is not available on this machine`;
180
+ result[target] = { copyAvailable: false, embeddedTerminalAvailable: false, copyReason: reason, embeddedTerminalReason: reason };
181
+ continue;
182
+ }
183
+ result[target] = hostAvailable
184
+ ? { copyAvailable: true, embeddedTerminalAvailable: true }
185
+ : { copyAvailable: true, embeddedTerminalAvailable: false, embeddedTerminalReason: "interactive terminal host cannot spawn local processes on this machine" };
186
+ }
187
+ return result;
188
+ }
189
+ export class InteractiveTerminalManager {
190
+ pty;
191
+ sessions = new Map();
192
+ constructor(pty) {
193
+ this.pty = pty;
194
+ }
195
+ start(spec, identity, env, leasePolicy = LEGACY_INTERACTIVE_TERMINAL_LEASES) {
196
+ const process = this.pty.spawn(spec.hostExecutable ?? spec.executable, spec.argv, {
197
+ cwd: spec.cwd,
198
+ cols: 100,
199
+ rows: 30,
200
+ env: nativeTerminalEnvironment(env),
201
+ });
202
+ const dataListeners = new Set();
203
+ const exitListeners = new Set();
204
+ const id = identity?.id ?? randomUUID();
205
+ const capability = identity?.capability ?? randomUUID();
206
+ let claimed = false;
207
+ let exited;
208
+ let buffer = "";
209
+ let outputTruncated = false;
210
+ let leaseTimer;
211
+ const clearLease = () => {
212
+ if (leaseTimer)
213
+ clearTimeout(leaseTimer);
214
+ leaseTimer = undefined;
215
+ };
216
+ const close = () => {
217
+ clearLease();
218
+ try {
219
+ if (exited === undefined)
220
+ process.kill();
221
+ }
222
+ finally {
223
+ this.sessions.delete(id);
224
+ }
225
+ };
226
+ const armLease = (timeoutMs) => {
227
+ clearLease();
228
+ if (timeoutMs !== undefined)
229
+ leaseTimer = setTimeout(close, timeoutMs);
230
+ };
231
+ const session = {
232
+ id, capability, target: spec.target, handoffPath: spec.handoff_path, fallbackCommand: launchFallbackCommand(spec),
233
+ claim: (presented) => {
234
+ if (presented !== capability || claimed)
235
+ return false;
236
+ claimed = true;
237
+ clearLease();
238
+ return true;
239
+ },
240
+ detach: () => {
241
+ if (!claimed || exited !== undefined)
242
+ return;
243
+ claimed = false;
244
+ armLease(leasePolicy.postClaimDetachTimeoutMs);
245
+ },
246
+ close,
247
+ write: (data) => process.write(data),
248
+ resize: (columns, rows) => process.resize(columns, rows),
249
+ onData: (listener) => { dataListeners.add(listener); return () => dataListeners.delete(listener); },
250
+ onExit: (listener) => { exitListeners.add(listener); return () => exitListeners.delete(listener); },
251
+ };
252
+ process.onData((data) => {
253
+ const next = `${buffer}${data}`;
254
+ const retained = retainTerminalOutput(next);
255
+ if (retained.length !== next.length)
256
+ outputTruncated = true;
257
+ buffer = retained;
258
+ dataListeners.forEach((listener) => listener(data));
259
+ });
260
+ process.onExit(({ exitCode }) => { exited = exitCode; exitListeners.forEach((listener) => listener(exitCode)); clearLease(); setTimeout(() => this.sessions.delete(id), TERMINAL_EXIT_RETENTION_MS); });
261
+ this.sessions.set(session.id, session);
262
+ // A legacy PTY must not survive if the browser never completes its WS
263
+ // upgrade. Native Sessions opt out: their durable service records the
264
+ // timeout state and revokes browser/MCP/recovery capabilities atomically.
265
+ armLease(leasePolicy.initialAttachmentTimeoutMs);
266
+ // An attachment receives one replay followed by live data. Register its
267
+ // live listener before invoking either callback, then queue any
268
+ // synchronous re-entrant data until the replay has completed. This closes
269
+ // the snapshot/listener gap without duplicating output on reconnect.
270
+ const originalOnData = session.onData;
271
+ const originalOnExit = session.onExit;
272
+ session.onData = (listener, onReplay) => {
273
+ const replay = buffer;
274
+ const metadata = {
275
+ truncated: outputTruncated,
276
+ retainedBytes: Buffer.byteLength(replay, "utf8"),
277
+ retentionLimitBytes: TERMINAL_OUTPUT_RETENTION_LIMIT_BYTES,
278
+ };
279
+ const queued = [];
280
+ let nextQueued = 0;
281
+ let replaying = true;
282
+ const remove = originalOnData((data) => {
283
+ if (replaying)
284
+ queued.push(data);
285
+ else
286
+ listener(data);
287
+ });
288
+ try {
289
+ onReplay?.(metadata);
290
+ if (replay)
291
+ listener(replay);
292
+ // Keep replay mode active until the queue is empty. A transport
293
+ // callback can synchronously cause more PTY output; index-based
294
+ // draining consumes those additions after the already queued bytes.
295
+ while (nextQueued < queued.length)
296
+ listener(queued[nextQueued++]);
297
+ }
298
+ finally {
299
+ replaying = false;
300
+ queued.length = 0;
301
+ }
302
+ return remove;
303
+ };
304
+ session.onExit = (listener) => { if (exited !== undefined)
305
+ listener(exited); return originalOnExit(listener); };
306
+ return session;
307
+ }
308
+ get(id) { return this.sessions.get(id); }
309
+ /** Runtime changes revoke every non-durable compatibility terminal immediately. */
310
+ closeAll() {
311
+ for (const session of [...this.sessions.values()])
312
+ session.close();
313
+ }
314
+ }
315
+ /** Retain a UTF-8-safe tail without splitting a Unicode code point. */
316
+ function retainTerminalOutput(value) {
317
+ if (Buffer.byteLength(value, "utf8") <= TERMINAL_OUTPUT_RETENTION_LIMIT_BYTES)
318
+ return value;
319
+ let retainedBytes = 0;
320
+ let start = value.length;
321
+ while (start > 0) {
322
+ let next = start - 1;
323
+ const code = value.charCodeAt(next);
324
+ if (code >= 0xdc00 && code <= 0xdfff && next > 0) {
325
+ const previous = value.charCodeAt(next - 1);
326
+ if (previous >= 0xd800 && previous <= 0xdbff)
327
+ next -= 1;
328
+ }
329
+ const bytes = Buffer.byteLength(value.slice(next, start), "utf8");
330
+ if (retainedBytes + bytes > TERMINAL_OUTPUT_RETENTION_LIMIT_BYTES)
331
+ break;
332
+ retainedBytes += bytes;
333
+ start = next;
334
+ }
335
+ return value.slice(start);
336
+ }