From 79b5ce58e9d3ad940330c2fd82784a4d8b7e004d Mon Sep 17 00:00:00 2001 From: Shantur Rathore Date: Wed, 25 Feb 2026 14:25:26 +0000 Subject: [PATCH] feat(core): add message delete endpoint (#14417) --- .../opencode/src/server/routes/session.ts | 36 +++++++++++++++++ packages/opencode/src/session/index.ts | 8 +++- packages/sdk/js/src/v2/gen/sdk.gen.ts | 38 ++++++++++++++++++ packages/sdk/js/src/v2/gen/types.gen.ts | 40 +++++++++++++++++++ 4 files changed, 120 insertions(+), 2 deletions(-) diff --git a/packages/opencode/src/server/routes/session.ts b/packages/opencode/src/server/routes/session.ts index 1195529e0..12938aeab 100644 --- a/packages/opencode/src/server/routes/session.ts +++ b/packages/opencode/src/server/routes/session.ts @@ -618,6 +618,42 @@ export const SessionRoutes = lazy(() => return c.json(message) }, ) + .delete( + "/:sessionID/message/:messageID", + describeRoute({ + summary: "Delete message", + description: + "Permanently delete a specific message (and all of its parts) from a session. This does not revert any file changes that may have been made while processing the message.", + operationId: "session.deleteMessage", + responses: { + 200: { + description: "Successfully deleted message", + content: { + "application/json": { + schema: resolver(z.boolean()), + }, + }, + }, + ...errors(400, 404), + }, + }), + validator( + "param", + z.object({ + sessionID: z.string().meta({ description: "Session ID" }), + messageID: z.string().meta({ description: "Message ID" }), + }), + ), + async (c) => { + const params = c.req.valid("param") + SessionPrompt.assertNotBusy(params.sessionID) + await Session.removeMessage({ + sessionID: params.sessionID, + messageID: params.messageID, + }) + return c.json(true) + }, + ) .delete( "/:sessionID/message/:messageID/part/:partID", describeRoute({ diff --git a/packages/opencode/src/session/index.ts b/packages/opencode/src/session/index.ts index 8454a9c3e..22de477f8 100644 --- a/packages/opencode/src/session/index.ts +++ b/packages/opencode/src/session/index.ts @@ -697,7 +697,9 @@ export namespace Session { async (input) => { // CASCADE delete handles parts automatically Database.use((db) => { - db.delete(MessageTable).where(eq(MessageTable.id, input.messageID)).run() + db.delete(MessageTable) + .where(and(eq(MessageTable.id, input.messageID), eq(MessageTable.session_id, input.sessionID))) + .run() Database.effect(() => Bus.publish(MessageV2.Event.Removed, { sessionID: input.sessionID, @@ -717,7 +719,9 @@ export namespace Session { }), async (input) => { Database.use((db) => { - db.delete(PartTable).where(eq(PartTable.id, input.partID)).run() + db.delete(PartTable) + .where(and(eq(PartTable.id, input.partID), eq(PartTable.session_id, input.sessionID))) + .run() Database.effect(() => Bus.publish(MessageV2.Event.PartRemoved, { sessionID: input.sessionID, diff --git a/packages/sdk/js/src/v2/gen/sdk.gen.ts b/packages/sdk/js/src/v2/gen/sdk.gen.ts index b4848e605..6165c0f7b 100644 --- a/packages/sdk/js/src/v2/gen/sdk.gen.ts +++ b/packages/sdk/js/src/v2/gen/sdk.gen.ts @@ -107,6 +107,8 @@ import type { SessionCreateErrors, SessionCreateResponses, SessionDeleteErrors, + SessionDeleteMessageErrors, + SessionDeleteMessageResponses, SessionDeleteResponses, SessionDiffResponses, SessionForkResponses, @@ -1561,6 +1563,42 @@ export class Session2 extends HeyApiClient { }) } + /** + * Delete message + * + * Permanently delete a specific message (and all of its parts) from a session. This does not revert any file changes that may have been made while processing the message. + */ + public deleteMessage( + parameters: { + sessionID: string + messageID: string + directory?: string + }, + options?: Options, + ) { + const params = buildClientParams( + [parameters], + [ + { + args: [ + { in: "path", key: "sessionID" }, + { in: "path", key: "messageID" }, + { in: "query", key: "directory" }, + ], + }, + ], + ) + return (options?.client ?? this.client).delete< + SessionDeleteMessageResponses, + SessionDeleteMessageErrors, + ThrowOnError + >({ + url: "/session/{sessionID}/message/{messageID}", + ...options, + ...params, + }) + } + /** * Get message * diff --git a/packages/sdk/js/src/v2/gen/types.gen.ts b/packages/sdk/js/src/v2/gen/types.gen.ts index 4050ef157..28d5caa02 100644 --- a/packages/sdk/js/src/v2/gen/types.gen.ts +++ b/packages/sdk/js/src/v2/gen/types.gen.ts @@ -3564,6 +3564,46 @@ export type SessionPromptResponses = { export type SessionPromptResponse = SessionPromptResponses[keyof SessionPromptResponses] +export type SessionDeleteMessageData = { + body?: never + path: { + /** + * Session ID + */ + sessionID: string + /** + * Message ID + */ + messageID: string + } + query?: { + directory?: string + } + url: "/session/{sessionID}/message/{messageID}" +} + +export type SessionDeleteMessageErrors = { + /** + * Bad request + */ + 400: BadRequestError + /** + * Not found + */ + 404: NotFoundError +} + +export type SessionDeleteMessageError = SessionDeleteMessageErrors[keyof SessionDeleteMessageErrors] + +export type SessionDeleteMessageResponses = { + /** + * Successfully deleted message + */ + 200: boolean +} + +export type SessionDeleteMessageResponse = SessionDeleteMessageResponses[keyof SessionDeleteMessageResponses] + export type SessionMessageData = { body?: never path: {