From d02a6a83432591b0a45282a3ea07f6605fc14475 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Mon, 7 Jul 2025 00:15:58 -0400 Subject: [PATCH] sync --- packages/opencode/src/session/index.ts | 133 ++++------------ packages/web/src/components/Share.tsx | 1 + .../components/share/content-code.module.css | 1 + .../web/src/components/share/part.module.css | 51 +++++-- packages/web/src/components/share/part.tsx | 143 +++++++++++++----- 5 files changed, 177 insertions(+), 152 deletions(-) diff --git a/packages/opencode/src/session/index.ts b/packages/opencode/src/session/index.ts index 82636dab..b3567a5c 100644 --- a/packages/opencode/src/session/index.ts +++ b/packages/opencode/src/session/index.ts @@ -129,9 +129,7 @@ export namespace Session { id: Identifier.descending("session"), version: Installation.VERSION, parentID, - title: - (parentID ? "Child session - " : "New Session - ") + - new Date().toISOString(), + title: (parentID ? "Child session - " : "New Session - ") + new Date().toISOString(), time: { created: Date.now(), updated: Date.now(), @@ -220,9 +218,7 @@ export namespace Session { } export async function getMessage(sessionID: string, messageID: string) { - return Storage.readJSON( - "session/message/" + sessionID + "/" + messageID, - ) + return Storage.readJSON("session/message/" + sessionID + "/" + messageID) } export async function* list() { @@ -274,10 +270,7 @@ export namespace Session { } async function updateMessage(msg: MessageV2.Info) { - await Storage.writeJSON( - "session/message/" + msg.sessionID + "/" + msg.id, - msg, - ) + await Storage.writeJSON("session/message/" + msg.sessionID + "/" + msg.id, msg) Bus.publish(MessageV2.Event.Updated, { info: msg, }) @@ -301,13 +294,8 @@ export namespace Session { if (session.revert) { const trimmed = [] for (const msg of msgs) { - if ( - msg.id > session.revert.messageID || - (msg.id === session.revert.messageID && session.revert.part === 0) - ) { - await Storage.remove( - "session/message/" + input.sessionID + "/" + msg.id, - ) + if (msg.id > session.revert.messageID || (msg.id === session.revert.messageID && session.revert.part === 0)) { + await Storage.remove("session/message/" + input.sessionID + "/" + msg.id) await Bus.publish(MessageV2.Event.Removed, { sessionID: input.sessionID, messageID: msg.id, @@ -332,17 +320,10 @@ export namespace Session { // auto summarize if too long if (previous) { const tokens = - previous.tokens.input + - previous.tokens.cache.read + - previous.tokens.cache.write + - previous.tokens.output + previous.tokens.input + previous.tokens.cache.read + previous.tokens.cache.write + previous.tokens.output if ( model.info.limit.context && - tokens > - Math.max( - (model.info.limit.context - (model.info.limit.output ?? 0)) * 0.9, - 0, - ) + tokens > Math.max((model.info.limit.context - (model.info.limit.output ?? 0)) * 0.9, 0) ) { await summarize({ sessionID: input.sessionID, @@ -353,9 +334,7 @@ export namespace Session { } } - const lastSummary = msgs.findLast( - (msg) => msg.role === "assistant" && msg.summary === true, - ) + const lastSummary = msgs.findLast((msg) => msg.role === "assistant" && msg.summary === true) if (lastSummary) msgs = msgs.filter((msg) => msg.id >= lastSummary.id) const app = App.info() @@ -384,12 +363,7 @@ export namespace Session { return [ { type: "text", - text: [ - "Called the Read tool on " + url.pathname, - "", - text, - "", - ].join("\n"), + text: ["Called the Read tool on " + url.pathname, "", text, ""].join("\n"), }, ] } @@ -401,9 +375,7 @@ export namespace Session { }, { type: "file", - url: - `data:${part.mime};base64,` + - Buffer.from(await file.bytes()).toString("base64url"), + url: `data:${part.mime};base64,` + Buffer.from(await file.bytes()).toString("base64url"), mime: part.mime, filename: part.filename!, }, @@ -500,8 +472,7 @@ export namespace Session { messageID: next.id, metadata: async (val) => { const match = next.parts.find( - (p): p is MessageV2.ToolPart => - p.type === "tool" && p.id === opts.toolCallId, + (p): p is MessageV2.ToolPart => p.type === "tool" && p.id === opts.toolCallId, ) if (match && match.state.status === "running") { match.state.title = val.title @@ -567,11 +538,7 @@ export namespace Session { async transformParams(args) { if (args.type === "stream") { // @ts-expect-error - args.params.prompt = ProviderTransform.message( - args.params.prompt, - input.providerID, - input.modelID, - ) + args.params.prompt = ProviderTransform.message(args.params.prompt, input.providerID, input.modelID) } return args.params }, @@ -607,10 +574,7 @@ export namespace Session { break case "tool-call": { - const match = next.parts.find( - (p): p is MessageV2.ToolPart => - p.type === "tool" && p.id === value.toolCallId, - ) + const match = next.parts.find((p): p is MessageV2.ToolPart => p.type === "tool" && p.id === value.toolCallId) if (match) { match.state = { status: "running", @@ -628,10 +592,7 @@ export namespace Session { break } case "tool-result": { - const match = next.parts.find( - (p): p is MessageV2.ToolPart => - p.type === "tool" && p.id === value.toolCallId, - ) + const match = next.parts.find((p): p is MessageV2.ToolPart => p.type === "tool" && p.id === value.toolCallId) if (match && match.state.status === "running") { match.state = { status: "completed", @@ -654,10 +615,7 @@ export namespace Session { } case "tool-error": { - const match = next.parts.find( - (p): p is MessageV2.ToolPart => - p.type === "tool" && p.id === value.toolCallId, - ) + const match = next.parts.find((p): p is MessageV2.ToolPart => p.type === "tool" && p.id === value.toolCallId) if (match && match.state.status === "running") { match.state = { status: "error", @@ -696,16 +654,10 @@ export namespace Session { ).toObject() break case e instanceof Error: - next.error = new NamedError.Unknown( - { message: e.toString() }, - { cause: e }, - ).toObject() + next.error = new NamedError.Unknown({ message: e.toString() }, { cause: e }).toObject() break default: - next.error = new NamedError.Unknown( - { message: JSON.stringify(e) }, - { cause: e }, - ) + next.error = new NamedError.Unknown({ message: JSON.stringify(e) }, { cause: e }) } Bus.publish(Event.Error, { error: next.error, @@ -719,11 +671,7 @@ export namespace Session { break case "finish-step": - const usage = getUsage( - model.info, - value.usage, - value.providerMetadata, - ) + const usage = getUsage(model.info, value.usage, value.providerMetadata) next.cost += usage.cost next.tokens = usage.tokens break @@ -733,10 +681,10 @@ export namespace Session { type: "text", text: "", } - next.parts.push(text) break case "text": + if (text.text === "") next.parts.push(text) text.text += value.text break @@ -763,11 +711,7 @@ export namespace Session { return next } - export async function revert(_input: { - sessionID: string - messageID: string - part: number - }) { + export async function revert(_input: { sessionID: string; messageID: string; part: number }) { // TODO /* const message = await getMessage(input.sessionID, input.messageID) @@ -804,23 +748,16 @@ export namespace Session { const session = await get(sessionID) if (!session) return if (!session.revert) return - if (session.revert.snapshot) - await Snapshot.restore(sessionID, session.revert.snapshot) + if (session.revert.snapshot) await Snapshot.restore(sessionID, session.revert.snapshot) update(sessionID, (draft) => { draft.revert = undefined }) } - export async function summarize(input: { - sessionID: string - providerID: string - modelID: string - }) { + export async function summarize(input: { sessionID: string; providerID: string; modelID: string }) { using abort = lock(input.sessionID) const msgs = await messages(input.sessionID) - const lastSummary = msgs.findLast( - (msg) => msg.role === "assistant" && msg.summary === true, - )?.id + const lastSummary = msgs.findLast((msg) => msg.role === "assistant" && msg.summary === true)?.id const filtered = msgs.filter((msg) => !lastSummary || msg.id >= lastSummary) const model = await Provider.getModel(input.providerID, input.modelID) const app = App.info() @@ -930,11 +867,7 @@ export namespace Session { } } - function getUsage( - model: ModelsDev.Model, - usage: LanguageModelUsage, - metadata?: ProviderMetadata, - ) { + function getUsage(model: ModelsDev.Model, usage: LanguageModelUsage, metadata?: ProviderMetadata) { const tokens = { input: usage.inputTokens ?? 0, output: usage.outputTokens ?? 0, @@ -951,16 +884,8 @@ export namespace Session { cost: new Decimal(0) .add(new Decimal(tokens.input).mul(model.cost.input).div(1_000_000)) .add(new Decimal(tokens.output).mul(model.cost.output).div(1_000_000)) - .add( - new Decimal(tokens.cache.read) - .mul(model.cost.cache_read ?? 0) - .div(1_000_000), - ) - .add( - new Decimal(tokens.cache.write) - .mul(model.cost.cache_write ?? 0) - .div(1_000_000), - ) + .add(new Decimal(tokens.cache.read).mul(model.cost.cache_read ?? 0).div(1_000_000)) + .add(new Decimal(tokens.cache.write).mul(model.cost.cache_write ?? 0).div(1_000_000)) .toNumber(), tokens, } @@ -972,11 +897,7 @@ export namespace Session { } } - export async function initialize(input: { - sessionID: string - modelID: string - providerID: string - }) { + export async function initialize(input: { sessionID: string; modelID: string; providerID: string }) { const app = App.info() await Session.chat({ sessionID: input.sessionID, diff --git a/packages/web/src/components/Share.tsx b/packages/web/src/components/Share.tsx index bf97b670..eefa8f06 100644 --- a/packages/web/src/components/Share.tsx +++ b/packages/web/src/components/Share.tsx @@ -324,6 +324,7 @@ export default function Share(props: { } } } + console.log(result.messages) return result }) diff --git a/packages/web/src/components/share/content-code.module.css b/packages/web/src/components/share/content-code.module.css index 08e699ac..18e68ece 100644 --- a/packages/web/src/components/share/content-code.module.css +++ b/packages/web/src/components/share/content-code.module.css @@ -7,6 +7,7 @@ &[data-flush="true"] { border: none; + background-color: transparent; padding: 0 } diff --git a/packages/web/src/components/share/part.module.css b/packages/web/src/components/share/part.module.css index 8b632413..345550d2 100644 --- a/packages/web/src/components/share/part.module.css +++ b/packages/web/src/components/share/part.module.css @@ -122,15 +122,15 @@ display: flex; flex-direction: column; align-items: flex-start; - gap: .375rem; + gap: 0.375rem; padding-bottom: 1rem; [data-slot="provider"] { line-height: 18px; - font-size: .875rem; + font-size: 0.875rem; text-transform: uppercase; - letter-spacing: -.5px; - color: var(--sl-color-text-secondary) + letter-spacing: -0.5px; + color: var(--sl-color-text-secondary); } [data-slot="model"] { @@ -173,21 +173,20 @@ align-items: flex-start; gap: 0.375rem; padding-bottom: 1rem; - } [data-component="tool-title"] { line-height: 18px; - font-size: .875rem; + font-size: 0.875rem; color: var(--sl-color-text-secondary); max-width: var(--md-tool-width); display: flex; align-items: flex-start; - gap: .375rem; + gap: 0.375rem; [data-slot="name"] { text-transform: uppercase; - letter-spacing: -.5px; + letter-spacing: -0.5px; } [data-slot="target"] { @@ -201,7 +200,7 @@ display: flex; flex-direction: column; align-items: flex-start; - gap: .5rem; + gap: 0.5rem; } [data-component="todos"] { @@ -285,4 +284,38 @@ } } + [data-component="terminal"] { + width: 100%; + max-width: var(--md-tool-width); + + [data-slot="body"] { + display: flex; + flex-direction: column; + border: 1px solid var(--sl-color-divider); + border-radius: 0.25rem; + overflow: hidden; + } + + [data-slot="header"] { + padding: 0.5rem 0.75rem; + border-bottom: 1px solid var(--sl-color-divider); + font-size: 0.75rem; + color: var(--sl-color-text-secondary); + } + + [data-slot="content"] { + display: flex; + flex-direction: column; + + &> :global(.astro-code) { + margin: 0; + border-radius: 0; + border: none; + } + + [data-slot="error"] { + border-top: 1px solid var(--sl-color-divider); + } + } + } } diff --git a/packages/web/src/components/share/part.tsx b/packages/web/src/components/share/part.tsx index ef46d45f..baa18ed5 100644 --- a/packages/web/src/components/share/part.tsx +++ b/packages/web/src/components/share/part.tsx @@ -8,7 +8,7 @@ import { DateTime } from "luxon" import CodeBlock from "../CodeBlock" import map from "lang-map" import type { Diagnostic } from "vscode-languageserver-types" -import { BashTool, FallbackTool } from "./tool" +import { FallbackTool } from "./tool" import { ContentCode } from "./content-code" import { ContentDiff } from "./content-diff" @@ -88,42 +88,89 @@ export function Part(props: PartProps) {
{props.message.modelID}
)} - {props.part.type === "tool" && props.part.state.status === "completed" && props.message.role === "assistant" && ( -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- )} + {props.part.type === "tool" && + props.part.state.status === "completed" && + props.message.role === "assistant" && ( +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ )} ) @@ -439,6 +486,28 @@ export function EditTool(props: ToolProps) { ) } +export function BashTool(props: ToolProps) { + const command = () => props.state.metadata?.title + const result = () => props.state.metadata?.stdout + const error = () => props.state.metadata?.stderr + + return ( + <> +
+
+
+ {props.state.metadata.description} +
+
+ + +
+
+
+ + ) +} + export function GlobTool(props: ToolProps) { const count = () => props.state.metadata?.count const pattern = () => props.state.input.pattern