Skip to content
Merged
2 changes: 1 addition & 1 deletion packages/opencode/src/cli/cmd/tui/ui/dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ function init() {
useKeyboard((evt) => {
if (store.stack.length === 0) return
if (evt.defaultPrevented) return
if ((evt.name === "escape" || (evt.ctrl && evt.name === "c")) && renderer.getSelection()) return
if ((evt.name === "escape" || (evt.ctrl && evt.name === "c")) && renderer.getSelection()?.getSelectedText()) return
if (evt.name === "escape" || (evt.ctrl && evt.name === "c")) {
const current = store.stack.at(-1)!
current.onClose?.()
Expand Down
2 changes: 1 addition & 1 deletion packages/opencode/src/project/vcs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export namespace Vcs {
log.info("initialized", { branch: current })

const unsubscribe = Bus.subscribe(FileWatcher.Event.Updated, async (evt) => {
if (evt.properties.file.endsWith("HEAD")) return
if (!evt.properties.file.endsWith("HEAD")) return
const next = await currentBranch()
if (next !== current) {
log.info("branch changed", { from: current, to: next })
Expand Down
3 changes: 2 additions & 1 deletion packages/opencode/src/provider/error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,8 @@ export namespace ProviderError {

export function parseAPICallError(input: { providerID: ProviderID; error: APICallError }): ParsedAPICallError {
const m = message(input.providerID, input.error)
if (isOverflow(m) || input.error.statusCode === 413) {
const body = json(input.error.responseBody)
if (isOverflow(m) || input.error.statusCode === 413 || body?.error?.code === "context_length_exceeded") {
return {
type: "context_overflow",
message: m,
Expand Down
4 changes: 3 additions & 1 deletion packages/opencode/src/session/compaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,8 @@ When constructing the summary, try to stick to this template:
---`

const promptText = compacting.prompt ?? [defaultPrompt, ...compacting.context].join("\n\n")
const msgs = structuredClone(messages)
await Plugin.trigger("experimental.chat.messages.transform", {}, { messages: msgs })
const result = await processor.process({
user: userMessage,
agent,
Expand All @@ -212,7 +214,7 @@ When constructing the summary, try to stick to this template:
tools: {},
system: [],
messages: [
...MessageV2.toModelMessages(messages, model, { stripMedia: true }),
...MessageV2.toModelMessages(msgs, model, { stripMedia: true }),
{
role: "user",
content: [
Expand Down
8 changes: 6 additions & 2 deletions packages/opencode/src/session/llm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export namespace LLM {
messages: ModelMessage[]
small?: boolean
tools: Record<string, Tool>
permission?: PermissionNext.Ruleset
retries?: number
toolChoice?: "auto" | "required" | "none"
}
Expand Down Expand Up @@ -255,8 +256,11 @@ export namespace LLM {
})
}

async function resolveTools(input: Pick<StreamInput, "tools" | "agent" | "user">) {
const disabled = PermissionNext.disabled(Object.keys(input.tools), input.agent.permission)
async function resolveTools(input: Pick<StreamInput, "tools" | "agent" | "user" | "permission">) {
const disabled = PermissionNext.disabled(
Object.keys(input.tools),
PermissionNext.merge(input.agent.permission, input.permission ?? []),
)
for (const tool of Object.keys(input.tools)) {
if (input.user.tools?.[tool] === false || disabled.has(tool)) {
delete input.tools[tool]
Expand Down
21 changes: 21 additions & 0 deletions packages/opencode/src/session/prompt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -709,6 +709,7 @@ export namespace SessionPrompt {
: []),
],
tools,
permission: session.permission,
model,
toolChoice: format.type === "json_schema" ? "required" : undefined,
})
Expand Down Expand Up @@ -1351,6 +1352,26 @@ export namespace SessionPrompt {
},
)

const infoResult = MessageV2.Info.safeParse(info)
if (!infoResult.success) {
log.error("info schema validation failed before save", {
sessionID: input.sessionID,
messageID: info.id,
issues: infoResult.error.issues,
})
}
for (const [i, part] of parts.entries()) {
const partResult = MessageV2.Part.safeParse(part)
if (!partResult.success) {
log.error("part schema validation failed before save", {
sessionID: input.sessionID,
partID: part.id,
partType: part.type,
index: i,
issues: partResult.error.issues,
})
}
}
await Session.updateMessage(info)
for (const part of parts) {
await Session.updatePart(part)
Expand Down
3 changes: 3 additions & 0 deletions packages/opencode/src/util/fn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ export function fn<T extends z.ZodType, Result>(schema: T, cb: (input: z.infer<T
parsed = schema.parse(input)
} catch (e) {
console.trace("schema validation failure stack trace:")
if (e instanceof z.ZodError) {
console.error("schema validation issues:", JSON.stringify(e.issues, null, 2))
}
throw e
}

Expand Down
14 changes: 2 additions & 12 deletions packages/ui/src/components/message-part.css
Original file line number Diff line number Diff line change
Expand Up @@ -1050,18 +1050,8 @@
line-height: var(--line-height-large);
color: var(--text-base);
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}

[data-slot="question-option"][data-custom="true"] {
[data-slot="option-description"] {
overflow: visible;
text-overflow: clip;
white-space: normal;
overflow-wrap: anywhere;
}
overflow-wrap: anywhere;
white-space: normal;
}

[data-slot="question-custom"] {
Expand Down
Loading