Bläddra i källkod

chore: generate

opencode-agent[bot] 2 månader sedan
förälder
incheckning
6003217eaa

+ 3 - 1
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>,
+    commands: Object.fromEntries(
+      (options.commands ?? []).map((command) => [command.name, command]),
+    ) as ChildrenOf<Commands>,
   }
 }
 

+ 12 - 6
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> = Value extends CliApi.Node<infer _Name, infer Spec, infer _Commands>
-  ? Input<Spec>
-  : Value extends Command.Command<infer _Name, infer Input, infer _Context, infer _Error, infer _Requirements>
-    ? Input
-    : never
+export type Input<Value> =
+  Value extends CliApi.Node<infer _Name, infer Spec, infer _Commands>
+    ? Input<Spec>
+    : Value extends Command.Command<infer _Name, infer Input, infer _Context, infer _Error, infer _Requirements>
+      ? Input
+      : never
 
 type RuntimeHandler = (input: unknown) => Effect.Effect<void, unknown>
 type Loader<Node extends CliApi.Any> = () => Promise<{ default: (input: Input<Node>) => Effect.Effect<void, any> }>
@@ -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<const Node extends CliApi.Any, Error>(
   _node: Node,

+ 21 - 8
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),
+  ),
+)

+ 1 - 3
packages/core/src/session/projector.ts

@@ -76,9 +76,7 @@ function messageData(
   return rest as DeepMutable<typeof rest>
 }
 
-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<typeof rest>
 }

+ 143 - 26
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<Schema.Schema.Type<typeof Info>>

+ 13 - 13
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,

+ 3 - 1
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(

+ 4 - 1
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 } },

+ 4 - 1
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<ConfigPluginV1.Spec> {
+export async function resolvePluginSpec(
+  plugin: ConfigPluginV1.Spec,
+  configFilepath: string,
+): Promise<ConfigPluginV1.Spec> {
   const spec = pluginSpecifier(plugin)
   if (!isPathPluginSpec(spec)) return plugin
 

+ 1 - 5
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()

+ 1 - 3
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:

+ 5 - 5
packages/opencode/src/session/prompt.ts

@@ -1500,11 +1500,11 @@ export const layer = Layer.effect(
       },
     )
 
-    const loop: (input: LoopInput) => Effect.Effect<SessionV1.WithParts> = 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<SessionV1.WithParts> = 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<SessionV1.WithParts, Session.BusyError> = Effect.fn(
       "SessionPrompt.shell",

+ 1 - 4
packages/opencode/src/session/session.ts

@@ -493,10 +493,7 @@ export interface Interface {
   readonly setShare: (input: { sessionID: SessionID; share: Info["share"] }) => Effect.Effect<void>
   readonly setWorkspace: (input: { sessionID: SessionID; workspaceID: Info["workspaceID"] }) => Effect.Effect<void>
   readonly diff: (sessionID: SessionID) => Effect.Effect<Snapshot.FileDiff[]>
-  readonly messages: (input: {
-    sessionID: SessionID
-    limit?: number
-  }) => Effect.Effect<SessionV1.WithParts[], NotFound>
+  readonly messages: (input: { sessionID: SessionID; limit?: number }) => Effect.Effect<SessionV1.WithParts[], NotFound>
   readonly children: (parentID: SessionID) => Effect.Effect<Info[]>
   readonly remove: (sessionID: SessionID) => Effect.Effect<void, NotFound>
   readonly updateMessage: <T extends SessionV1.Info>(msg: T) => Effect.Effect<T>

+ 1 - 3
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) {

+ 1 - 3
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 })

+ 1 - 5
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(),

+ 1 - 3
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")