summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorDax Raad <[email protected]>2025-07-04 12:16:55 -0400
committerDax Raad <[email protected]>2025-07-04 12:24:01 -0400
commitee01f01271f1e8c04a0efeacad0c36a44fd18515 (patch)
treea66fa3ef0c42ffc5e7ef7770590993c74f3f335f
parent32d5db4f0a0b0c1a90ba4301cbf0bb7bc2519613 (diff)
downloadopencode-ee01f01271f1e8c04a0efeacad0c36a44fd18515.tar.gz
opencode-ee01f01271f1e8c04a0efeacad0c36a44fd18515.zip
file attachments
-rw-r--r--packages/opencode/src/session/index.ts64
-rw-r--r--packages/tui/internal/components/chat/editor.go2
2 files changed, 51 insertions, 15 deletions
diff --git a/packages/opencode/src/session/index.ts b/packages/opencode/src/session/index.ts
index 5a2c1b5e7..437ce09bd 100644
--- a/packages/opencode/src/session/index.ts
+++ b/packages/opencode/src/session/index.ts
@@ -1,4 +1,4 @@
-import path from "path"
+import path from "node:path"
import { App } from "../app/app"
import { Identifier } from "../id/id"
import { Storage } from "../storage/storage"
@@ -15,6 +15,7 @@ import {
type UIMessage,
type ProviderMetadata,
wrapLanguageModel,
+ type Attachment,
} from "ai"
import { z, ZodSchema } from "zod"
import { Decimal } from "decimal.js"
@@ -187,7 +188,6 @@ export namespace Session {
export async function unshare(id: string) {
const share = await getShare(id)
if (!share) return
- console.log("share", share)
await Storage.remove("session/share/" + id)
await update(id, (draft) => {
draft.share = undefined
@@ -361,6 +361,36 @@ export namespace Session {
if (lastSummary) msgs = msgs.filter((msg) => msg.id >= lastSummary.id)
const app = App.info()
+ input.parts = await Promise.all(
+ input.parts.map(async (part) => {
+ if (part.type === "file") {
+ const url = new URL(part.url)
+ switch (url.protocol) {
+ case "file:":
+ let content = await Bun.file(
+ path.join(app.path.cwd, url.pathname),
+ ).text()
+ const range = {
+ start: url.searchParams.get("start"),
+ end: url.searchParams.get("end"),
+ }
+ if (range.start != null) {
+ const lines = content.split("\n")
+ const start = parseInt(range.start)
+ const end = range.end ? parseInt(range.end) : lines.length
+ content = lines.slice(start, end).join("\n")
+ }
+ return {
+ type: "file",
+ url: "data:text/plain;base64," + btoa(content),
+ mediaType: "text/plain",
+ filename: part.filename,
+ }
+ }
+ }
+ return part
+ }),
+ )
if (msgs.length === 0 && !session.parentID) {
generateText({
maxTokens: input.providerID === "google" ? 1024 : 20,
@@ -376,7 +406,7 @@ export namespace Session {
{
role: "user",
content: "",
- parts: toParts(input.parts),
+ ...toParts(input.parts),
},
]),
],
@@ -1028,7 +1058,7 @@ function toUIMessage(msg: Message.Info): UIMessage {
id: msg.id,
role: "assistant",
content: "",
- parts: toParts(msg.parts),
+ ...toParts(msg.parts),
}
}
@@ -1037,35 +1067,41 @@ function toUIMessage(msg: Message.Info): UIMessage {
id: msg.id,
role: "user",
content: "",
- parts: toParts(msg.parts),
+ ...toParts(msg.parts),
}
}
throw new Error("not implemented")
}
-function toParts(parts: Message.MessagePart[]): UIMessage["parts"] {
- const result: UIMessage["parts"] = []
+function toParts(parts: Message.MessagePart[]) {
+ const result: {
+ parts: UIMessage["parts"]
+ experimental_attachments: Attachment[]
+ } = {
+ parts: [],
+ experimental_attachments: [],
+ }
for (const part of parts) {
switch (part.type) {
case "text":
- result.push({ type: "text", text: part.text })
+ result.parts.push({ type: "text", text: part.text })
break
case "file":
- result.push({
- type: "file",
- data: part.url,
- mimeType: part.mediaType,
+ result.experimental_attachments.push({
+ url: part.url,
+ contentType: part.mediaType,
+ name: part.filename,
})
break
case "tool-invocation":
- result.push({
+ result.parts.push({
type: "tool-invocation",
toolInvocation: part.toolInvocation,
})
break
case "step-start":
- result.push({
+ result.parts.push({
type: "step-start",
})
break
diff --git a/packages/tui/internal/components/chat/editor.go b/packages/tui/internal/components/chat/editor.go
index 427fcc3cf..99925e16d 100644
--- a/packages/tui/internal/components/chat/editor.go
+++ b/packages/tui/internal/components/chat/editor.go
@@ -108,7 +108,7 @@ func (m *editorComponent) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
attachment := &textarea.Attachment{
ID: uuid.NewString(),
Display: "@" + fileName,
- URL: fmt.Sprintf("file://%s", filePath),
+ URL: fmt.Sprintf("file://./%s", filePath),
Filename: fileName,
MediaType: mediaType,
}