Skip to content

Commit 7a9bf58

Browse files
committed
sync: merge upstream v1.1.19 into integration
Upstream changes: - feat: add plan mode with enter/exit tools (anomalyco#8281) - feat: Add GitLab Duo Agentic Chat Provider Support (anomalyco#7333) - feat(desktop): Ask Question Tool Support (anomalyco#8232) - feat: add Undertale and Deltarune built-in themes (anomalyco#8240) - fix: Add Plugin Mocks to Provider Tests (anomalyco#8276) - fix: update User-Agent string to latest Chrome version in webfetch (anomalyco#8284) - fix(prompt-input): handle Shift+Enter before IME check - fix(state): delete key from recordsByKey on instance disposal - fix(mcp): close existing client before reassignment to prevent leaks Resolved conflicts: - bun.lock: regenerated with fork dependencies - global-sync.tsx: adopted upstream variable naming (question instead of request) - session/index.tsx: merged upstream workdir display with fork's ANSI terminal emulation - amazon-bedrock.test.ts: added all mock modules (fork + upstream) - vscode/package.json: kept fork branding, updated version Fork commits included: - refactor(tui): use SDK client for tool list dialog - chore: update lockfile with fork dependencies
2 parents e660de4 + 3565d8e commit 7a9bf58

64 files changed

Lines changed: 2672 additions & 168 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

bun.lock

Lines changed: 52 additions & 15 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

nix/hashes.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"nodeModules": {
3-
"x86_64-linux": "sha256-UCPTTk4b7d2bets7KgCeYBHWAUwUAPUyKm+xDYkSexE=",
4-
"aarch64-darwin": "sha256-Y3o6lovahSWoG9un/l1qxu7hCmIlZXm2LxOLKNiPQfQ="
3+
"x86_64-linux": "sha256-x6A/XT1i3bjakfAj0A1wV4n2s9rpflMDceTeppdP6tE=",
4+
"aarch64-darwin": "sha256-RkamQYbpjJqpHHf76em9lPgeI9k4/kaCf7T+4xHaizY="
55
}
66
}

packages/app/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@opencode-ai/app",
3-
"version": "1.1.18",
3+
"version": "1.1.19",
44
"description": "",
55
"type": "module",
66
"exports": {

packages/app/src/components/dialog-select-model.tsx

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@ import { Button } from "@opencode-ai/ui/button"
77
import { Tag } from "@opencode-ai/ui/tag"
88
import { Dialog } from "@opencode-ai/ui/dialog"
99
import { List } from "@opencode-ai/ui/list"
10-
import { ProviderIcon } from "@opencode-ai/ui/provider-icon"
11-
import type { IconName } from "@opencode-ai/ui/icons/provider"
1210
import { DialogSelectProvider } from "./dialog-select-provider"
1311
import { DialogManageModels } from "./dialog-manage-models"
1412

@@ -37,12 +35,6 @@ const ModelList: Component<{
3735
filterKeys={["provider.name", "name", "id"]}
3836
sortBy={(a, b) => a.name.localeCompare(b.name)}
3937
groupBy={(x) => x.provider.name}
40-
groupHeader={(group) => (
41-
<div class="flex items-center gap-x-3">
42-
<ProviderIcon data-slot="list-item-extra-icon" id={group.items[0].provider.id as IconName} />
43-
<span>{group.category}</span>
44-
</div>
45-
)}
4638
sortGroupsBy={(a, b) => {
4739
if (a.category === "Recent" && b.category !== "Recent") return -1
4840
if (b.category === "Recent" && a.category !== "Recent") return 1
@@ -60,8 +52,7 @@ const ModelList: Component<{
6052
}}
6153
>
6254
{(i) => (
63-
<div class="w-full flex items-center gap-x-3 pl-1 text-13-regular">
64-
<ProviderIcon data-slot="list-item-extra-icon" id={i.provider.id as IconName} />
55+
<div class="w-full flex items-center gap-x-2 text-13-regular">
6556
<span class="truncate">{i.name}</span>
6657
<Show when={i.provider.id === "opencode" && (!i.cost || i.cost?.input === 0)}>
6758
<Tag>Free</Tag>

packages/app/src/components/prompt-input.tsx

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,12 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
364364
if (!isFocused()) setStore("popover", null)
365365
})
366366

367+
// Safety: reset composing state on focus change to prevent stuck state
368+
// This handles edge cases where compositionend event may not fire
369+
createEffect(() => {
370+
if (!isFocused()) setComposing(false)
371+
})
372+
367373
type AtOption = { type: "agent"; name: string; display: string } | { type: "file"; path: string; display: string }
368374

369375
const agentList = createMemo(() =>
@@ -892,6 +898,14 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
892898
}
893899
}
894900

901+
// Handle Shift+Enter BEFORE IME check - Shift+Enter is never used for IME input
902+
// and should always insert a newline regardless of composition state
903+
if (event.key === "Enter" && event.shiftKey) {
904+
addPart({ type: "text", content: "\n", start: 0, end: 0 })
905+
event.preventDefault()
906+
return
907+
}
908+
895909
if (event.key === "Enter" && isImeComposing(event)) {
896910
return
897911
}
@@ -955,11 +969,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
955969
return
956970
}
957971

958-
if (event.key === "Enter" && event.shiftKey) {
959-
addPart({ type: "text", content: "\n", start: 0, end: 0 })
960-
event.preventDefault()
961-
return
962-
}
972+
// Note: Shift+Enter is handled earlier, before IME check
963973
if (event.key === "Enter" && !event.shiftKey) {
964974
handleSubmit(event)
965975
}

packages/app/src/context/global-sync.tsx

Lines changed: 32 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -230,28 +230,28 @@ function createGlobalSync() {
230230
}),
231231
sdk.question.list().then((x) => {
232232
const grouped: Record<string, QuestionRequest[]> = {}
233-
for (const request of x.data ?? []) {
234-
if (!request?.id || !request.sessionID) continue
235-
const existing = grouped[request.sessionID]
233+
for (const question of x.data ?? []) {
234+
if (!question?.id || !question.sessionID) continue
235+
const existing = grouped[question.sessionID]
236236
if (existing) {
237-
existing.push(request)
237+
existing.push(question)
238238
continue
239239
}
240-
grouped[request.sessionID] = [request]
240+
grouped[question.sessionID] = [question]
241241
}
242242

243243
batch(() => {
244244
for (const sessionID of Object.keys(store.question)) {
245245
if (grouped[sessionID]) continue
246246
setStore("question", sessionID, [])
247247
}
248-
for (const [sessionID, requests] of Object.entries(grouped)) {
248+
for (const [sessionID, questions] of Object.entries(grouped)) {
249249
setStore(
250250
"question",
251251
sessionID,
252252
reconcile(
253-
requests
254-
.filter((request) => !!request?.id)
253+
questions
254+
.filter((q) => !!q?.id)
255255
.slice()
256256
.sort((a, b) => a.id.localeCompare(b.id)),
257257
{ key: "id" },
@@ -448,38 +448,40 @@ function createGlobalSync() {
448448
)
449449
break
450450
}
451-
case "question.replied":
452-
case "question.rejected": {
453-
const requests = store.question[event.properties.sessionID]
454-
if (!requests) break
455-
const result = Binary.search(requests, event.properties.requestID, (r) => r.id)
456-
if (!result.found) break
451+
case "question.asked": {
452+
const sessionID = event.properties.sessionID
453+
const questions = store.question[sessionID]
454+
if (!questions) {
455+
setStore("question", sessionID, [event.properties])
456+
break
457+
}
458+
459+
const result = Binary.search(questions, event.properties.id, (q) => q.id)
460+
if (result.found) {
461+
setStore("question", sessionID, result.index, reconcile(event.properties))
462+
break
463+
}
464+
457465
setStore(
458466
"question",
459-
event.properties.sessionID,
467+
sessionID,
460468
produce((draft) => {
461-
draft.splice(result.index, 1)
469+
draft.splice(result.index, 0, event.properties)
462470
}),
463471
)
464472
break
465473
}
466-
case "question.asked": {
467-
const request = event.properties
468-
const requests = store.question[request.sessionID]
469-
if (!requests) {
470-
setStore("question", request.sessionID, [request])
471-
break
472-
}
473-
const result = Binary.search(requests, request.id, (r) => r.id)
474-
if (result.found) {
475-
setStore("question", request.sessionID, result.index, reconcile(request))
476-
break
477-
}
474+
case "question.replied":
475+
case "question.rejected": {
476+
const questions = store.question[event.properties.sessionID]
477+
if (!questions) break
478+
const result = Binary.search(questions, event.properties.requestID, (q) => q.id)
479+
if (!result.found) break
478480
setStore(
479481
"question",
480-
request.sessionID,
482+
event.properties.sessionID,
481483
produce((draft) => {
482-
draft.splice(result.index, 0, request)
484+
draft.splice(result.index, 1)
483485
}),
484486
)
485487
break

packages/app/src/pages/directory-layout.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { LocalProvider } from "@/context/local"
77
import { base64Decode } from "@opencode-ai/util/encode"
88
import { DataProvider } from "@opencode-ai/ui/context"
99
import { iife } from "@opencode-ai/util/iife"
10+
import type { QuestionAnswer } from "@opencode-ai/sdk/v2"
1011

1112
export default function Layout(props: ParentProps) {
1213
const params = useParams()
@@ -27,6 +28,11 @@ export default function Layout(props: ParentProps) {
2728
response: "once" | "always" | "reject"
2829
}) => sdk.client.permission.respond(input)
2930

31+
const replyToQuestion = (input: { requestID: string; answers: QuestionAnswer[] }) =>
32+
sdk.client.question.reply(input)
33+
34+
const rejectQuestion = (input: { requestID: string }) => sdk.client.question.reject(input)
35+
3036
const navigateToSession = (sessionID: string) => {
3137
navigate(`/${params.dir}/session/${sessionID}`)
3238
}
@@ -36,6 +42,8 @@ export default function Layout(props: ParentProps) {
3642
data={sync.data}
3743
directory={directory()}
3844
onPermissionRespond={respond}
45+
onQuestionReply={replyToQuestion}
46+
onQuestionReject={rejectQuestion}
3947
onNavigateToSession={navigateToSession}
4048
>
4149
<LocalProvider>{props.children}</LocalProvider>

packages/console/app/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@opencode-ai/console-app",
3-
"version": "1.1.18",
3+
"version": "1.1.19",
44
"type": "module",
55
"license": "MIT",
66
"scripts": {

packages/console/core/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"$schema": "https://json.schemastore.org/package.json",
33
"name": "@opencode-ai/console-core",
4-
"version": "1.1.18",
4+
"version": "1.1.19",
55
"private": true,
66
"type": "module",
77
"license": "MIT",

packages/console/function/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@opencode-ai/console-function",
3-
"version": "1.1.18",
3+
"version": "1.1.19",
44
"$schema": "https://json.schemastore.org/package.json",
55
"private": true,
66
"type": "module",

0 commit comments

Comments
 (0)