diff --git a/bun.lock b/bun.lock index 115100e1048..6dbc147de7c 100644 --- a/bun.lock +++ b/bun.lock @@ -1900,7 +1900,7 @@ "@solidjs/router": ["@solidjs/router@0.15.4", "", { "peerDependencies": { "solid-js": "^1.8.6" } }, "sha512-WOpgg9a9T638cR+5FGbFi/IV4l2FpmBs1GpIMSPa0Ce9vyJN7Wts+X2PqMf9IYn0zUj2MlSJtm1gp7/HI/n5TQ=="], - "@solidjs/start": ["@solidjs/start@https://pkg.pr.new/@solidjs/start@dfb2020", { "dependencies": { "@babel/core": "^7.28.3", "@babel/traverse": "^7.28.3", "@babel/types": "^7.28.5", "@solidjs/meta": "^0.29.4", "@tanstack/server-functions-plugin": "1.134.5", "@types/babel__traverse": "^7.28.0", "@types/micromatch": "^4.0.9", "cookie-es": "^2.0.0", "defu": "^6.1.4", "error-stack-parser": "^2.1.4", "es-module-lexer": "^1.7.0", "esbuild": "^0.25.3", "fast-glob": "^3.3.3", "h3": "npm:h3@2.0.1-rc.4", "html-to-image": "^1.11.13", "micromatch": "^4.0.8", "path-to-regexp": "^8.2.0", "pathe": "^2.0.3", "radix3": "^1.1.2", "seroval": "^1.3.2", "seroval-plugins": "^1.2.1", "shiki": "^1.26.1", "solid-js": "^1.9.9", "source-map-js": "^1.2.1", "srvx": "^0.9.1", "terracotta": "^1.0.6", "vite": "7.1.10", "vite-plugin-solid": "^2.11.9", "vitest": "^4.0.10" } }, "sha512-7JjjA49VGNOsMRI8QRUhVudZmv0CnJ18SliSgK1ojszs/c3ijftgVkzvXdkSLN4miDTzbkXewf65D6ZBo6W+GQ=="], + "@solidjs/start": ["@solidjs/start@https://pkg.pr.new/@solidjs/start@dfb2020", { "dependencies": { "@babel/core": "^7.28.3", "@babel/traverse": "^7.28.3", "@babel/types": "^7.28.5", "@solidjs/meta": "^0.29.4", "@tanstack/server-functions-plugin": "1.134.5", "@types/babel__traverse": "^7.28.0", "@types/micromatch": "^4.0.9", "cookie-es": "^2.0.0", "defu": "^6.1.4", "error-stack-parser": "^2.1.4", "es-module-lexer": "^1.7.0", "esbuild": "^0.25.3", "fast-glob": "^3.3.3", "h3": "npm:h3@2.0.1-rc.4", "html-to-image": "^1.11.13", "micromatch": "^4.0.8", "path-to-regexp": "^8.2.0", "pathe": "^2.0.3", "radix3": "^1.1.2", "seroval": "^1.3.2", "seroval-plugins": "^1.2.1", "shiki": "^1.26.1", "solid-js": "^1.9.9", "source-map-js": "^1.2.1", "srvx": "^0.9.1", "terracotta": "^1.0.6", "vite": "7.1.10", "vite-plugin-solid": "^2.11.9", "vitest": "^4.0.10" } }], "@speed-highlight/core": ["@speed-highlight/core@1.2.14", "", {}, "sha512-G4ewlBNhUtlLvrJTb88d2mdy2KRijzs4UhnlrOSRT4bmjh/IqNElZa3zkrZ+TC47TwtlDWzVLFADljF1Ijp5hA=="], @@ -3020,7 +3020,7 @@ "get-symbol-description": ["get-symbol-description@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6" } }, "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg=="], - "ghostty-web": ["ghostty-web@github:anomalyco/ghostty-web#4af877d", {}, "anomalyco-ghostty-web-4af877d", "sha512-fbEK8mtr7ar4ySsF+JUGjhaZrane7dKphanN+SxHt5XXI6yLMAh/Hpf6sNCOyyVa2UlGCd7YpXG/T2v2RUAX+A=="], + "ghostty-web": ["ghostty-web@github:anomalyco/ghostty-web#4af877d", {}, "anomalyco-ghostty-web-4af877d"], "gifwrap": ["gifwrap@0.10.1", "", { "dependencies": { "image-q": "^4.0.0", "omggif": "^1.0.10" } }, "sha512-2760b1vpJHNmLzZ/ubTtNnEx5WApN/PYWJvXvgS+tL1egTTthayFYIQQNi136FLEDcN/IyEY2EcGpIITD6eYUw=="], diff --git a/packages/opencode/src/server/routes/session.ts b/packages/opencode/src/server/routes/session.ts index 613c8b05c17..38906a46be3 100644 --- a/packages/opencode/src/server/routes/session.ts +++ b/packages/opencode/src/server/routes/session.ts @@ -114,7 +114,7 @@ export const SessionRoutes = lazy(() => validator( "param", z.object({ - sessionID: Session.get.schema, + sessionID: SessionID.zod, }), ), async (c) => { @@ -185,6 +185,38 @@ export const SessionRoutes = lazy(() => return c.json(todos) }, ) + .post( + "/:sessionID/todo", + describeRoute({ + summary: "Update session todos", + description: "Update the todo list for a specific session. This triggers a todo.updated event that refreshes the TUI sidebar.", + operationId: "session.todoUpdate", + responses: { + 200: { + description: "Todos updated successfully", + content: { + "application/json": { + schema: resolver(Todo.Info.array()), + }, + }, + }, + ...errors(400, 404), + }, + }), + validator( + "param", + z.object({ + sessionID: SessionID.zod, + }), + ), + validator("json", z.object({ todos: z.array(Todo.Info) })), + async (c) => { + const sessionID = c.req.valid("param").sessionID + const { todos } = c.req.valid("json") + await Todo.update({ sessionID, todos }) + return c.json(todos) + }, + ) .post( "/", describeRoute({ diff --git a/packages/sdk/js/src/v2/gen/sdk.gen.ts b/packages/sdk/js/src/v2/gen/sdk.gen.ts index aa759bb1e09..baa6e2675df 100644 --- a/packages/sdk/js/src/v2/gen/sdk.gen.ts +++ b/packages/sdk/js/src/v2/gen/sdk.gen.ts @@ -143,6 +143,8 @@ import type { SessionSummarizeResponses, SessionTodoErrors, SessionTodoResponses, + SessionTodoUpdateErrors, + SessionTodoUpdateResponses, SessionUnrevertErrors, SessionUnrevertResponses, SessionUnshareErrors, @@ -151,6 +153,7 @@ import type { SessionUpdateResponses, SubtaskPartInput, TextPartInput, + Todo, ToolIdsErrors, ToolIdsResponses, ToolListErrors, @@ -1527,6 +1530,45 @@ export class Session2 extends HeyApiClient { }) } + /** + * Update session todos + * + * Update the todo list for a specific session. This triggers a todo.updated event that refreshes the TUI sidebar. + */ + public todoUpdate( + parameters: { + sessionID: string + directory?: string + workspace?: string + todos?: Array + }, + options?: Options, + ) { + const params = buildClientParams( + [parameters], + [ + { + args: [ + { in: "path", key: "sessionID" }, + { in: "query", key: "directory" }, + { in: "query", key: "workspace" }, + { in: "body", key: "todos" }, + ], + }, + ], + ) + return (options?.client ?? this.client).post({ + url: "/session/{sessionID}/todo", + ...options, + ...params, + headers: { + "Content-Type": "application/json", + ...options?.headers, + ...params.headers, + }, + }) + } + /** * Initialize session * diff --git a/packages/sdk/js/src/v2/gen/types.gen.ts b/packages/sdk/js/src/v2/gen/types.gen.ts index 41aa248171c..f771d06e29e 100644 --- a/packages/sdk/js/src/v2/gen/types.gen.ts +++ b/packages/sdk/js/src/v2/gen/types.gen.ts @@ -3032,6 +3032,42 @@ export type SessionTodoResponses = { export type SessionTodoResponse = SessionTodoResponses[keyof SessionTodoResponses] +export type SessionTodoUpdateData = { + body?: { + todos: Array + } + path: { + sessionID: string + } + query?: { + directory?: string + workspace?: string + } + url: "/session/{sessionID}/todo" +} + +export type SessionTodoUpdateErrors = { + /** + * Bad request + */ + 400: BadRequestError + /** + * Not found + */ + 404: NotFoundError +} + +export type SessionTodoUpdateError = SessionTodoUpdateErrors[keyof SessionTodoUpdateErrors] + +export type SessionTodoUpdateResponses = { + /** + * Todos updated successfully + */ + 200: Array +} + +export type SessionTodoUpdateResponse = SessionTodoUpdateResponses[keyof SessionTodoUpdateResponses] + export type SessionInitData = { body?: { modelID: string