diff --git a/packages/cli/src/cli-api.ts b/packages/cli/src/cli-api.ts index 5c1e6b200..038428d66 100644 --- a/packages/cli/src/cli-api.ts +++ b/packages/cli/src/cli-api.ts @@ -29,7 +29,9 @@ export function make< return { name, spec, - commands: Object.fromEntries((options.commands ?? []).map((command) => [command.name, command])) as ChildrenOf, + commands: Object.fromEntries( + (options.commands ?? []).map((command) => [command.name, command]), + ) as ChildrenOf, } } diff --git a/packages/cli/src/cli-builder.ts b/packages/cli/src/cli-builder.ts index f58b7fd87..e3dbaf8cb 100644 --- a/packages/cli/src/cli-builder.ts +++ b/packages/cli/src/cli-builder.ts @@ -2,11 +2,12 @@ import * as Effect from "effect/Effect" import * as Command from "effect/unstable/cli/Command" import { CliApi } from "./cli-api" -export type Input = Value extends CliApi.Node - ? Input - : Value extends Command.Command - ? Input - : never +export type Input = + Value extends CliApi.Node + ? Input + : Value extends Command.Command + ? Input + : never type RuntimeHandler = (input: unknown) => Effect.Effect type Loader = () => Promise<{ default: (input: Input) => Effect.Effect }> @@ -21,7 +22,12 @@ interface LazyHandler { readonly load: () => Promise<{ default: RuntimeHandler }> } -type RuntimeHandlers = (() => Promise<{ default: RuntimeHandler }>) | { readonly $?: () => Promise<{ default: RuntimeHandler }>; readonly [key: string]: RuntimeHandlers | (() => Promise<{ default: RuntimeHandler }>) | undefined } +type RuntimeHandlers = + | (() => Promise<{ default: RuntimeHandler }>) + | { + readonly $?: () => Promise<{ default: RuntimeHandler }> + readonly [key: string]: RuntimeHandlers | (() => Promise<{ default: RuntimeHandler }>) | undefined + } export function handler( _node: Node, diff --git a/packages/cli/src/handlers/debug/agents.ts b/packages/cli/src/handlers/debug/agents.ts index 7df07a8e2..85eec4555 100644 --- a/packages/cli/src/handlers/debug/agents.ts +++ b/packages/cli/src/handlers/debug/agents.ts @@ -7,11 +7,24 @@ import * as Effect from "effect/Effect" import { Api } from "../../api" import { CliBuilder } from "../../cli-builder" -export default CliBuilder.handler(Api.commands.debug.commands.agents, Effect.fn("cli.debug.agents")(function* () { - const svc = { - plugin: yield* PluginBoot.Service, - agent: yield* AgentV2.Service, - } - yield* svc.plugin.wait() - process.stdout.write(JSON.stringify((yield* svc.agent.all()).sort((a, b) => a.id.localeCompare(b.id)), null, 2) + EOL) -}, Effect.provide(LocationServiceMap.get({ directory: AbsolutePath.make(process.cwd()) })), Effect.provide(LocationServiceMap.layer))) +export default CliBuilder.handler( + Api.commands.debug.commands.agents, + Effect.fn("cli.debug.agents")( + function* () { + const svc = { + plugin: yield* PluginBoot.Service, + agent: yield* AgentV2.Service, + } + yield* svc.plugin.wait() + process.stdout.write( + JSON.stringify( + (yield* svc.agent.all()).sort((a, b) => a.id.localeCompare(b.id)), + null, + 2, + ) + EOL, + ) + }, + Effect.provide(LocationServiceMap.get({ directory: AbsolutePath.make(process.cwd()) })), + Effect.provide(LocationServiceMap.layer), + ), +) diff --git a/packages/core/src/session/projector.ts b/packages/core/src/session/projector.ts index c0a833836..413332f83 100644 --- a/packages/core/src/session/projector.ts +++ b/packages/core/src/session/projector.ts @@ -76,9 +76,7 @@ function messageData( return rest as DeepMutable } -function partData( - part: (typeof SessionV1.Event.PartUpdated.Type)["data"]["part"], -): typeof PartTable.$inferInsert.data { +function partData(part: (typeof SessionV1.Event.PartUpdated.Type)["data"]["part"]): typeof PartTable.$inferInsert.data { const { id: _, messageID: __, sessionID: ___, ...rest } = part return rest as DeepMutable } diff --git a/packages/core/src/v1/config/config.ts b/packages/core/src/v1/config/config.ts index dfd60a4ed..f24d378d0 100644 --- a/packages/core/src/v1/config/config.ts +++ b/packages/core/src/v1/config/config.ts @@ -30,40 +30,157 @@ const LogLevelRef = Schema.Literals(["DEBUG", "INFO", "WARN", "ERROR"]).annotate }) export const Info = Schema.Struct({ - $schema: Schema.optional(Schema.String).annotate({ description: "JSON schema reference for configuration validation" }), + $schema: Schema.optional(Schema.String).annotate({ + description: "JSON schema reference for configuration validation", + }), shell: Schema.optional(Schema.String).annotate({ description: "Default shell to use for terminal and bash tool" }), logLevel: Schema.optional(LogLevelRef).annotate({ description: "Log level" }), - server: Schema.optional(ConfigServerV1.Server).annotate({ description: "Server configuration for opencode serve and web commands" }), - command: Schema.optional(Schema.Record(Schema.String, ConfigCommandV1.Info)).annotate({ description: "Command configuration, see https://opencode.ai/docs/commands" }), + server: Schema.optional(ConfigServerV1.Server).annotate({ + description: "Server configuration for opencode serve and web commands", + }), + command: Schema.optional(Schema.Record(Schema.String, ConfigCommandV1.Info)).annotate({ + description: "Command configuration, see https://opencode.ai/docs/commands", + }), skills: Schema.optional(ConfigSkillsV1.Info).annotate({ description: "Additional skill folder paths" }), - reference: Schema.optional(ConfigReferenceV1.Info).annotate({ description: "Named git or local directory references that can be mentioned as @alias or @alias/path" }), + reference: Schema.optional(ConfigReferenceV1.Info).annotate({ + description: "Named git or local directory references that can be mentioned as @alias or @alias/path", + }), watcher: Schema.optional(Schema.Struct({ ignore: Schema.optional(Schema.mutable(Schema.Array(Schema.String))) })), - snapshot: Schema.optional(Schema.Boolean).annotate({ description: "Enable or disable snapshot tracking. When false, filesystem snapshots are not recorded and undoing or reverting will not undo/redo file changes. Defaults to true." }), + snapshot: Schema.optional(Schema.Boolean).annotate({ + description: + "Enable or disable snapshot tracking. When false, filesystem snapshots are not recorded and undoing or reverting will not undo/redo file changes. Defaults to true.", + }), plugin: Schema.optional(Schema.mutable(Schema.Array(ConfigPluginV1.Spec))), - share: Schema.optional(Schema.Literals(["manual", "auto", "disabled"])).annotate({ description: "Control sharing behavior:'manual' allows manual sharing via commands, 'auto' enables automatic sharing, 'disabled' disables all sharing" }), - autoshare: Schema.optional(Schema.Boolean).annotate({ description: "@deprecated Use 'share' field instead. Share newly created sessions automatically" }), - autoupdate: Schema.optional(Schema.Union([Schema.Boolean, Schema.Literal("notify")])).annotate({ description: "Automatically update to the latest version. Set to true to auto-update, false to disable, or 'notify' to show update notifications" }), - disabled_providers: Schema.optional(Schema.mutable(Schema.Array(Schema.String))).annotate({ description: "Disable providers that are loaded automatically" }), - enabled_providers: Schema.optional(Schema.mutable(Schema.Array(Schema.String))).annotate({ description: "When set, ONLY these providers will be enabled. All other providers will be ignored" }), - model: Schema.optional(Schema.String).annotate({ description: "Model to use in the format of provider/model, eg anthropic/claude-2" }), - small_model: Schema.optional(Schema.String).annotate({ description: "Small model to use for tasks like title generation in the format of provider/model" }), - default_agent: Schema.optional(Schema.String).annotate({ description: "Default agent to use when none is specified. Must be a primary agent. Falls back to 'build' if not set or if the specified agent is invalid." }), - username: Schema.optional(Schema.String).annotate({ description: "Custom username to display in conversations instead of system username" }), - mode: Schema.optional(Schema.StructWithRest(Schema.Struct({ build: Schema.optional(ConfigAgentV1.Info), plan: Schema.optional(ConfigAgentV1.Info) }), [Schema.Record(Schema.String, ConfigAgentV1.Info)])).annotate({ description: "@deprecated Use `agent` field instead." }), - agent: Schema.optional(Schema.StructWithRest(Schema.Struct({ plan: Schema.optional(ConfigAgentV1.Info), build: Schema.optional(ConfigAgentV1.Info), general: Schema.optional(ConfigAgentV1.Info), explore: Schema.optional(ConfigAgentV1.Info), title: Schema.optional(ConfigAgentV1.Info), summary: Schema.optional(ConfigAgentV1.Info), compaction: Schema.optional(ConfigAgentV1.Info) }), [Schema.Record(Schema.String, ConfigAgentV1.Info)])).annotate({ description: "Agent configuration, see https://opencode.ai/docs/agents" }), - provider: Schema.optional(Schema.Record(Schema.String, ConfigProviderV1.Info)).annotate({ description: "Custom provider configurations and model overrides" }), - mcp: Schema.optional(Schema.Record(Schema.String, Schema.Union([ConfigMCPV1.Info, Schema.Struct({ enabled: Schema.Boolean })]))).annotate({ description: "MCP (Model Context Protocol) server configurations" }), - formatter: Schema.optional(ConfigFormatterV1.Info).annotate({ description: "Enable or configure formatters. Omit or set to false to disable, true to enable built-ins, or an object to enable built-ins with overrides." }), - lsp: Schema.optional(ConfigLSPV1.Info).annotate({ description: "Enable or configure LSP servers. Omit or set to false to disable, true to enable built-ins, or an object to enable built-ins with overrides." }), - instructions: Schema.optional(Schema.mutable(Schema.Array(Schema.String))).annotate({ description: "Additional instruction files or patterns to include" }), + share: Schema.optional(Schema.Literals(["manual", "auto", "disabled"])).annotate({ + description: + "Control sharing behavior:'manual' allows manual sharing via commands, 'auto' enables automatic sharing, 'disabled' disables all sharing", + }), + autoshare: Schema.optional(Schema.Boolean).annotate({ + description: "@deprecated Use 'share' field instead. Share newly created sessions automatically", + }), + autoupdate: Schema.optional(Schema.Union([Schema.Boolean, Schema.Literal("notify")])).annotate({ + description: + "Automatically update to the latest version. Set to true to auto-update, false to disable, or 'notify' to show update notifications", + }), + disabled_providers: Schema.optional(Schema.mutable(Schema.Array(Schema.String))).annotate({ + description: "Disable providers that are loaded automatically", + }), + enabled_providers: Schema.optional(Schema.mutable(Schema.Array(Schema.String))).annotate({ + description: "When set, ONLY these providers will be enabled. All other providers will be ignored", + }), + model: Schema.optional(Schema.String).annotate({ + description: "Model to use in the format of provider/model, eg anthropic/claude-2", + }), + small_model: Schema.optional(Schema.String).annotate({ + description: "Small model to use for tasks like title generation in the format of provider/model", + }), + default_agent: Schema.optional(Schema.String).annotate({ + description: + "Default agent to use when none is specified. Must be a primary agent. Falls back to 'build' if not set or if the specified agent is invalid.", + }), + username: Schema.optional(Schema.String).annotate({ + description: "Custom username to display in conversations instead of system username", + }), + mode: Schema.optional( + Schema.StructWithRest( + Schema.Struct({ build: Schema.optional(ConfigAgentV1.Info), plan: Schema.optional(ConfigAgentV1.Info) }), + [Schema.Record(Schema.String, ConfigAgentV1.Info)], + ), + ).annotate({ description: "@deprecated Use `agent` field instead." }), + agent: Schema.optional( + Schema.StructWithRest( + Schema.Struct({ + plan: Schema.optional(ConfigAgentV1.Info), + build: Schema.optional(ConfigAgentV1.Info), + general: Schema.optional(ConfigAgentV1.Info), + explore: Schema.optional(ConfigAgentV1.Info), + title: Schema.optional(ConfigAgentV1.Info), + summary: Schema.optional(ConfigAgentV1.Info), + compaction: Schema.optional(ConfigAgentV1.Info), + }), + [Schema.Record(Schema.String, ConfigAgentV1.Info)], + ), + ).annotate({ description: "Agent configuration, see https://opencode.ai/docs/agents" }), + provider: Schema.optional(Schema.Record(Schema.String, ConfigProviderV1.Info)).annotate({ + description: "Custom provider configurations and model overrides", + }), + mcp: Schema.optional( + Schema.Record(Schema.String, Schema.Union([ConfigMCPV1.Info, Schema.Struct({ enabled: Schema.Boolean })])), + ).annotate({ description: "MCP (Model Context Protocol) server configurations" }), + formatter: Schema.optional(ConfigFormatterV1.Info).annotate({ + description: + "Enable or configure formatters. Omit or set to false to disable, true to enable built-ins, or an object to enable built-ins with overrides.", + }), + lsp: Schema.optional(ConfigLSPV1.Info).annotate({ + description: + "Enable or configure LSP servers. Omit or set to false to disable, true to enable built-ins, or an object to enable built-ins with overrides.", + }), + instructions: Schema.optional(Schema.mutable(Schema.Array(Schema.String))).annotate({ + description: "Additional instruction files or patterns to include", + }), layout: Schema.optional(ConfigLayoutV1.Layout).annotate({ description: "@deprecated Always uses stretch layout." }), permission: Schema.optional(ConfigPermissionV1.Info), tools: Schema.optional(Schema.Record(Schema.String, Schema.Boolean)), - attachment: Schema.optional(ConfigAttachmentV1.Info).annotate({ description: "Attachment processing configuration, including image size limits and resizing behavior" }), - enterprise: Schema.optional(Schema.Struct({ url: Schema.optional(Schema.String).annotate({ description: "Enterprise URL" }) })), - tool_output: Schema.optional(Schema.Struct({ max_lines: Schema.optional(PositiveInt).annotate({ description: "Maximum lines of tool output before it is truncated and saved to disk (default: 2000)" }), max_bytes: Schema.optional(PositiveInt).annotate({ description: "Maximum bytes of tool output before it is truncated and saved to disk (default: 51200)" }) })).annotate({ description: "Thresholds for truncating tool output. When output exceeds either limit, the full text is written to the truncation directory and a preview is returned." }), - compaction: Schema.optional(Schema.Struct({ auto: Schema.optional(Schema.Boolean).annotate({ description: "Enable automatic compaction when context is full (default: true)" }), prune: Schema.optional(Schema.Boolean).annotate({ description: "Enable pruning of old tool outputs (default: true)" }), tail_turns: Schema.optional(NonNegativeInt).annotate({ description: "Number of recent user turns, including their following assistant/tool responses, to keep verbatim during compaction (default: 2)" }), preserve_recent_tokens: Schema.optional(NonNegativeInt).annotate({ description: "Maximum number of tokens from recent turns to preserve verbatim after compaction" }), reserved: Schema.optional(NonNegativeInt).annotate({ description: "Token buffer for compaction. Leaves enough window to avoid overflow during compaction." }) })), - experimental: Schema.optional(Schema.Struct({ disable_paste_summary: Schema.optional(Schema.Boolean), batch_tool: Schema.optional(Schema.Boolean).annotate({ description: "Enable the batch tool" }), openTelemetry: Schema.optional(Schema.Boolean).annotate({ description: "Enable OpenTelemetry spans for AI SDK calls (using the 'experimental_telemetry' flag)" }), primary_tools: Schema.optional(Schema.mutable(Schema.Array(Schema.String))).annotate({ description: "Tools that should only be available to primary agents." }), continue_loop_on_deny: Schema.optional(Schema.Boolean).annotate({ description: "Continue the agent loop when a tool call is denied" }), mcp_timeout: Schema.optional(PositiveInt).annotate({ description: "Timeout in milliseconds for model context protocol (MCP) requests" }), policies: Schema.optional(Schema.mutable(Schema.Array(ConfigExperimental.Policy))).annotate({ description: "Policy statements applied to supported resources, such as provider access" }) })), + attachment: Schema.optional(ConfigAttachmentV1.Info).annotate({ + description: "Attachment processing configuration, including image size limits and resizing behavior", + }), + enterprise: Schema.optional( + Schema.Struct({ url: Schema.optional(Schema.String).annotate({ description: "Enterprise URL" }) }), + ), + tool_output: Schema.optional( + Schema.Struct({ + max_lines: Schema.optional(PositiveInt).annotate({ + description: "Maximum lines of tool output before it is truncated and saved to disk (default: 2000)", + }), + max_bytes: Schema.optional(PositiveInt).annotate({ + description: "Maximum bytes of tool output before it is truncated and saved to disk (default: 51200)", + }), + }), + ).annotate({ + description: + "Thresholds for truncating tool output. When output exceeds either limit, the full text is written to the truncation directory and a preview is returned.", + }), + compaction: Schema.optional( + Schema.Struct({ + auto: Schema.optional(Schema.Boolean).annotate({ + description: "Enable automatic compaction when context is full (default: true)", + }), + prune: Schema.optional(Schema.Boolean).annotate({ + description: "Enable pruning of old tool outputs (default: true)", + }), + tail_turns: Schema.optional(NonNegativeInt).annotate({ + description: + "Number of recent user turns, including their following assistant/tool responses, to keep verbatim during compaction (default: 2)", + }), + preserve_recent_tokens: Schema.optional(NonNegativeInt).annotate({ + description: "Maximum number of tokens from recent turns to preserve verbatim after compaction", + }), + reserved: Schema.optional(NonNegativeInt).annotate({ + description: "Token buffer for compaction. Leaves enough window to avoid overflow during compaction.", + }), + }), + ), + experimental: Schema.optional( + Schema.Struct({ + disable_paste_summary: Schema.optional(Schema.Boolean), + batch_tool: Schema.optional(Schema.Boolean).annotate({ description: "Enable the batch tool" }), + openTelemetry: Schema.optional(Schema.Boolean).annotate({ + description: "Enable OpenTelemetry spans for AI SDK calls (using the 'experimental_telemetry' flag)", + }), + primary_tools: Schema.optional(Schema.mutable(Schema.Array(Schema.String))).annotate({ + description: "Tools that should only be available to primary agents.", + }), + continue_loop_on_deny: Schema.optional(Schema.Boolean).annotate({ + description: "Continue the agent loop when a tool call is denied", + }), + mcp_timeout: Schema.optional(PositiveInt).annotate({ + description: "Timeout in milliseconds for model context protocol (MCP) requests", + }), + policies: Schema.optional(Schema.mutable(Schema.Array(ConfigExperimental.Policy))).annotate({ + description: "Policy statements applied to supported resources, such as provider access", + }), + }), + ), }).annotate({ identifier: "Config" }) export type Info = DeepMutable> diff --git a/packages/core/src/v1/config/migrate.ts b/packages/core/src/v1/config/migrate.ts index faf24d486..4db2c2d88 100644 --- a/packages/core/src/v1/config/migrate.ts +++ b/packages/core/src/v1/config/migrate.ts @@ -136,19 +136,19 @@ function mcp(info: typeof ConfigV1.Info.Type) { function migrateMcp(info: ConfigMCPV1.Info) { const disabled = info.enabled === undefined ? undefined : !info.enabled - if (info.type === "local") return { type: info.type, command: info.command, environment: info.environment, disabled, timeout: info.timeout } + if (info.type === "local") + return { type: info.type, command: info.command, environment: info.environment, disabled, timeout: info.timeout } return { type: info.type, url: info.url, headers: info.headers, - oauth: - info.oauth && { - client_id: info.oauth.clientId, - client_secret: info.oauth.clientSecret, - scope: info.oauth.scope, - callback_port: info.oauth.callbackPort, - redirect_uri: info.oauth.redirectUri, - }, + oauth: info.oauth && { + client_id: info.oauth.clientId, + client_secret: info.oauth.clientSecret, + scope: info.oauth.scope, + callback_port: info.oauth.callbackPort, + redirect_uri: info.oauth.redirectUri, + }, disabled, timeout: info.timeout, } @@ -169,7 +169,9 @@ function migrateProvider(info: ConfigProviderV1.Info) { url: info.api ?? (typeof info.options?.baseURL === "string" ? info.options.baseURL : undefined), }, options: info.options && { body: info.options }, - models: info.models && Object.fromEntries(Object.entries(info.models).map(([name, model]) => [name, migrateModel(model)])), + models: + info.models && + Object.fromEntries(Object.entries(info.models).map(([name, model]) => [name, migrateModel(model)])), } } @@ -202,9 +204,7 @@ function migrateModel(info: typeof ConfigProviderV1.Model.Type) { endpoint: info.provider?.npm && { type: "aisdk" as const, package: info.provider.npm, url: info.provider.api }, capabilities, options: (info.headers || info.options) && { headers: info.headers, body: info.options }, - variants: - info.variants && - Object.entries(info.variants).map(([id, options]) => ({ id, body: options })), + variants: info.variants && Object.entries(info.variants).map(([id, options]) => ({ id, body: options })), cost: costs, disabled: info.status === "deprecated" ? true : undefined, limit: info.limit, diff --git a/packages/core/src/v1/config/provider.ts b/packages/core/src/v1/config/provider.ts index 0c8bcac2b..f6cae7d78 100644 --- a/packages/core/src/v1/config/provider.ts +++ b/packages/core/src/v1/config/provider.ts @@ -55,7 +55,9 @@ export const Model = Schema.Struct({ ), experimental: Schema.optional(Schema.Boolean), status: Schema.optional(ModelStatus), - provider: Schema.optional(Schema.Struct({ npm: Schema.optional(Schema.String), api: Schema.optional(Schema.String) })), + provider: Schema.optional( + Schema.Struct({ npm: Schema.optional(Schema.String), api: Schema.optional(Schema.String) }), + ), options: Schema.optional(Schema.Record(Schema.String, Schema.Any)), headers: Schema.optional(Schema.Record(Schema.String, Schema.String)), variants: Schema.optional( diff --git a/packages/core/test/config/config.test.ts b/packages/core/test/config/config.test.ts index ea1f38bd5..070183c6d 100644 --- a/packages/core/test/config/config.test.ts +++ b/packages/core/test/config/config.test.ts @@ -372,7 +372,10 @@ describe("Config", () => { permission: { read: "allow" }, }, }, - plugin: ["opencode-helicone-session", ["@my-org/audit-plugin", { endpoint: "https://audit.example.com" }]], + plugin: [ + "opencode-helicone-session", + ["@my-org/audit-plugin", { endpoint: "https://audit.example.com" }], + ], skills: { paths: ["./skills"], urls: ["https://example.com/.well-known/skills/"] }, reference: { docs: { path: "../docs" } }, attachment: { image: { auto_resize: false, max_width: 1200 } }, diff --git a/packages/opencode/src/config/plugin.ts b/packages/opencode/src/config/plugin.ts index 2807b9ad9..60bba4d63 100644 --- a/packages/opencode/src/config/plugin.ts +++ b/packages/opencode/src/config/plugin.ts @@ -39,7 +39,10 @@ export function pluginOptions(plugin: ConfigPluginV1.Spec): ConfigPluginV1.Optio // Path-like specs are resolved relative to the config file that declared them so merges later on do not // accidentally reinterpret `./plugin.ts` relative to some other directory. -export async function resolvePluginSpec(plugin: ConfigPluginV1.Spec, configFilepath: string): Promise { +export async function resolvePluginSpec( + plugin: ConfigPluginV1.Spec, + configFilepath: string, +): Promise { const spec = pluginSpecifier(plugin) if (!isPathPluginSpec(spec)) return plugin diff --git a/packages/opencode/src/permission/index.ts b/packages/opencode/src/permission/index.ts index e92efa7e1..8e8a84498 100644 --- a/packages/opencode/src/permission/index.ts +++ b/packages/opencode/src/permission/index.ts @@ -38,11 +38,7 @@ interface State { approved: PermissionV1.Rule[] } -export function evaluate( - permission: string, - pattern: string, - ...rulesets: PermissionV1.Ruleset[] -): PermissionV1.Rule { +export function evaluate(permission: string, pattern: string, ...rulesets: PermissionV1.Ruleset[]): PermissionV1.Rule { return ( rulesets .flat() diff --git a/packages/opencode/src/session/compaction.ts b/packages/opencode/src/session/compaction.ts index ae7762075..c714fd414 100644 --- a/packages/opencode/src/session/compaction.ts +++ b/packages/opencode/src/session/compaction.ts @@ -354,9 +354,7 @@ export const layer = Layer.effect( throw new Error(`Compaction parent must be a user message: ${input.parentID}`) } const userMessage = parent.info - const compactionPart = parent.parts.find( - (part): part is SessionV1.CompactionPart => part.type === "compaction", - ) + const compactionPart = parent.parts.find((part): part is SessionV1.CompactionPart => part.type === "compaction") let messages = input.messages let replay: diff --git a/packages/opencode/src/session/prompt.ts b/packages/opencode/src/session/prompt.ts index 1c2ac53c9..1486f203f 100644 --- a/packages/opencode/src/session/prompt.ts +++ b/packages/opencode/src/session/prompt.ts @@ -1500,11 +1500,11 @@ export const layer = Layer.effect( }, ) - const loop: (input: LoopInput) => Effect.Effect = Effect.fn("SessionPrompt.loop")( - function* (input: LoopInput) { - return yield* state.ensureRunning(input.sessionID, lastAssistant(input.sessionID), runLoop(input.sessionID)) - }, - ) + const loop: (input: LoopInput) => Effect.Effect = Effect.fn("SessionPrompt.loop")(function* ( + input: LoopInput, + ) { + return yield* state.ensureRunning(input.sessionID, lastAssistant(input.sessionID), runLoop(input.sessionID)) + }) const shell: (input: ShellInput) => Effect.Effect = Effect.fn( "SessionPrompt.shell", diff --git a/packages/opencode/src/session/session.ts b/packages/opencode/src/session/session.ts index c854b4fc2..4b577bc38 100644 --- a/packages/opencode/src/session/session.ts +++ b/packages/opencode/src/session/session.ts @@ -493,10 +493,7 @@ export interface Interface { readonly setShare: (input: { sessionID: SessionID; share: Info["share"] }) => Effect.Effect readonly setWorkspace: (input: { sessionID: SessionID; workspaceID: Info["workspaceID"] }) => Effect.Effect readonly diff: (sessionID: SessionID) => Effect.Effect - readonly messages: (input: { - sessionID: SessionID - limit?: number - }) => Effect.Effect + readonly messages: (input: { sessionID: SessionID; limit?: number }) => Effect.Effect readonly children: (parentID: SessionID) => Effect.Effect readonly remove: (sessionID: SessionID) => Effect.Effect readonly updateMessage: (msg: T) => Effect.Effect diff --git a/packages/opencode/src/session/summary.ts b/packages/opencode/src/session/summary.ts index 55688d435..29e39e985 100644 --- a/packages/opencode/src/session/summary.ts +++ b/packages/opencode/src/session/summary.ts @@ -78,9 +78,7 @@ export const layer = Layer.effect( const events = yield* EventV2Bridge.Service const config = yield* Config.Service - const computeDiff = Effect.fn("SessionSummary.computeDiff")(function* (input: { - messages: SessionV1.WithParts[] - }) { + const computeDiff = Effect.fn("SessionSummary.computeDiff")(function* (input: { messages: SessionV1.WithParts[] }) { let from: string | undefined let to: string | undefined for (const item of input.messages) { diff --git a/packages/opencode/src/skill/index.ts b/packages/opencode/src/skill/index.ts index 84f8d8ea0..34e5edba8 100644 --- a/packages/opencode/src/skill/index.ts +++ b/packages/opencode/src/skill/index.ts @@ -109,9 +109,7 @@ const add = Effect.fnUntraced(function* (state: State, match: string, events: Ev }).pipe( Effect.catch( Effect.fnUntraced(function* (err) { - const message = FrontmatterError.isInstance(err) - ? err.data.message - : `Failed to parse skill ${match}` + const message = FrontmatterError.isInstance(err) ? err.data.message : `Failed to parse skill ${match}` const { Session } = yield* Effect.promise(() => import("@/session/session")) yield* events.publish(Session.Event.Error, { error: new NamedError.Unknown({ message }).toObject() }) log.error("failed to load skill", { skill: match, err }) diff --git a/packages/opencode/test/cli/github-action.test.ts b/packages/opencode/test/cli/github-action.test.ts index 9b80b9888..57567d8c9 100644 --- a/packages/opencode/test/cli/github-action.test.ts +++ b/packages/opencode/test/cli/github-action.test.ts @@ -26,11 +26,7 @@ function createReasoningPart(text: string): SessionV1.Part { } } -function createToolPart( - tool: string, - title: string, - status: "completed" | "running" = "completed", -): SessionV1.Part { +function createToolPart(tool: string, title: string, status: "completed" | "running" = "completed"): SessionV1.Part { if (status === "completed") { return { id: PartID.ascending(), diff --git a/packages/opencode/test/session/processor-effect.test.ts b/packages/opencode/test/session/processor-effect.test.ts index 53684f587..06f50017a 100644 --- a/packages/opencode/test/session/processor-effect.test.ts +++ b/packages/opencode/test/session/processor-effect.test.ts @@ -330,9 +330,7 @@ it.live("session.processor effect tests preserve text start time", () => gate.resolve() const exit = yield* Fiber.await(run) - const text = (yield* MessageV2.parts(msg.id)).find( - (part): part is SessionV1.TextPart => part.type === "text", - ) + const text = (yield* MessageV2.parts(msg.id)).find((part): part is SessionV1.TextPart => part.type === "text") expect(Exit.isSuccess(exit)).toBe(true) expect(text?.text).toBe("hello")