Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 0 additions & 11 deletions packages/opencode/src/acp/tool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -296,17 +296,6 @@ function isShell(toolName: string) {
return tool === "bash" || tool === "shell"
}

export const mapToolKind = toToolKind
export const extractLocations = toLocations
export const buildCompletedToolContent = completedToolContent
export const buildCompletedRawOutput = completedToolRawOutput
export const extractShellOutputSnapshot = shellOutputSnapshot
export const buildPendingToolCall = pendingToolCall
export const buildRunningToolUpdate = runningToolUpdate
export const buildDuplicateRunningToolUpdate = duplicateRunningToolUpdate
export const buildCompletedToolUpdate = completedToolUpdate
export const buildErrorToolUpdate = errorToolUpdate

function locationFrom(...values: unknown[]): ToolCallLocation[] {
return Array.from(
new Set(
Expand Down
22 changes: 0 additions & 22 deletions packages/opencode/src/acp/usage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,6 @@ export type MessagesInput = {
readonly directory: string
}

export type SDK = {
readonly session: {
readonly messages: (
parameters: { readonly sessionID: string; readonly directory: string },
options: { readonly throwOnError: true },
) => Promise<{ readonly data?: readonly SessionMessage[] | null }>
}
}

export interface MessageLoaderInterface {
readonly messages: (input: MessagesInput) => Effect.Effect<readonly SessionMessage[], unknown>
}
Expand Down Expand Up @@ -70,19 +61,6 @@ export class ContextLimitLoader extends Context.Service<ContextLimitLoader, Cont

export class Service extends Context.Service<Service, Interface>()("@opencode/ACPUsage") {}

export function messageLoaderFromSDK(sdk: SDK): MessageLoaderInterface {
return MessageLoader.of({
messages: (input) =>
Effect.promise(() =>
sdk.session
.messages({ sessionID: input.sessionID, directory: input.directory }, { throwOnError: true })
.then((response) => response.data ?? []),
),
})
}

export const messageLoaderLayer = (sdk: SDK) => Layer.succeed(MessageLoader, messageLoaderFromSDK(sdk))

export function contextTokens(message: AssistantTokenCost): number {
return message.tokens.input + message.tokens.cache.read + message.tokens.cache.write
}
Expand Down
143 changes: 0 additions & 143 deletions packages/opencode/src/patch/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Effect, Schema } from "effect"
import * as path from "path"
import { FSUtil } from "@opencode-ai/core/fs-util"
import * as Bom from "../util/bom"

Expand Down Expand Up @@ -28,44 +27,19 @@ export interface UpdateFileChunk {
is_end_of_file?: boolean
}

export interface ApplyPatchAction {
changes: Map<string, ApplyPatchFileChange>
patch: string
cwd: string
}

export type ApplyPatchFileChange =
| { type: "add"; content: string }
| { type: "delete"; content: string }
| { type: "update"; unified_diff: string; move_path?: string; new_content: string }

export interface AffectedPaths {
added: string[]
modified: string[]
deleted: string[]
}

export enum ApplyPatchError {
ParseError = "ParseError",
IoError = "IoError",
ComputeReplacements = "ComputeReplacements",
ImplicitInvocation = "ImplicitInvocation",
}

export enum MaybeApplyPatch {
Body = "Body",
ShellParseError = "ShellParseError",
PatchParseError = "PatchParseError",
NotApplyPatch = "NotApplyPatch",
}

export enum MaybeApplyPatchVerified {
Body = "Body",
ShellParseError = "ShellParseError",
CorrectnessError = "CorrectnessError",
NotApplyPatch = "NotApplyPatch",
}

// Parser implementation
function parsePatchHeader(
lines: string[],
Expand Down Expand Up @@ -566,121 +540,4 @@ export const applyPatch = Effect.fn("Patch.applyPatch")(function* (patchText: st
return yield* applyHunksToFiles(hunks)
})

type MaybeApplyPatchVerifiedResult =
| { type: MaybeApplyPatchVerified.Body; action: ApplyPatchAction }
| { type: MaybeApplyPatchVerified.CorrectnessError; error: Error }
| { type: MaybeApplyPatchVerified.NotApplyPatch }

// Effectful verified-parse: needs FSUtil.Service to read existing files
export const maybeParseApplyPatchVerified = Effect.fn("Patch.maybeParseApplyPatchVerified")(function* (
argv: string[],
cwd: string,
) {
// Detect implicit patch invocation (raw patch without apply_patch command)
if (argv.length === 1) {
try {
parsePatch(argv[0])
return {
type: MaybeApplyPatchVerified.CorrectnessError,
error: new Error(ApplyPatchError.ImplicitInvocation),
} satisfies MaybeApplyPatchVerifiedResult
} catch {
// Not a patch, continue
}
}

const result = maybeParseApplyPatch(argv)

switch (result.type) {
case MaybeApplyPatch.Body: {
const fs = yield* FSUtil.Service
const args = result.args
const effectiveCwd = args.workdir ? path.resolve(cwd, args.workdir) : cwd
const changes = new Map<string, ApplyPatchFileChange>()

for (const hunk of args.hunks) {
const resolvedPath = path.resolve(
effectiveCwd,
hunk.type === "update" && hunk.move_path ? hunk.move_path : hunk.path,
)

switch (hunk.type) {
case "add":
changes.set(resolvedPath, {
type: "add",
content: hunk.contents,
})
break

case "delete": {
const deletePath = path.resolve(effectiveCwd, hunk.path)
const content = yield* fs.readFileString(deletePath).pipe(Effect.catch(() => Effect.succeed(undefined)))
if (content === undefined) {
return {
type: MaybeApplyPatchVerified.CorrectnessError,
error: new Error(`Failed to read file for deletion: ${deletePath}`),
} satisfies MaybeApplyPatchVerifiedResult
}
changes.set(resolvedPath, {
type: "delete",
content,
})
break
}

case "update": {
const updatePath = path.resolve(effectiveCwd, hunk.path)
const originalText = yield* fs
.readFileString(updatePath)
.pipe(
Effect.catch((cause) =>
Effect.succeed(new Error(`Failed to read file ${updatePath}: ${cause}`, { cause })),
),
)
if (originalText instanceof Error) {
return {
type: MaybeApplyPatchVerified.CorrectnessError,
error: originalText,
} satisfies MaybeApplyPatchVerifiedResult
}
try {
const fileUpdate = deriveNewContentsFromChunks(updatePath, hunk.chunks, originalText)
changes.set(resolvedPath, {
type: "update",
unified_diff: fileUpdate.unified_diff,
move_path: hunk.move_path ? path.resolve(effectiveCwd, hunk.move_path) : undefined,
new_content: fileUpdate.content,
})
} catch (error) {
return {
type: MaybeApplyPatchVerified.CorrectnessError,
error: error as Error,
} satisfies MaybeApplyPatchVerifiedResult
}
break
}
}
}

return {
type: MaybeApplyPatchVerified.Body,
action: {
changes,
patch: args.patch,
cwd: effectiveCwd,
},
} satisfies MaybeApplyPatchVerifiedResult
}

case MaybeApplyPatch.PatchParseError:
return {
type: MaybeApplyPatchVerified.CorrectnessError,
error: result.error,
} satisfies MaybeApplyPatchVerifiedResult

case MaybeApplyPatch.NotApplyPatch:
return { type: MaybeApplyPatchVerified.NotApplyPatch } satisfies MaybeApplyPatchVerifiedResult
}
})

export * as Patch from "."
3 changes: 0 additions & 3 deletions packages/opencode/src/tool/tool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,6 @@ interface Metadata {
[key: string]: any
}

// TODO: remove this hack
export type DynamicDescription = (agent: Agent.Info) => Effect.Effect<string>

/**
* Raised when the LLM calls a tool with arguments that fail the parameter
* schema. This is the canonical "rewrite the input" tool error: the typed
Expand Down
Loading