Skip to content

Commit 8bec747

Browse files
committed
Merge remote-tracking branch 'origin/dev' into fix/app-subagent-permission-reply
# Conflicts: # packages/app/e2e/utils/mock-server.ts
2 parents 0c6a13a + 1f66db0 commit 8bec747

21 files changed

Lines changed: 1598 additions & 166 deletions

bun.lock

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

nix/hashes.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
{
22
"nodeModules": {
3-
"x86_64-linux": "sha256-6s5msV+dYMHHt9Gc/CqvCrUj8K7ELjxoAe6ejHSJo4I=",
4-
"aarch64-linux": "sha256-SP94UPy5LePd7ZdC3eENIXiozc67blpg1SN9Ug5yiv8=",
5-
"aarch64-darwin": "sha256-jv3lffiAQ5kxDAbXNavsHD+tjjdgT0dT0JxN0bWMYTE=",
6-
"x86_64-darwin": "sha256-aYNtcarg516ZmqaO62mnUZYiSyWt8rJjUHQslhrhGHM="
3+
"x86_64-linux": "sha256-gqXxbi1OwLoDSDtlmYWcTTPT/fqhVGb53JXS/9a1vWw=",
4+
"aarch64-linux": "sha256-+ARbWtOUDx0J5k55mmre/2kPekUsQtWWS0QZnyX6NTo=",
5+
"aarch64-darwin": "sha256-Q6ioI6lFUhNKogbvbtGWsZcXJfbRgUAWS5X2X4mKdg4=",
6+
"x86_64-darwin": "sha256-8K/hmcvXi9FGqprNbTKzMB80h5zZhO2TjOQbtve3ltQ="
77
}
88
}

packages/app/e2e/regression/session-timeline-collapse-state.spec.ts

Lines changed: 9 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { expect, test, type Locator, type Page, type Route } from "@playwright/test"
1+
import { expect, test, type Locator, type Page } from "@playwright/test"
2+
import { mockOpenCodeServer } from "../utils/mock-server"
23

34
const directory = "C:/OpenCode/TimelineStateRegression"
45
const projectID = "proj_timeline_state_regression"
@@ -299,39 +300,13 @@ function readExpanded(element: Element) {
299300
}
300301

301302
async function mockServer(page: Page, events: EventPayload[]) {
302-
await page.route("**/*", async (route) => {
303-
const url = new URL(route.request().url())
304-
const targetPort = process.env.PLAYWRIGHT_SERVER_PORT ?? "4096"
305-
if (url.port !== targetPort) return route.fallback()
306-
307-
const path = url.pathname
308-
if (path === "/global/event") return sse(route, events.splice(0))
309-
if (
310-
path === "/global/config" ||
311-
path === "/config" ||
312-
path === "/provider/auth" ||
313-
path === "/mcp" ||
314-
path === "/session/status"
315-
)
316-
return json(route, {})
317-
if (
318-
["/skill", "/command", "/lsp", "/formatter", "/permission", "/question", "/vcs/status", "/vcs/diff"].includes(
319-
path,
320-
)
321-
)
322-
return json(route, [])
323-
if (path === "/provider") return json(route, provider())
324-
if (path === "/path")
325-
return json(route, { state: directory, config: directory, worktree: directory, directory, home: "C:/OpenCode" })
326-
if (path === "/project") return json(route, [project()])
327-
if (path === "/project/current") return json(route, project())
328-
if (path === "/agent") return json(route, [{ name: "build", mode: "primary" }])
329-
if (path === "/vcs") return json(route, { branch: "main", default_branch: "main" })
330-
if (path === "/session") return json(route, [session()])
331-
if (path === `/session/${sessionID}`) return json(route, session())
332-
if (/^\/session\/[^/]+\/(children|todo|diff)$/.test(path)) return json(route, [])
333-
if (path === `/session/${sessionID}/message`) return json(route, [userMessage, assistantMessage])
334-
return json(route, {})
303+
await mockOpenCodeServer(page, {
304+
directory,
305+
project: project(),
306+
provider: provider(),
307+
sessions: [session()],
308+
pageMessages: () => ({ items: [userMessage, assistantMessage] }),
309+
events: () => events.splice(0),
335310
})
336311
}
337312

@@ -372,24 +347,6 @@ function provider() {
372347
}
373348
}
374349

375-
function json(route: Route, body: unknown, headers?: Record<string, string>) {
376-
return route.fulfill({
377-
status: 200,
378-
contentType: "application/json",
379-
headers: { "access-control-allow-origin": "*", "access-control-expose-headers": "x-next-cursor", ...headers },
380-
body: JSON.stringify(body ?? null),
381-
})
382-
}
383-
384-
function sse(route: Route, events: EventPayload[]) {
385-
return route.fulfill({
386-
status: 200,
387-
contentType: "text/event-stream",
388-
headers: { "access-control-allow-origin": "*" },
389-
body: events.map((event) => `data: ${JSON.stringify(event)}\n\n`).join(""),
390-
})
391-
}
392-
393350
function base64Encode(value: string) {
394351
return Buffer.from(value, "utf8").toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "")
395352
}

packages/app/e2e/regression/session-timeline-context-resize.spec.ts

Lines changed: 8 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { expect, test, type Page, type Route } from "@playwright/test"
1+
import { expect, test, type Page } from "@playwright/test"
2+
import { mockOpenCodeServer } from "../utils/mock-server"
23

34
const directory = "C:/OpenCode/ContextResizeRegression"
45
const projectID = "proj_context_resize_regression"
@@ -207,33 +208,12 @@ function contextTool(partID: string, messageID: string, tool: string, input: Rec
207208
}
208209

209210
async function mockServer(page: Page) {
210-
await page.route("**/*", async (route) => {
211-
const url = new URL(route.request().url())
212-
const targetPort = process.env.PLAYWRIGHT_SERVER_PORT ?? "4096"
213-
if (url.port !== targetPort) return route.fallback()
214-
215-
const path = url.pathname
216-
if (path === "/global/event" || path === "/event") return sse(route)
217-
if (["/global/config", "/config", "/provider/auth", "/mcp", "/session/status"].includes(path))
218-
return json(route, {})
219-
if (
220-
["/skill", "/command", "/lsp", "/formatter", "/permission", "/question", "/vcs/status", "/vcs/diff"].includes(
221-
path,
222-
)
223-
)
224-
return json(route, [])
225-
if (path === "/provider") return json(route, provider())
226-
if (path === "/path")
227-
return json(route, { state: directory, config: directory, worktree: directory, directory, home: "C:/OpenCode" })
228-
if (path === "/project") return json(route, [project()])
229-
if (path === "/project/current") return json(route, project())
230-
if (path === "/agent") return json(route, [{ name: "build", mode: "primary" }])
231-
if (path === "/vcs") return json(route, { branch: "main", default_branch: "main" })
232-
if (path === "/session") return json(route, [session()])
233-
if (path === `/session/${sessionID}`) return json(route, session())
234-
if (/^\/session\/[^/]+\/(children|todo|diff)$/.test(path)) return json(route, [])
235-
if (path === `/session/${sessionID}/message`) return json(route, messages)
236-
return json(route, {})
211+
await mockOpenCodeServer(page, {
212+
directory,
213+
project: project(),
214+
provider: provider(),
215+
sessions: [session()],
216+
pageMessages: () => ({ items: messages }),
237217
})
238218
}
239219

@@ -282,19 +262,6 @@ function provider() {
282262
}
283263
}
284264

285-
function json(route: Route, body: unknown, headers?: Record<string, string>) {
286-
return route.fulfill({
287-
status: 200,
288-
contentType: "application/json",
289-
headers: { "access-control-allow-origin": "*", "access-control-expose-headers": "x-next-cursor", ...headers },
290-
body: JSON.stringify(body ?? null),
291-
})
292-
}
293-
294-
function sse(route: Route) {
295-
return route.fulfill({ status: 200, contentType: "text/event-stream", body: ": ok\n\n" })
296-
}
297-
298265
function base64Encode(value: string) {
299266
return Buffer.from(value, "utf8").toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "")
300267
}

packages/app/e2e/utils/mock-server.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export interface MockServerConfig {
2121
onPermissionReply?: (input: { requestID: string; body: unknown }) => void
2222
onDeprecatedPermissionRespond?: (input: { sessionID: string; permissionID: string; body: unknown }) => void
2323
pageMessages: (sessionId: string, limit: number, before?: string) => { items: unknown[]; cursor?: string }
24+
events?: () => unknown[]
2425
}
2526

2627
export async function mockOpenCodeServer(page: Page, config: MockServerConfig) {
@@ -48,7 +49,8 @@ export async function mockOpenCodeServer(page: Page, config: MockServerConfig) {
4849
}
4950

5051
const path = url.pathname
51-
if (path === "/global/event" || path === "/event") return sse(route)
52+
if (path === "/global/event" || path === "/event") return sse(route, config.events?.())
53+
if (path === "/global/health") return json(route, { healthy: true })
5254
if (path === "/permission") return json(route, config.permissions ?? [])
5355
if (emptyObject.has(path)) return json(route, {})
5456
if (emptyList.has(path)) return json(route, [])
@@ -139,6 +141,10 @@ function postBody(route: Route) {
139141
return JSON.parse(text) as unknown
140142
}
141143

142-
function sse(route: Route) {
143-
return route.fulfill({ status: 200, contentType: "text/event-stream", body: ": ok\n\n" })
144+
function sse(route: Route, events?: unknown[]) {
145+
return route.fulfill({
146+
status: 200,
147+
contentType: "text/event-stream",
148+
body: events?.map((event) => `data: ${JSON.stringify(event)}\n\n`).join("") || ": ok\n\n",
149+
})
144150
}

packages/app/src/context/server.tsx

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -36,24 +36,30 @@ export function resolveServerList(input: {
3636
props?: Array<ServerConnection.Any>
3737
stored: StoredServer[]
3838
}): Array<ServerConnection.Any> {
39-
const servers = [
40-
...(input.props ?? []),
41-
...input.stored.map((value) =>
39+
const deduped = new Map<ServerConnection.Key, ServerConnection.Any>(
40+
input.props?.map((v) => [ServerConnection.key(v), v]) ?? [],
41+
)
42+
43+
for (const value of input.stored) {
44+
const conn: ServerConnection.Http =
4245
typeof value === "string"
4346
? {
4447
type: "http" as const,
4548
http: { url: value },
4649
}
47-
: value,
48-
),
49-
]
50-
51-
const deduped = new Map<ServerConnection.Key, ServerConnection.Any>()
52-
for (const value of servers) {
53-
const conn: ServerConnection.Any = "type" in value ? value : { type: "http", http: value }
50+
: "http" in value
51+
? value
52+
: { type: "http", http: value }
5453
const key = ServerConnection.key(conn)
55-
if (deduped.has(key) && conn.type === "http" && !conn.authToken) continue
56-
deduped.set(key, conn)
54+
55+
const existing = deduped.get(key)
56+
if (existing)
57+
deduped.set(key, {
58+
...existing,
59+
...conn,
60+
http: { ...existing.http, ...conn.http },
61+
})
62+
else deduped.set(key, conn)
5763
}
5864

5965
return [...deduped.values()]

packages/app/src/entry.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,11 @@ if (root instanceof HTMLElement) {
168168
() => (
169169
<PlatformProvider value={platform}>
170170
<AppBaseProviders>
171-
<AppInterface defaultServer={ServerConnection.Key.make(getDefaultUrl())} servers={[server]} />
171+
<AppInterface
172+
defaultServer={ServerConnection.Key.make(getDefaultUrl())}
173+
servers={[server]}
174+
disableHealthCheck
175+
/>
172176
</AppBaseProviders>
173177
</PlatformProvider>
174178
),

packages/core/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
"@ai-sdk/cohere": "3.0.27",
3535
"@ai-sdk/deepinfra": "2.0.41",
3636
"@ai-sdk/gateway": "3.0.104",
37-
"@ai-sdk/google": "3.0.75",
37+
"@ai-sdk/google": "3.0.63",
3838
"@ai-sdk/google-vertex": "4.0.131",
3939
"@ai-sdk/groq": "3.0.31",
4040
"@ai-sdk/mistral": "3.0.27",

packages/opencode/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@
8181
"@ai-sdk/cohere": "3.0.27",
8282
"@ai-sdk/deepinfra": "2.0.41",
8383
"@ai-sdk/gateway": "3.0.104",
84-
"@ai-sdk/google": "3.0.75",
84+
"@ai-sdk/google": "3.0.63",
8585
"@ai-sdk/google-vertex": "4.0.131",
8686
"@ai-sdk/groq": "3.0.31",
8787
"@ai-sdk/mistral": "3.0.27",

packages/opencode/src/acp-next/agent.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,14 @@ import {
44
type AgentSideConnection,
55
type AuthenticateRequest,
66
type CancelNotification,
7+
type CloseSessionRequest,
8+
type ForkSessionRequest,
79
type InitializeRequest,
10+
type ListSessionsRequest,
811
type LoadSessionRequest,
912
type NewSessionRequest,
1013
type PromptRequest,
14+
type ResumeSessionRequest,
1115
type SetSessionConfigOptionRequest,
1216
type SetSessionModelRequest,
1317
type SetSessionModeRequest,
@@ -44,6 +48,22 @@ export class Agent implements ACPAgent {
4448
return run(this.service.loadSession(params))
4549
}
4650

51+
listSessions(params: ListSessionsRequest) {
52+
return run(this.service.listSessions(params))
53+
}
54+
55+
resumeSession(params: ResumeSessionRequest) {
56+
return run(this.service.resumeSession(params))
57+
}
58+
59+
closeSession(params: CloseSessionRequest) {
60+
return run(this.service.closeSession(params))
61+
}
62+
63+
unstable_forkSession(params: ForkSessionRequest) {
64+
return run(this.service.forkSession(params))
65+
}
66+
4767
setSessionConfigOption(params: SetSessionConfigOptionRequest) {
4868
return run(this.service.setSessionConfigOption(params))
4969
}

0 commit comments

Comments
 (0)