+
{label}
{labelBadge ?
{labelBadge} : null}
@@ -105,7 +114,9 @@ export function SettingsWithControl({
) : null}
-
{children}
+
+ {children}
+
);
}
diff --git a/apps/server/src/services/plugins/plugin-settings.ts b/apps/server/src/services/plugins/plugin-settings.ts
index 4509ba072a..0131fe025f 100644
--- a/apps/server/src/services/plugins/plugin-settings.ts
+++ b/apps/server/src/services/plugins/plugin-settings.ts
@@ -43,6 +43,7 @@ const descriptorSchema = z.discriminatedUnion("type", [
type: z.literal("string"),
...baseFields,
secret: z.literal(true).optional(),
+ multiline: z.literal(true).optional(),
default: z.string().optional(),
})
.strict(),
@@ -182,7 +183,8 @@ export async function readPluginSettingsValues(
) {
parsed = undefined;
}
- values[key] = (parsed as PluginSettingValue | undefined) ?? descriptor.default;
+ values[key] =
+ (parsed as PluginSettingValue | undefined) ?? descriptor.default;
}
return values;
}
diff --git a/apps/server/src/services/skills/builtin-skills/bb-plugin-authoring/SKILL.md b/apps/server/src/services/skills/builtin-skills/bb-plugin-authoring/SKILL.md
index 6dcd6bc7a0..b538208645 100644
--- a/apps/server/src/services/skills/builtin-skills/bb-plugin-authoring/SKILL.md
+++ b/apps/server/src/services/skills/builtin-skills/bb-plugin-authoring/SKILL.md
@@ -206,6 +206,8 @@ in Extensions → Plugins and editable via `bb plugin config
set
const settings = bb.settings.define({
apiKey: { type: "string", label: "API key", secret: true }, // 0600 file, never in db or frontend
teamKey: { type: "string", label: "Team", default: "" },
+ preamble: { type: "string", label: "Preamble", multiline: true, default: "" }, // textarea, not a one-line input
+
mode: {
type: "select",
label: "Mode",
@@ -225,6 +227,14 @@ Typing rule: a descriptor **with** `default` yields a non-optional value
from `get()`; without one the value is `string | boolean | undefined` — so
give non-secrets defaults and handle missing secrets explicitly.
+`multiline: true` renders a string in a textarea on its own full-width row,
+for prose and other newline-bearing text. The stored value is still a plain
+string, so nothing about `get()` changes. It composes with `secret` — use
+both for a PEM key or service-account JSON. A secret textarea stays
+write-only (empty, with a `[set]`/`[not set]` placeholder) but is not
+masked: a textarea has no password mode, and hiding a pasted key would
+obscure the paste without protecting a value the server never sends back.
+
### bb.storage
- `bb.storage.kv` — namespaced JSON key-value rows in bb.db:
@@ -295,22 +305,22 @@ that need the singleton personal project use
methods, not their arguments — read `types/bb-plugin-sdk.d.ts` for exact
signatures.
-| Area | Methods |
-| --- | --- |
-| `threads` | `list` `get` `search` `spawn` `fork` `send` `update` `delete` `stop` `wait` `open` `output` `timeline` `conversationOutline` `promptHistory` `archive` `archiveAll` `unarchive` `pin` `unpin` `reorderPinned` `markRead` `markUnread` `childSummary` `paneAction` `timelineTurnSummaryDetails` `storageFiles` `storagePaths` `cancelPlan` `clearGoal` `continueAfterRateLimit` `rateLimitRecovery` `defaultExecutionOptions`; sub-areas `events` (`list` `wait`), `interactions` (`get` `list` `cancel` `resolve` `respond`), `queuedMessages` (`create` `list` `update` `delete` `send` `reorder` `setGroupBoundary`), `tabs` (`get` `update`) |
-| `threadSections` | `list` `create` `update` `delete` |
-| `projects` | `list` `get` `create` `update` `delete` `reorder` `paths` `files` `fileContent` `branches` `commands` `defaultExecutionOptions` `promptHistory`; sub-areas `attachments` (`upload` `read` `copy`), `sources` (`add` `update` `delete`) |
-| `environments` | `get` `update` `status` `paths` `commit` `archiveThreads` `diff` `diffFile` `diffFiles` `diffBranches` `diffPatch` `pullRequest` `markPullRequestDraft` `markPullRequestReady` `mergePullRequest` `squashMerge` |
-| `hosts` | `list` `get` `update` `delete` `directory` `pathsExist` `pickFolder` `cloneDefaultPath` `createJoinCode` `retryUpdate` `providerCliStatus` `installProviderCli` |
-| `files` | `read` `write` `list` `listPaths` `mkdir` `move` `remove` `createPreview` |
-| `terminals` | `list` `create` `get` `input` `output` `resize` `rename` `restart` `close` |
-| `providers` | `list` `models` |
-| `skills` | `list` `listFiles` `getContent` `update` `remove`; sub-area `registry` (`search` `get` `detail` `install` `repositoryStars`) |
-| `plugins` | `list` `install` `remove` `enable` `disable` `reload` `token` `callRpc` `getSource` `getSettings` `updateSettings` `checkUpdates` `listUpdateResults` `applyUpdate`; sub-area `catalog` (`search` `status` `install`) |
-| `theme` | `get` `catalog` `set` |
-| `status` | `get` |
-| `system` | `version` `config` `reloadConfig` `attention` `usageLimits` `executionOptions` `transcribeVoice` `updateGeneralSettings` `updateKeyboardSettings` `updateExperiments` `cliSkillsStatus` `installCliSkills` `onboardingAgents` `onboardingRepos` `onboardingEvent` |
-| `guide` | `render` (the `bb guide` text; local, no request) |
+| Area | Methods |
+| ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `threads` | `list` `get` `search` `spawn` `fork` `send` `update` `delete` `stop` `wait` `open` `output` `timeline` `conversationOutline` `promptHistory` `archive` `archiveAll` `unarchive` `pin` `unpin` `reorderPinned` `markRead` `markUnread` `childSummary` `paneAction` `timelineTurnSummaryDetails` `storageFiles` `storagePaths` `cancelPlan` `clearGoal` `continueAfterRateLimit` `rateLimitRecovery` `defaultExecutionOptions`; sub-areas `events` (`list` `wait`), `interactions` (`get` `list` `cancel` `resolve` `respond`), `queuedMessages` (`create` `list` `update` `delete` `send` `reorder` `setGroupBoundary`), `tabs` (`get` `update`) |
+| `threadSections` | `list` `create` `update` `delete` |
+| `projects` | `list` `get` `create` `update` `delete` `reorder` `paths` `files` `fileContent` `branches` `commands` `defaultExecutionOptions` `promptHistory`; sub-areas `attachments` (`upload` `read` `copy`), `sources` (`add` `update` `delete`) |
+| `environments` | `get` `update` `status` `paths` `commit` `archiveThreads` `diff` `diffFile` `diffFiles` `diffBranches` `diffPatch` `pullRequest` `markPullRequestDraft` `markPullRequestReady` `mergePullRequest` `squashMerge` |
+| `hosts` | `list` `get` `update` `delete` `directory` `pathsExist` `pickFolder` `cloneDefaultPath` `createJoinCode` `retryUpdate` `providerCliStatus` `installProviderCli` |
+| `files` | `read` `write` `list` `listPaths` `mkdir` `move` `remove` `createPreview` |
+| `terminals` | `list` `create` `get` `input` `output` `resize` `rename` `restart` `close` |
+| `providers` | `list` `models` |
+| `skills` | `list` `listFiles` `getContent` `update` `remove`; sub-area `registry` (`search` `get` `detail` `install` `repositoryStars`) |
+| `plugins` | `list` `install` `remove` `enable` `disable` `reload` `token` `callRpc` `getSource` `getSettings` `updateSettings` `checkUpdates` `listUpdateResults` `applyUpdate`; sub-area `catalog` (`search` `status` `install`) |
+| `theme` | `get` `catalog` `set` |
+| `status` | `get` |
+| `system` | `version` `config` `reloadConfig` `attention` `usageLimits` `executionOptions` `transcribeVoice` `updateGeneralSettings` `updateKeyboardSettings` `updateExperiments` `cliSkillsStatus` `installCliSkills` `onboardingAgents` `onboardingRepos` `onboardingEvent` |
+| `guide` | `render` (the `bb guide` text; local, no request) |
Prefer your own `bb.settings` and `bb.storage` over `sdk.system` and
`sdk.plugins` for your plugin's own configuration. The `system` and `plugins`
diff --git a/apps/server/test/services/plugins/plugin-settings-storage.test.ts b/apps/server/test/services/plugins/plugin-settings-storage.test.ts
index 63ee05e51d..262ec88b75 100644
--- a/apps/server/test/services/plugins/plugin-settings-storage.test.ts
+++ b/apps/server/test/services/plugins/plugin-settings-storage.test.ts
@@ -303,6 +303,73 @@ describe("plugin settings + storage", () => {
expect(entry?.status).toBe("error");
expect(entry?.statusDetail).toContain("broken");
});
+
+ it("round-trips a multiline string with embedded newlines", async () => {
+ const rootDir = await writePlugin(workDir, {
+ name: "bb-plugin-prose",
+ serverSource: `
+ export default async function plugin(bb: any) {
+ const settings = bb.settings.define({
+ preamble: { type: "string", label: "Preamble", multiline: true, default: "" },
+ });
+ (globalThis as any).__prose = { settings };
+ }
+ `,
+ });
+ expect((await service.installPath(rootDir)).status).toBe("running");
+
+ const body = "first\nsecond\n\nfourth";
+ const view = await service.updateSettings("prose", { preamble: body });
+ expect(view?.values.preamble).toBe(body);
+ expect(view?.schema.preamble).toMatchObject({
+ type: "string",
+ multiline: true,
+ });
+
+ const prose = (globalThis as Record).__prose as {
+ settings: { get(): Promise> };
+ };
+ expect((await prose.settings.get()).preamble).toBe(body);
+ });
+
+ it("keeps a secret multiline value in its 0600 file, newlines intact", async () => {
+ const rootDir = await writePlugin(workDir, {
+ name: "bb-plugin-keyholder",
+ serverSource: `
+ export default async function plugin(bb: any) {
+ const settings = bb.settings.define({
+ signingKey: { type: "string", label: "Signing key", secret: true, multiline: true },
+ });
+ (globalThis as any).__keyholder = { settings };
+ }
+ `,
+ });
+ expect((await service.installPath(rootDir)).status).toBe("running");
+
+ const pem =
+ "-----BEGIN PRIVATE KEY-----\nabc\ndef\n-----END PRIVATE KEY-----";
+ const view = await service.updateSettings("keyholder", {
+ signingKey: pem,
+ });
+
+ // Secret storage rules still apply: 0600 file, never on the wire.
+ const secretPath = join(
+ dataDir,
+ "plugins",
+ "keyholder",
+ "secrets",
+ "signingKey",
+ );
+ expect(await readFile(secretPath, "utf8")).toBe(pem);
+ expect((await stat(secretPath)).mode & 0o777).toBe(0o600);
+ expect(view?.values.signingKey).toEqual({ set: true });
+ expect(JSON.stringify(view)).not.toContain("BEGIN PRIVATE KEY");
+
+ const keyholder = (globalThis as Record).__keyholder as {
+ settings: { get(): Promise> };
+ };
+ expect((await keyholder.settings.get()).signingKey).toBe(pem);
+ });
});
describe("kv storage", () => {
diff --git a/packages/plugin-sdk/bundled-types/bb-plugin-sdk.d.ts b/packages/plugin-sdk/bundled-types/bb-plugin-sdk.d.ts
index d1b309de69..c05bfa7edf 100644
--- a/packages/plugin-sdk/bundled-types/bb-plugin-sdk.d.ts
+++ b/packages/plugin-sdk/bundled-types/bb-plugin-sdk.d.ts
@@ -2569,8 +2569,8 @@ type SkillFilesResponse = z$1.infer;
declare const projectResponseSchema: z$1.ZodObject<{
id: z$1.ZodString;
kind: z$1.ZodEnum<{
- standard: "standard";
personal: "personal";
+ standard: "standard";
}>;
name: z$1.ZodString;
gitRemoteUrl: z$1.ZodNullable;
@@ -2591,8 +2591,8 @@ type ProjectResponse = z$1.infer;
declare const projectWithThreadsResponseSchema: z$1.ZodObject<{
id: z$1.ZodString;
kind: z$1.ZodEnum<{
- standard: "standard";
personal: "personal";
+ standard: "standard";
}>;
name: z$1.ZodString;
gitRemoteUrl: z$1.ZodNullable;
@@ -2692,8 +2692,8 @@ declare const projectWithThreadsResponseSchema: z$1.ZodObject<{
ultra: "ultra";
}>;
permissionMode: z$1.ZodEnum<{
- "accept-edits": "accept-edits";
auto: "auto";
+ "accept-edits": "accept-edits";
full: "full";
}>;
}, z$1.core.$strip>>;
@@ -2882,8 +2882,8 @@ declare const environmentDiffFileResponseSchema: z$1.ZodObject<{
path: z$1.ZodString;
content: z$1.ZodString;
contentEncoding: z$1.ZodEnum<{
- base64: "base64";
utf8: "utf8";
+ base64: "base64";
}>;
mimeType: z$1.ZodOptional;
sizeBytes: z$1.ZodNumber;
@@ -3112,8 +3112,8 @@ declare const environmentPullRequestResponseSchema: z$1.ZodDiscriminatedUnion<[z
}>>;
}, z$1.core.$strict>;
attention: z$1.ZodEnum<{
- none: "none";
blocked: "blocked";
+ none: "none";
merged: "merged";
draft: "draft";
closed: "closed";
@@ -3556,8 +3556,8 @@ declare const hostDaemonCommandRegistry: {
}, z$1.core.$strict>], "kind">>;
disallowedTools: z$1.ZodOptional>;
instructionMode: z$1.ZodEnum<{
- append: "append";
replace: "replace";
+ append: "append";
}>;
type: z$1.ZodLiteral<"thread.start">;
requestId: z$1.ZodString;
@@ -4045,8 +4045,8 @@ declare const hostDaemonCommandRegistry: {
}>;
}, z$1.core.$strip>;
instructionMode: z$1.ZodEnum<{
- append: "append";
replace: "replace";
+ append: "append";
}>;
projectId: z$1.ZodString;
providerId: z$1.ZodString;
@@ -4341,8 +4341,8 @@ declare const hostDaemonCommandRegistry: {
}>;
}, z$1.core.$strip>;
instructionMode: z$1.ZodEnum<{
- append: "append";
replace: "replace";
+ append: "append";
}>;
projectId: z$1.ZodString;
providerId: z$1.ZodString;
@@ -5348,9 +5348,9 @@ declare const hostDaemonCommandRegistry: {
executablePath: z$1.ZodNullable;
installed: z$1.ZodBoolean;
installSource: z$1.ZodEnum<{
- external: "external";
notInstalled: "notInstalled";
npmGlobal: "npmGlobal";
+ external: "external";
}>;
currentVersion: z$1.ZodNullable;
latestVersion: z$1.ZodNullable;
@@ -5523,13 +5523,13 @@ declare const hostDaemonCommandRegistry: {
outcome: z$1.ZodLiteral<"unavailable">;
failure: z$1.ZodObject<{
code: z$1.ZodEnum<{
+ unknown: "unknown";
path_not_found: "path_not_found";
not_git_repo: "not_git_repo";
not_worktree: "not_worktree";
workspace_type_mismatch: "workspace_type_mismatch";
permission_denied: "permission_denied";
unknown_environment: "unknown_environment";
- unknown: "unknown";
}>;
workspacePath: z$1.ZodString;
message: z$1.ZodString;
@@ -5573,13 +5573,13 @@ declare const hostDaemonCommandRegistry: {
outcome: z$1.ZodLiteral<"unavailable">;
failure: z$1.ZodObject<{
code: z$1.ZodEnum<{
+ unknown: "unknown";
path_not_found: "path_not_found";
not_git_repo: "not_git_repo";
not_worktree: "not_worktree";
workspace_type_mismatch: "workspace_type_mismatch";
permission_denied: "permission_denied";
unknown_environment: "unknown_environment";
- unknown: "unknown";
}>;
workspacePath: z$1.ZodString;
message: z$1.ZodString;
@@ -5635,13 +5635,13 @@ declare const hostDaemonCommandRegistry: {
outcome: z$1.ZodLiteral<"unavailable">;
failure: z$1.ZodObject<{
code: z$1.ZodEnum<{
+ unknown: "unknown";
path_not_found: "path_not_found";
not_git_repo: "not_git_repo";
not_worktree: "not_worktree";
workspace_type_mismatch: "workspace_type_mismatch";
permission_denied: "permission_denied";
unknown_environment: "unknown_environment";
- unknown: "unknown";
}>;
workspacePath: z$1.ZodString;
message: z$1.ZodString;
@@ -5683,13 +5683,13 @@ declare const hostDaemonCommandRegistry: {
outcome: z$1.ZodLiteral<"unavailable">;
failure: z$1.ZodObject<{
code: z$1.ZodEnum<{
+ unknown: "unknown";
path_not_found: "path_not_found";
not_git_repo: "not_git_repo";
not_worktree: "not_worktree";
workspace_type_mismatch: "workspace_type_mismatch";
permission_denied: "permission_denied";
unknown_environment: "unknown_environment";
- unknown: "unknown";
}>;
workspacePath: z$1.ZodString;
message: z$1.ZodString;
@@ -5807,9 +5807,9 @@ declare const providerCliStatusResponseSchema: z$1.ZodRecord;
installed: z$1.ZodBoolean;
installSource: z$1.ZodEnum<{
- external: "external";
notInstalled: "notInstalled";
npmGlobal: "npmGlobal";
+ external: "external";
}>;
currentVersion: z$1.ZodNullable;
latestVersion: z$1.ZodNullable;
@@ -6339,6 +6339,7 @@ declare const pluginSettingsResponseSchema: z$1.ZodObject<{
schema: z$1.ZodRecord;
secret: z$1.ZodOptional>;
+ multiline: z$1.ZodOptional>;
default: z$1.ZodOptional;
label: z$1.ZodString;
description: z$1.ZodOptional;
@@ -6399,8 +6400,8 @@ declare const systemExecutionOptionsResponseSchema: z$1.ZodObject<{
supportsUserQuestion: z$1.ZodBoolean;
supportsFork: z$1.ZodBoolean;
supportedPermissionModes: z$1.ZodArray>;
}, z$1.core.$strip>;
@@ -6431,8 +6432,8 @@ declare const systemExecutionOptionsResponseSchema: z$1.ZodObject<{
available: z$1.ZodBoolean;
}, z$1.core.$strip>>;
permissionCeiling: z$1.ZodEnum<{
- "accept-edits": "accept-edits";
auto: "auto";
+ "accept-edits": "accept-edits";
full: "full";
}>;
models: z$1.ZodArray>;
permissionMode: z$1.ZodOptional, z$1.ZodLiteral<"workspace-write">]>, z$1.ZodTransform<"accept-edits" | "auto" | "full", "accept-edits" | "auto" | "full" | "workspace-write">>>;
+ }>, z$1.ZodLiteral<"workspace-write">]>, z$1.ZodTransform<"auto" | "accept-edits" | "full", "auto" | "accept-edits" | "full" | "workspace-write">>>;
executionInputSources: z$1.ZodOptional>>>;
title: z$1.ZodOptional;
permissionMode: z$1.ZodOptional, z$1.ZodLiteral<"workspace-write">]>, z$1.ZodTransform<"accept-edits" | "auto" | "full", "accept-edits" | "auto" | "full" | "workspace-write">>>;
+ }>, z$1.ZodLiteral<"workspace-write">]>, z$1.ZodTransform<"auto" | "accept-edits" | "full", "auto" | "accept-edits" | "full" | "workspace-write">>>;
visibility: z$1.ZodDefault>;
permissionMode: z$1.ZodOptional, z$1.ZodLiteral<"workspace-write">]>, z$1.ZodTransform<"accept-edits" | "auto" | "full", "accept-edits" | "auto" | "full" | "workspace-write">>>;
+ }>, z$1.ZodLiteral<"workspace-write">]>, z$1.ZodTransform<"auto" | "accept-edits" | "full", "auto" | "accept-edits" | "full" | "workspace-write">>>;
executionInputSources: z$1.ZodOptional>;
permissionMode: z$1.ZodOptional, z$1.ZodLiteral<"workspace-write">]>, z$1.ZodTransform<"accept-edits" | "auto" | "full", "accept-edits" | "auto" | "full" | "workspace-write">>>;
+ }>, z$1.ZodLiteral<"workspace-write">]>, z$1.ZodTransform<"auto" | "accept-edits" | "full", "auto" | "accept-edits" | "full" | "workspace-write">>>;
executionInputSources: z$1.ZodOptional;
permissionMode: z$1.ZodEnum<{
- "accept-edits": "accept-edits";
auto: "auto";
+ "accept-edits": "accept-edits";
full: "full";
}>;
serviceTier: z$1.ZodEnum<{
@@ -9060,9 +9061,9 @@ declare const threadWithIncludesResponseSchema: z$1.ZodObject<{
isGitRepo: z$1.ZodBoolean;
isWorktree: z$1.ZodBoolean;
workspaceProvisionType: z$1.ZodEnum<{
- personal: "personal";
- "managed-worktree": "managed-worktree";
unmanaged: "unmanaged";
+ "managed-worktree": "managed-worktree";
+ personal: "personal";
}>;
branchName: z$1.ZodNullable;
baseBranch: z$1.ZodNullable;
@@ -9090,8 +9091,8 @@ declare const threadWithIncludesResponseSchema: z$1.ZodObject<{
connected: "connected";
}>;
maxPermissionMode: z$1.ZodEnum<{
- "accept-edits": "accept-edits";
auto: "auto";
+ "accept-edits": "accept-edits";
full: "full";
}>;
lastSeenAt: z$1.ZodNullable;
@@ -9361,8 +9362,8 @@ declare const threadQueuedMessageListResponseSchema: z$1.ZodArray;
permissionMode: z$1.ZodEnum<{
- "accept-edits": "accept-edits";
auto: "auto";
+ "accept-edits": "accept-edits";
full: "full";
}>;
serviceTier: z$1.ZodEnum<{
@@ -9730,8 +9731,8 @@ declare const threadTimelineResponseSchema: z$1.ZodObject<{
originalModel: z$1.ZodString;
fallbackModel: z$1.ZodString;
reason: z$1.ZodEnum<{
- provider: "provider";
refusal: "refusal";
+ provider: "provider";
}>;
message: z$1.ZodString;
}, z$1.core.$strip>>;
@@ -12475,6 +12476,14 @@ type PluginSettingDescriptor = {
description?: string;
/** Stored in a 0600 file under /plugins//secrets/, never in the db or sent to the frontend. */
secret?: true;
+ /**
+ * Render the value in a textarea rather than a single-line input, for
+ * prose and other newline-bearing text. The stored value is still a
+ * plain string; only the control changes. Composes with `secret` —
+ * a pasted PEM key or service-account JSON needs the height, and the
+ * value is write-only either way.
+ */
+ multiline?: true;
default?: string;
} | {
type: "boolean";
diff --git a/packages/plugin-sdk/src/backend-contract.ts b/packages/plugin-sdk/src/backend-contract.ts
index 57fde780e4..523782b86c 100644
--- a/packages/plugin-sdk/src/backend-contract.ts
+++ b/packages/plugin-sdk/src/backend-contract.ts
@@ -44,6 +44,14 @@ export type PluginSettingDescriptor =
description?: string;
/** Stored in a 0600 file under /plugins//secrets/, never in the db or sent to the frontend. */
secret?: true;
+ /**
+ * Render the value in a textarea rather than a single-line input, for
+ * prose and other newline-bearing text. The stored value is still a
+ * plain string; only the control changes. Composes with `secret` —
+ * a pasted PEM key or service-account JSON needs the height, and the
+ * value is write-only either way.
+ */
+ multiline?: true;
default?: string;
}
| { type: "boolean"; label: string; description?: string; default?: boolean }
diff --git a/packages/plugin-sdk/src/testing/fake-plugin-host.ts b/packages/plugin-sdk/src/testing/fake-plugin-host.ts
index 8b8049701a..aadc33494a 100644
--- a/packages/plugin-sdk/src/testing/fake-plugin-host.ts
+++ b/packages/plugin-sdk/src/testing/fake-plugin-host.ts
@@ -283,7 +283,6 @@ export interface FakeAgentToolRecord {
): PluginAgentToolResult | Promise;
}
-
export interface FakeMentionProviderRecord {
id: string;
label: string;
@@ -501,6 +500,7 @@ const settingDescriptorSchema = z.discriminatedUnion("type", [
type: z.literal("string"),
...settingsBaseFields,
secret: z.literal(true).optional(),
+ multiline: z.literal(true).optional(),
default: z.string().optional(),
})
.strict(),
diff --git a/packages/server-contract/src/api/plugins.ts b/packages/server-contract/src/api/plugins.ts
index cb631e542b..24d4639e77 100644
--- a/packages/server-contract/src/api/plugins.ts
+++ b/packages/server-contract/src/api/plugins.ts
@@ -251,6 +251,7 @@ export const pluginSettingDescriptorSchema = z.discriminatedUnion("type", [
...pluginSettingBaseSchema,
type: z.literal("string"),
secret: z.literal(true).optional(),
+ multiline: z.literal(true).optional(),
default: z.string().optional(),
})
.strict(),
diff --git a/packages/templates/src/generated/plugin-sdk-dts.generated.ts b/packages/templates/src/generated/plugin-sdk-dts.generated.ts
index 70ea49c25d..ea747276f5 100644
--- a/packages/templates/src/generated/plugin-sdk-dts.generated.ts
+++ b/packages/templates/src/generated/plugin-sdk-dts.generated.ts
@@ -2,6 +2,6 @@
// Generated by packages/templates/scripts/generate-templates.mjs from
// @bb/plugin-sdk/bundled-types. Do not edit directly.
-export const PLUGIN_SDK_DTS = "// Portable type declarations for `@bb/plugin-sdk`. Unpublished BB\n// workspace contracts are flattened; public subpaths may reuse the\n// package root without requiring any other @bb/* package.\n//\n// Confused by the API, or need a symbol that isn't here? Clone the BB repo\n// and read the real source: https://github.com/get-bb/bb\n\nimport * as react from 'react';\nimport { ComponentType, ReactNode } from 'react';\nimport * as z from 'zod';\nimport { z as z$1 } from 'zod';\nimport Database from 'better-sqlite3';\nimport { Context } from 'hono';\n\n/**\n * App-wide server-backed preferences.\n * Client-local settings stay in the frontend localStorage helpers instead.\n */\ndeclare const appSettingsSchema: z$1.ZodObject<{\n caffeinate: z$1.ZodBoolean;\n showKeyboardHints: z$1.ZodBoolean;\n steerActiveThreadOnEnter: z$1.ZodBoolean;\n showUnhandledProviderEvents: z$1.ZodBoolean;\n codexMemoryEnabled: z$1.ZodBoolean;\n claudeCodeMemoryEnabled: z$1.ZodBoolean;\n codexSubagentsDisabled: z$1.ZodBoolean;\n claudeCodeSubagentsDisabled: z$1.ZodBoolean;\n claudeCodeWorkflowsDisabled: z$1.ZodBoolean;\n onboardingCompletedAt: z$1.ZodNullable;\n}, z$1.core.$strict>;\ntype AppSettings = z$1.infer;\n\ndeclare const appKeybindingOverridesSchema: z$1.ZodArray;\n shortcut: z$1.ZodNullable>;\n}, z$1.core.$strict>>;\ntype AppKeybindingOverrides = z$1.infer;\n\ndeclare const appThemeSchema: z$1.ZodObject<{\n themeId: z$1.ZodString;\n customCss: z$1.ZodNullable;\n faviconColor: z$1.ZodEnum<{\n default: \"default\";\n red: \"red\";\n orange: \"orange\";\n yellow: \"yellow\";\n green: \"green\";\n teal: \"teal\";\n blue: \"blue\";\n purple: \"purple\";\n pink: \"pink\";\n }>;\n}, z$1.core.$strip>;\ntype AppTheme = z$1.infer;\n/**\n * The complete appearance selection a client sends when changing the palette\n * and/or favicon tint. The server validates `themeId` (built-in id or an\n * existing custom theme) and resolves the CSS from disk for custom themes.\n * Callers changing only one facet must carry the other facet forward explicitly.\n */\ndeclare const appThemeSelectionSchema: z$1.ZodObject<{\n themeId: z$1.ZodString;\n faviconColor: z$1.ZodEnum<{\n default: \"default\";\n red: \"red\";\n orange: \"orange\";\n yellow: \"yellow\";\n green: \"green\";\n teal: \"teal\";\n blue: \"blue\";\n purple: \"purple\";\n pink: \"pink\";\n }>;\n}, z$1.core.$strip>;\ntype AppThemeSelection = z$1.infer;\n\ndeclare const changedMessageSchema: z$1.ZodDiscriminatedUnion<[z$1.ZodObject<{\n type: z$1.ZodLiteral<\"changed\">;\n entity: z$1.ZodLiteral<\"thread\">;\n id: z$1.ZodOptional;\n metadata: z$1.ZodOptional;\n eventTypes: z$1.ZodOptional>>>>;\n hasPendingInteraction: z$1.ZodOptional;\n projectId: z$1.ZodOptional;\n }, z$1.core.$strict>>;\n changes: z$1.ZodReadonly>>;\n}, z$1.core.$strict>, z$1.ZodObject<{\n type: z$1.ZodLiteral<\"changed\">;\n entity: z$1.ZodLiteral<\"project\">;\n id: z$1.ZodOptional;\n changes: z$1.ZodReadonly>>;\n}, z$1.core.$strict>, z$1.ZodObject<{\n type: z$1.ZodLiteral<\"changed\">;\n entity: z$1.ZodLiteral<\"environment\">;\n id: z$1.ZodOptional;\n changes: z$1.ZodReadonly>>;\n}, z$1.core.$strict>, z$1.ZodObject<{\n type: z$1.ZodLiteral<\"changed\">;\n entity: z$1.ZodLiteral<\"host\">;\n id: z$1.ZodOptional;\n changes: z$1.ZodReadonly>>;\n}, z$1.core.$strict>, z$1.ZodObject<{\n type: z$1.ZodLiteral<\"changed\">;\n entity: z$1.ZodLiteral<\"system\">;\n changes: z$1.ZodReadonly>>;\n}, z$1.core.$strict>], \"entity\">;\ntype ChangedMessage = z$1.infer;\n\ndeclare const environmentSchema: z$1.ZodObject<{\n id: z$1.ZodString;\n name: z$1.ZodNullable;\n projectId: z$1.ZodString;\n hostId: z$1.ZodString;\n path: z$1.ZodNullable;\n managed: z$1.ZodBoolean;\n isGitRepo: z$1.ZodBoolean;\n isWorktree: z$1.ZodBoolean;\n workspaceProvisionType: z$1.ZodEnum<{\n unmanaged: \"unmanaged\";\n \"managed-worktree\": \"managed-worktree\";\n personal: \"personal\";\n }>;\n branchName: z$1.ZodNullable;\n baseBranch: z$1.ZodNullable;\n defaultBranch: z$1.ZodNullable;\n mergeBaseBranch: z$1.ZodNullable;\n status: z$1.ZodEnum<{\n error: \"error\";\n provisioning: \"provisioning\";\n ready: \"ready\";\n retiring: \"retiring\";\n destroying: \"destroying\";\n destroyed: \"destroyed\";\n }>;\n createdAt: z$1.ZodNumber;\n updatedAt: z$1.ZodNumber;\n}, z$1.core.$strip>;\ntype Environment = z$1.infer;\n\n/**\n * User-opt-in experiments (the Settings → Experiments toggles). Distinct from\n * `FeatureFlags`: flags are operator-set via env at server start, experiments\n * are user-toggled at runtime and persisted server-side so server-owned\n * policy (e.g. skill injection) can honor them.\n *\n * Every experiment defaults to off — opting in is the point.\n */\ndeclare const experimentsSchema: z$1.ZodObject<{\n claudeCodeMockCliTraffic: z$1.ZodBoolean;\n newOnboarding: z$1.ZodBoolean;\n toolsHub: z$1.ZodBoolean;\n}, z$1.core.$strip>;\ntype Experiments = z$1.infer;\n\ndeclare const hostSchema: z$1.ZodObject<{\n id: z$1.ZodString;\n name: z$1.ZodString;\n type: z$1.ZodEnum<{\n persistent: \"persistent\";\n }>;\n status: z$1.ZodEnum<{\n connected: \"connected\";\n disconnected: \"disconnected\";\n }>;\n maxPermissionMode: z$1.ZodEnum<{\n full: \"full\";\n auto: \"auto\";\n \"accept-edits\": \"accept-edits\";\n }>;\n lastSeenAt: z$1.ZodNullable;\n lastRejectedProtocolVersion: z$1.ZodNullable;\n createdAt: z$1.ZodNumber;\n updatedAt: z$1.ZodNumber;\n}, z$1.core.$strip>;\ntype Host = z$1.infer;\n\ninterface JsonObject {\n [key: string]: JsonValue$1;\n}\ntype JsonValue$1 = string | number | boolean | null | JsonValue$1[] | JsonObject;\n\ndeclare const pendingInteractionResolutionSchema: z$1.ZodUnion;\n grantedPermissions: z$1.ZodNullable;\n }, z$1.core.$strip>>;\n fileSystem: z$1.ZodNullable;\n write: z$1.ZodArray;\n }, z$1.core.$strip>>;\n }, z$1.core.$strict>>;\n}, z$1.core.$strip>, z$1.ZodObject<{\n decision: z$1.ZodLiteral<\"allow_for_session\">;\n grantedPermissions: z$1.ZodNullable;\n }, z$1.core.$strip>>;\n fileSystem: z$1.ZodNullable;\n write: z$1.ZodArray;\n }, z$1.core.$strip>>;\n }, z$1.core.$strict>>;\n}, z$1.core.$strip>, z$1.ZodObject<{\n decision: z$1.ZodLiteral<\"deny\">;\n}, z$1.core.$strip>], \"decision\">, z$1.ZodObject<{\n kind: z$1.ZodLiteral<\"user_answer\">;\n answers: z$1.ZodRecord;\n freeText: z$1.ZodOptional;\n }, z$1.core.$strip>>;\n}, z$1.core.$strip>, z$1.ZodObject<{\n kind: z$1.ZodLiteral<\"plugin_submitted\">;\n}, z$1.core.$strip>]>;\ntype PendingInteractionResolution = z$1.infer;\ndeclare const providerPendingInteractionSchema: z$1.ZodObject<{\n id: z$1.ZodString;\n threadId: z$1.ZodString;\n status: z$1.ZodEnum<{\n pending: \"pending\";\n interrupted: \"interrupted\";\n resolving: \"resolving\";\n resolved: \"resolved\";\n }>;\n statusReason: z$1.ZodNullable;\n createdAt: z$1.ZodNumber;\n expiresAt: z$1.ZodOptional>;\n resolvedAt: z$1.ZodNullable;\n turnId: z$1.ZodString;\n providerId: z$1.ZodString;\n providerThreadId: z$1.ZodString;\n providerRequestId: z$1.ZodString;\n origin: z$1.ZodOptional;\n providerId: z$1.ZodString;\n providerThreadId: z$1.ZodString;\n providerRequestId: z$1.ZodString;\n }, z$1.core.$strip>>;\n payload: z$1.ZodUnion;\n subject: z$1.ZodDiscriminatedUnion<[z$1.ZodObject<{\n kind: z$1.ZodLiteral<\"command\">;\n itemId: z$1.ZodString;\n command: z$1.ZodString;\n cwd: z$1.ZodNullable;\n actions: z$1.ZodArray;\n command: z$1.ZodString;\n name: z$1.ZodString;\n path: z$1.ZodString;\n }, z$1.core.$strip>, z$1.ZodObject<{\n type: z$1.ZodLiteral<\"listFiles\">;\n command: z$1.ZodString;\n path: z$1.ZodNullable;\n }, z$1.core.$strip>, z$1.ZodObject<{\n type: z$1.ZodLiteral<\"search\">;\n command: z$1.ZodString;\n query: z$1.ZodNullable;\n path: z$1.ZodNullable;\n }, z$1.core.$strip>, z$1.ZodObject<{\n type: z$1.ZodLiteral<\"unknown\">;\n command: z$1.ZodString;\n }, z$1.core.$strip>], \"type\">>;\n sessionGrant: z$1.ZodNullable;\n }, z$1.core.$strip>>;\n fileSystem: z$1.ZodNullable;\n write: z$1.ZodArray;\n }, z$1.core.$strip>>;\n }, z$1.core.$strict>>;\n }, z$1.core.$strip>, z$1.ZodObject<{\n kind: z$1.ZodLiteral<\"file_change\">;\n itemId: z$1.ZodString;\n writeScope: z$1.ZodNullable;\n sessionGrant: z$1.ZodNullable;\n }, z$1.core.$strip>>;\n fileSystem: z$1.ZodNullable;\n write: z$1.ZodArray;\n }, z$1.core.$strip>>;\n }, z$1.core.$strict>>;\n }, z$1.core.$strip>, z$1.ZodObject<{\n kind: z$1.ZodLiteral<\"permission_grant\">;\n itemId: z$1.ZodString;\n toolName: z$1.ZodNullable;\n permissions: z$1.ZodObject<{\n network: z$1.ZodNullable;\n }, z$1.core.$strip>>;\n fileSystem: z$1.ZodNullable;\n write: z$1.ZodArray;\n }, z$1.core.$strip>>;\n }, z$1.core.$strict>;\n }, z$1.core.$strip>], \"kind\">;\n reason: z$1.ZodNullable;\n availableDecisions: z$1.ZodArray>;\n }, z$1.core.$strip>, z$1.ZodObject<{\n kind: z$1.ZodLiteral<\"user_question\">;\n questions: z$1.ZodArray;\n multiSelect: z$1.ZodBoolean;\n options: z$1.ZodOptional;\n }, z$1.core.$strip>>>;\n allowFreeText: z$1.ZodBoolean;\n }, z$1.core.$strip>>;\n }, z$1.core.$strip>]>;\n resolution: z$1.ZodNullable;\n grantedPermissions: z$1.ZodNullable;\n }, z$1.core.$strip>>;\n fileSystem: z$1.ZodNullable;\n write: z$1.ZodArray;\n }, z$1.core.$strip>>;\n }, z$1.core.$strict>>;\n }, z$1.core.$strip>, z$1.ZodObject<{\n decision: z$1.ZodLiteral<\"allow_for_session\">;\n grantedPermissions: z$1.ZodNullable;\n }, z$1.core.$strip>>;\n fileSystem: z$1.ZodNullable;\n write: z$1.ZodArray;\n }, z$1.core.$strip>>;\n }, z$1.core.$strict>>;\n }, z$1.core.$strip>, z$1.ZodObject<{\n decision: z$1.ZodLiteral<\"deny\">;\n }, z$1.core.$strip>], \"decision\">, z$1.ZodObject<{\n kind: z$1.ZodLiteral<\"user_answer\">;\n answers: z$1.ZodRecord;\n freeText: z$1.ZodOptional;\n }, z$1.core.$strip>>;\n }, z$1.core.$strip>]>>;\n}, z$1.core.$strip>;\ntype ProviderPendingInteraction = z$1.infer;\ndeclare const pluginPendingInteractionSchema: z$1.ZodObject<{\n id: z$1.ZodString;\n threadId: z$1.ZodString;\n status: z$1.ZodEnum<{\n pending: \"pending\";\n interrupted: \"interrupted\";\n resolving: \"resolving\";\n resolved: \"resolved\";\n }>;\n statusReason: z$1.ZodNullable;\n createdAt: z$1.ZodNumber;\n expiresAt: z$1.ZodOptional>;\n resolvedAt: z$1.ZodNullable;\n turnId: z$1.ZodNullable;\n origin: z$1.ZodObject<{\n kind: z$1.ZodLiteral<\"plugin\">;\n pluginId: z$1.ZodString;\n rendererId: z$1.ZodString;\n }, z$1.core.$strip>;\n payload: z$1.ZodObject<{\n kind: z$1.ZodLiteral<\"plugin\">;\n title: z$1.ZodString;\n data: z$1.ZodType>;\n }, z$1.core.$strip>;\n resolution: z$1.ZodNullable;\n }, z$1.core.$strip>>;\n}, z$1.core.$strip>;\ntype PluginPendingInteraction = z$1.infer;\ntype PendingInteraction = ProviderPendingInteraction | PluginPendingInteraction;\n\ndeclare const projectSourceSchema: z$1.ZodObject<{\n id: z$1.ZodString;\n projectId: z$1.ZodString;\n isDefault: z$1.ZodBoolean;\n createdAt: z$1.ZodNumber;\n updatedAt: z$1.ZodNumber;\n type: z$1.ZodLiteral<\"local_path\">;\n hostId: z$1.ZodString;\n path: z$1.ZodString;\n}, z$1.core.$strip>;\ntype ProjectSource = z$1.infer;\n\ndeclare const reasoningLevelSchema: z$1.ZodEnum<{\n none: \"none\";\n low: \"low\";\n medium: \"medium\";\n high: \"high\";\n xhigh: \"xhigh\";\n ultracode: \"ultracode\";\n max: \"max\";\n ultra: \"ultra\";\n}>;\ntype ReasoningLevel = z$1.infer;\ndeclare const serviceTierSchema: z$1.ZodEnum<{\n default: \"default\";\n fast: \"fast\";\n}>;\ntype ServiceTier = z$1.infer;\ndeclare const permissionModeSchema: z$1.ZodEnum<{\n full: \"full\";\n auto: \"auto\";\n \"accept-edits\": \"accept-edits\";\n}>;\ntype PermissionMode = z$1.infer;\ndeclare const promptInputSchema: z$1.ZodDiscriminatedUnion<[z$1.ZodObject<{\n visibility: z$1.ZodOptional>;\n type: z$1.ZodLiteral<\"text\">;\n text: z$1.ZodString;\n mentions: z$1.ZodDefault, z$1.ZodDiscriminatedUnion<[z$1.ZodObject<{\n kind: z$1.ZodLiteral<\"thread\">;\n threadId: z$1.ZodString;\n projectId: z$1.ZodOptional;\n label: z$1.ZodString;\n }, z$1.core.$strip>, z$1.ZodObject<{\n kind: z$1.ZodLiteral<\"project\">;\n projectId: z$1.ZodString;\n label: z$1.ZodString;\n }, z$1.core.$strip>, z$1.ZodObject<{\n kind: z$1.ZodLiteral<\"section\">;\n sectionId: z$1.ZodString;\n label: z$1.ZodString;\n }, z$1.core.$strip>, z$1.ZodObject<{\n kind: z$1.ZodLiteral<\"path\">;\n source: z$1.ZodEnum<{\n workspace: \"workspace\";\n \"thread-storage\": \"thread-storage\";\n }>;\n entryKind: z$1.ZodEnum<{\n file: \"file\";\n directory: \"directory\";\n }>;\n path: z$1.ZodString;\n label: z$1.ZodString;\n }, z$1.core.$strip>, z$1.ZodObject<{\n kind: z$1.ZodLiteral<\"command\">;\n trigger: z$1.ZodEnum<{\n \"/\": \"/\";\n }>;\n name: z$1.ZodString;\n source: z$1.ZodEnum<{\n command: \"command\";\n skill: \"skill\";\n }>;\n origin: z$1.ZodEnum<{\n user: \"user\";\n project: \"project\";\n builtin: \"builtin\";\n }>;\n label: z$1.ZodString;\n argumentHint: z$1.ZodNullable;\n }, z$1.core.$strip>, z$1.ZodObject<{\n kind: z$1.ZodLiteral<\"plugin\">;\n pluginId: z$1.ZodString;\n icon: z$1.ZodOptional>;\n itemId: z$1.ZodString;\n label: z$1.ZodString;\n }, z$1.core.$strip>], \"kind\">>;\n }, z$1.core.$strip>>>;\n}, z$1.core.$strip>, z$1.ZodObject<{\n visibility: z$1.ZodOptional>;\n type: z$1.ZodLiteral<\"image\">;\n url: z$1.ZodString;\n}, z$1.core.$strip>, z$1.ZodObject<{\n visibility: z$1.ZodOptional>;\n type: z$1.ZodLiteral<\"localImage\">;\n path: z$1.ZodString;\n}, z$1.core.$strip>, z$1.ZodObject<{\n visibility: z$1.ZodOptional>;\n type: z$1.ZodLiteral<\"localFile\">;\n path: z$1.ZodString;\n name: z$1.ZodOptional;\n sizeBytes: z$1.ZodOptional;\n mimeType: z$1.ZodOptional;\n}, z$1.core.$strip>], \"type\">;\ntype PromptInput = z$1.infer;\ndeclare const resolvedThreadExecutionOptionsSchema: z$1.ZodObject<{\n seq: z$1.ZodOptional;\n model: z$1.ZodString;\n serviceTier: z$1.ZodEnum<{\n default: \"default\";\n fast: \"fast\";\n }>;\n reasoningLevel: z$1.ZodEnum<{\n none: \"none\";\n low: \"low\";\n medium: \"medium\";\n high: \"high\";\n xhigh: \"xhigh\";\n ultracode: \"ultracode\";\n max: \"max\";\n ultra: \"ultra\";\n }>;\n permissionMode: z$1.ZodEnum<{\n full: \"full\";\n auto: \"auto\";\n \"accept-edits\": \"accept-edits\";\n }>;\n source: z$1.ZodEnum<{\n \"client/thread/start\": \"client/thread/start\";\n \"client/turn/requested\": \"client/turn/requested\";\n \"client/turn/start\": \"client/turn/start\";\n }>;\n}, z$1.core.$strip>;\ntype ResolvedThreadExecutionOptions = z$1.infer;\ndeclare const projectExecutionDefaultsSchema: z$1.ZodObject<{\n providerId: z$1.ZodString;\n model: z$1.ZodString;\n serviceTier: z$1.ZodEnum<{\n default: \"default\";\n fast: \"fast\";\n }>;\n reasoningLevel: z$1.ZodEnum<{\n none: \"none\";\n low: \"low\";\n medium: \"medium\";\n high: \"high\";\n xhigh: \"xhigh\";\n ultracode: \"ultracode\";\n max: \"max\";\n ultra: \"ultra\";\n }>;\n permissionMode: z$1.ZodEnum<{\n full: \"full\";\n auto: \"auto\";\n \"accept-edits\": \"accept-edits\";\n }>;\n}, z$1.core.$strip>;\ntype ProjectExecutionDefaults = z$1.infer;\n\n/** All thread events — provider-originated or system-originated. */\ndeclare const threadEventSchema: z$1.ZodPipe;\n threadId: z$1.ZodString;\n}, z$1.core.$strip>, z$1.ZodObject<{\n type: z$1.ZodLiteral<\"thread/identity\">;\n threadId: z$1.ZodString;\n providerThreadId: z$1.ZodString;\n}, z$1.core.$strip>, z$1.ZodObject<{\n type: z$1.ZodLiteral<\"turn/started\">;\n threadId: z$1.ZodString;\n providerThreadId: z$1.ZodString;\n parentToolCallId: z$1.ZodOptional;\n}, z$1.core.$strip>, z$1.ZodObject<{\n type: z$1.ZodLiteral<\"turn/completed\">;\n threadId: z$1.ZodString;\n providerThreadId: z$1.ZodNullable;\n status: z$1.ZodEnum<{\n completed: \"completed\";\n failed: \"failed\";\n interrupted: \"interrupted\";\n }>;\n error: z$1.ZodOptional>;\n}, z$1.core.$strip>, z$1.ZodObject<{\n type: z$1.ZodLiteral<\"turn/input/accepted\">;\n threadId: z$1.ZodString;\n providerThreadId: z$1.ZodString;\n clientRequestId: z$1.ZodString;\n scope: z$1.ZodDiscriminatedUnion<[z$1.ZodObject<{\n kind: z$1.ZodLiteral<\"thread\">;\n }, z$1.core.$strip>, z$1.ZodObject<{\n kind: z$1.ZodLiteral<\"turn\">;\n turnId: z$1.ZodString;\n }, z$1.core.$strip>], \"kind\">;\n}, z$1.core.$strict>, z$1.ZodObject<{\n type: z$1.ZodLiteral<\"thread/name/updated\">;\n threadId: z$1.ZodString;\n providerThreadId: z$1.ZodString;\n threadName: z$1.ZodString;\n}, z$1.core.$strip>, z$1.ZodObject<{\n type: z$1.ZodLiteral<\"thread/compacted\">;\n threadId: z$1.ZodString;\n providerThreadId: z$1.ZodString;\n}, z$1.core.$strip>, z$1.ZodObject<{\n type: z$1.ZodLiteral<\"thread/goal/updated\">;\n threadId: z$1.ZodString;\n providerThreadId: z$1.ZodString;\n objective: z$1.ZodString;\n status: z$1.ZodEnum<{\n paused: \"paused\";\n active: \"active\";\n budgetLimited: \"budgetLimited\";\n complete: \"complete\";\n }>;\n tokenBudget: z$1.ZodNullable;\n tokensUsed: z$1.ZodNumber;\n timeUsedSeconds: z$1.ZodNumber;\n}, z$1.core.$strip>, z$1.ZodObject<{\n type: z$1.ZodLiteral<\"thread/goal/cleared\">;\n threadId: z$1.ZodString;\n providerThreadId: z$1.ZodString;\n}, z$1.core.$strip>, z$1.ZodObject<{\n type: z$1.ZodLiteral<\"item/started\">;\n threadId: z$1.ZodString;\n providerThreadId: z$1.ZodString;\n item: z$1.ZodDiscriminatedUnion<[z$1.ZodObject<{\n type: z$1.ZodLiteral<\"userMessage\">;\n id: z$1.ZodString;\n content: z$1.ZodArray;\n text: z$1.ZodString;\n }, z$1.core.$strip>, z$1.ZodObject<{\n type: z$1.ZodLiteral<\"image\">;\n url: z$1.ZodString;\n }, z$1.core.$strip>, z$1.ZodObject<{\n type: z$1.ZodLiteral<\"localImage\">;\n path: z$1.ZodString;\n }, z$1.core.$strip>, z$1.ZodObject<{\n type: z$1.ZodLiteral<\"localFile\">;\n path: z$1.ZodString;\n }, z$1.core.$strip>], \"type\">>;\n clientRequestId: z$1.ZodOptional;\n parentToolCallId: z$1.ZodOptional;\n }, z$1.core.$strict>, z$1.ZodObject<{\n type: z$1.ZodLiteral<\"agentMessage\">;\n id: z$1.ZodString;\n text: z$1.ZodString;\n parentToolCallId: z$1.ZodOptional;\n }, z$1.core.$strip>, z$1.ZodObject<{\n type: z$1.ZodLiteral<\"commandExecution\">;\n id: z$1.ZodString;\n command: z$1.ZodString;\n cwd: z$1.ZodString;\n status: z$1.ZodEnum<{\n pending: \"pending\";\n completed: \"completed\";\n failed: \"failed\";\n interrupted: \"interrupted\";\n }>;\n approvalStatus: z$1.ZodNullable>;\n aggregatedOutput: z$1.ZodOptional;\n exitCode: z$1.ZodOptional;\n durationMs: z$1.ZodOptional;\n truncation: z$1.ZodOptional>;\n result: z$1.ZodOptional>;\n resultText: z$1.ZodOptional>;\n }, z$1.core.$strip>>;\n parentToolCallId: z$1.ZodOptional;\n }, z$1.core.$strip>, z$1.ZodObject<{\n type: z$1.ZodLiteral<\"fileChange\">;\n id: z$1.ZodString;\n changes: z$1.ZodArray;\n movePath: z$1.ZodOptional;\n diff: z$1.ZodOptional;\n }, z$1.core.$strip>>;\n status: z$1.ZodEnum<{\n pending: \"pending\";\n completed: \"completed\";\n failed: \"failed\";\n interrupted: \"interrupted\";\n }>;\n approvalStatus: z$1.ZodNullable>;\n parentToolCallId: z$1.ZodOptional;\n }, z$1.core.$strip>, z$1.ZodObject<{\n type: z$1.ZodLiteral<\"webSearch\">;\n id: z$1.ZodString;\n queries: z$1.ZodArray;\n resultText: z$1.ZodNullable;\n parentToolCallId: z$1.ZodOptional;\n }, z$1.core.$strip>, z$1.ZodObject<{\n type: z$1.ZodLiteral<\"webFetch\">;\n id: z$1.ZodString;\n url: z$1.ZodString;\n prompt: z$1.ZodNullable;\n pattern: z$1.ZodNullable;\n resultText: z$1.ZodNullable;\n parentToolCallId: z$1.ZodOptional;\n }, z$1.core.$strip>, z$1.ZodObject<{\n type: z$1.ZodLiteral<\"imageView\">;\n id: z$1.ZodString;\n path: z$1.ZodString;\n parentToolCallId: z$1.ZodOptional;\n }, z$1.core.$strip>, z$1.ZodObject<{\n type: z$1.ZodLiteral<\"toolCall\">;\n id: z$1.ZodString;\n server: z$1.ZodOptional;\n tool: z$1.ZodString;\n arguments: z$1.ZodOptional>;\n statusLabels: z$1.ZodOptional>;\n status: z$1.ZodEnum<{\n pending: \"pending\";\n completed: \"completed\";\n failed: \"failed\";\n interrupted: \"interrupted\";\n }>;\n result: z$1.ZodOptional;\n error: z$1.ZodOptional;\n durationMs: z$1.ZodOptional;\n truncation: z$1.ZodOptional>;\n result: z$1.ZodOptional>;\n resultText: z$1.ZodOptional>;\n }, z$1.core.$strip>>;\n parentToolCallId: z$1.ZodOptional;\n }, z$1.core.$strip>, z$1.ZodObject<{\n type: z$1.ZodLiteral<\"reasoning\">;\n id: z$1.ZodString;\n summary: z$1.ZodArray;\n content: z$1.ZodArray;\n parentToolCallId: z$1.ZodOptional;\n }, z$1.core.$strip>, z$1.ZodObject<{\n type: z$1.ZodLiteral<\"plan\">;\n id: z$1.ZodString;\n text: z$1.ZodString;\n parentToolCallId: z$1.ZodOptional;\n }, z$1.core.$strip>, z$1.ZodObject<{\n type: z$1.ZodLiteral<\"contextCompaction\">;\n id: z$1.ZodString;\n parentToolCallId: z$1.ZodOptional;\n }, z$1.core.$strip>, z$1.ZodObject<{\n type: z$1.ZodLiteral<\"backgroundTask\">;\n id: z$1.ZodString;\n taskType: z$1.ZodString;\n description: z$1.ZodString;\n status: z$1.ZodEnum<{\n pending: \"pending\";\n completed: \"completed\";\n failed: \"failed\";\n interrupted: \"interrupted\";\n }>;\n taskStatus: z$1.ZodEnum<{\n pending: \"pending\";\n running: \"running\";\n paused: \"paused\";\n completed: \"completed\";\n failed: \"failed\";\n killed: \"killed\";\n stopped: \"stopped\";\n }>;\n skipTranscript: z$1.ZodBoolean;\n workflowName: z$1.ZodOptional;\n workflow: z$1.ZodOptional;\n }, z$1.core.$strip>>;\n agents: z$1.ZodArray;\n model: z$1.ZodString;\n attempt: z$1.ZodNumber;\n cached: z$1.ZodBoolean;\n lastProgressAt: z$1.ZodNumber;\n phaseIndex: z$1.ZodOptional;\n phaseTitle: z$1.ZodOptional;\n agentType: z$1.ZodOptional;\n isolation: z$1.ZodOptional;\n queuedAt: z$1.ZodOptional;\n startedAt: z$1.ZodOptional;\n lastToolName: z$1.ZodOptional;\n lastToolSummary: z$1.ZodOptional;\n promptPreview: z$1.ZodOptional;\n resultPreview: z$1.ZodOptional;\n error: z$1.ZodOptional;\n tokens: z$1.ZodOptional;\n toolCalls: z$1.ZodOptional;\n durationMs: z$1.ZodOptional;\n }, z$1.core.$strip>>;\n }, z$1.core.$strip>>;\n usage: z$1.ZodOptional>;\n summary: z$1.ZodOptional;\n error: z$1.ZodOptional;\n outputFile: z$1.ZodOptional;\n parentToolCallId: z$1.ZodOptional;\n }, z$1.core.$strip>], \"type\">;\n}, z$1.core.$strip>, z$1.ZodObject<{\n type: z$1.ZodLiteral<\"item/completed\">;\n threadId: z$1.ZodString;\n providerThreadId: z$1.ZodString;\n item: z$1.ZodDiscriminatedUnion<[z$1.ZodObject<{\n type: z$1.ZodLiteral<\"userMessage\">;\n id: z$1.ZodString;\n content: z$1.ZodArray;\n text: z$1.ZodString;\n }, z$1.core.$strip>, z$1.ZodObject<{\n type: z$1.ZodLiteral<\"image\">;\n url: z$1.ZodString;\n }, z$1.core.$strip>, z$1.ZodObject<{\n type: z$1.ZodLiteral<\"localImage\">;\n path: z$1.ZodString;\n }, z$1.core.$strip>, z$1.ZodObject<{\n type: z$1.ZodLiteral<\"localFile\">;\n path: z$1.ZodString;\n }, z$1.core.$strip>], \"type\">>;\n clientRequestId: z$1.ZodOptional;\n parentToolCallId: z$1.ZodOptional;\n }, z$1.core.$strict>, z$1.ZodObject<{\n type: z$1.ZodLiteral<\"agentMessage\">;\n id: z$1.ZodString;\n text: z$1.ZodString;\n parentToolCallId: z$1.ZodOptional;\n }, z$1.core.$strip>, z$1.ZodObject<{\n type: z$1.ZodLiteral<\"commandExecution\">;\n id: z$1.ZodString;\n command: z$1.ZodString;\n cwd: z$1.ZodString;\n status: z$1.ZodEnum<{\n pending: \"pending\";\n completed: \"completed\";\n failed: \"failed\";\n interrupted: \"interrupted\";\n }>;\n approvalStatus: z$1.ZodNullable>;\n aggregatedOutput: z$1.ZodOptional;\n exitCode: z$1.ZodOptional;\n durationMs: z$1.ZodOptional;\n truncation: z$1.ZodOptional>;\n result: z$1.ZodOptional>;\n resultText: z$1.ZodOptional>;\n }, z$1.core.$strip>>;\n parentToolCallId: z$1.ZodOptional;\n }, z$1.core.$strip>, z$1.ZodObject<{\n type: z$1.ZodLiteral<\"fileChange\">;\n id: z$1.ZodString;\n changes: z$1.ZodArray;\n movePath: z$1.ZodOptional;\n diff: z$1.ZodOptional;\n }, z$1.core.$strip>>;\n status: z$1.ZodEnum<{\n pending: \"pending\";\n completed: \"completed\";\n failed: \"failed\";\n interrupted: \"interrupted\";\n }>;\n approvalStatus: z$1.ZodNullable>;\n parentToolCallId: z$1.ZodOptional;\n }, z$1.core.$strip>, z$1.ZodObject<{\n type: z$1.ZodLiteral<\"webSearch\">;\n id: z$1.ZodString;\n queries: z$1.ZodArray;\n resultText: z$1.ZodNullable;\n parentToolCallId: z$1.ZodOptional;\n }, z$1.core.$strip>, z$1.ZodObject<{\n type: z$1.ZodLiteral<\"webFetch\">;\n id: z$1.ZodString;\n url: z$1.ZodString;\n prompt: z$1.ZodNullable;\n pattern: z$1.ZodNullable;\n resultText: z$1.ZodNullable;\n parentToolCallId: z$1.ZodOptional;\n }, z$1.core.$strip>, z$1.ZodObject<{\n type: z$1.ZodLiteral<\"imageView\">;\n id: z$1.ZodString;\n path: z$1.ZodString;\n parentToolCallId: z$1.ZodOptional;\n }, z$1.core.$strip>, z$1.ZodObject<{\n type: z$1.ZodLiteral<\"toolCall\">;\n id: z$1.ZodString;\n server: z$1.ZodOptional;\n tool: z$1.ZodString;\n arguments: z$1.ZodOptional>;\n statusLabels: z$1.ZodOptional>;\n status: z$1.ZodEnum<{\n pending: \"pending\";\n completed: \"completed\";\n failed: \"failed\";\n interrupted: \"interrupted\";\n }>;\n result: z$1.ZodOptional;\n error: z$1.ZodOptional;\n durationMs: z$1.ZodOptional;\n truncation: z$1.ZodOptional>;\n result: z$1.ZodOptional>;\n resultText: z$1.ZodOptional>;\n }, z$1.core.$strip>>;\n parentToolCallId: z$1.ZodOptional;\n }, z$1.core.$strip>, z$1.ZodObject<{\n type: z$1.ZodLiteral<\"reasoning\">;\n id: z$1.ZodString;\n summary: z$1.ZodArray;\n content: z$1.ZodArray;\n parentToolCallId: z$1.ZodOptional;\n }, z$1.core.$strip>, z$1.ZodObject<{\n type: z$1.ZodLiteral<\"plan\">;\n id: z$1.ZodString;\n text: z$1.ZodString;\n parentToolCallId: z$1.ZodOptional