@bubblebrain-ai/bubble 0.0.35 → 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.
- package/README.md +27 -7
- package/dist/agent/categories.d.ts +66 -2
- package/dist/agent/categories.js +120 -19
- package/dist/agent/discovery-barrier.js +1 -1
- package/dist/agent/profiles.d.ts +1 -0
- package/dist/agent/profiles.js +25 -11
- package/dist/agent/routing-catalog.d.ts +113 -0
- package/dist/agent/routing-catalog.js +240 -0
- package/dist/agent/workflow/control.d.ts +14 -0
- package/dist/agent/workflow/control.js +30 -9
- package/dist/agent.d.ts +75 -5
- package/dist/agent.js +313 -61
- package/dist/approval/controller.js +28 -0
- package/dist/approval/tool-helper.js +2 -0
- package/dist/approval/types.d.ts +21 -1
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +2 -1
- package/dist/config.d.ts +4 -0
- package/dist/config.js +4 -0
- package/dist/context/compact-llm.d.ts +2 -2
- package/dist/external-runtime/grok-acp.d.ts +56 -0
- package/dist/external-runtime/grok-acp.js +386 -0
- package/dist/external-runtime/grok-binary.d.ts +25 -0
- package/dist/external-runtime/grok-binary.js +108 -0
- package/dist/external-runtime/grok-errors.d.ts +7 -0
- package/dist/external-runtime/grok-errors.js +15 -0
- package/dist/external-runtime/grok-input-policy.d.ts +58 -0
- package/dist/external-runtime/grok-input-policy.js +52 -0
- package/dist/external-runtime/grok-ndjson.d.ts +14 -0
- package/dist/external-runtime/grok-ndjson.js +193 -0
- package/dist/external-runtime/grok-network.d.ts +15 -0
- package/dist/external-runtime/grok-network.js +81 -0
- package/dist/external-runtime/grok-preflight.d.ts +28 -0
- package/dist/external-runtime/grok-preflight.js +194 -0
- package/dist/external-runtime/grok-process.d.ts +26 -0
- package/dist/external-runtime/grok-process.js +184 -0
- package/dist/external-runtime/grok-profile.d.ts +32 -0
- package/dist/external-runtime/grok-profile.js +419 -0
- package/dist/external-runtime/grok-provider.d.ts +31 -0
- package/dist/external-runtime/grok-provider.js +41 -0
- package/dist/external-runtime/grok-runtime.d.ts +91 -0
- package/dist/external-runtime/grok-runtime.js +882 -0
- package/dist/external-runtime/index.d.ts +13 -0
- package/dist/external-runtime/index.js +13 -0
- package/dist/external-runtime/session-policy.d.ts +11 -0
- package/dist/external-runtime/session-policy.js +30 -0
- package/dist/external-runtime/types.d.ts +65 -0
- package/dist/external-runtime/types.js +1 -0
- package/dist/feishu/agent-host/approval-card.js +21 -0
- package/dist/feishu/agent-host/run-driver.js +19 -3
- package/dist/feishu/serve.js +1 -0
- package/dist/goal/tools.d.ts +6 -3
- package/dist/goal/tools.js +7 -19
- package/dist/main.js +105 -27
- package/dist/memory/db.d.ts +2 -0
- package/dist/memory/db.js +10 -0
- package/dist/memory/index.d.ts +1 -0
- package/dist/memory/index.js +1 -0
- package/dist/memory/phase1.js +14 -2
- package/dist/memory/phase2.js +21 -2
- package/dist/memory/prompts.js +1 -1
- package/dist/memory/session-policy.d.ts +25 -0
- package/dist/memory/session-policy.js +55 -0
- package/dist/memory/storage.d.ts +1 -0
- package/dist/memory/storage.js +13 -0
- package/dist/model-catalog.d.ts +18 -0
- package/dist/model-catalog.js +87 -41
- package/dist/model-config.d.ts +3 -1
- package/dist/model-config.js +11 -0
- package/dist/oauth/grok.d.ts +25 -0
- package/dist/oauth/grok.js +218 -0
- package/dist/oauth/index.d.ts +1 -0
- package/dist/oauth/index.js +1 -0
- package/dist/oauth/storage.d.ts +9 -0
- package/dist/oauth/storage.js +25 -0
- package/dist/orchestrator/default-hooks.js +17 -7
- package/dist/prompt/compose.d.ts +3 -0
- package/dist/prompt/compose.js +3 -0
- package/dist/prompt/delegation.js +22 -3
- package/dist/prompt/environment.js +0 -4
- package/dist/prompt/reminders.d.ts +0 -1
- package/dist/prompt/reminders.js +1 -11
- package/dist/prompt/routing.d.ts +14 -0
- package/dist/prompt/routing.js +71 -0
- package/dist/prompt/runtime.js +1 -1
- package/dist/prompt/task-reminders.d.ts +19 -0
- package/dist/prompt/task-reminders.js +49 -0
- package/dist/provider-ai-sdk.js +2 -2
- package/dist/provider-grok.d.ts +26 -0
- package/dist/provider-grok.js +59 -0
- package/dist/provider-openai-codex.d.ts +17 -1
- package/dist/provider-openai-codex.js +101 -29
- package/dist/provider-registry.d.ts +72 -0
- package/dist/provider-registry.js +336 -59
- package/dist/provider-transform.d.ts +1 -1
- package/dist/provider-transform.js +28 -5
- package/dist/provider.d.ts +3 -0
- package/dist/provider.js +14 -0
- package/dist/session-types.d.ts +19 -1
- package/dist/session.d.ts +1 -0
- package/dist/session.js +4 -0
- package/dist/slash-commands/commands.js +224 -14
- package/dist/slash-commands/types.d.ts +14 -1
- package/dist/system-prompt.d.ts +2 -0
- package/dist/tools/agent-lifecycle.js +9 -11
- package/dist/tools/bash.js +1 -1
- package/dist/tools/child-tools.js +2 -0
- package/dist/tools/index.d.ts +2 -2
- package/dist/tools/index.js +7 -5
- package/dist/tools/lsp.js +1 -0
- package/dist/tools/memory.d.ts +7 -2
- package/dist/tools/memory.js +28 -43
- package/dist/tools/server.js +4 -0
- package/dist/tools/tool-search.d.ts +1 -1
- package/dist/tools/tool-search.js +23 -3
- package/dist/tui/model-switch.d.ts +7 -0
- package/dist/tui/model-switch.js +15 -5
- package/dist/tui-ink/app.d.ts +3 -1
- package/dist/tui-ink/app.js +648 -232
- package/dist/tui-ink/approval/approval-dialog.js +36 -0
- package/dist/tui-ink/composer-buffer.d.ts +55 -0
- package/dist/tui-ink/composer-buffer.js +317 -0
- package/dist/tui-ink/footer.d.ts +1 -0
- package/dist/tui-ink/footer.js +7 -2
- package/dist/tui-ink/input-box.d.ts +35 -1
- package/dist/tui-ink/input-box.js +215 -122
- package/dist/tui-ink/model-picker.d.ts +61 -4
- package/dist/tui-ink/model-picker.js +453 -123
- package/dist/tui-ink/paste-operation-tracker.d.ts +13 -0
- package/dist/tui-ink/paste-operation-tracker.js +25 -0
- package/dist/tui-ink/run.d.ts +3 -0
- package/dist/tui-ink/run.js +1 -1
- package/dist/tui-ink/subagent-view.d.ts +31 -0
- package/dist/tui-ink/subagent-view.js +82 -1
- package/dist/types.d.ts +5 -5
- package/dist/types.js +1 -1
- package/dist/variant/thinking-level.d.ts +2 -2
- package/dist/variant/thinking-level.js +2 -1
- package/dist/variant/variant-resolver.d.ts +9 -0
- package/dist/variant/variant-resolver.js +17 -0
- package/package.json +2 -1
- package/dist/tools/task.d.ts +0 -2
- package/dist/tools/task.js +0 -58
|
@@ -11,9 +11,13 @@ import { normalizeSingleLine } from "../text-display.js";
|
|
|
11
11
|
import { formatRelativeTime } from "../tui/recent-activity.js";
|
|
12
12
|
import { HOOK_EVENT_NAMES, isHookEventName } from "../hooks/index.js";
|
|
13
13
|
import { isThinkingLevel } from "../variant/thinking-level.js";
|
|
14
|
+
import { normalizeInheritedThinkingLevel } from "../variant/variant-resolver.js";
|
|
14
15
|
import { collectUsageStatsBundle, formatStatsText } from "../stats/usage.js";
|
|
15
16
|
import { buildMemoryPrompt, getMemoryStatus, isMemoryDisabled, resetMemory, searchMemory, } from "../memory/index.js";
|
|
16
17
|
import { feishuCommand } from "./feishu.js";
|
|
18
|
+
import { GROK_LOCAL_COMMAND_HELP } from "../external-runtime/grok-input-policy.js";
|
|
19
|
+
import { isGrokSubscriptionProviderId, } from "../external-runtime/grok-provider.js";
|
|
20
|
+
import { classifyExternalRuntimeBinding } from "../external-runtime/session-policy.js";
|
|
17
21
|
const VALID_SCOPES = ["user", "project", "local"];
|
|
18
22
|
const VALID_LISTS = ["allow", "deny"];
|
|
19
23
|
function isScope(value) {
|
|
@@ -141,13 +145,15 @@ function formatMcpContextStatus(ctx) {
|
|
|
141
145
|
lines.push("- MCP prompts are slash commands; they do not enter context until invoked.");
|
|
142
146
|
return lines.join("\n");
|
|
143
147
|
}
|
|
144
|
-
function switchToProviderModel(providerId, modelId, ctx, thinkingLevel) {
|
|
148
|
+
function switchToProviderModel(providerId, modelId, ctx, thinkingLevel, preparedProvider) {
|
|
145
149
|
const provider = ctx.registry.getConfigured().find((item) => item.id === providerId);
|
|
146
150
|
if (!provider?.apiKey) {
|
|
147
151
|
return false;
|
|
148
152
|
}
|
|
149
|
-
ctx.agent.thinking =
|
|
150
|
-
|
|
153
|
+
ctx.agent.thinking = thinkingLevel !== undefined
|
|
154
|
+
? normalizeThinkingLevel(thinkingLevel, getAvailableThinkingLevels(providerId, modelId))
|
|
155
|
+
: normalizeInheritedThinkingLevel(providerId, modelId, ctx.agent.thinking);
|
|
156
|
+
ctx.agent.setProvider(preparedProvider ?? ctx.createProvider(providerId, provider.apiKey, provider.baseURL));
|
|
151
157
|
ctx.agent.providerId = providerId;
|
|
152
158
|
ctx.agent.model = encodeModel(providerId, modelId);
|
|
153
159
|
syncSystemPrompt(ctx, ctx.agent.model);
|
|
@@ -182,6 +188,122 @@ function displaySelectedModel(model, thinkingLevel) {
|
|
|
182
188
|
const defaultLevel = providerId ? getDefaultThinkingLevel(providerId, modelId) : "off";
|
|
183
189
|
return thinkingLevel === "off" || thinkingLevel === defaultLevel ? label : `${label} (${thinkingLevel})`;
|
|
184
190
|
}
|
|
191
|
+
function getGrokSessionId(ctx) {
|
|
192
|
+
const binding = ctx.sessionManager?.getMetadata().externalRuntime;
|
|
193
|
+
return binding && isGrokSubscriptionProviderId(binding.id) ? binding.sessionId : undefined;
|
|
194
|
+
}
|
|
195
|
+
function isGrokSessionActive(ctx) {
|
|
196
|
+
return classifyExternalRuntimeBinding(ctx.sessionManager?.getMetadata().externalRuntime) === "grok";
|
|
197
|
+
}
|
|
198
|
+
function hasExternalRuntimeSession(ctx) {
|
|
199
|
+
return classifyExternalRuntimeBinding(ctx.sessionManager?.getMetadata().externalRuntime) !== "none";
|
|
200
|
+
}
|
|
201
|
+
async function transitionGrokSessionToNative(ctx) {
|
|
202
|
+
if (!hasExternalRuntimeSession(ctx))
|
|
203
|
+
return;
|
|
204
|
+
if (!ctx.transitionToNative) {
|
|
205
|
+
throw new Error("Switching from Grok subscription to a native Bubble session is unavailable in this mode.");
|
|
206
|
+
}
|
|
207
|
+
const next = await ctx.transitionToNative();
|
|
208
|
+
if (!next) {
|
|
209
|
+
throw new Error("Bubble could not start a fresh native session.");
|
|
210
|
+
}
|
|
211
|
+
// Handlers below may persist provider/model metadata after the transition.
|
|
212
|
+
// Keep those writes on the new native session rather than the old Grok log.
|
|
213
|
+
ctx.sessionManager = next;
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Sign in to the Grok subscription as a native model provider (like ChatGPT
|
|
217
|
+
* OAuth): browser PKCE against auth.x.ai, tokens in ~/.bubble/auth.json, and
|
|
218
|
+
* grok models served through Bubble's own agent loop. An existing Grok CLI
|
|
219
|
+
* login is imported so users who already ran `grok login` skip the browser.
|
|
220
|
+
*/
|
|
221
|
+
async function loginGrokSubscription(ctx) {
|
|
222
|
+
const providerId = "grok";
|
|
223
|
+
const { loginGrok, importGrokCliCredentials } = await import("../oauth/grok.js");
|
|
224
|
+
const storage = ctx.registry.getAuthStorage();
|
|
225
|
+
const runBrowserLogin = async () => {
|
|
226
|
+
const tokens = await loginGrok({
|
|
227
|
+
onStatus: (msg) => ctx.addMessage("assistant", msg),
|
|
228
|
+
});
|
|
229
|
+
storage.set(providerId, {
|
|
230
|
+
type: "oauth",
|
|
231
|
+
accessToken: tokens.accessToken,
|
|
232
|
+
refreshToken: tokens.refreshToken,
|
|
233
|
+
expiresAt: tokens.expiresAt,
|
|
234
|
+
});
|
|
235
|
+
};
|
|
236
|
+
let importedExisting = false;
|
|
237
|
+
if (!storage.has(providerId)) {
|
|
238
|
+
const imported = importGrokCliCredentials();
|
|
239
|
+
if (imported) {
|
|
240
|
+
storage.set(providerId, {
|
|
241
|
+
type: "oauth",
|
|
242
|
+
accessToken: imported.accessToken,
|
|
243
|
+
refreshToken: imported.refreshToken,
|
|
244
|
+
expiresAt: imported.expiresAt,
|
|
245
|
+
});
|
|
246
|
+
importedExisting = true;
|
|
247
|
+
ctx.addMessage("assistant", "Imported the existing Grok CLI sign-in.");
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
const hadStoredLogin = storage.has(providerId);
|
|
251
|
+
if (!hadStoredLogin) {
|
|
252
|
+
await runBrowserLogin();
|
|
253
|
+
}
|
|
254
|
+
try {
|
|
255
|
+
await ctx.registry.prepareProvider(providerId);
|
|
256
|
+
}
|
|
257
|
+
catch (error) {
|
|
258
|
+
// Stored/imported credentials whose refresh token was revoked get exactly
|
|
259
|
+
// one browser repair attempt; a failure right after browser login is real.
|
|
260
|
+
if (!hadStoredLogin)
|
|
261
|
+
throw error;
|
|
262
|
+
ctx.addMessage("assistant", `Stored Grok credentials could not be refreshed (${error instanceof Error ? error.message : String(error)}). Signing in again…`);
|
|
263
|
+
storage.remove(providerId);
|
|
264
|
+
await runBrowserLogin();
|
|
265
|
+
await ctx.registry.prepareProvider(providerId);
|
|
266
|
+
}
|
|
267
|
+
const provider = ctx.registry.getConfigured().find((item) => item.id === providerId);
|
|
268
|
+
const defaultModel = ctx.registry.getDefaultModel(providerId, "oauth");
|
|
269
|
+
if (!provider?.apiKey || !defaultModel) {
|
|
270
|
+
return `Grok subscription login succeeded, but the provider could not be activated. Tokens saved to ${storage.getPath()}`;
|
|
271
|
+
}
|
|
272
|
+
// Complete every fallible preparation step before leaving a legacy
|
|
273
|
+
// Grok-runtime session; commit only after the native switch succeeds.
|
|
274
|
+
const preparedProvider = ctx.createProvider(providerId, provider.apiKey, provider.baseURL);
|
|
275
|
+
await transitionGrokSessionToNative(ctx);
|
|
276
|
+
const switched = switchToProviderModel(providerId, defaultModel, ctx, undefined, preparedProvider);
|
|
277
|
+
if (!switched) {
|
|
278
|
+
return `Grok subscription login succeeded, but the provider could not be activated. Tokens saved to ${storage.getPath()}`;
|
|
279
|
+
}
|
|
280
|
+
ctx.registry.setDefault(providerId);
|
|
281
|
+
const via = importedExisting ? " (reused your Grok CLI sign-in)" : "";
|
|
282
|
+
return `Grok subscription login successful${via}. Switched to ${displayModel(ctx.agent.model)}. Tokens saved to ${storage.getPath()}`;
|
|
283
|
+
}
|
|
284
|
+
async function logoutGrokSubscription(ctx) {
|
|
285
|
+
const storage = ctx.registry.getAuthStorage();
|
|
286
|
+
const hadNativeLogin = storage.has("grok");
|
|
287
|
+
storage.remove("grok");
|
|
288
|
+
// Legacy cleanup: sessions bound to the old Grok ACP runtime, and the
|
|
289
|
+
// isolated CLI profile's credentials.
|
|
290
|
+
if (isGrokSessionActive(ctx) && ctx.externalRuntime && ctx.startFreshSession) {
|
|
291
|
+
await ctx.externalRuntime.cancel(getGrokSessionId(ctx)).catch(() => undefined);
|
|
292
|
+
await ctx.externalRuntime.dispose().catch(() => undefined);
|
|
293
|
+
await ctx.externalRuntime.logout().catch(() => undefined);
|
|
294
|
+
const freshSession = await ctx.startFreshSession();
|
|
295
|
+
freshSession.clearExternalRuntimeMetadata();
|
|
296
|
+
freshSession.appendMarker("runtime_switch", "native");
|
|
297
|
+
ctx.sessionManager = freshSession;
|
|
298
|
+
ctx.onExternalRuntimeChange?.(freshSession);
|
|
299
|
+
return "Grok subscription logged out. Started a fresh native Bubble session.";
|
|
300
|
+
}
|
|
301
|
+
await ctx.externalRuntime?.logout().catch(() => undefined);
|
|
302
|
+
if (!hadNativeLogin) {
|
|
303
|
+
return "No Grok subscription login was stored on this device.";
|
|
304
|
+
}
|
|
305
|
+
return "Grok subscription logged out. Only this device's local login was removed.";
|
|
306
|
+
}
|
|
185
307
|
function parseMemoryScopeArgs(args) {
|
|
186
308
|
const tokens = args.trim().split(/\s+/).filter(Boolean);
|
|
187
309
|
let scope = "project";
|
|
@@ -298,6 +420,16 @@ const builtinSlashCommandEntries = [
|
|
|
298
420
|
name: "help",
|
|
299
421
|
description: "Show available slash commands",
|
|
300
422
|
async handler(args, ctx) {
|
|
423
|
+
if (hasExternalRuntimeSession(ctx)) {
|
|
424
|
+
const grok = isGrokSessionActive(ctx);
|
|
425
|
+
return [
|
|
426
|
+
grok
|
|
427
|
+
? "Grok Subscription · workspace tools · Bubble approvals"
|
|
428
|
+
: "Unsupported external runtime session · recovery-only mode",
|
|
429
|
+
"Available commands:",
|
|
430
|
+
...GROK_LOCAL_COMMAND_HELP.map((command) => (` ${command.usage} - ${command.description}`)),
|
|
431
|
+
].join("\n");
|
|
432
|
+
}
|
|
301
433
|
const { registry } = await import("./index.js");
|
|
302
434
|
const lines = ["Available commands:"];
|
|
303
435
|
for (const cmd of registry.list()) {
|
|
@@ -475,11 +607,12 @@ const builtinSlashCommandEntries = [
|
|
|
475
607
|
name: "provider",
|
|
476
608
|
description: "Manage providers. /provider to switch, /provider --add [id] to add, /provider --remove <id>, /provider --set <id>",
|
|
477
609
|
async handler(args, ctx) {
|
|
478
|
-
|
|
610
|
+
const trimmed = args.trim();
|
|
611
|
+
if (!trimmed) {
|
|
479
612
|
ctx.openPicker("provider");
|
|
480
613
|
return;
|
|
481
614
|
}
|
|
482
|
-
const parts =
|
|
615
|
+
const parts = trimmed.split(/\s+/);
|
|
483
616
|
const flag = parts[0];
|
|
484
617
|
const value = parts[1];
|
|
485
618
|
if (flag === "--add") {
|
|
@@ -487,6 +620,9 @@ const builtinSlashCommandEntries = [
|
|
|
487
620
|
ctx.openPicker("provider-add");
|
|
488
621
|
return;
|
|
489
622
|
}
|
|
623
|
+
if (isGrokSubscriptionProviderId(value)) {
|
|
624
|
+
return "Grok Subscription is built in. Use /provider --set grok or /login grok to activate it.";
|
|
625
|
+
}
|
|
490
626
|
const builtin = BUILTIN_PROVIDERS.find((p) => p.id === value && isUserVisibleProvider(p.id));
|
|
491
627
|
if (!builtin) {
|
|
492
628
|
const ids = BUILTIN_PROVIDERS.filter((p) => isUserVisibleProvider(p.id)).map((p) => p.id).join(", ");
|
|
@@ -498,17 +634,27 @@ const builtinSlashCommandEntries = [
|
|
|
498
634
|
return;
|
|
499
635
|
}
|
|
500
636
|
if (flag === "--remove" && value) {
|
|
637
|
+
if (isGrokSubscriptionProviderId(value)) {
|
|
638
|
+
return "Grok Subscription is built in and cannot be removed. Use /logout grok to remove its local login.";
|
|
639
|
+
}
|
|
501
640
|
if (ctx.registry.getModelConfig().hasProvider(value)) {
|
|
502
641
|
return `Provider ${value} is defined in ~/.bubble/models.json. Please edit that file directly.`;
|
|
503
642
|
}
|
|
504
643
|
ctx.registry.removeProvider(value);
|
|
644
|
+
// Registry mutation without a model switch still changes the routing
|
|
645
|
+
// world (design §1.6): refresh the routing menu in the system prompt.
|
|
646
|
+
syncSystemPrompt(ctx, ctx.agent.model);
|
|
505
647
|
return `Provider ${value} removed.`;
|
|
506
648
|
}
|
|
507
649
|
if (flag === "--set" && value) {
|
|
650
|
+
if (isGrokSubscriptionProviderId(value)) {
|
|
651
|
+
return loginGrokSubscription(ctx);
|
|
652
|
+
}
|
|
508
653
|
const providers = ctx.registry.getConfigured();
|
|
509
654
|
const p = providers.find((x) => x.id === value);
|
|
510
655
|
if (!p)
|
|
511
656
|
return `Provider ${value} is not configured.`;
|
|
657
|
+
await transitionGrokSessionToNative(ctx);
|
|
512
658
|
ctx.registry.setDefault(value);
|
|
513
659
|
if (ctx.registry.getModelConfig().hasProvider(value)) {
|
|
514
660
|
return `Default provider set to ${p.name}. Note: config is managed via ~/.bubble/models.json.`;
|
|
@@ -517,13 +663,17 @@ const builtinSlashCommandEntries = [
|
|
|
517
663
|
}
|
|
518
664
|
if (flag === "--list") {
|
|
519
665
|
const providers = ctx.registry.getConfigured();
|
|
520
|
-
const
|
|
666
|
+
const grokActive = isGrokSessionActive(ctx);
|
|
667
|
+
const lines = ["Available providers:"];
|
|
521
668
|
for (const p of providers) {
|
|
522
|
-
const marker = p.id === ctx.registry.getDefault()?.id ? "* " : " ";
|
|
669
|
+
const marker = !grokActive && p.id === ctx.registry.getDefault()?.id ? "* " : " ";
|
|
523
670
|
const source = ctx.registry.getModelConfig().hasProvider(p.id) ? " [models.json]" : "";
|
|
524
671
|
const oauth = ctx.registry.getAuthStorage().has(p.id) ? " [oauth]" : "";
|
|
525
672
|
lines.push(`${marker}${p.name} (${p.id}) ${p.enabled ? "" : "[disabled]"}${oauth}${source}`);
|
|
526
673
|
}
|
|
674
|
+
if (!providers.some((provider) => provider.id === "grok")) {
|
|
675
|
+
lines.push(`${grokActive ? "* " : " "}Grok Subscription (grok) [not signed in — /login grok]`);
|
|
676
|
+
}
|
|
527
677
|
if (ctx.registry.getModelConfig().getLoadError()) {
|
|
528
678
|
lines.push(`Warning: failed to load models.json: ${ctx.registry.getModelConfig().getLoadError()}`);
|
|
529
679
|
}
|
|
@@ -534,15 +684,18 @@ const builtinSlashCommandEntries = [
|
|
|
534
684
|
},
|
|
535
685
|
{
|
|
536
686
|
name: "login",
|
|
537
|
-
description: "OAuth
|
|
687
|
+
description: "Login to OpenAI OAuth or Grok Subscription. Usage: /login [openai|grok]",
|
|
538
688
|
async handler(args, ctx) {
|
|
539
689
|
const providerId = args?.trim() || "openai";
|
|
540
690
|
if (!providerId) {
|
|
541
691
|
ctx.openPicker("login");
|
|
542
692
|
return;
|
|
543
693
|
}
|
|
694
|
+
if (isGrokSubscriptionProviderId(providerId)) {
|
|
695
|
+
return loginGrokSubscription(ctx);
|
|
696
|
+
}
|
|
544
697
|
if (!ctx.registry.supportsOAuth(providerId)) {
|
|
545
|
-
return `Unsupported
|
|
698
|
+
return `Unsupported login provider: ${providerId}. Supported providers: 'openai' and 'grok'.`;
|
|
546
699
|
}
|
|
547
700
|
const { loginOpenAICodex } = await import("../oauth/openai-codex.js");
|
|
548
701
|
const tokens = await loginOpenAICodex({
|
|
@@ -557,17 +710,25 @@ const builtinSlashCommandEntries = [
|
|
|
557
710
|
accountId: tokens.accountId,
|
|
558
711
|
});
|
|
559
712
|
await ctx.registry.prepareProvider(providerId);
|
|
560
|
-
ctx.registry.setDefault(providerId);
|
|
561
713
|
const provider = ctx.registry.getConfigured().find((item) => item.id === providerId);
|
|
562
714
|
const discoveredModels = provider ? await ctx.registry.listModels(provider) : [];
|
|
563
715
|
const defaultModel = discoveredModels[0]?.id || ctx.registry.getDefaultModel(providerId, "oauth");
|
|
564
716
|
if (!defaultModel) {
|
|
565
717
|
return `OpenAI Codex OAuth login succeeded, but no default model is configured for ${providerId}.`;
|
|
566
718
|
}
|
|
567
|
-
|
|
719
|
+
if (!provider?.apiKey) {
|
|
720
|
+
return `OpenAI Codex OAuth login succeeded, but the provider could not be activated. Tokens saved to ${ctx.registry.getAuthStorage().getPath()}`;
|
|
721
|
+
}
|
|
722
|
+
// Complete every fallible OAuth/provider/model preparation step before
|
|
723
|
+
// leaving a Grok-bound session. The provider object is created once and
|
|
724
|
+
// committed only after the fresh native session switch succeeds.
|
|
725
|
+
const preparedProvider = ctx.createProvider(providerId, provider.apiKey, provider.baseURL);
|
|
726
|
+
await transitionGrokSessionToNative(ctx);
|
|
727
|
+
const switched = switchToProviderModel(providerId, defaultModel, ctx, undefined, preparedProvider);
|
|
568
728
|
if (!switched) {
|
|
569
729
|
return `OpenAI Codex OAuth login succeeded, but the provider could not be activated. Tokens saved to ${ctx.registry.getAuthStorage().getPath()}`;
|
|
570
730
|
}
|
|
731
|
+
ctx.registry.setDefault(providerId);
|
|
571
732
|
return `OpenAI Codex OAuth login successful. Switched to ${displayModel(ctx.agent.model)}. Account: ${tokens.accountId || "unknown"}. Tokens saved to ${ctx.registry.getAuthStorage().getPath()}`;
|
|
572
733
|
},
|
|
573
734
|
},
|
|
@@ -576,6 +737,13 @@ const builtinSlashCommandEntries = [
|
|
|
576
737
|
description: "Switch model. Use /model <id> [--reasoning-effort <level>] or just /model to open picker.",
|
|
577
738
|
async handler(args, ctx) {
|
|
578
739
|
if (!args) {
|
|
740
|
+
if (hasExternalRuntimeSession(ctx)) {
|
|
741
|
+
if (isGrokSessionActive(ctx)) {
|
|
742
|
+
ctx.openPicker("model");
|
|
743
|
+
return;
|
|
744
|
+
}
|
|
745
|
+
return "This external runtime manages its model and reasoning settings. Bubble's model picker is unavailable until you switch to a native session.";
|
|
746
|
+
}
|
|
579
747
|
if (ctx.registry.getEnabled().length === 0) {
|
|
580
748
|
ctx.openPicker("model");
|
|
581
749
|
return;
|
|
@@ -588,30 +756,69 @@ const builtinSlashCommandEntries = [
|
|
|
588
756
|
return parsed.error;
|
|
589
757
|
}
|
|
590
758
|
if (!parsed.model) {
|
|
759
|
+
if (hasExternalRuntimeSession(ctx)) {
|
|
760
|
+
if (isGrokSessionActive(ctx)) {
|
|
761
|
+
ctx.openPicker("model");
|
|
762
|
+
return;
|
|
763
|
+
}
|
|
764
|
+
return "This external runtime manages its model and reasoning settings. Bubble's model picker is unavailable until you switch to a native session.";
|
|
765
|
+
}
|
|
591
766
|
ctx.openPicker("model");
|
|
592
767
|
return;
|
|
593
768
|
}
|
|
769
|
+
const explicitModelProvider = parsed.model.includes(":") ? parsed.model.split(":", 1)[0] : undefined;
|
|
770
|
+
if (isGrokSessionActive(ctx) && (!explicitModelProvider || isGrokSubscriptionProviderId(explicitModelProvider))) {
|
|
771
|
+
if (!ctx.externalRuntime)
|
|
772
|
+
return "Grok Subscription runtime is unavailable.";
|
|
773
|
+
const boundSessionId = getGrokSessionId(ctx);
|
|
774
|
+
if (boundSessionId)
|
|
775
|
+
await ctx.externalRuntime.hydrateSession(boundSessionId);
|
|
776
|
+
const requestedModel = parsed.model.includes(":")
|
|
777
|
+
? parsed.model.split(":").slice(1).join(":")
|
|
778
|
+
: parsed.model;
|
|
779
|
+
const selection = await ctx.externalRuntime.setModel(requestedModel, parsed.thinkingLevel);
|
|
780
|
+
const metadata = ctx.sessionManager?.getMetadata().externalRuntime;
|
|
781
|
+
if (metadata && isGrokSubscriptionProviderId(metadata.id)) {
|
|
782
|
+
ctx.sessionManager?.updateMetadata({
|
|
783
|
+
externalRuntime: {
|
|
784
|
+
...metadata,
|
|
785
|
+
modelId: selection.modelId,
|
|
786
|
+
reasoningEffort: selection.reasoningEffort,
|
|
787
|
+
},
|
|
788
|
+
});
|
|
789
|
+
}
|
|
790
|
+
ctx.sessionManager?.appendMarker("model_switch", selection.modelId ?? requestedModel);
|
|
791
|
+
ctx.sessionManager?.appendMarker("thinking_level_switch", selection.reasoningEffort);
|
|
792
|
+
ctx.onExternalRuntimeChange?.(ctx.sessionManager);
|
|
793
|
+
return `Grok model switched to ${selection.modelId ?? requestedModel}${selection.reasoningEffort !== "off" ? ` (${selection.reasoningEffort})` : ""}.`;
|
|
794
|
+
}
|
|
594
795
|
const defaultProvider = ctx.registry.getDefault()?.id || "openai";
|
|
595
796
|
const next = parsed.model.includes(":") ? parsed.model : encodeModel(defaultProvider, parsed.model);
|
|
596
797
|
const { providerId, modelId } = decodeModel(next);
|
|
597
798
|
const targetProviderId = providerId || defaultProvider;
|
|
598
799
|
await ctx.registry.prepareProvider(targetProviderId);
|
|
599
|
-
const
|
|
600
|
-
if (!
|
|
800
|
+
const targetProvider = ctx.registry.getConfigured().find((item) => item.id === targetProviderId);
|
|
801
|
+
if (!targetProvider?.apiKey) {
|
|
601
802
|
return `Provider ${targetProviderId} is not configured or has no active credentials.`;
|
|
602
803
|
}
|
|
804
|
+
const preparedProvider = ctx.createProvider(targetProviderId, targetProvider.apiKey, targetProvider.baseURL);
|
|
805
|
+
await transitionGrokSessionToNative(ctx);
|
|
806
|
+
switchToProviderModel(targetProviderId, modelId, ctx, parsed.thinkingLevel, preparedProvider);
|
|
603
807
|
return `Model switched to ${displaySelectedModel(next, ctx.agent.thinking)}.`;
|
|
604
808
|
},
|
|
605
809
|
},
|
|
606
810
|
{
|
|
607
811
|
name: "logout",
|
|
608
|
-
description: "Remove
|
|
812
|
+
description: "Remove local login credentials. Usage: /logout [openai|grok]",
|
|
609
813
|
async handler(args, ctx) {
|
|
610
814
|
const providerId = args?.trim() || "openai";
|
|
611
815
|
if (!providerId) {
|
|
612
816
|
ctx.openPicker("logout");
|
|
613
817
|
return;
|
|
614
818
|
}
|
|
819
|
+
if (isGrokSubscriptionProviderId(providerId)) {
|
|
820
|
+
return logoutGrokSubscription(ctx);
|
|
821
|
+
}
|
|
615
822
|
if (!ctx.registry.getAuthStorage().has(providerId)) {
|
|
616
823
|
return `No OAuth credentials found for ${providerId}.`;
|
|
617
824
|
}
|
|
@@ -629,6 +836,9 @@ const builtinSlashCommandEntries = [
|
|
|
629
836
|
else if (ctx.agent.providerId === providerId) {
|
|
630
837
|
ctx.agent.providerId = "";
|
|
631
838
|
}
|
|
839
|
+
// No-fallback logout mutates the routing world without a model switch
|
|
840
|
+
// (design §1.6): refresh the menu so it stops advertising the provider.
|
|
841
|
+
syncSystemPrompt(ctx, ctx.agent.model);
|
|
632
842
|
return `OAuth credentials for ${providerId} removed.`;
|
|
633
843
|
},
|
|
634
844
|
},
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Agent } from "../agent.js";
|
|
2
|
-
import type { SessionManager } from "../session.js";
|
|
2
|
+
import type { SessionManager, SessionMetadata } from "../session.js";
|
|
3
3
|
import type { Provider } from "../types.js";
|
|
4
4
|
import type { ProviderRegistry } from "../provider-registry.js";
|
|
5
5
|
import type { SkillRegistry } from "../skills/registry.js";
|
|
@@ -10,6 +10,7 @@ import type { LspService } from "../lsp/index.js";
|
|
|
10
10
|
import type { MemoryScope } from "../memory/index.js";
|
|
11
11
|
import type { ThemeMode } from "../config.js";
|
|
12
12
|
import type { ExternalHookController } from "../hooks/controller.js";
|
|
13
|
+
import type { ExternalRuntimeManager } from "../external-runtime/types.js";
|
|
13
14
|
/**
|
|
14
15
|
* Live progress for a manual `/compact` run, pushed to the TUI so it can render
|
|
15
16
|
* a progress bar. `phase` advances collecting → summarizing → applying;
|
|
@@ -61,6 +62,18 @@ export interface SlashCommandContext {
|
|
|
61
62
|
* while compacting and `null` to clear the indicator. Absent in non-TUI hosts.
|
|
62
63
|
*/
|
|
63
64
|
compactionProgress?: (progress: CompactionProgress | null) => void;
|
|
65
|
+
/** External agent runtime used by Grok subscription chat. */
|
|
66
|
+
externalRuntime?: ExternalRuntimeManager;
|
|
67
|
+
/**
|
|
68
|
+
* Atomically create and bind a fresh Bubble session with optional metadata.
|
|
69
|
+
* External runtimes are session-affine, so crossing the native/external
|
|
70
|
+
* boundary never reuses history.
|
|
71
|
+
*/
|
|
72
|
+
startFreshSession?: (metadata?: Partial<SessionMetadata>) => Promise<SessionManager>;
|
|
73
|
+
/** Stop the external runtime and atomically enter a fresh native session. */
|
|
74
|
+
transitionToNative?: () => Promise<SessionManager | undefined>;
|
|
75
|
+
/** Refresh TUI state after a command changes the external session binding. */
|
|
76
|
+
onExternalRuntimeChange?: (manager?: SessionManager) => void;
|
|
64
77
|
}
|
|
65
78
|
/**
|
|
66
79
|
* Return types for a slash command handler:
|
package/dist/system-prompt.d.ts
CHANGED
|
@@ -32,5 +32,7 @@ export interface SystemPromptOptions {
|
|
|
32
32
|
memoryPrompt?: string;
|
|
33
33
|
/** Durable child-agent profile prompt used for subagents. */
|
|
34
34
|
agentProfilePrompt?: string;
|
|
35
|
+
/** Rendered subagent model-routing menu (design §4); parents only. */
|
|
36
|
+
modelRoutingPrompt?: string;
|
|
35
37
|
}
|
|
36
38
|
export declare function buildSystemPrompt(options?: SystemPromptOptions): string;
|
|
@@ -2,7 +2,8 @@ import { createHash } from "node:crypto";
|
|
|
2
2
|
import { readFileSync } from "node:fs";
|
|
3
3
|
import { discoverAgentProfiles, findAgentProfile } from "../agent/profiles.js";
|
|
4
4
|
import { parseThinkingLevel } from "../agent/categories.js";
|
|
5
|
-
import {
|
|
5
|
+
import { THINKING_LEVELS } from "../types.js";
|
|
6
|
+
import { buildWorkflowResultBlock, workflowMemberWarning } from "../agent/workflow/control.js";
|
|
6
7
|
import { precompileWorkflowScript } from "../agent/workflow/runtime.js";
|
|
7
8
|
import { formatSubagentRoute } from "../agent/subagent-route-format.js";
|
|
8
9
|
/**
|
|
@@ -124,7 +125,7 @@ export function createSpawnAgentTool(options = {}, sharedTrust) {
|
|
|
124
125
|
agent: { type: "string", description: "Alias for agent_type." },
|
|
125
126
|
category: { type: "string", description: "Optional semantic category for model/thinking routing, such as quick, deep, explore, review, frontend, or writing." },
|
|
126
127
|
model: { type: "string", description: "Optional per-call model for this child, overriding category and profile. Bare name (e.g. claude-haiku-4-5) uses the parent provider; provider:model (e.g. anthropic:claude-opus-4-1) selects cross-provider." },
|
|
127
|
-
effort: { type: "string", enum: [
|
|
128
|
+
effort: { type: "string", enum: [...THINKING_LEVELS], description: "Optional per-call thinking level for this child, overriding category and profile." },
|
|
128
129
|
message: { type: "string", description: "Initial task for the subagent." },
|
|
129
130
|
task: { type: "string", description: "Alias for message." },
|
|
130
131
|
fork_context: { type: "boolean", description: "When true, copy recent parent conversation into the child thread." },
|
|
@@ -291,6 +292,7 @@ export function createSendInputTool() {
|
|
|
291
292
|
export function createCloseAgentTool() {
|
|
292
293
|
return {
|
|
293
294
|
name: "close_agent",
|
|
295
|
+
deferred: true,
|
|
294
296
|
readOnly: true,
|
|
295
297
|
effect: "read",
|
|
296
298
|
description: "Close a spawned subagent only when the delegated task is cancelled, stale, or no longer needed. Running children are cancelled before closing; do not close a child just because you started doing the same delegated work locally.",
|
|
@@ -327,6 +329,7 @@ export function createCloseAgentTool() {
|
|
|
327
329
|
export function createListAgentsTool() {
|
|
328
330
|
return {
|
|
329
331
|
name: "list_agents",
|
|
332
|
+
deferred: true,
|
|
330
333
|
readOnly: true,
|
|
331
334
|
effect: "read",
|
|
332
335
|
description: "List this session's subagents with their current status. Use it to recall which children exist before spawning duplicates or narrating progress.",
|
|
@@ -401,8 +404,8 @@ export function createRunWorkflowTool(options = {}, sharedTrust) {
|
|
|
401
404
|
"This is THE tool whenever the user asks for a workflow, an orchestration, or an agent team — even a small one; do not substitute parallel spawn_agent calls for an explicit request.",
|
|
402
405
|
"Also use it for tasks that need loops, conditional fan-out, or staged pipelines over dozens of subagents whose intermediate steps should stay out of this conversation — e.g. a codebase-wide audit, a migration, or cross-checked research.",
|
|
403
406
|
"The script's only capability is agent(prompt, opts?) — each call spawns a sandboxed readonly subagent; opts may set {model, effort, agentType, category, schema, label}. Give each agent a short unique label. Also available: parallel(thunks), pipeline(items, ...stages), phase(title), log(msg), the global args, and budget {total, spent(), remaining()}.",
|
|
404
|
-
"End the script with `return <value>`; that value (only) comes back to you. The script has no filesystem/shell/network/clock/random access. run_workflow must be the ONLY tool call in your response; it blocks until the workflow finishes.",
|
|
405
|
-
"A failed or blocked agent() resolves to null inside parallel/pipeline (a bare await agent() throws instead): check for null slots and return which items failed alongside the results — never silently drop them.",
|
|
407
|
+
"End the script with `return <value>`; that value (only) comes back to you. Return distilled conclusions, not raw agent transcripts — reduce inside the script (plain JS filtering or a final synthesis agent); a return value past ~8000 chars reaches you truncated (full copy on disk). The script has no filesystem/shell/network/clock/random access. run_workflow must be the ONLY tool call in your response; it blocks until the workflow finishes.",
|
|
408
|
+
"A failed or blocked agent() resolves to null inside parallel/pipeline (a bare await agent() throws instead): check for null slots and return which items failed alongside the results — never silently drop them. Keep a final synthesis/aggregation step inside try/catch (or parallel) so its failure cannot discard every completed member's work.",
|
|
406
409
|
"Write plain JavaScript only — no TypeScript syntax, no import/require, and no Date or Math.random (they throw; pass timestamps in via args).",
|
|
407
410
|
"parallel() takes an array of FUNCTIONS, not promises: await parallel(items.map(item => () => agent(...))). Passing promises throws a TypeError.",
|
|
408
411
|
"Example: `export const meta = { name: 'audit', description: 'auth audit' };\\nconst files = args;\\nconst findings = await parallel(files.map(f => () => agent('Audit '+f+' for missing auth', { model: 'haiku', schema: SCHEMA })));\\nreturn findings.filter(Boolean);`",
|
|
@@ -510,9 +513,6 @@ export function createWaitWorkflowTool() {
|
|
|
510
513
|
metadata: { kind: "subagent", mode: "workflow", subagents: snapshot.snapshots.map(snapshotToMetadata) },
|
|
511
514
|
};
|
|
512
515
|
}
|
|
513
|
-
const rendered = typeof snapshot.result.value === "string"
|
|
514
|
-
? snapshot.result.value
|
|
515
|
-
: JSON.stringify(snapshot.result.value, null, 2);
|
|
516
516
|
const memberWarning = workflowMemberWarning(snapshot);
|
|
517
517
|
return {
|
|
518
518
|
content: [
|
|
@@ -520,9 +520,7 @@ export function createWaitWorkflowTool() {
|
|
|
520
520
|
...(memberWarning ? [memberWarning] : []),
|
|
521
521
|
...(snapshot.logs.length > 0 ? ["", "Log:", ...snapshot.logs.slice(-20)] : []),
|
|
522
522
|
"",
|
|
523
|
-
|
|
524
|
-
truncateText(rendered, 8000),
|
|
525
|
-
"--- end workflow result ---",
|
|
523
|
+
...buildWorkflowResultBlock(snapshot),
|
|
526
524
|
].join("\n"),
|
|
527
525
|
status: "success",
|
|
528
526
|
metadata: { kind: "subagent", mode: "workflow", subagents: snapshot.snapshots.map(snapshotToMetadata) },
|
|
@@ -744,7 +742,7 @@ function parseEffortArg(value) {
|
|
|
744
742
|
if (!parsed) {
|
|
745
743
|
return {
|
|
746
744
|
error: {
|
|
747
|
-
content: `Error: effort must be one of
|
|
745
|
+
content: `Error: effort must be one of ${THINKING_LEVELS.join(", ")} (got ${JSON.stringify(value)}).`,
|
|
748
746
|
isError: true,
|
|
749
747
|
},
|
|
750
748
|
};
|
package/dist/tools/bash.js
CHANGED
|
@@ -16,7 +16,7 @@ export function createBashTool(cwd, approval, _fileState) {
|
|
|
16
16
|
name: "bash",
|
|
17
17
|
effect: "unknown",
|
|
18
18
|
requiresApproval: true,
|
|
19
|
-
description: "Execute a bounded bash command in the working directory. Use timeout for long-running commands. For persistent dev servers or watchers such as npm run dev, next dev, vite, or webpack --watch, use
|
|
19
|
+
description: "Execute a bounded bash command in the working directory. Use timeout for long-running commands. For persistent dev servers or watchers such as npm run dev, next dev, vite, or webpack --watch, use the managed server tools instead of backgrounding a bash command (deferred: load them first via tool_search with query 'select:start_server,server_status,server_logs,stop_server').",
|
|
20
20
|
parameters: {
|
|
21
21
|
type: "object",
|
|
22
22
|
properties: {
|
|
@@ -76,6 +76,8 @@ export class WorktreeApprovalController {
|
|
|
76
76
|
return { action: "approve" };
|
|
77
77
|
case "agent_profile":
|
|
78
78
|
return { action: "reject", feedback: "Subagents cannot approve agent profiles." };
|
|
79
|
+
case "external_tool":
|
|
80
|
+
return { action: "reject", feedback: "Subagents cannot approve external runtime tools." };
|
|
79
81
|
}
|
|
80
82
|
}
|
|
81
83
|
checkRules() {
|
package/dist/tools/index.d.ts
CHANGED
|
@@ -19,7 +19,7 @@ export { createTodoTool, type TodoStore } from "./todo.js";
|
|
|
19
19
|
export { createExitPlanModeTool, type PlanController } from "./exit-plan-mode.js";
|
|
20
20
|
export { createToolSearchTool, type ToolSearchController } from "./tool-search.js";
|
|
21
21
|
export { createQuestionTool } from "./question.js";
|
|
22
|
-
export {
|
|
22
|
+
export { createMemoryTool } from "./memory.js";
|
|
23
23
|
import type { ToolRegistryEntry } from "../types.js";
|
|
24
24
|
import type { ApprovalController } from "../approval/types.js";
|
|
25
25
|
import type { SkillRegistry } from "../skills/registry.js";
|
|
@@ -45,7 +45,7 @@ export interface CreateAllToolsOptions {
|
|
|
45
45
|
* files before mutating them so /rewind can restore.
|
|
46
46
|
*/
|
|
47
47
|
checkpoints?: () => CheckpointStore | undefined;
|
|
48
|
-
/** Shared goal state; when present, registers the
|
|
48
|
+
/** Shared goal state; when present, registers the update_goal tool. */
|
|
49
49
|
goalStore?: GoalStore;
|
|
50
50
|
}
|
|
51
51
|
export declare function createAllTools(cwd: string, skillRegistry?: SkillRegistry, options?: CreateAllToolsOptions): ToolRegistryEntry[];
|
package/dist/tools/index.js
CHANGED
|
@@ -19,7 +19,7 @@ export { createTodoTool } from "./todo.js";
|
|
|
19
19
|
export { createExitPlanModeTool } from "./exit-plan-mode.js";
|
|
20
20
|
export { createToolSearchTool } from "./tool-search.js";
|
|
21
21
|
export { createQuestionTool } from "./question.js";
|
|
22
|
-
export {
|
|
22
|
+
export { createMemoryTool } from "./memory.js";
|
|
23
23
|
import { createBashTool } from "./bash.js";
|
|
24
24
|
import { createManagedServerTools } from "./server.js";
|
|
25
25
|
import { createEditTool } from "./edit.js";
|
|
@@ -38,7 +38,7 @@ import { createWebFetchTool } from "./web-fetch.js";
|
|
|
38
38
|
import { createWebSearchTool } from "./web-search.js";
|
|
39
39
|
import { createWriteTool } from "./write.js";
|
|
40
40
|
import { createQuestionTool } from "./question.js";
|
|
41
|
-
import {
|
|
41
|
+
import { createMemoryTool } from "./memory.js";
|
|
42
42
|
import { FileStateTracker } from "./file-state.js";
|
|
43
43
|
import { createGoalTools } from "../goal/tools.js";
|
|
44
44
|
export function createAllTools(cwd, skillRegistry, options = {}) {
|
|
@@ -56,14 +56,16 @@ export function createAllTools(cwd, skillRegistry, options = {}) {
|
|
|
56
56
|
createLspTool(cwd, lsp, approval),
|
|
57
57
|
createWebSearchTool(),
|
|
58
58
|
createWebFetchTool(approval),
|
|
59
|
-
|
|
60
|
-
createMemoryReadSummaryTool(cwd),
|
|
59
|
+
createMemoryTool(cwd),
|
|
61
60
|
...createAgentLifecycleTools({ cwd, approval }),
|
|
61
|
+
// Always registered: any host can carry deferred tools (MCP entries mark
|
|
62
|
+
// themselves deferred), so every host needs the unlock path. Falls back
|
|
63
|
+
// to the executing agent when no controller is wired.
|
|
64
|
+
createToolSearchTool(options.toolSearchController),
|
|
62
65
|
...(options.questionController ? [createQuestionTool(options.questionController)] : []),
|
|
63
66
|
...(skillRegistry ? [createSkillSearchTool(skillRegistry), createSkillTool(skillRegistry)] : []),
|
|
64
67
|
...(options.todoStore ? [createTodoTool(options.todoStore)] : []),
|
|
65
68
|
...(options.planController ? [createExitPlanModeTool(options.planController)] : []),
|
|
66
|
-
...(options.toolSearchController ? [createToolSearchTool(options.toolSearchController)] : []),
|
|
67
69
|
...(options.goalStore ? createGoalTools(options.goalStore) : []),
|
|
68
70
|
];
|
|
69
71
|
}
|
package/dist/tools/lsp.js
CHANGED
package/dist/tools/memory.d.ts
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
1
|
import type { ToolRegistryEntry } from "../types.js";
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Single entry point to persistent memory: without a query it reads the
|
|
4
|
+
* concise memory summary, with a query it searches memory. One schema in the
|
|
5
|
+
* per-turn tool list instead of two (the read/search split cost context on
|
|
6
|
+
* every turn while both modes share this file's backing code).
|
|
7
|
+
*/
|
|
8
|
+
export declare function createMemoryTool(cwd: string): ToolRegistryEntry;
|