Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
17189d1
refactor(client): nest experimental endpoints under client.experimental
kitlangton Oct 25, 2026
e4fdeca
test(httpapi-codegen): cover namespaced groups in the effect client
kitlangton Oct 25, 2026
3ff1e46
fix(httpapi-codegen): key projected raw clients by client group name;…
kitlangton Oct 25, 2026
1411067
Merge branch 'dev' into kit/sdk-experimental-namespace
kitlangton Oct 25, 2026
ee63362
chore(app): clear lint:changed findings in files touched by the exper…
kitlangton Oct 26, 2026
faa72dc
Merge remote-tracking branch 'origin/dev' into kit/sdk-experimental-n…
kitlangton Oct 26, 2026
b6449b3
chore(app): clear lint:changed findings in renamed experimental callers
kitlangton Oct 26, 2026
bb5669f
docs: add experimental API rules to AGENTS.md and enforce experimenta…
kitlangton Oct 26, 2026
b824ae5
Merge remote-tracking branch 'origin/dev' into kit/sdk-experimental-n…
kitlangton Oct 26, 2026
8820bdd
feat(plugin): move experimental-backed plugin methods under ctx.exper…
kitlangton Oct 26, 2026
b5b7122
Merge remote-tracking branch 'origin/dev' into kit/sdk-experimental-n…
kitlangton Oct 26, 2026
363eda2
feat(sdk): keep pre-experimental SDK and plugin paths as deprecated a…
kitlangton Oct 26, 2026
da96c1c
fix(sdk): keep pre-experimental members on the Effect group interfaces
kitlangton Oct 26, 2026
fbf56c2
Merge remote-tracking branch 'origin/dev' into kit/sdk-experimental-n…
kitlangton Oct 26, 2026
5bee8ee
feat(client): generate deprecated SDK aliases from the codegen
kitlangton Oct 26, 2026
28ea6d1
Merge remote-tracking branch 'origin/dev' into kit/sdk-experimental-n…
kitlangton Oct 27, 2026
7109413
Merge remote-tracking branch 'origin/dev' into kit/sdk-experimental-n…
kitlangton Oct 27, 2026
10d53e3
chore: drop release reminders doc
kitlangton Oct 28, 2026
e7fec90
Merge remote-tracking branch 'origin/dev' into kit/sdk-experimental-n…
kitlangton Oct 28, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/app/src/home/projects/controller.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ export function createHomeProjectsController(home: HomeController) {
Schema.fromJsonString(Schema.Record(Schema.String, Schema.Json)),
)(await file.text())

const api = home.server.context(conn).sdk.api.session
const api = home.server.context(conn).sdk.api.experimental.session

// SAFETY: the import endpoint validates the transcript, including exports written by earlier releases.
const imported = await api.import({
Expand Down
4 changes: 2 additions & 2 deletions packages/app/src/providers/connect/mcp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,11 @@ export function useMcpToggle(directory?: Accessor<string | undefined>, onSuccess
const enabled = typeof input === "string" ? server.status.status !== "connected" : input.enabled

if (!enabled) {
await serverSDK.api.mcp.disconnect({ server: name, location: ref })
await serverSDK.api.experimental.mcp.disconnect({ server: name, location: ref })
}

if (enabled && server.status.status !== "needs_auth") {
await serverSDK.api.mcp.connect({ server: name, location: ref })
await serverSDK.api.experimental.mcp.connect({ server: name, location: ref })
}

data.location.mcp.server.invalidate(ref)
Expand Down
6 changes: 3 additions & 3 deletions packages/app/src/runtime/server/sync.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ export function createServerSyncContextInner(serverSDK: ServerSDK, data: Data) {
const updateConfigMutation = useMutation(() => ({
mutationFn: async (config: Config) => {
// TODO: Restore config updates when the V2 client exposes a config API.
// await serverSDK.api.config.update({ config })
// await serverSDK.api.experimental.config.update({ config })
throw new Error(`Config updates are unavailable: ${Object.keys(config).length} fields were not saved`)
},
onSuccess: () => {
Expand Down Expand Up @@ -318,10 +318,10 @@ export function createServerSyncContextInner(serverSDK: ServerSDK, data: Data) {
await toggleMcp({
status,
connect: async () => {
await serverSDK.api.mcp.connect({ server: name, location: { directory: key } })
await serverSDK.api.experimental.mcp.connect({ server: name, location: { directory: key } })
},
disconnect: async () => {
await serverSDK.api.mcp.disconnect({ server: name, location: { directory: key } })
await serverSDK.api.experimental.mcp.disconnect({ server: name, location: { directory: key } })
},
authenticate: async () => {
const server = (await serverSDK.api.mcp.list({ location: { directory: key } })).data.find(
Expand Down
2 changes: 1 addition & 1 deletion packages/app/src/session/revert.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export function createSessionRevert(input: {
// Like the TUI, stop at the first failure instead of waiting on work that was never interrupted.
if (!(await request(() => server.api.session.interrupt({ sessionID })))) return

if (!(await request(() => server.api.session.wait({ sessionID })))) return
if (!(await request(() => server.api.experimental.session.wait({ sessionID })))) return

if (!(await request(() => server.api.session.revert.stage({ sessionID, messageID: message.id })))) return
// Like the TUI, pending inputs are left alone: the revert hides them, committing it drops them,
Expand Down
2 changes: 1 addition & 1 deletion packages/app/src/settings/general/controllers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export function createServerShellController(server: Accessor<ServerConnection.An
const previous = state.latest
actions.mutate({ ...previous, shell: value || undefined })
void serverCtx()
.sdk.api.config.update({ shell: value || null })
.sdk.api.experimental.config.update({ shell: value || null })
.catch((error: unknown) => {
actions.mutate(previous)
showToast({
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/acp/sessions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ export const make = Effect.fnUntraced(function* (input: {
const config = mcpConfig(server)
if (isDeepStrictEqual(registered.get(server.name), config)) return Effect.void
registered.set(server.name, config)
return input.client.mcp.add({ server: server.name, location: { directory: attached.cwd }, config }).pipe(
return input.client.experimental.mcp.add({ server: server.name, location: { directory: attached.cwd }, config }).pipe(
Effect.catch(ACPClient.classify),
Effect.onError(() => Effect.sync(() => registered.delete(server.name))),
Effect.uninterruptible,
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/commands/handlers/auth/login.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ const findIntegration = Effect.fn("cli.auth.login.integration")(function* (clien
if (protocol === "http:" || protocol === "https:") {
const progress = spinner()
progress.start("Discovering authentication provider...")
yield* request((signal) => client.integration.wellknown.add({ url: target, location }, { signal })).pipe(
yield* request((signal) => client.experimental.integration.wellknown.add({ url: target, location }, { signal })).pipe(
Effect.tap(() => Effect.sync(() => progress.stop("Authentication provider discovered"))),
Effect.tapCause(() => Effect.sync(() => progress.stop("Discovery failed", 1))),
)
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/commands/handlers/session/export.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ export default Runtime.handler(
})
if (!sessionID) return
const data = yield* Effect.tryPromise({
try: () => client.session.export({ sessionID, sanitize: input.sanitize }),
try: () => client.experimental.session.export({ sessionID, sanitize: input.sanitize }),
catch: (cause) => cause,
})
process.stdout.write(JSON.stringify(data, null, 2) + EOL)
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/commands/handlers/stats.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ const handler = Effect.fn("cli.stats")(function* (input: Runtime.Input<typeof Co
: project
const details = input.models || input.tools || input.cost || input.full
const stats = yield* request(server.endpoint.url, (signal) =>
client.session.stats(
client.experimental.session.stats(
{
from: range.from,
to: range.to,
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/run/noninteractive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -758,7 +758,7 @@ export async function runNonInteractivePrompt(input: Input) {
return
}

const waiting = input.client.session.wait({ sessionID: input.sessionID })
const waiting = input.client.experimental.session.wait({ sessionID: input.sessionID })
await Promise.race([waiting, completed.then(() => waiting)])
finalizing = true
const projected = await reconcile()
Expand Down
8 changes: 4 additions & 4 deletions packages/cli/test/fixture/mini-wait.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@ const timeout = (error: unknown) =>
error.cause.name === "TimeoutError"

await Promise.all([
connection.sdk.session.wait({ sessionID: "ses_initial" }),
replacement.session.wait({ sessionID: "ses_reconnected" }),
assert.rejects(OpenCode.make({ baseUrl: endpoint.url }).session.wait({ sessionID: "ses_control" }), timeout),
connection.sdk.experimental.session.wait({ sessionID: "ses_initial" }),
replacement.experimental.session.wait({ sessionID: "ses_reconnected" }),
assert.rejects(OpenCode.make({ baseUrl: endpoint.url }).experimental.session.wait({ sessionID: "ses_control" }), timeout),
assert.rejects(
connection.sdk.session.wait({ sessionID: "ses_cancelled" }, { signal: AbortSignal.timeout(100) }),
connection.sdk.experimental.session.wait({ sessionID: "ses_cancelled" }, { signal: AbortSignal.timeout(100) }),
timeout,
),
])
4 changes: 2 additions & 2 deletions packages/cli/test/run/noninteractive.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ async function run(input: {
)
spyOn(sdk.session.form, "cancel").mockImplementation((request) => (input.cancel?.(request) ?? ok(undefined)) as never)
let promptID = "msg_prompt"
spyOn(sdk.session, "wait").mockImplementation(() => input.wait?.() ?? wait.promise)
spyOn(sdk.experimental.session, "wait").mockImplementation(() => input.wait?.() ?? wait.promise)
spyOn(sdk.message, "list").mockImplementation(() =>
ok({
data: input.messages?.(promptID) ?? [{ id: promptID, type: "user", text: "hello", time: { created: 1 } }],
Expand Down Expand Up @@ -479,7 +479,7 @@ describe("runNonInteractivePrompt", () => {
])
expect(output.stderr).toBe("")
const sdk = await run({ compatibility: "v1", turn: (messageID) => [prompted(messageID), settled()] })
expect(sdk.session.wait).not.toHaveBeenCalled()
expect(sdk.experimental.session.wait).not.toHaveBeenCalled()
expect(sdk.message.list).not.toHaveBeenCalled()
})

Expand Down
6 changes: 3 additions & 3 deletions packages/client/script/build.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { NodeFileSystem } from "@effect/platform-node"
import { compile, emitEffectImported, emitEffectShape, emitPromise, write } from "@opencode/httpapi-codegen"
import { ClientApi, effectOmitEndpoints, groupNames, promiseOmitEndpoints } from "@opencode/protocol/client"
import { ClientApi, clientNamespace, effectOmitEndpoints, groupNames, promiseOmitEndpoints } from "@opencode/protocol/client"
import { Agent } from "@opencode/schema/agent"
import { Command } from "@opencode/schema/command"
import { Config } from "@opencode/schema/config"
Expand Down Expand Up @@ -43,8 +43,8 @@ import { Workspace } from "@opencode/schema/workspace"
import { Effect, Schema } from "effect"
import { fileURLToPath } from "url"

const promiseContract = compile(ClientApi, { groupNames, omitEndpoints: promiseOmitEndpoints })
const effectContract = compile(ClientApi, { groupNames, omitEndpoints: effectOmitEndpoints })
const promiseContract = compile(ClientApi, { groupNames, omitEndpoints: promiseOmitEndpoints, namespace: clientNamespace })
const effectContract = compile(ClientApi, { groupNames, omitEndpoints: effectOmitEndpoints, namespace: clientNamespace })
const effectTypeReferences = [
...namespaceTypes("Agent", "@opencode/schema/agent", Agent),
...namespaceTypes("Command", "@opencode/schema/command", Command),
Expand Down
2 changes: 1 addition & 1 deletion packages/client/src/contract.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export { ClientApi, effectOmitEndpoints, groupNames, promiseOmitEndpoints } from "@opencode/protocol/client"
export { ClientApi, clientNamespace, effectOmitEndpoints, groupNames, promiseOmitEndpoints } from "@opencode/protocol/client"
Loading
Loading