fix(httpapi): record deferred v2 prompts
This commit is contained in:
parent
61390dbb49
commit
82071ff90c
3 changed files with 76 additions and 2 deletions
|
|
@ -72,7 +72,7 @@ export const SessionGroup = HttpApiGroup.make("v2.session")
|
||||||
OpenApi.annotations({
|
OpenApi.annotations({
|
||||||
identifier: "v2.session.prompt",
|
identifier: "v2.session.prompt",
|
||||||
summary: "Send v2 message",
|
summary: "Send v2 message",
|
||||||
description: "Create a v2 session message and queue it for the agent loop.",
|
description: "Create a deferred v2 user message. Immediate agent delivery is not available yet.",
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -314,7 +314,50 @@ export const layer = Layer.effect(
|
||||||
}),
|
}),
|
||||||
prompt: Effect.fn("V2Session.prompt")(function* (input) {
|
prompt: Effect.fn("V2Session.prompt")(function* (input) {
|
||||||
yield* result.get(input.sessionID)
|
yield* result.get(input.sessionID)
|
||||||
return yield* new OperationUnavailableError({ operation: "prompt" })
|
if (input.delivery !== "deferred") return yield* new OperationUnavailableError({ operation: "prompt" })
|
||||||
|
const event = yield* events.publish(
|
||||||
|
SessionEvent.Prompted,
|
||||||
|
{
|
||||||
|
sessionID: input.sessionID,
|
||||||
|
timestamp: DateTime.makeUnsafe(Date.now()),
|
||||||
|
prompt: input.prompt,
|
||||||
|
},
|
||||||
|
{ id: input.id },
|
||||||
|
)
|
||||||
|
const message = new SessionMessage.User({
|
||||||
|
id: event.id,
|
||||||
|
type: "user",
|
||||||
|
metadata: event.metadata,
|
||||||
|
text: event.data.prompt.text,
|
||||||
|
files: event.data.prompt.files,
|
||||||
|
agents: event.data.prompt.agents,
|
||||||
|
references: event.data.prompt.references,
|
||||||
|
time: { created: event.data.timestamp },
|
||||||
|
})
|
||||||
|
// The bridge currently publishes the event but does not guarantee this request path has a projector attached.
|
||||||
|
Database.use((db) => {
|
||||||
|
const existing = db.select().from(SessionMessageTable).where(eq(SessionMessageTable.id, message.id)).get()
|
||||||
|
if (existing) return
|
||||||
|
db.insert(SessionMessageTable)
|
||||||
|
.values([
|
||||||
|
{
|
||||||
|
id: message.id,
|
||||||
|
session_id: input.sessionID,
|
||||||
|
type: message.type,
|
||||||
|
time_created: DateTime.toEpochMillis(message.time.created),
|
||||||
|
data: {
|
||||||
|
metadata: message.metadata,
|
||||||
|
text: message.text,
|
||||||
|
files: message.files,
|
||||||
|
agents: message.agents,
|
||||||
|
references: message.references,
|
||||||
|
time: { created: DateTime.toEpochMillis(message.time.created) },
|
||||||
|
} as NonNullable<(typeof SessionMessageTable.$inferInsert)["data"]>,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.run()
|
||||||
|
})
|
||||||
|
return message
|
||||||
}),
|
}),
|
||||||
shell: Effect.fn("V2Session.shell")(function* (_input) {}),
|
shell: Effect.fn("V2Session.shell")(function* (_input) {}),
|
||||||
skill: Effect.fn("V2Session.skill")(function* (_input) {}),
|
skill: Effect.fn("V2Session.skill")(function* (_input) {}),
|
||||||
|
|
|
||||||
|
|
@ -460,6 +460,37 @@ describe("session HttpApi", () => {
|
||||||
{ git: true, config: { formatter: false, lsp: false } },
|
{ git: true, config: { formatter: false, lsp: false } },
|
||||||
)
|
)
|
||||||
|
|
||||||
|
it.instance(
|
||||||
|
"records deferred v2 prompts as projected user messages",
|
||||||
|
() =>
|
||||||
|
Effect.gen(function* () {
|
||||||
|
const test = yield* TestInstance
|
||||||
|
const headers = { "x-opencode-directory": test.directory }
|
||||||
|
const session = yield* createSession({ title: "v2 deferred prompt" })
|
||||||
|
|
||||||
|
const prompt = yield* request(`/api/session/${session.id}/prompt`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: { ...headers, "content-type": "application/json" },
|
||||||
|
body: JSON.stringify({ prompt: { text: "hello deferred" }, delivery: "deferred" }),
|
||||||
|
})
|
||||||
|
expect(prompt.status).toBe(200)
|
||||||
|
const promptBody = yield* responseJson(prompt)
|
||||||
|
expect(promptBody).toMatchObject({
|
||||||
|
type: "user",
|
||||||
|
text: "hello deferred",
|
||||||
|
})
|
||||||
|
|
||||||
|
const messages = yield* requestJson<{ items: SessionMessage.Message[] }>(`/api/session/${session.id}/message`, {
|
||||||
|
headers,
|
||||||
|
})
|
||||||
|
expect(messages.items).toMatchObject([{ type: "user", text: "hello deferred" }])
|
||||||
|
|
||||||
|
const context = yield* requestJson<SessionMessage.Message[]>(`/api/session/${session.id}/context`, { headers })
|
||||||
|
expect(context).toMatchObject([{ type: "user", text: "hello deferred" }])
|
||||||
|
}),
|
||||||
|
{ git: true, config: { formatter: false, lsp: false } },
|
||||||
|
)
|
||||||
|
|
||||||
it.instance(
|
it.instance(
|
||||||
"returns v2 public unavailable errors for unfinished session mutations",
|
"returns v2 public unavailable errors for unfinished session mutations",
|
||||||
() =>
|
() =>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue