@bubblebrain-ai/bubble 0.0.34 → 0.0.36

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 (168) hide show
  1. package/README.md +27 -7
  2. package/dist/agent/abort-errors.d.ts +2 -2
  3. package/dist/agent/budget-ledger.d.ts +4 -45
  4. package/dist/agent/budget-ledger.js +5 -75
  5. package/dist/agent/categories.d.ts +66 -2
  6. package/dist/agent/categories.js +120 -19
  7. package/dist/agent/child-runner.d.ts +1 -3
  8. package/dist/agent/child-runner.js +6 -37
  9. package/dist/agent/discovery-barrier.js +1 -1
  10. package/dist/agent/profiles.d.ts +1 -2
  11. package/dist/agent/profiles.js +25 -14
  12. package/dist/agent/routing-catalog.d.ts +113 -0
  13. package/dist/agent/routing-catalog.js +240 -0
  14. package/dist/agent/subagent-control.d.ts +5 -12
  15. package/dist/agent/subagent-control.js +0 -2
  16. package/dist/agent/subagent-store.d.ts +1 -1
  17. package/dist/agent/subagent-store.js +1 -3
  18. package/dist/agent/workflow/control.d.ts +21 -0
  19. package/dist/agent/workflow/control.js +50 -8
  20. package/dist/agent/workflow/runtime.d.ts +15 -2
  21. package/dist/agent/workflow/runtime.js +112 -30
  22. package/dist/agent.d.ts +82 -48
  23. package/dist/agent.js +360 -232
  24. package/dist/approval/controller.js +28 -0
  25. package/dist/approval/tool-helper.js +2 -0
  26. package/dist/approval/types.d.ts +21 -1
  27. package/dist/cli.d.ts +1 -1
  28. package/dist/cli.js +2 -1
  29. package/dist/config.d.ts +5 -3
  30. package/dist/config.js +10 -5
  31. package/dist/context/compact-llm.d.ts +2 -2
  32. package/dist/external-runtime/grok-acp.d.ts +56 -0
  33. package/dist/external-runtime/grok-acp.js +386 -0
  34. package/dist/external-runtime/grok-binary.d.ts +25 -0
  35. package/dist/external-runtime/grok-binary.js +108 -0
  36. package/dist/external-runtime/grok-errors.d.ts +7 -0
  37. package/dist/external-runtime/grok-errors.js +15 -0
  38. package/dist/external-runtime/grok-input-policy.d.ts +58 -0
  39. package/dist/external-runtime/grok-input-policy.js +52 -0
  40. package/dist/external-runtime/grok-ndjson.d.ts +14 -0
  41. package/dist/external-runtime/grok-ndjson.js +193 -0
  42. package/dist/external-runtime/grok-network.d.ts +15 -0
  43. package/dist/external-runtime/grok-network.js +81 -0
  44. package/dist/external-runtime/grok-preflight.d.ts +28 -0
  45. package/dist/external-runtime/grok-preflight.js +194 -0
  46. package/dist/external-runtime/grok-process.d.ts +26 -0
  47. package/dist/external-runtime/grok-process.js +184 -0
  48. package/dist/external-runtime/grok-profile.d.ts +32 -0
  49. package/dist/external-runtime/grok-profile.js +419 -0
  50. package/dist/external-runtime/grok-provider.d.ts +31 -0
  51. package/dist/external-runtime/grok-provider.js +41 -0
  52. package/dist/external-runtime/grok-runtime.d.ts +91 -0
  53. package/dist/external-runtime/grok-runtime.js +882 -0
  54. package/dist/external-runtime/index.d.ts +13 -0
  55. package/dist/external-runtime/index.js +13 -0
  56. package/dist/external-runtime/session-policy.d.ts +11 -0
  57. package/dist/external-runtime/session-policy.js +30 -0
  58. package/dist/external-runtime/types.d.ts +65 -0
  59. package/dist/external-runtime/types.js +1 -0
  60. package/dist/feishu/agent-host/approval-card.js +21 -0
  61. package/dist/feishu/agent-host/run-driver.js +19 -3
  62. package/dist/feishu/serve.js +1 -0
  63. package/dist/goal/tools.d.ts +6 -3
  64. package/dist/goal/tools.js +7 -19
  65. package/dist/main.js +114 -28
  66. package/dist/memory/db.d.ts +2 -0
  67. package/dist/memory/db.js +10 -0
  68. package/dist/memory/index.d.ts +1 -0
  69. package/dist/memory/index.js +1 -0
  70. package/dist/memory/phase1.js +14 -2
  71. package/dist/memory/phase2.js +21 -2
  72. package/dist/memory/prompts.js +1 -1
  73. package/dist/memory/session-policy.d.ts +25 -0
  74. package/dist/memory/session-policy.js +55 -0
  75. package/dist/memory/storage.d.ts +1 -0
  76. package/dist/memory/storage.js +13 -0
  77. package/dist/model-catalog.d.ts +18 -0
  78. package/dist/model-catalog.js +92 -52
  79. package/dist/model-config.d.ts +3 -1
  80. package/dist/model-config.js +11 -0
  81. package/dist/oauth/grok.d.ts +25 -0
  82. package/dist/oauth/grok.js +218 -0
  83. package/dist/oauth/index.d.ts +1 -0
  84. package/dist/oauth/index.js +1 -0
  85. package/dist/oauth/storage.d.ts +9 -0
  86. package/dist/oauth/storage.js +25 -0
  87. package/dist/orchestrator/default-hooks.js +23 -7
  88. package/dist/prompt/compose.d.ts +3 -0
  89. package/dist/prompt/compose.js +3 -0
  90. package/dist/prompt/delegation.d.ts +1 -1
  91. package/dist/prompt/delegation.js +41 -8
  92. package/dist/prompt/environment.js +0 -4
  93. package/dist/prompt/reminders.d.ts +0 -1
  94. package/dist/prompt/reminders.js +1 -11
  95. package/dist/prompt/routing.d.ts +14 -0
  96. package/dist/prompt/routing.js +71 -0
  97. package/dist/prompt/runtime.js +1 -1
  98. package/dist/prompt/task-reminders.d.ts +20 -0
  99. package/dist/prompt/task-reminders.js +73 -1
  100. package/dist/provider-ai-sdk.js +2 -2
  101. package/dist/provider-grok.d.ts +26 -0
  102. package/dist/provider-grok.js +59 -0
  103. package/dist/provider-openai-codex.d.ts +17 -1
  104. package/dist/provider-openai-codex.js +101 -29
  105. package/dist/provider-registry.d.ts +72 -0
  106. package/dist/provider-registry.js +336 -59
  107. package/dist/provider-transform.d.ts +1 -1
  108. package/dist/provider-transform.js +35 -17
  109. package/dist/provider.d.ts +3 -0
  110. package/dist/provider.js +14 -0
  111. package/dist/session-types.d.ts +19 -1
  112. package/dist/session.d.ts +1 -0
  113. package/dist/session.js +4 -0
  114. package/dist/slash-commands/commands.js +224 -14
  115. package/dist/slash-commands/types.d.ts +14 -1
  116. package/dist/system-prompt.d.ts +2 -0
  117. package/dist/tools/agent-lifecycle.d.ts +1 -9
  118. package/dist/tools/agent-lifecycle.js +42 -272
  119. package/dist/tools/bash.js +1 -1
  120. package/dist/tools/child-tools.js +2 -0
  121. package/dist/tools/index.d.ts +2 -2
  122. package/dist/tools/index.js +7 -5
  123. package/dist/tools/lsp.js +1 -0
  124. package/dist/tools/memory.d.ts +7 -2
  125. package/dist/tools/memory.js +28 -43
  126. package/dist/tools/server.js +4 -0
  127. package/dist/tools/tool-search.d.ts +1 -1
  128. package/dist/tools/tool-search.js +23 -3
  129. package/dist/tui/model-switch.d.ts +7 -0
  130. package/dist/tui/model-switch.js +15 -5
  131. package/dist/tui-ink/app.d.ts +3 -1
  132. package/dist/tui-ink/app.js +672 -243
  133. package/dist/tui-ink/approval/approval-dialog.js +37 -1
  134. package/dist/tui-ink/approval/diff-view.js +4 -4
  135. package/dist/tui-ink/approval/select.js +2 -2
  136. package/dist/tui-ink/composer-buffer.d.ts +55 -0
  137. package/dist/tui-ink/composer-buffer.js +317 -0
  138. package/dist/tui-ink/feedback-dialog.js +2 -2
  139. package/dist/tui-ink/feishu-setup-picker.js +3 -3
  140. package/dist/tui-ink/footer.d.ts +1 -0
  141. package/dist/tui-ink/footer.js +7 -2
  142. package/dist/tui-ink/input-box.d.ts +36 -7
  143. package/dist/tui-ink/input-box.js +229 -157
  144. package/dist/tui-ink/markdown.js +11 -9
  145. package/dist/tui-ink/message-list.d.ts +5 -1
  146. package/dist/tui-ink/message-list.js +15 -2
  147. package/dist/tui-ink/model-picker.d.ts +61 -4
  148. package/dist/tui-ink/model-picker.js +464 -128
  149. package/dist/tui-ink/paste-operation-tracker.d.ts +13 -0
  150. package/dist/tui-ink/paste-operation-tracker.js +25 -0
  151. package/dist/tui-ink/plan-confirm.js +1 -1
  152. package/dist/tui-ink/run.d.ts +3 -0
  153. package/dist/tui-ink/run.js +1 -1
  154. package/dist/tui-ink/subagent-view.d.ts +34 -1
  155. package/dist/tui-ink/subagent-view.js +82 -1
  156. package/dist/tui-ink/theme.d.ts +19 -2
  157. package/dist/tui-ink/theme.js +25 -8
  158. package/dist/tui-ink/welcome.d.ts +3 -2
  159. package/dist/tui-ink/welcome.js +10 -6
  160. package/dist/types.d.ts +8 -31
  161. package/dist/types.js +1 -1
  162. package/dist/variant/thinking-level.d.ts +2 -2
  163. package/dist/variant/thinking-level.js +2 -1
  164. package/dist/variant/variant-resolver.d.ts +22 -0
  165. package/dist/variant/variant-resolver.js +43 -0
  166. package/package.json +6 -2
  167. package/dist/tools/task.d.ts +0 -2
  168. package/dist/tools/task.js +0 -58
@@ -0,0 +1,419 @@
1
+ import { constants as fsConstants, realpathSync, statSync } from "node:fs";
2
+ import { chmod, lstat, mkdir, open, readFile, realpath, rm, stat } from "node:fs/promises";
3
+ import { dirname, join, resolve, sep } from "node:path";
4
+ import { getBubbleHome } from "../bubble-home.js";
5
+ import { GrokRuntimeError } from "./grok-errors.js";
6
+ const DIRECTORY_MODE = 0o700;
7
+ const FILE_MODE = 0o600;
8
+ // Skills the pinned Grok CLI bundles and can materialize on its own. They are
9
+ // disabled by name because the runtime re-creates their files at startup.
10
+ const GROK_BUNDLED_SKILLS = [
11
+ "code-review",
12
+ "xlsx",
13
+ "create-skill",
14
+ "pptx",
15
+ "check-work",
16
+ "docx",
17
+ "imagine",
18
+ "help",
19
+ ];
20
+ // `[skills] disabled` entries are written into TOML basic strings. Only accept
21
+ // names that cannot alter the document structure or smuggle escapes.
22
+ const SAFE_GROK_SKILL_NAME = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
23
+ export function isSafeGrokSkillName(name) {
24
+ return SAFE_GROK_SKILL_NAME.test(name);
25
+ }
26
+ function grokDisabledSkillsLine(extraDisabledSkills) {
27
+ for (const name of extraDisabledSkills) {
28
+ if (!isSafeGrokSkillName(name)) {
29
+ throw new GrokRuntimeError("profile_unsafe", "A discovered Grok skill name cannot be disabled safely.");
30
+ }
31
+ }
32
+ const disabled = [...new Set([...GROK_BUNDLED_SKILLS, ...extraDisabledSkills])].sort();
33
+ return `disabled = [${disabled.map((name) => JSON.stringify(name)).join(", ")}]`;
34
+ }
35
+ // Keep credentials and runtime configuration isolated while allowing the
36
+ // official Grok runtime to work inside the user's selected workspace. Tool
37
+ // execution is still gated by ACP permission requests handled by Bubble.
38
+ export function buildGrokConfigToml(extraDisabledSkills = []) {
39
+ return `[cli]
40
+ auto_update = false
41
+
42
+ [session]
43
+ load_envrc = false
44
+
45
+ [sandbox]
46
+ profile = "strict"
47
+ auto_allow_bash = false
48
+
49
+ [features]
50
+ web_fetch = false
51
+ write_file = true
52
+ tool_search = false
53
+ lsp_tools = false
54
+
55
+ [subagents]
56
+ enabled = false
57
+
58
+ [memory]
59
+ enabled = false
60
+
61
+ [skills]
62
+ ${grokDisabledSkillsLine(extraDisabledSkills)}
63
+
64
+ [compat.cursor]
65
+ skills = false
66
+ rules = false
67
+ agents = false
68
+ mcps = false
69
+ hooks = false
70
+
71
+ [compat.claude]
72
+ skills = false
73
+ rules = false
74
+ agents = false
75
+ mcps = false
76
+ hooks = false
77
+
78
+ # Grok's Claude permission compatibility is independent from the extension
79
+ # compatibility switches above. Mark the one-time import as complete so the
80
+ # runtime never reads workspace .claude/settings*.json permission rules.
81
+ [claude_compat]
82
+ imported = true
83
+ `;
84
+ }
85
+ export function buildGrokRequirementsToml(extraDisabledSkills = []) {
86
+ return `[cli]
87
+ auto_update = false
88
+
89
+ [session]
90
+ load_envrc = false
91
+
92
+ [sandbox]
93
+ profile = "strict"
94
+ auto_allow_bash = false
95
+
96
+ [features]
97
+ web_fetch = false
98
+ write_file = true
99
+ tool_search = false
100
+ lsp_tools = false
101
+
102
+ [subagents]
103
+ enabled = false
104
+
105
+ [memory]
106
+ enabled = false
107
+
108
+ [skills]
109
+ ${grokDisabledSkillsLine(extraDisabledSkills)}
110
+
111
+ [compat.cursor]
112
+ skills = false
113
+ rules = false
114
+ agents = false
115
+ mcps = false
116
+ hooks = false
117
+
118
+ [compat.claude]
119
+ skills = false
120
+ rules = false
121
+ agents = false
122
+ mcps = false
123
+ hooks = false
124
+
125
+ [claude_compat]
126
+ imported = true
127
+ `;
128
+ }
129
+ export const GROK_CONFIG_TOML = buildGrokConfigToml();
130
+ export const GROK_REQUIREMENTS_TOML = buildGrokRequirementsToml();
131
+ export function getGrokProfile(bubbleHome = getBubbleHome()) {
132
+ const root = resolve(bubbleHome, "runtimes", "grok");
133
+ const grokHome = join(root, "grok-home");
134
+ return {
135
+ root,
136
+ home: join(root, "home"),
137
+ grokHome,
138
+ tmp: join(root, "tmp"),
139
+ workspace: join(root, "workspace"),
140
+ configPath: join(grokHome, "config.toml"),
141
+ requirementsPath: join(grokHome, "requirements.toml"),
142
+ lockPath: join(root, "runtime.lock"),
143
+ };
144
+ }
145
+ async function ensurePrivateDirectory(path, uid, repairMode = true) {
146
+ let created = false;
147
+ try {
148
+ const before = await lstat(path);
149
+ if (!before.isDirectory() || before.isSymbolicLink() || before.uid !== uid) {
150
+ throw new GrokRuntimeError("profile_unsafe", "The isolated Grok profile contains an unsafe path.");
151
+ }
152
+ }
153
+ catch (error) {
154
+ if (error.code !== "ENOENT")
155
+ throw error;
156
+ await mkdir(path, { recursive: true, mode: DIRECTORY_MODE });
157
+ created = true;
158
+ }
159
+ if (created || repairMode)
160
+ await chmod(path, DIRECTORY_MODE);
161
+ const after = await stat(path);
162
+ if (!after.isDirectory() || after.uid !== uid || (after.mode & 0o077) !== 0) {
163
+ throw new GrokRuntimeError("profile_unsafe", "The isolated Grok profile must be private to the current user.");
164
+ }
165
+ }
166
+ /**
167
+ * Establish only the private directory needed to create runtime.lock. Existing
168
+ * profile content is validated but never chmodded or rewritten here, because
169
+ * another Bubble process may already own the lock and be using that profile.
170
+ */
171
+ export async function prepareGrokProfileLockRoot(profile = getGrokProfile(), uid = process.getuid?.()) {
172
+ if (uid === undefined) {
173
+ throw new GrokRuntimeError("unsupported_platform", "The Grok runtime requires a POSIX user account.");
174
+ }
175
+ await ensurePrivateDirectory(dirname(profile.root), uid, false);
176
+ await ensurePrivateDirectory(profile.root, uid, false);
177
+ return profile;
178
+ }
179
+ async function writePrivateFile(path, contents, uid) {
180
+ try {
181
+ const existing = await lstat(path);
182
+ if (!existing.isFile() || existing.isSymbolicLink() || existing.uid !== uid) {
183
+ throw new GrokRuntimeError("profile_unsafe", "The isolated Grok profile contains an unsafe configuration file.");
184
+ }
185
+ }
186
+ catch (error) {
187
+ if (error.code !== "ENOENT")
188
+ throw error;
189
+ }
190
+ const handle = await open(path, fsConstants.O_WRONLY | fsConstants.O_CREAT | fsConstants.O_TRUNC | fsConstants.O_NOFOLLOW, FILE_MODE);
191
+ try {
192
+ await handle.writeFile(contents, { encoding: "utf8" });
193
+ }
194
+ finally {
195
+ await handle.close();
196
+ }
197
+ await chmod(path, FILE_MODE);
198
+ const metadata = await stat(path);
199
+ if (!metadata.isFile() || metadata.uid !== uid || (metadata.mode & 0o077) !== 0) {
200
+ throw new GrokRuntimeError("profile_unsafe", "The isolated Grok configuration must be private to the current user.");
201
+ }
202
+ }
203
+ export async function prepareGrokProfile(profile = getGrokProfile(), uid = process.getuid?.(), extraDisabledSkills = []) {
204
+ if (uid === undefined) {
205
+ throw new GrokRuntimeError("unsupported_platform", "The Grok runtime requires a POSIX user account.");
206
+ }
207
+ await ensurePrivateDirectory(dirname(profile.root), uid);
208
+ await ensurePrivateDirectory(profile.root, uid);
209
+ for (const path of [profile.home, profile.grokHome, profile.tmp, profile.workspace]) {
210
+ await ensurePrivateDirectory(path, uid);
211
+ }
212
+ const canonicalRoot = await realpath(profile.root);
213
+ for (const path of [profile.home, profile.grokHome, profile.tmp, profile.workspace]) {
214
+ const canonical = await realpath(path);
215
+ if (canonical !== canonicalRoot && !canonical.startsWith(`${canonicalRoot}${sep}`)) {
216
+ throw new GrokRuntimeError("profile_unsafe", "The isolated Grok profile escaped its private root.");
217
+ }
218
+ }
219
+ await writePrivateFile(profile.configPath, buildGrokConfigToml(extraDisabledSkills), uid);
220
+ await writePrivateFile(profile.requirementsPath, buildGrokRequirementsToml(extraDisabledSkills), uid);
221
+ return profile;
222
+ }
223
+ export function buildGrokChildEnv(profile, parentEnv = process.env, proxy) {
224
+ const env = {
225
+ HOME: profile.home,
226
+ GROK_HOME: profile.grokHome,
227
+ TMPDIR: profile.tmp,
228
+ PATH: "/usr/bin:/bin:/usr/sbin:/sbin",
229
+ GROK_DISABLE_AUTOUPDATER: "1",
230
+ GROK_SANDBOX: "strict",
231
+ GROK_SANDBOX_AUTO_ALLOW_BASH: "0",
232
+ GROK_MEMORY: "0",
233
+ GROK_SUBAGENTS: "0",
234
+ GROK_WEB_FETCH: "0",
235
+ GROK_WRITE_FILE: "1",
236
+ GROK_TOOL_SEARCH: "0",
237
+ GROK_LSP_TOOLS: "0",
238
+ GROK_CURSOR_SKILLS_ENABLED: "0",
239
+ GROK_CURSOR_RULES_ENABLED: "0",
240
+ GROK_CURSOR_AGENTS_ENABLED: "0",
241
+ GROK_CURSOR_MCPS_ENABLED: "0",
242
+ GROK_CURSOR_HOOKS_ENABLED: "0",
243
+ GROK_CLAUDE_SKILLS_ENABLED: "0",
244
+ GROK_CLAUDE_RULES_ENABLED: "0",
245
+ GROK_CLAUDE_AGENTS_ENABLED: "0",
246
+ GROK_CLAUDE_MCPS_ENABLED: "0",
247
+ GROK_CLAUDE_HOOKS_ENABLED: "0",
248
+ // Pinned Grok 0.2.93 has a separate Claude permission compatibility gate.
249
+ // Keep its exact process override as defense in depth for every child.
250
+ _GROK_CLAUDE_MARKER_OVERRIDE: "1",
251
+ GROK_CRASH_HANDLER: "0",
252
+ };
253
+ const uid = process.getuid?.();
254
+ const safePath = parentEnv.PATH?.split(":").filter((entry) => {
255
+ if (!entry.startsWith("/") || /[\r\n\0]/.test(entry))
256
+ return false;
257
+ try {
258
+ const canonical = realpathSync(entry);
259
+ const metadata = statSync(canonical);
260
+ return metadata.isDirectory()
261
+ && (uid === undefined || metadata.uid === 0 || metadata.uid === uid)
262
+ && (metadata.mode & 0o022) === 0;
263
+ }
264
+ catch {
265
+ return false;
266
+ }
267
+ });
268
+ if (safePath?.length)
269
+ env.PATH = [...new Set(safePath)].join(":");
270
+ for (const name of ["LANG", "LC_ALL", "TERM", "COLORTERM", "NO_COLOR"]) {
271
+ const value = parentEnv[name];
272
+ if (value && !/[\r\n\0]/.test(value))
273
+ env[name] = value;
274
+ }
275
+ if (proxy) {
276
+ env.HTTPS_PROXY = proxy;
277
+ env.ALL_PROXY = proxy;
278
+ }
279
+ return env;
280
+ }
281
+ export async function acquireGrokProfileLock(profile) {
282
+ const uid = process.getuid?.();
283
+ const openNewLock = async () => await open(profile.lockPath, "wx", FILE_MODE);
284
+ let handle;
285
+ try {
286
+ handle = await openNewLock();
287
+ }
288
+ catch (error) {
289
+ if (error.code !== "EEXIST")
290
+ throw error;
291
+ const metadata = await lstat(profile.lockPath);
292
+ if (!metadata.isFile() || metadata.isSymbolicLink() || uid === undefined || metadata.uid !== uid || (metadata.mode & 0o077) !== 0) {
293
+ throw new GrokRuntimeError("profile_unsafe", "The isolated Grok runtime lock is unsafe.");
294
+ }
295
+ const rawPid = (await readFile(profile.lockPath, "utf8")).trim();
296
+ if (!/^[1-9][0-9]*$/.test(rawPid)) {
297
+ throw new GrokRuntimeError("profile_unsafe", "The isolated Grok runtime lock is invalid.");
298
+ }
299
+ const pid = Number(rawPid);
300
+ if (!Number.isSafeInteger(pid) || pid > 2_147_483_647) {
301
+ throw new GrokRuntimeError("profile_unsafe", "The isolated Grok runtime lock is invalid.");
302
+ }
303
+ let stale = false;
304
+ try {
305
+ process.kill(pid, 0);
306
+ }
307
+ catch (probeError) {
308
+ if (probeError.code === "ESRCH")
309
+ stale = true;
310
+ else
311
+ throw new GrokRuntimeError("profile_locked", "The isolated Grok runtime is already in use.");
312
+ }
313
+ if (!stale) {
314
+ throw new GrokRuntimeError("profile_locked", "The isolated Grok runtime is already in use.");
315
+ }
316
+ await rm(profile.lockPath);
317
+ try {
318
+ handle = await openNewLock();
319
+ }
320
+ catch (retryError) {
321
+ if (retryError.code === "EEXIST") {
322
+ throw new GrokRuntimeError("profile_locked", "The isolated Grok runtime is already in use.");
323
+ }
324
+ throw retryError;
325
+ }
326
+ }
327
+ await handle.writeFile(`${process.pid}\n`, { encoding: "utf8" });
328
+ let released = false;
329
+ return {
330
+ async release() {
331
+ if (released)
332
+ return;
333
+ released = true;
334
+ await handle.close();
335
+ await rm(profile.lockPath, { force: true });
336
+ },
337
+ };
338
+ }
339
+ export async function grokProfileHasAuth(profile) {
340
+ for (const path of [join(profile.grokHome, "auth.json"), join(profile.grokHome, "auth")]) {
341
+ try {
342
+ const metadata = await lstat(path);
343
+ if (metadata.isFile() || metadata.isDirectory())
344
+ return true;
345
+ }
346
+ catch (error) {
347
+ if (error.code !== "ENOENT")
348
+ throw error;
349
+ }
350
+ }
351
+ return false;
352
+ }
353
+ export async function clearGrokRuntimeData(profile, includeAuth) {
354
+ const names = [
355
+ "sessions",
356
+ "logs",
357
+ "log",
358
+ "crash",
359
+ "leader.sock",
360
+ "leader.pid",
361
+ "socket",
362
+ "sockets",
363
+ "active_sessions.json",
364
+ "active_sessions.lock",
365
+ "sandbox-events.jsonl",
366
+ "auth.json.lock",
367
+ ];
368
+ if (includeAuth)
369
+ names.push("auth.json", "auth");
370
+ for (const name of names) {
371
+ await rm(join(profile.grokHome, name), { recursive: true, force: true });
372
+ }
373
+ // OAuth helpers may place browser hand-off logs or sockets under TMPDIR.
374
+ // The directory is dedicated to this isolated runtime, so clear and
375
+ // recreate it at every authentication boundary.
376
+ await rm(profile.tmp, { recursive: true, force: true });
377
+ await mkdir(profile.tmp, { mode: DIRECTORY_MODE });
378
+ await chmod(profile.tmp, DIRECTORY_MODE);
379
+ // The process workspace is intentionally disposable and never points at the user's project.
380
+ await rm(profile.workspace, { recursive: true, force: true });
381
+ await mkdir(profile.workspace, { mode: DIRECTORY_MODE });
382
+ await chmod(profile.workspace, DIRECTORY_MODE);
383
+ }
384
+ /** Remove extension surfaces that the pinned CLI may materialize at startup. */
385
+ export async function clearGrokGeneratedExtensions(profile) {
386
+ for (const name of [
387
+ "skills",
388
+ "plugins",
389
+ "marketplaces",
390
+ "hooks",
391
+ "agents",
392
+ "mcp",
393
+ "lsp",
394
+ "docs",
395
+ // The pinned CLI can materialize transcript-bearing archives and logs
396
+ // even with memory disabled. They are never inputs to a later turn.
397
+ "upload_queue",
398
+ "logs",
399
+ "log",
400
+ "crash",
401
+ "sandbox-events.jsonl",
402
+ "README.md",
403
+ ".config-init.lock",
404
+ "managed_config.lock",
405
+ "auth.json.lock",
406
+ "active_sessions.json",
407
+ "active_sessions.lock",
408
+ "leader.sock",
409
+ "leader.pid",
410
+ "socket",
411
+ "sockets",
412
+ ]) {
413
+ await rm(join(profile.grokHome, name), { recursive: true, force: true });
414
+ }
415
+ // Bundled skills/docs are written under isolated HOME rather than GROK_HOME.
416
+ // Remove them after every short-lived process so the next preflight/sidecar
417
+ // starts from the same empty extension surface it verified.
418
+ await rm(join(profile.home, ".grok"), { recursive: true, force: true });
419
+ }
@@ -0,0 +1,31 @@
1
+ /** Canonical product/picker identity for the subscription-backed runtime. */
2
+ export declare const GROK_SUBSCRIPTION_PROVIDER_ID: "grok-subscription";
3
+ /** Short command alias accepted by `/provider`, `/login`, and `/logout`. */
4
+ export declare const GROK_SUBSCRIPTION_COMMAND_ALIAS: "grok";
5
+ export declare const GROK_SUBSCRIPTION_PROVIDER_NAME: "Grok Subscription";
6
+ export declare const GROK_SUBSCRIPTION_PROVIDER_LABEL: "Grok Subscription [OAuth]";
7
+ export declare const GROK_SUBSCRIPTION_PROVIDER: Readonly<{
8
+ id: "grok-subscription";
9
+ commandAlias: "grok";
10
+ name: "Grok Subscription";
11
+ label: "Grok Subscription [OAuth]";
12
+ }>;
13
+ export interface ExternalProviderPickerEntry {
14
+ id: string;
15
+ name: string;
16
+ enabled: boolean;
17
+ }
18
+ export interface GrokSubscriptionPickerOptions {
19
+ label?: string;
20
+ enabled?: boolean;
21
+ }
22
+ /** Normalize only Grok's public command alias; unrelated provider IDs pass through. */
23
+ export declare function normalizeGrokSubscriptionProviderId(providerId: string): string;
24
+ export declare function isGrokSubscriptionProviderId(providerId: string | undefined): boolean;
25
+ /**
26
+ * Append one canonical Grok row after native provider rows. Existing canonical
27
+ * or command-alias rows are replaced, so independently composed picker lists
28
+ * cannot show Grok twice. This helper is intentionally independent from
29
+ * ProviderRegistry: Grok Subscription remains an external runtime.
30
+ */
31
+ export declare function withGrokSubscriptionProvider(entries: readonly ExternalProviderPickerEntry[], options?: GrokSubscriptionPickerOptions): ExternalProviderPickerEntry[];
@@ -0,0 +1,41 @@
1
+ /** Canonical product/picker identity for the subscription-backed runtime. */
2
+ export const GROK_SUBSCRIPTION_PROVIDER_ID = "grok-subscription";
3
+ /** Short command alias accepted by `/provider`, `/login`, and `/logout`. */
4
+ export const GROK_SUBSCRIPTION_COMMAND_ALIAS = "grok";
5
+ export const GROK_SUBSCRIPTION_PROVIDER_NAME = "Grok Subscription";
6
+ export const GROK_SUBSCRIPTION_PROVIDER_LABEL = "Grok Subscription [OAuth]";
7
+ export const GROK_SUBSCRIPTION_PROVIDER = Object.freeze({
8
+ id: GROK_SUBSCRIPTION_PROVIDER_ID,
9
+ commandAlias: GROK_SUBSCRIPTION_COMMAND_ALIAS,
10
+ name: GROK_SUBSCRIPTION_PROVIDER_NAME,
11
+ label: GROK_SUBSCRIPTION_PROVIDER_LABEL,
12
+ });
13
+ /** Normalize only Grok's public command alias; unrelated provider IDs pass through. */
14
+ export function normalizeGrokSubscriptionProviderId(providerId) {
15
+ const candidate = providerId.trim().toLowerCase();
16
+ if (candidate === GROK_SUBSCRIPTION_COMMAND_ALIAS || candidate === GROK_SUBSCRIPTION_PROVIDER_ID) {
17
+ return GROK_SUBSCRIPTION_PROVIDER_ID;
18
+ }
19
+ return providerId;
20
+ }
21
+ export function isGrokSubscriptionProviderId(providerId) {
22
+ return providerId !== undefined
23
+ && normalizeGrokSubscriptionProviderId(providerId) === GROK_SUBSCRIPTION_PROVIDER_ID;
24
+ }
25
+ /**
26
+ * Append one canonical Grok row after native provider rows. Existing canonical
27
+ * or command-alias rows are replaced, so independently composed picker lists
28
+ * cannot show Grok twice. This helper is intentionally independent from
29
+ * ProviderRegistry: Grok Subscription remains an external runtime.
30
+ */
31
+ export function withGrokSubscriptionProvider(entries, options = {}) {
32
+ const withoutGrok = entries.filter((entry) => !isGrokSubscriptionProviderId(entry.id));
33
+ return [
34
+ ...withoutGrok,
35
+ {
36
+ id: GROK_SUBSCRIPTION_PROVIDER_ID,
37
+ name: options.label ?? GROK_SUBSCRIPTION_PROVIDER_LABEL,
38
+ enabled: options.enabled ?? true,
39
+ },
40
+ ];
41
+ }
@@ -0,0 +1,91 @@
1
+ import type { AgentEvent } from "../types.js";
2
+ import type { ThinkingLevel } from "../types.js";
3
+ import type { PermissionMode } from "../types.js";
4
+ import type { ApprovalController } from "../approval/types.js";
5
+ import type { ExternalRuntimeManager, ExternalRuntimeRunOptions, ExternalRuntimeSession, ExternalRuntimeModel, ExternalRuntimeModelSelection, ExternalRuntimeStatus } from "./types.js";
6
+ import type { GrokBinaryDependencies } from "./grok-binary.js";
7
+ import { type GrokSpawn } from "./grok-process.js";
8
+ import { type GrokAcpFactory } from "./grok-acp.js";
9
+ import { type GrokNetworkRoute } from "./grok-network.js";
10
+ export declare const GROK_RUNTIME_CAPABILITIES: Readonly<{
11
+ chat: true;
12
+ tools: true;
13
+ memory: false;
14
+ subagents: false;
15
+ plan: false;
16
+ web: false;
17
+ sessionLoad: true;
18
+ workspace: true;
19
+ modelControl: true;
20
+ reasoningControl: true;
21
+ }>;
22
+ export interface GrokRuntimeDependencies extends GrokBinaryDependencies {
23
+ bubbleHome?: string;
24
+ parentEnv?: NodeJS.ProcessEnv;
25
+ spawn?: GrokSpawn;
26
+ acpFactory?: GrokAcpFactory;
27
+ oauthOpener?: GrokOAuthOpener;
28
+ networkResolver?: () => Promise<GrokNetworkRoute>;
29
+ workspace?: string;
30
+ approvalController?: ApprovalController;
31
+ getPermissionMode?: () => PermissionMode;
32
+ }
33
+ export type GrokOAuthOpener = (url: string, signal: AbortSignal) => Promise<void>;
34
+ export declare function grokLoginFailureMessage(diagnostic: string): string;
35
+ export declare class GrokRuntimeManager implements ExternalRuntimeManager {
36
+ private readonly deps;
37
+ private readonly spawn;
38
+ private readonly acpFactory;
39
+ private readonly oauthOpener;
40
+ private readonly profile;
41
+ private readonly workspace;
42
+ private binary?;
43
+ private acp?;
44
+ private lock?;
45
+ private state;
46
+ private currentSessionId?;
47
+ private attachedSessionId?;
48
+ private activeTurn?;
49
+ private nextGeneration;
50
+ private closingSidecar?;
51
+ private loginController?;
52
+ private loginOperation?;
53
+ private sessionLifecyclePhase;
54
+ private networkRoute?;
55
+ private networkRouteOperation?;
56
+ private modelId?;
57
+ private reasoningEffort;
58
+ private modelCache?;
59
+ private readonly activeTools;
60
+ constructor(deps?: GrokRuntimeDependencies);
61
+ private status;
62
+ private ensureNetworkRoute;
63
+ private prepareAndVerify;
64
+ private acquireProfileLock;
65
+ inspect(): Promise<ExternalRuntimeStatus>;
66
+ login(signal?: AbortSignal, onBrowserOpened?: () => void): Promise<void>;
67
+ private performLogin;
68
+ logout(): Promise<void>;
69
+ private ensureConnected;
70
+ private applyAcpModels;
71
+ newSession(): Promise<ExternalRuntimeSession>;
72
+ loadSession(id: string): Promise<ExternalRuntimeSession>;
73
+ hydrateSession(id: string, modelId?: string, reasoningEffort?: ThinkingLevel): Promise<ExternalRuntimeSession>;
74
+ listModels(): Promise<ExternalRuntimeModel[]>;
75
+ getModelSelection(): ExternalRuntimeModelSelection;
76
+ setModel(modelId: string, reasoningEffort?: ThinkingLevel): Promise<ExternalRuntimeModelSelection>;
77
+ private ensureSession;
78
+ run(prompt: string, options?: ExternalRuntimeRunOptions): AsyncIterable<AgentEvent>;
79
+ cancel(sessionId?: string | undefined): Promise<void>;
80
+ private handlePolicyViolation;
81
+ private handleSessionUpdate;
82
+ private handleCrash;
83
+ private closeSidecar;
84
+ private disconnect;
85
+ /**
86
+ * Closes the current sidecar and releases the profile lock. The manager is
87
+ * intentionally reusable: a later new/load/run call starts a fresh sidecar.
88
+ */
89
+ dispose(): Promise<void>;
90
+ }
91
+ export declare function createGrokRuntimeManager(deps?: GrokRuntimeDependencies): GrokRuntimeManager;