Merge remote-tracking branch 'origin/v2' into search-integration

# Conflicts:
#	packages/client/test/promise.test.ts
#	packages/core/schema.json
#	packages/core/src/database/migration.gen.ts
#	packages/core/src/tool/websearch.ts
#	packages/sdk-next/src/index.ts
#	packages/sdk/js/src/v2/gen/types.gen.ts
This commit is contained in:
Shoubhit Dash 2026-07-07 17:38:46 +05:30
commit 7b8d8b8861
666 changed files with 46671 additions and 20220 deletions

View file

@ -69,9 +69,9 @@ export const endpointNames = {
"integration.attempt.status": "attemptStatus",
"integration.attempt.complete": "attemptComplete",
"integration.attempt.cancel": "attemptCancel",
"session.context.entry.list": "listContextEntries",
"session.context.entry.put": "putContextEntry",
"session.context.entry.remove": "removeContextEntry",
"session.instructions.entry.list": ["instructions", "entry", "list"],
"session.instructions.entry.put": ["instructions", "entry", "put"],
"session.instructions.entry.remove": ["instructions", "entry", "remove"],
"session.revert.stage": "revertStage",
"session.revert.clear": "revertClear",
"session.revert.commit": "revertCommit",

View file

@ -1,5 +1,4 @@
import { Event } from "@opencode-ai/schema/event"
import { EventLog } from "@opencode-ai/schema/event-log"
import { EventManifest } from "@opencode-ai/schema/event-manifest"
import { Location } from "@opencode-ai/schema/location"
import type { Definition } from "@opencode-ai/schema/event"
@ -39,19 +38,7 @@ const make = <const Definitions extends ReadonlyArray<Definition>>(definitions:
identifier: "v2.event.subscribe",
summary: "Subscribe to events",
description:
"Subscribe to native event payloads for the server. Volatile by contract: a slow consumer overflows and fails the stream, and events during disconnection are missed. Consumers that need reliability should combine the changes feed with durable session log reads.",
}),
),
)
.add(
HttpApiEndpoint.get("event.changes", "/api/event/changes", {
success: HttpApiSchema.StreamSse({ data: EventLog.Change }),
}).annotateMerge(
OpenApi.annotations({
identifier: "v2.event.changes",
summary: "Subscribe to change hints",
description:
"Payload-free hint channel: after an event commits, a subscriber eventually receives a hint for that aggregate with seq at or beyond the event, or a sweep-required marker. Hints coalesce to the latest seq per aggregate under backpressure and the stream never fails from overflow. No consumer may derive correctness from receiving a hint; correctness always comes from durable log reads plus the consumer's own checkpoint. A sweep-required marker is emitted first on every (re)subscribe and whenever hint retention is exceeded: treat every aggregate as potentially dirty and recover via bounded sweep plus log reads.",
"Subscribe to native event payloads for the server. Volatile by contract: a slow consumer overflows and fails the stream, and events during disconnection are missed.",
}),
),
)

View file

@ -1,7 +1,5 @@
import { Session } from "@opencode-ai/schema/session"
import { SessionMessage } from "@opencode-ai/schema/session-message"
import { optional } from "@opencode-ai/schema/schema"
import { Event } from "@opencode-ai/schema/event"
import { Schema } from "effect"
import { HttpApiEndpoint, HttpApiGroup, OpenApi } from "effect/unstable/httpapi"
import { InvalidCursorError, SessionNotFoundError, UnknownError } from "../errors.js"
@ -30,10 +28,6 @@ export const MessageGroup = HttpApiGroup.make("server.message")
query: SessionMessagesQuery,
success: Schema.Struct({
data: Schema.Array(SessionMessage.Message),
watermark: optional(Event.Seq).annotate({
description:
"Durable log seq this snapshot was computed at, read before the snapshot. Attach a live log read after the watermark to compose fetch and stream gap-free; events between the watermark and the snapshot read may be redelivered by the tail and are safe to re-apply. Absent when the session has no durable events.",
}),
cursor: Schema.Struct({
previous: Schema.String.pipe(Schema.optional),
next: Schema.String.pipe(Schema.optional),

View file

@ -2,7 +2,7 @@ import { SessionMessage } from "@opencode-ai/schema/session-message"
import { SessionInput } from "@opencode-ai/schema/session-input"
import { PromptInput } from "@opencode-ai/schema/prompt-input"
import { Session } from "@opencode-ai/schema/session"
import { SessionContextEntry } from "@opencode-ai/schema/session-context-entry"
import { InstructionEntry } from "@opencode-ai/schema/instruction-entry"
import { Project } from "@opencode-ai/schema/project"
import { AbsolutePath, PositiveInt, RelativePath, statics } from "@opencode-ai/schema/schema"
import { Event } from "@opencode-ai/schema/event"
@ -104,12 +104,6 @@ const SessionActive = Schema.Struct({
type: Schema.Literal("running"),
}).annotate({ identifier: "SessionActive" })
const SessionWatermarks = Schema.Record(Session.ID, Event.Seq).annotate({
identifier: "SessionWatermarks",
description:
"Durable log seq each session's snapshot was computed at. Attach a live log read after the watermark to compose fetch and stream gap-free; apply a snapshot only where its watermark is at or beyond already-applied events. Sessions without durable events are absent.",
})
const BooleanFromString = Schema.Literals(["true", "false"]).pipe(
Schema.decodeTo(Schema.Boolean, {
decode: SchemaGetter.transform((value) => value === "true"),
@ -136,7 +130,6 @@ export const makeSessionGroup = <I extends HttpApiMiddleware.AnyId, S>(sessionLo
query: SessionsQuery,
success: Schema.Struct({
data: Schema.Array(Session.Info),
watermarks: SessionWatermarks,
cursor: Schema.Struct({
previous: SessionsCursor.pipe(Schema.optional),
next: SessionsCursor.pipe(Schema.optional),
@ -171,13 +164,13 @@ export const makeSessionGroup = <I extends HttpApiMiddleware.AnyId, S>(sessionLo
)
.add(
HttpApiEndpoint.get("session.active", "/api/session/active", {
success: Schema.Struct({ data: Schema.Record(Session.ID, SessionActive), watermarks: SessionWatermarks }),
success: Schema.Struct({ data: Schema.Record(Session.ID, SessionActive) }),
}).annotateMerge(
OpenApi.annotations({
identifier: "v2.session.active",
summary: "List active sessions",
description:
"Retrieve foreground Session drains currently owned by this OpenCode process. Sessions absent from the result are inactive. Watermarks are the durable log positions read alongside the activity snapshot; activity itself is process state, so the pairing is advisory rather than transactional.",
"Retrieve foreground Session drains currently owned by this OpenCode process. Sessions absent from the result are inactive.",
}),
),
)
@ -196,6 +189,21 @@ export const makeSessionGroup = <I extends HttpApiMiddleware.AnyId, S>(sessionLo
}),
),
)
.add(
HttpApiEndpoint.delete("session.remove", "/api/session/:sessionID", {
params: { sessionID: Session.ID },
success: HttpApiSchema.NoContent,
error: SessionNotFoundError,
})
.middleware(sessionLocationMiddleware)
.annotateMerge(
OpenApi.annotations({
identifier: "v2.session.remove",
summary: "Delete session",
description: "Delete a session and its child sessions.",
}),
),
)
.add(
HttpApiEndpoint.post("session.fork", "/api/session/:sessionID/fork", {
params: { sessionID: Session.ID },
@ -271,7 +279,7 @@ export const makeSessionGroup = <I extends HttpApiMiddleware.AnyId, S>(sessionLo
resume: Schema.Boolean.pipe(Schema.optional),
}),
success: Schema.Struct({ data: SessionInput.Admitted }),
error: [ConflictError, SessionNotFoundError],
error: [ConflictError, InvalidRequestError, SessionNotFoundError],
})
.middleware(sessionLocationMiddleware)
.annotateMerge(
@ -297,7 +305,7 @@ export const makeSessionGroup = <I extends HttpApiMiddleware.AnyId, S>(sessionLo
resume: Schema.Boolean.pipe(Schema.optional),
}),
success: Schema.Struct({ data: SessionInput.Admitted }),
error: [ConflictError, SessionNotFoundError, CommandNotFoundError, CommandEvaluationError],
error: [ConflictError, InvalidRequestError, SessionNotFoundError, CommandNotFoundError, CommandEvaluationError],
})
.middleware(sessionLocationMiddleware)
.annotateMerge(
@ -372,15 +380,16 @@ export const makeSessionGroup = <I extends HttpApiMiddleware.AnyId, S>(sessionLo
.add(
HttpApiEndpoint.post("session.compact", "/api/session/:sessionID/compact", {
params: { sessionID: Session.ID },
success: HttpApiSchema.NoContent,
error: [SessionNotFoundError, SessionBusyError, ServiceUnavailableError, UnknownError],
payload: Schema.Struct({ id: SessionMessage.ID.pipe(Schema.optional) }),
success: Schema.Struct({ data: SessionInput.Compaction }),
error: [ConflictError, SessionNotFoundError],
})
.middleware(sessionLocationMiddleware)
.annotateMerge(
OpenApi.annotations({
identifier: "v2.session.compact",
summary: "Compact session",
description: "Compact a session conversation.",
description: "Queue a durable session compaction request.",
}),
),
)
@ -451,23 +460,23 @@ export const makeSessionGroup = <I extends HttpApiMiddleware.AnyId, S>(sessionLo
),
)
.add(
HttpApiEndpoint.get("session.context.entry.list", "/api/session/:sessionID/context-entry", {
HttpApiEndpoint.get("session.instructions.entry.list", "/api/session/:sessionID/instructions/entries", {
params: { sessionID: Session.ID },
success: Schema.Struct({ data: Schema.Array(SessionContextEntry.Info) }),
success: Schema.Struct({ data: Schema.Array(InstructionEntry.Info) }),
error: SessionNotFoundError,
})
.middleware(sessionLocationMiddleware)
.annotateMerge(
OpenApi.annotations({
identifier: "v2.session.context.entry.list",
summary: "List context entries",
description: "List API-managed context entries attached to the session's system context.",
identifier: "v2.session.instructions.entry.list",
summary: "List instruction entries",
description: "List API-managed instruction entries attached to the session.",
}),
),
)
.add(
HttpApiEndpoint.put("session.context.entry.put", "/api/session/:sessionID/context-entry/:key", {
params: { sessionID: Session.ID, key: SessionContextEntry.Key },
HttpApiEndpoint.put("session.instructions.entry.put", "/api/session/:sessionID/instructions/entries/:key", {
params: { sessionID: Session.ID, key: InstructionEntry.Key },
payload: Schema.Struct({ value: Schema.Json }),
success: HttpApiSchema.NoContent,
error: SessionNotFoundError,
@ -475,30 +484,31 @@ export const makeSessionGroup = <I extends HttpApiMiddleware.AnyId, S>(sessionLo
.middleware(sessionLocationMiddleware)
.annotateMerge(
OpenApi.annotations({
identifier: "v2.session.context.entry.put",
summary: "Put context entry",
identifier: "v2.session.instructions.entry.put",
summary: "Put instruction entry",
description:
"Attach or replace one durable context entry. The value is rendered into the session's system context; changes announce as updates at the next turn boundary.",
"Attach or replace one durable instruction entry. Changes announce as updates at the next step boundary.",
}),
),
)
.add(
HttpApiEndpoint.delete("session.context.entry.remove", "/api/session/:sessionID/context-entry/:key", {
params: { sessionID: Session.ID, key: SessionContextEntry.Key },
HttpApiEndpoint.delete("session.instructions.entry.remove", "/api/session/:sessionID/instructions/entries/:key", {
params: { sessionID: Session.ID, key: InstructionEntry.Key },
success: HttpApiSchema.NoContent,
error: SessionNotFoundError,
})
.middleware(sessionLocationMiddleware)
.annotateMerge(
OpenApi.annotations({
identifier: "v2.session.context.entry.remove",
summary: "Remove context entry",
description: "Remove one context entry; the removal is announced to the model at the next turn boundary.",
identifier: "v2.session.instructions.entry.remove",
summary: "Remove instruction entry",
description:
"Remove one instruction entry; the removal is announced to the model at the next step boundary.",
}),
),
)
.add(
HttpApiEndpoint.get("session.log", "/api/session/:sessionID/log", {
HttpApiEndpoint.get("session.log", "/api/experimental/session/:sessionID/log", {
params: { sessionID: Session.ID },
query: {
after: Schema.NumberFromString.pipe(Schema.decodeTo(Event.Seq), Schema.optional),
@ -515,7 +525,7 @@ export const makeSessionGroup = <I extends HttpApiMiddleware.AnyId, S>(sessionLo
identifier: "v2.session.log",
summary: "Read the session log",
description:
"Durable, ordered, gap-free read of public session events after an exclusive aggregate sequence. Emits a synced marker once replay reaches the captured watermark, then completes; with follow=true it continues with live events instead. The only event API that promises reliability: attach after a snapshot watermark to compose fetch and stream without a race window.",
"Experimental durable session event log. Reads events after an exclusive aggregate sequence and continues with live events when follow=true.",
}),
),
)

View file

@ -1,10 +1,15 @@
import { Shell } from "@opencode-ai/schema/shell"
import { Location } from "@opencode-ai/schema/location"
import { NonNegativeInt } from "@opencode-ai/schema/schema"
import { Schema } from "effect"
import { HttpApiEndpoint, HttpApiGroup, HttpApiSchema, OpenApi } from "effect/unstable/httpapi"
import { ShellNotFoundError } from "../errors.js"
import { LocationQuery, locationQueryOpenApi } from "./location.js"
const TimeoutInput = Schema.Struct({
timeout: NonNegativeInt,
})
export const ShellGroup = HttpApiGroup.make("server.shell")
.add(
HttpApiEndpoint.get("shell.list", "/api/shell", {
@ -52,6 +57,23 @@ export const ShellGroup = HttpApiGroup.make("server.shell")
}),
),
)
.add(
HttpApiEndpoint.patch("shell.timeout", "/api/shell/:id/timeout", {
params: { id: Shell.ID },
query: LocationQuery,
payload: TimeoutInput,
success: Location.response(Shell.Info),
error: ShellNotFoundError,
})
.annotateMerge(locationQueryOpenApi)
.annotateMerge(
OpenApi.annotations({
identifier: "v2.shell.timeout",
summary: "Update shell timeout",
description: "Replace a running shell command's timeout from now, or clear it with zero.",
}),
),
)
.add(
HttpApiEndpoint.get("shell.output", "/api/shell/:id/output", {
params: { id: Shell.ID },