Skip to content
Merged
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
40 changes: 18 additions & 22 deletions packages/opencode/src/acp/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@ import { Storage } from "@/storage/storage"
import { Command } from "@/command"
import { Agent as Agents } from "@/agent/agent"
import { Permission } from "@/permission"
import { Session } from "@/session"
import { Identifier } from "@/id/id"
import { SessionCompaction } from "@/session/compaction"
import type { Config } from "@/config/config"
import { MCP } from "@/mcp"
Expand Down Expand Up @@ -89,7 +87,11 @@ export namespace ACP {
})
if (!res) return
if (res.outcome.outcome !== "selected") {
Permission.respond({ sessionID: permission.sessionID, permissionID: permission.id, response: "reject" })
Permission.respond({
sessionID: permission.sessionID,
permissionID: permission.id,
response: "reject",
})
return
}
Permission.respond({
Expand All @@ -111,9 +113,11 @@ export namespace ACP {
const acpSession = this.sessionManager.get(part.sessionID)
if (!acpSession) return

const message = await Storage.read<MessageV2.Info>(["message", part.sessionID, part.messageID]).catch(
() => undefined,
)
const message = await Storage.read<MessageV2.Info>([
"message",
part.sessionID,
part.messageID,
]).catch(() => undefined)
if (!message || message.role !== "assistant") return

if (part.type === "tool") {
Expand Down Expand Up @@ -192,7 +196,9 @@ export namespace ACP {
sessionUpdate: "plan",
entries: parsedTodos.data.map((todo) => {
const status: PlanEntry["status"] =
todo.status === "cancelled" ? "completed" : (todo.status as PlanEntry["status"])
todo.status === "cancelled"
? "completed"
: (todo.status as PlanEntry["status"])
return {
priority: "medium",
status,
Expand Down Expand Up @@ -375,11 +381,6 @@ export namespace ACP {
description: command.description ?? "",
}))
const names = new Set(availableCommands.map((c) => c.name))
if (!names.has("init"))
availableCommands.push({
name: "init",
description: "create/update a AGENTS.md",
})
if (!names.has("compact"))
availableCommands.push({
name: "compact",
Expand All @@ -404,7 +405,8 @@ export namespace ACP {
description: agent.description,
}))

const currentModeId = availableModes.find((m) => m.name === "build")?.id ?? availableModes[0].id
const currentModeId =
availableModes.find((m) => m.name === "build")?.id ?? availableModes[0].id

const mcpServers: Record<string, Config.Mcp> = {}
for (const server of params.mcpServers) {
Expand Down Expand Up @@ -585,14 +587,6 @@ export namespace ACP {
}

switch (cmd.name) {
case "init":
await Session.initialize({
sessionID,
messageID: Identifier.ascending("message"),
providerID: model.providerID,
modelID: model.modelID,
})
break
case "compact":
await SessionCompaction.run({
sessionID,
Expand Down Expand Up @@ -665,7 +659,9 @@ export namespace ACP {

function parseUri(
uri: string,
): { type: "file"; url: string; filename: string; mime: string } | { type: "text"; text: string } {
):
| { type: "file"; url: string; filename: string; mime: string }
| { type: "text"; text: string } {
try {
if (uri.startsWith("file://")) {
const path = uri.slice(7)
Expand Down
27 changes: 27 additions & 0 deletions packages/opencode/src/command/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,27 @@
import z from "zod"
import { Config } from "../config/config"
import { Instance } from "../project/instance"
import PROMPT_INITIALIZE from "./template/initialize.txt"
import { Bus } from "../bus"
import { Identifier } from "../id/id"

export namespace Command {
export const Default = {
INIT: "init",
} as const

export const Event = {
Executed: Bus.event(
"command.executed",
z.object({
name: z.string(),
sessionID: Identifier.schema("session"),
arguments: z.string(),
messageID: Identifier.schema("message"),
}),
),
}

export const Info = z
.object({
name: z.string(),
Expand Down Expand Up @@ -33,6 +52,14 @@ export namespace Command {
}
}

if (result[Default.INIT] === undefined) {
result[Default.INIT] = {
name: Default.INIT,
description: "create/update AGENTS.md",
template: PROMPT_INITIALIZE.replace("${path}", Instance.worktree),
}
}

return result
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@ The file you create will be given to agentic coding agents (such as yourself) th
If there are Cursor rules (in .cursor/rules/ or .cursorrules) or Copilot rules (in .github/copilot-instructions.md), make sure to include them.

If there's already an AGENTS.md, improve it if it's located in ${path}

$ARGUMENTS
10 changes: 10 additions & 0 deletions packages/opencode/src/project/bootstrap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ import { LSP } from "../lsp"
import { FileWatcher } from "../file/watcher"
import { File } from "../file"
import { Flag } from "../flag/flag"
import { Project } from "./project"
import { Bus } from "../bus"
import { Command } from "../command"
import { Instance } from "./instance"

export async function InstanceBootstrap() {
if (Flag.OPENCODE_EXPERIMENTAL_NO_BOOTSTRAP) return
Expand All @@ -14,4 +18,10 @@ export async function InstanceBootstrap() {
await LSP.init()
FileWatcher.init()
File.init()

Bus.subscribe(Command.Event.Executed, async (payload) => {
if (payload.properties.name === Command.Default.INIT) {
await Project.setInitialized(Instance.project.id)
}
})
}
32 changes: 14 additions & 18 deletions packages/opencode/src/session/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ import { Decimal } from "decimal.js"
import z from "zod"
import { type LanguageModelUsage, type ProviderMetadata } from "ai"

import PROMPT_INITIALIZE from "../session/prompt/initialize.txt"

import { Bus } from "../bus"
import { Config } from "../config/config"
import { Flag } from "../flag/flag"
Expand All @@ -14,11 +12,11 @@ import { Share } from "../share/share"
import { Storage } from "../storage/storage"
import { Log } from "../util/log"
import { MessageV2 } from "./message-v2"
import { Project } from "../project/project"
import { Instance } from "../project/instance"
import { SessionPrompt } from "./prompt"
import { fn } from "@/util/fn"
import { Snapshot } from "@/snapshot"
import { Command } from "../command"

export namespace Session {
const log = Log.create({ service: "session" })
Expand Down Expand Up @@ -164,7 +162,12 @@ export namespace Session {
})
})

export async function createNext(input: { id?: string; title?: string; parentID?: string; directory: string }) {
export async function createNext(input: {
id?: string
title?: string
parentID?: string
directory: string
}) {
const result: Info = {
id: Identifier.descending("session", input.id),
version: Installation.VERSION,
Expand Down Expand Up @@ -402,7 +405,9 @@ export namespace Session {
.add(new Decimal(tokens.input).mul(input.model.cost?.input ?? 0).div(1_000_000))
.add(new Decimal(tokens.output).mul(input.model.cost?.output ?? 0).div(1_000_000))
.add(new Decimal(tokens.cache.read).mul(input.model.cost?.cache_read ?? 0).div(1_000_000))
.add(new Decimal(tokens.cache.write).mul(input.model.cost?.cache_write ?? 0).div(1_000_000))
.add(
new Decimal(tokens.cache.write).mul(input.model.cost?.cache_write ?? 0).div(1_000_000),
)
.toNumber(),
tokens,
}
Expand All @@ -423,22 +428,13 @@ export namespace Session {
messageID: Identifier.schema("message"),
}),
async (input) => {
await SessionPrompt.prompt({
await SessionPrompt.command({
sessionID: input.sessionID,
messageID: input.messageID,
model: {
providerID: input.providerID,
modelID: input.modelID,
},
parts: [
{
id: Identifier.ascending("part"),
type: "text",
text: PROMPT_INITIALIZE.replace("${path}", Instance.worktree),
},
],
model: input.providerID + "/" + input.modelID,
command: Command.Default.INIT,
arguments: "",
})
await Project.setInitialized(Instance.project.id)
},
)
}
30 changes: 21 additions & 9 deletions packages/opencode/src/session/prompt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1593,6 +1593,7 @@ export namespace SessionPrompt {
let index = 0
template = template.replace(bashRegex, () => results[index++])
}
template = template.trim()

const parts = [
{
Expand Down Expand Up @@ -1657,6 +1658,8 @@ export namespace SessionPrompt {
})()

const agent = await Agent.get(agentName)
let result: MessageV2.WithParts

if ((agent.mode === "subagent" && command.subtask !== false) || command.subtask === true) {
using abort = lock(input.sessionID)

Expand Down Expand Up @@ -1732,7 +1735,7 @@ export namespace SessionPrompt {
}
await Session.updatePart(toolPart)

const result = await TaskTool.init().then((t) =>
const taskResult = await TaskTool.init().then((t) =>
t.execute(args, {
sessionID: input.sessionID,
abort: abort.signal,
Expand Down Expand Up @@ -1760,22 +1763,31 @@ export namespace SessionPrompt {
},
input: toolPart.state.input,
title: "",
metadata: result.metadata,
output: result.output,
metadata: taskResult.metadata,
output: taskResult.output,
}
await Session.updatePart(toolPart)
}

return { info: assistantMsg, parts: [toolPart] }
result = { info: assistantMsg, parts: [toolPart] }
} else {
result = await prompt({
sessionID: input.sessionID,
messageID: input.messageID,
model,
agent: agentName,
parts,
})
}

return prompt({
Bus.publish(Command.Event.Executed, {
name: input.command,
sessionID: input.sessionID,
messageID: input.messageID,
model,
agent: agentName,
parts,
arguments: input.arguments,
messageID: result.info.id,
})

return result
}

async function ensureTitle(input: {
Expand Down