Compare commits

..

3 Commits

Author SHA1 Message Date
opencode 935cd7481b release: v0.15.27 2025-10-29 18:41:24 +00:00
Dax Raad 5553efea5e only generate user message summary if no diffs 2025-10-29 14:28:43 -04:00
Adam 0ff73ed8a6 wip: desktop work 2025-10-29 16:59:45 +00:00
17 changed files with 98 additions and 55 deletions
+11 -11
View File
@@ -37,7 +37,7 @@
},
"packages/console/core": {
"name": "@opencode-ai/console-core",
"version": "0.15.26",
"version": "0.15.27",
"dependencies": {
"@aws-sdk/client-sts": "3.782.0",
"@jsx-email/render": "1.1.1",
@@ -64,7 +64,7 @@
},
"packages/console/function": {
"name": "@opencode-ai/console-function",
"version": "0.15.26",
"version": "0.15.27",
"dependencies": {
"@ai-sdk/anthropic": "2.0.0",
"@ai-sdk/openai": "2.0.2",
@@ -88,7 +88,7 @@
},
"packages/console/mail": {
"name": "@opencode-ai/console-mail",
"version": "0.15.26",
"version": "0.15.27",
"dependencies": {
"@jsx-email/all": "2.2.3",
"@jsx-email/cli": "1.4.3",
@@ -109,7 +109,7 @@
},
"packages/desktop": {
"name": "@opencode-ai/desktop",
"version": "0.15.26",
"version": "0.15.27",
"dependencies": {
"@kobalte/core": "catalog:",
"@opencode-ai/sdk": "workspace:*",
@@ -150,7 +150,7 @@
},
"packages/function": {
"name": "@opencode-ai/function",
"version": "0.15.26",
"version": "0.15.27",
"dependencies": {
"@octokit/auth-app": "8.0.1",
"@octokit/rest": "22.0.0",
@@ -166,7 +166,7 @@
},
"packages/opencode": {
"name": "opencode",
"version": "0.15.26",
"version": "0.15.27",
"bin": {
"opencode": "./bin/opencode",
},
@@ -230,7 +230,7 @@
},
"packages/plugin": {
"name": "@opencode-ai/plugin",
"version": "0.15.26",
"version": "0.15.27",
"dependencies": {
"@opencode-ai/sdk": "workspace:*",
"zod": "catalog:",
@@ -250,7 +250,7 @@
},
"packages/sdk/js": {
"name": "@opencode-ai/sdk",
"version": "0.15.26",
"version": "0.15.27",
"devDependencies": {
"@hey-api/openapi-ts": "0.81.0",
"@tsconfig/node22": "catalog:",
@@ -261,7 +261,7 @@
},
"packages/slack": {
"name": "@opencode-ai/slack",
"version": "0.15.26",
"version": "0.15.27",
"dependencies": {
"@opencode-ai/sdk": "workspace:*",
"@slack/bolt": "^3.17.1",
@@ -274,7 +274,7 @@
},
"packages/ui": {
"name": "@opencode-ai/ui",
"version": "0.15.26",
"version": "0.15.27",
"dependencies": {
"@kobalte/core": "catalog:",
"@pierre/precision-diffs": "catalog:",
@@ -297,7 +297,7 @@
},
"packages/web": {
"name": "@opencode-ai/web",
"version": "0.15.26",
"version": "0.15.27",
"dependencies": {
"@astrojs/cloudflare": "12.6.3",
"@astrojs/markdown-remark": "6.3.1",
+1 -1
View File
@@ -7,7 +7,7 @@
"dev:remote": "VITE_AUTH_URL=https://auth.dev.opencode.ai bun sst shell --stage=dev bun dev",
"build": "vinxi build && ../../opencode/script/schema.ts ./.output/public/config.json",
"start": "vinxi start",
"version": "0.15.26"
"version": "0.15.27"
},
"dependencies": {
"@ibm/plex": "6.4.1",
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@opencode-ai/console-core",
"version": "0.15.26",
"version": "0.15.27",
"private": true,
"type": "module",
"dependencies": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/console-function",
"version": "0.15.26",
"version": "0.15.27",
"$schema": "https://json.schemastore.org/package.json",
"private": true,
"type": "module",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/console-mail",
"version": "0.15.26",
"version": "0.15.27",
"dependencies": {
"@jsx-email/all": "2.2.3",
"@jsx-email/cli": "1.4.3",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/desktop",
"version": "0.15.26",
"version": "0.15.27",
"description": "",
"type": "module",
"scripts": {
@@ -1,4 +1,4 @@
import type { Part, AssistantMessage, ReasoningPart, TextPart, ToolPart, Message } from "@opencode-ai/sdk"
import type { Part, ReasoningPart, TextPart, ToolPart, Message, AssistantMessage, UserMessage } from "@opencode-ai/sdk"
import { children, Component, createMemo, For, Match, Show, Switch, type JSX } from "solid-js"
import { Dynamic } from "solid-js/web"
import { Markdown } from "./markdown"
@@ -17,7 +17,20 @@ import type { WriteTool } from "opencode/tool/write"
import type { TodoWriteTool } from "opencode/tool/todo"
import { DiffChanges } from "./diff-changes"
export function AssistantMessage(props: { message: AssistantMessage; parts: Part[] }) {
export function Message(props: { message: Message; parts: Part[] }) {
return (
<Switch>
<Match when={props.message.role === "user" && props.message}>
{(userMessage) => <UserMessage message={userMessage()} parts={props.parts} />}
</Match>
<Match when={props.message.role === "assistant" && props.message}>
{(assistantMessage) => <AssistantMessage message={assistantMessage()} parts={props.parts} />}
</Match>
</Switch>
)
}
function AssistantMessage(props: { message: AssistantMessage; parts: Part[] }) {
const filteredParts = createMemo(() => {
return props.parts?.filter((x) => {
if (x.type === "reasoning") return false
@@ -31,11 +44,26 @@ export function AssistantMessage(props: { message: AssistantMessage; parts: Part
)
}
export function Part(props: { part: Part; message: Message; readonly?: boolean }) {
function UserMessage(props: { message: UserMessage; parts: Part[] }) {
const text = createMemo(() =>
props.parts
?.filter((p) => p.type === "text" && !p.synthetic)
?.map((p) => (p as TextPart).text)
?.join(""),
)
return <div class="text-12-regular text-text-base line-clamp-3">{text()}</div>
}
export function Part(props: { part: Part; message: Message; hideDetails?: boolean }) {
const component = createMemo(() => PART_MAPPING[props.part.type as keyof typeof PART_MAPPING])
return (
<Show when={component()}>
<Dynamic component={component()} part={props.part as any} message={props.message} readonly={props.readonly} />
<Dynamic
component={component()}
part={props.part as any}
message={props.message}
hideDetails={props.hideDetails}
/>
</Show>
)
}
@@ -62,7 +90,7 @@ function TextPart(props: { part: TextPart; message: Message }) {
)
}
function ToolPart(props: { part: ToolPart; message: Message; readonly?: boolean }) {
function ToolPart(props: { part: ToolPart; message: Message; hideDetails?: boolean }) {
const component = createMemo(() => {
const render = ToolRegistry.render(props.part.tool) ?? GenericTool
const metadata = props.part.state.status === "pending" ? {} : (props.part.state.metadata ?? {})
@@ -75,7 +103,7 @@ function ToolPart(props: { part: ToolPart; message: Message; readonly?: boolean
tool={props.part.tool}
metadata={metadata}
output={props.part.state.status === "completed" ? props.part.state.output : undefined}
readonly={props.readonly}
hideDetails={props.hideDetails}
/>
)
})
@@ -101,7 +129,7 @@ function BasicTool(props: {
icon: IconProps["name"]
trigger: TriggerTitle | JSX.Element
children?: JSX.Element
readonly?: boolean
hideDetails?: boolean
}) {
const resolved = children(() => props.children)
return (
@@ -157,12 +185,12 @@ function BasicTool(props: {
</Switch>
</div>
</div>
<Show when={resolved() && !props.readonly}>
<Show when={resolved() && !props.hideDetails}>
<Collapsible.Arrow />
</Show>
</div>
</Collapsible.Trigger>
<Show when={resolved() && !props.readonly}>
<Show when={resolved() && !props.hideDetails}>
<Collapsible.Content>{resolved()}</Collapsible.Content>
</Show>
</Collapsible>
@@ -173,7 +201,7 @@ function BasicTool(props: {
}
function GenericTool(props: ToolProps<any>) {
return <BasicTool icon="mcp" trigger={{ title: props.tool }} readonly={props.readonly} />
return <BasicTool icon="mcp" trigger={{ title: props.tool }} hideDetails={props.hideDetails} />
}
type ToolProps<T extends Tool.Info> = {
@@ -181,7 +209,7 @@ type ToolProps<T extends Tool.Info> = {
metadata: Partial<Tool.InferMetadata<T>>
tool: string
output?: string
readonly?: boolean
hideDetails?: boolean
}
const ToolRegistry = (() => {
+17 -8
View File
@@ -33,7 +33,7 @@ import { Code } from "@/components/code"
import { useSync } from "@/context/sync"
import { useSDK } from "@/context/sdk"
import { ProgressCircle } from "@/components/progress-circle"
import { AssistantMessage, Part } from "@/components/assistant-message"
import { Message, Part } from "@/components/message"
import { type AssistantMessage as AssistantMessageType } from "@opencode-ai/sdk"
import { DiffChanges } from "@/components/diff-changes"
@@ -198,6 +198,7 @@ export default function Page() {
}
if (!session) return
local.session.setActive(session.id)
const toAbsolutePath = (path: string) => (path.startsWith("/") ? path : sync.absolute(path))
const text = parts.map((part) => part.content).join("")
@@ -259,7 +260,6 @@ export default function Page() {
],
},
})
local.session.setActive(session.id)
}
const handleNewSession = () => {
@@ -639,8 +639,9 @@ export default function Page() {
<For each={local.session.userMessages()}>
{(message) => {
const [expanded, setExpanded] = createSignal(false)
const title = createMemo(() => message.summary?.title)
const parts = createMemo(() => sync.data.part[message.id])
const prompt = createMemo(() => local.session.getMessageText(message))
const title = createMemo(() => message.summary?.title)
const summary = createMemo(() => message.summary?.body)
const assistantMessages = createMemo(() => {
return sync.data.message[activeSession().id]?.filter(
@@ -665,7 +666,9 @@ export default function Page() {
</h1>
</div>
<Show when={title}>
<div class="-mt-8 text-12-regular text-text-base line-clamp-3">{prompt()}</div>
<div class="-mt-8">
<Message message={message} parts={parts()} />
</div>
</Show>
{/* Response */}
<div class="w-full flex flex-col gap-2">
@@ -686,7 +689,7 @@ export default function Page() {
<For each={assistantMessages()}>
{(assistantMessage) => {
const parts = createMemo(() => sync.data.part[assistantMessage.id])
return <AssistantMessage message={assistantMessage} parts={parts()} />
return <Message message={assistantMessage} parts={parts()} />
}}
</For>
</div>
@@ -722,7 +725,9 @@ export default function Page() {
const lastTextPart = createMemo(() =>
sync.data.part[last().id].findLast((p) => p.type === "text"),
)
return <Part message={last()} part={lastTextPart()!} readonly />
return (
<Part message={last()} part={lastTextPart()!} hideDetails />
)
}}
</Match>
<Match when={lastMessageWithReasoning()}>
@@ -733,7 +738,11 @@ export default function Page() {
),
)
return (
<Part message={last()} part={lastReasoningPart()!} readonly />
<Part
message={last()}
part={lastReasoningPart()!}
hideDetails
/>
)
}}
</Match>
@@ -745,7 +754,7 @@ export default function Page() {
(p) => p.type === "tool" && p.state.status === "completed",
),
)
return <Part message={last()} part={lastToolPart()!} readonly />
return <Part message={last()} part={lastToolPart()!} hideDetails />
}}
</Show>
</div>
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/function",
"version": "0.15.26",
"version": "0.15.27",
"$schema": "https://json.schemastore.org/package.json",
"private": true,
"type": "module",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"$schema": "https://json.schemastore.org/package.json",
"version": "0.15.26",
"version": "0.15.27",
"name": "opencode",
"type": "module",
"private": true,
+18 -12
View File
@@ -98,23 +98,29 @@ export namespace SessionSummary {
m.parts.some((p) => p.type === "step-finish" && p.reason !== "tool-calls"),
)
) {
const result = await generateText({
model: small.language,
maxOutputTokens: 100,
messages: [
{
role: "user",
content: `
let summary = messages
.findLast((m) => m.info.role === "assistant")
?.parts.findLast((p) => p.type === "text")?.text
if (!summary || diffs.length > 0) {
const result = await generateText({
model: small.language,
maxOutputTokens: 100,
messages: [
{
role: "user",
content: `
Summarize the following conversation into 2 sentences MAX explaining what the assistant did and why. Do not explain the user's input. Do not speak in the third person about the assistant.
<conversation>
${JSON.stringify(MessageV2.toModelMessage(messages))}
</conversation>
`,
},
],
})
userMsg.summary.body = result.text
log.info("body", { body: result.text })
},
],
})
summary = result.text
}
userMsg.summary.body = summary
log.info("body", { body: summary })
await Session.updateMessage(userMsg)
}
}
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@opencode-ai/plugin",
"version": "0.15.26",
"version": "0.15.27",
"type": "module",
"scripts": {
"typecheck": "tsgo --noEmit",
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@opencode-ai/sdk",
"version": "0.15.26",
"version": "0.15.27",
"type": "module",
"scripts": {
"typecheck": "tsgo --noEmit",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/slack",
"version": "0.15.26",
"version": "0.15.27",
"type": "module",
"scripts": {
"dev": "bun run src/index.ts",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/ui",
"version": "0.15.26",
"version": "0.15.27",
"type": "module",
"exports": {
".": "./src/components/index.ts",
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@opencode-ai/web",
"type": "module",
"version": "0.15.26",
"version": "0.15.27",
"scripts": {
"dev": "astro dev",
"dev:remote": "VITE_API_URL=https://api.opencode.ai astro dev",
+1 -1
View File
@@ -2,7 +2,7 @@
"name": "opencode",
"displayName": "opencode",
"description": "opencode for VS Code",
"version": "0.15.26",
"version": "0.15.27",
"publisher": "sst-dev",
"repository": {
"type": "git",