feat(core): session.pending.list API with pending-only session_pending storage (#36126)
This commit is contained in:
parent
99156c10e6
commit
a72992e00f
43 changed files with 986 additions and 649 deletions
|
|
@ -20,6 +20,8 @@
|
|||
"effect": "catalog:"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@opencode-ai/httpapi-codegen": "workspace:*",
|
||||
"@opencode-ai/protocol": "workspace:*",
|
||||
"@tsconfig/bun": "catalog:",
|
||||
"@types/bun": "catalog:",
|
||||
"@typescript/native-preview": "catalog:"
|
||||
|
|
|
|||
|
|
@ -22,6 +22,6 @@ export { Question } from "@opencode-ai/schema/question"
|
|||
export { Reference } from "@opencode-ai/schema/reference"
|
||||
export { AbsolutePath, RelativePath } from "@opencode-ai/schema/schema"
|
||||
export { Session } from "@opencode-ai/schema/session"
|
||||
export { SessionInput } from "@opencode-ai/schema/session-input"
|
||||
export { SessionPending } from "@opencode-ai/schema/session-pending"
|
||||
export { SessionMessage } from "@opencode-ai/schema/session-message"
|
||||
export { Skill } from "@opencode-ai/schema/skill"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,23 @@
|
|||
import { expect, test } from "bun:test"
|
||||
import { AgentV2 } from "@opencode-ai/core/agent"
|
||||
import { Location as CoreLocation } from "@opencode-ai/core/location"
|
||||
import { ModelV2 } from "@opencode-ai/core/model"
|
||||
import { ProjectV2 } from "@opencode-ai/core/project"
|
||||
import { SessionV2 } from "@opencode-ai/core/session"
|
||||
import { SessionPending as CoreSessionPending } from "@opencode-ai/core/session/pending"
|
||||
import { SessionMessage as CoreSessionMessage } from "@opencode-ai/core/session/message"
|
||||
import { Agent } from "@opencode-ai/schema/agent"
|
||||
import { Location } from "@opencode-ai/schema/location"
|
||||
import { Model } from "@opencode-ai/schema/model"
|
||||
import { Project } from "@opencode-ai/schema/project"
|
||||
import { Provider } from "@opencode-ai/schema/provider"
|
||||
import { Session } from "@opencode-ai/schema/session"
|
||||
import { SessionPending } from "@opencode-ai/schema/session-pending"
|
||||
import { SessionMessage } from "@opencode-ai/schema/session-message"
|
||||
import { Workspace } from "@opencode-ai/schema/workspace"
|
||||
import { Api } from "@opencode-ai/server/api"
|
||||
import { ClientApi, groupNames, promiseOmitEndpoints } from "@opencode-ai/protocol/client"
|
||||
import { compile, emitPromise } from "@opencode-ai/httpapi-codegen"
|
||||
|
||||
const SDK = await import("../src/index")
|
||||
|
||||
|
|
@ -32,9 +48,38 @@ test("re-exports canonical contracts directly from Schema", () => {
|
|||
"Reference",
|
||||
"RelativePath",
|
||||
"Session",
|
||||
"SessionInput",
|
||||
"SessionMessage",
|
||||
"SessionPending",
|
||||
"Skill",
|
||||
"Tool",
|
||||
])
|
||||
})
|
||||
|
||||
test("Core and Server reuse the authoritative Schema and Protocol values", () => {
|
||||
expect(AgentV2.ID).toBe(Agent.ID)
|
||||
expect(CoreLocation.Ref).toBe(Location.Ref)
|
||||
expect(ModelV2.Ref).toBe(Model.Ref)
|
||||
expect(SessionV2.Info).toBe(Session.Info)
|
||||
expect(ProjectV2.Current).toBe(Project.Current)
|
||||
expect(ProjectV2.Directory).toBe(Project.Directory)
|
||||
expect(ProjectV2.Directories).toBe(Project.Directories)
|
||||
expect(CoreSessionPending.Message).toBe(SessionPending.Message)
|
||||
expect(CoreSessionPending.User).toBe(SessionPending.User)
|
||||
expect(CoreSessionPending.Synthetic).toBe(SessionPending.Synthetic)
|
||||
expect(CoreSessionMessage.Info).toBe(SessionMessage.Info)
|
||||
expect(CoreSessionMessage.AssistantText).toBe(SessionMessage.AssistantText)
|
||||
expect(Api.groups["server.session"].identifier).toBe("server.session")
|
||||
expect(Api.groups["server.project"].identifier).toBe("server.project")
|
||||
expect(Object.keys(ClientApi.groups)).toEqual(Object.keys(Api.groups))
|
||||
expect(Session.ID.create()).toStartWith("ses_")
|
||||
expect(String(Project.ID.global)).toBe("global")
|
||||
expect(String(Provider.ID.anthropic)).toBe("anthropic")
|
||||
expect(Workspace.ID.create()).toStartWith("wrk_")
|
||||
})
|
||||
|
||||
test("client and Server contracts generate identically", () => {
|
||||
const server = compile(Api, { groupNames, omitEndpoints: promiseOmitEndpoints })
|
||||
const client = compile(ClientApi, { groupNames, omitEndpoints: promiseOmitEndpoints })
|
||||
|
||||
expect(emitPromise(client)).toEqual(emitPromise(server))
|
||||
})
|
||||
|
|
|
|||
|
|
@ -229,6 +229,7 @@ it.live(
|
|||
resume: false,
|
||||
})
|
||||
const context = yield* opencode.sessions.context({ sessionID: id })
|
||||
const pendingAfterAdmit = yield* opencode.sessions.pending.list({ sessionID: id })
|
||||
yield* opencode.sessions.instructions.entry.put({ sessionID: id, key: "deploy-target", value: "production" })
|
||||
yield* opencode.sessions.instructions.entry.put({ sessionID: id, key: "flags", value: { beta: true } })
|
||||
const contextEntries = yield* opencode.sessions.instructions.entry.list({ sessionID: id })
|
||||
|
|
@ -245,6 +246,7 @@ it.live(
|
|||
Effect.map(Option.getOrThrow),
|
||||
)
|
||||
const wakeContext = yield* opencode.sessions.context({ sessionID: id })
|
||||
const pendingAfterPromote = yield* opencode.sessions.pending.list({ sessionID: id })
|
||||
const event = yield* opencode.sessions.log({ sessionID: id }).pipe(
|
||||
Stream.filter((item) => item.type !== "log.synced"),
|
||||
Stream.take(1),
|
||||
|
|
@ -264,6 +266,7 @@ it.live(
|
|||
opencode.sessions.interrupt({ sessionID: missingSessionID }).pipe(Effect.flip),
|
||||
opencode.sessions.message({ sessionID: missingSessionID, messageID: modelMessage.id }).pipe(Effect.flip),
|
||||
opencode.sessions.instructions.entry.list({ sessionID: missingSessionID }).pipe(Effect.flip),
|
||||
opencode.sessions.pending.list({ sessionID: missingSessionID }).pipe(Effect.flip),
|
||||
],
|
||||
{ concurrency: "unbounded" },
|
||||
)
|
||||
|
|
@ -280,7 +283,11 @@ it.live(
|
|||
expect(page.data.some((session) => session.id === id)).toBe(true)
|
||||
expect(active).toEqual({})
|
||||
expect(admitted.sessionID).toBe(id)
|
||||
expect(pendingAfterAdmit).toContainEqual(
|
||||
expect.objectContaining({ id: admitted.id, type: "user", delivery: "steer" }),
|
||||
)
|
||||
expect(prompted.type).toBe("session.input.promoted")
|
||||
expect(pendingAfterPromote.map((item) => item.id)).not.toContainAnyValues([admitted.id, wake.id])
|
||||
expect(wakeContext).toContainEqual(expect.objectContaining({ id: wake.id, type: "user" }))
|
||||
expect(contextEntries).toEqual([
|
||||
{ key: "deploy-target", value: "production" },
|
||||
|
|
@ -295,6 +302,7 @@ it.live(
|
|||
"SessionNotFoundError",
|
||||
"SessionNotFoundError",
|
||||
"SessionNotFoundError",
|
||||
"SessionNotFoundError",
|
||||
])
|
||||
expect(missingMessage._tag).toBe("MessageNotFoundError")
|
||||
}),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue