Skip to content

Commit 0e035b3

Browse files
committed
fix aborting issue
1 parent b855511 commit 0e035b3

File tree

4 files changed

+127
-87
lines changed

4 files changed

+127
-87
lines changed

packages/opencode/AGENTS.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,19 @@
1616
- **Naming**: camelCase for variables/functions, PascalCase for classes/namespaces
1717
- **Error handling**: Use Result patterns, avoid throwing exceptions in tools
1818
- **File structure**: Namespace-based organization (e.g., `Tool.define()`, `Session.create()`)
19+
20+
## IMPORTANT
21+
22+
- Try to keep things in one function unless composable or reusable
1923
- DO NOT do unnecessary destructuring of variables
2024
- DO NOT use else statements unless necessary
2125
- DO NOT use try catch if it can be avoided
26+
- AVOID try catch where possible
27+
- AVOID else statements
28+
- AVOID using `any` type
29+
- AVOID let statements
30+
- PREFER single word variable names where possible
31+
- Use as many bun apis as possible like Bun.file()
2232

2333
## Architecture
2434

@@ -27,4 +37,3 @@
2737
- **Validation**: All inputs validated with Zod schemas
2838
- **Logging**: Use `Log.create({ service: "name" })` pattern
2939
- **Storage**: Use `Storage` namespace for persistence
30-

packages/opencode/src/cli/cmd/run.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,9 @@ export const RunCommand = {
115115
})
116116

117117
const { providerID, modelID } = await Provider.defaultModel()
118+
setTimeout(() => {
119+
Session.abort(session.id)
120+
}, 8000)
118121
await Session.chat({
119122
sessionID: session.id,
120123
providerID,

packages/opencode/src/index.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,8 @@ import { App } from "./app/app"
33
import { Server } from "./server/server"
44
import fs from "fs/promises"
55
import path from "path"
6-
76
import { Share } from "./share/share"
8-
97
import { Global } from "./global"
10-
118
import yargs from "yargs"
129
import { hideBin } from "yargs/helpers"
1310
import { RunCommand } from "./cli/cmd/run"

packages/opencode/src/session/index.ts

Lines changed: 114 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -405,7 +405,7 @@ export namespace Session {
405405
await updateMessage(next)
406406
},
407407
onError(err) {
408-
log.error("error", err)
408+
log.error("callback error", err)
409409
switch (true) {
410410
case LoadAPIKeyError.isInstance(err.error):
411411
next.metadata.error = new Provider.AuthError(
@@ -460,100 +460,131 @@ export namespace Session {
460460
},
461461
model: model.language,
462462
})
463-
for await (const value of result.fullStream) {
464-
l.info("part", {
465-
type: value.type,
466-
})
467-
switch (value.type) {
468-
case "step-start":
469-
next.parts.push({
470-
type: "step-start",
471-
})
472-
break
473-
case "text-delta":
474-
if (!text) {
475-
text = {
476-
type: "text",
477-
text: value.textDelta,
478-
}
479-
next.parts.push(text)
463+
try {
464+
for await (const value of result.fullStream) {
465+
l.info("part", {
466+
type: value.type,
467+
})
468+
switch (value.type) {
469+
case "step-start":
470+
next.parts.push({
471+
type: "step-start",
472+
})
473+
break
474+
case "text-delta":
475+
if (!text) {
476+
text = {
477+
type: "text",
478+
text: value.textDelta,
479+
}
480+
next.parts.push(text)
481+
break
482+
} else text.text += value.textDelta
480483
break
481-
} else text.text += value.textDelta
482-
break
483-
484-
case "tool-call": {
485-
const [match] = next.parts.flatMap((p) =>
486-
p.type === "tool-invocation" &&
487-
p.toolInvocation.toolCallId === value.toolCallId
488-
? [p]
489-
: [],
490-
)
491-
if (!match) break
492-
match.toolInvocation.args = value.args
493-
match.toolInvocation.state = "call"
494-
Bus.publish(Message.Event.PartUpdated, {
495-
part: match,
496-
messageID: next.id,
497-
sessionID: next.metadata.sessionID,
498-
})
499-
break
500-
}
501-
502-
case "tool-call-streaming-start":
503-
next.parts.push({
504-
type: "tool-invocation",
505-
toolInvocation: {
506-
state: "partial-call",
507-
toolName: value.toolName,
508-
toolCallId: value.toolCallId,
509-
args: {},
510-
},
511-
})
512-
Bus.publish(Message.Event.PartUpdated, {
513-
part: next.parts[next.parts.length - 1],
514-
messageID: next.id,
515-
sessionID: next.metadata.sessionID,
516-
})
517-
break
518-
519-
case "tool-call-delta":
520-
break
521484

522-
// for some reason ai sdk claims to not send this part but it does
523-
// @ts-expect-error
524-
case "tool-result":
525-
const match = next.parts.find(
526-
(p) =>
485+
case "tool-call": {
486+
const [match] = next.parts.flatMap((p) =>
527487
p.type === "tool-invocation" &&
528-
// @ts-expect-error
529-
p.toolInvocation.toolCallId === value.toolCallId,
530-
)
531-
if (match && match.type === "tool-invocation") {
532-
match.toolInvocation = {
533-
// @ts-expect-error
534-
args: value.args,
535-
// @ts-expect-error
536-
toolCallId: value.toolCallId,
537-
// @ts-expect-error
538-
toolName: value.toolName,
539-
state: "result",
540-
// @ts-expect-error
541-
result: value.result as string,
542-
}
488+
p.toolInvocation.toolCallId === value.toolCallId
489+
? [p]
490+
: [],
491+
)
492+
if (!match) break
493+
match.toolInvocation.args = value.args
494+
match.toolInvocation.state = "call"
543495
Bus.publish(Message.Event.PartUpdated, {
544496
part: match,
545497
messageID: next.id,
546498
sessionID: next.metadata.sessionID,
547499
})
500+
break
548501
}
549-
break
550502

503+
case "tool-call-streaming-start":
504+
next.parts.push({
505+
type: "tool-invocation",
506+
toolInvocation: {
507+
state: "partial-call",
508+
toolName: value.toolName,
509+
toolCallId: value.toolCallId,
510+
args: {},
511+
},
512+
})
513+
Bus.publish(Message.Event.PartUpdated, {
514+
part: next.parts[next.parts.length - 1],
515+
messageID: next.id,
516+
sessionID: next.metadata.sessionID,
517+
})
518+
break
519+
520+
case "tool-call-delta":
521+
break
522+
523+
// for some reason ai sdk claims to not send this part but it does
524+
// @ts-expect-error
525+
case "tool-result":
526+
const match = next.parts.find(
527+
(p) =>
528+
p.type === "tool-invocation" &&
529+
// @ts-expect-error
530+
p.toolInvocation.toolCallId === value.toolCallId,
531+
)
532+
if (match && match.type === "tool-invocation") {
533+
match.toolInvocation = {
534+
// @ts-expect-error
535+
args: value.args,
536+
// @ts-expect-error
537+
toolCallId: value.toolCallId,
538+
// @ts-expect-error
539+
toolName: value.toolName,
540+
state: "result",
541+
// @ts-expect-error
542+
result: value.result as string,
543+
}
544+
Bus.publish(Message.Event.PartUpdated, {
545+
part: match,
546+
messageID: next.id,
547+
sessionID: next.metadata.sessionID,
548+
})
549+
}
550+
break
551+
552+
default:
553+
l.info("unhandled", {
554+
type: value.type,
555+
})
556+
}
557+
await updateMessage(next)
558+
}
559+
} catch (e: any) {
560+
log.error("stream error", {
561+
error: e,
562+
})
563+
switch (true) {
564+
case LoadAPIKeyError.isInstance(e):
565+
next.metadata.error = new Provider.AuthError(
566+
{
567+
providerID: input.providerID,
568+
message: e.message,
569+
},
570+
{ cause: e },
571+
).toObject()
572+
break
573+
case e instanceof Error:
574+
next.metadata.error = new NamedError.Unknown(
575+
{ message: e.toString() },
576+
{ cause: e },
577+
).toObject()
578+
break
551579
default:
552-
l.info("unhandled", {
553-
type: value.type,
554-
})
580+
next.metadata.error = new NamedError.Unknown(
581+
{ message: JSON.stringify(e) },
582+
{ cause: e },
583+
)
555584
}
556-
await updateMessage(next)
585+
Bus.publish(Event.Error, {
586+
error: next.metadata.error,
587+
})
557588
}
558589
next.metadata!.time.completed = Date.now()
559590
for (const part of next.parts) {

0 commit comments

Comments
 (0)