881 lines · 26.0 KB · TypeScript
RawDownload
import { Hono } from "hono"import { stream } from "hono/streaming"import { describeRoute, validator, resolver } from "hono-openapi"import z from "zod"import { Session } from "../../session"import { MessageV2 } from "../../session/message-v2"import { SessionPrompt } from "../../session/prompt"import { SessionCompaction } from "../../session/compaction"import { SessionRevert } from "../../session/revert"import { SessionStatus } from "@/session/status"import { SessionSummary } from "@/session/summary"import { Todo } from "../../session/todo"import { Agent } from "../../agent/agent"import { Snapshot } from "@/snapshot"import { Log } from "../../util/log"import { PermissionNext } from "@/permission/next"import { errors } from "../error"import { lazy } from "../../util/lazy" const log = Log.create({ service: "server" }) export const SessionRoutes = lazy(() =>  new Hono()    .get(      "/",      describeRoute({        summary: "List sessions",        description: "Get a list of all OpenScience sessions, sorted by most recently updated.",        operationId: "session.list",        responses: {          200: {            description: "List of sessions",            content: {              "application/json": {                schema: resolver(Session.Info.array()),              },            },          },        },      }),      validator(        "query",        z.object({          directory: z.string().optional().meta({ description: "Filter sessions by project directory" }),          roots: z.coerce.boolean().optional().meta({ description: "Only return root sessions (no parentID)" }),          start: z.coerce            .number()            .optional()            .meta({ description: "Filter sessions updated on or after this timestamp (milliseconds since epoch)" }),          search: z.string().optional().meta({ description: "Filter sessions by title (case-insensitive)" }),          limit: z.coerce.number().optional().meta({ description: "Maximum number of sessions to return" }),        }),      ),      async (c) => {        const query = c.req.valid("query")        const term = query.search?.toLowerCase()        const sessions: Session.Info[] = []        for await (const session of Session.list()) {          if (query.directory !== undefined && session.directory !== query.directory) continue          if (query.roots && session.parentID) continue          if (query.start !== undefined && session.time.updated < query.start) continue          if (term !== undefined && !session.title.toLowerCase().includes(term)) continue          sessions.push(session)          if (query.limit !== undefined && sessions.length >= query.limit) break        }        return c.json(sessions)      },    )    .get(      "/status",      describeRoute({        summary: "Get session status",        description: "Retrieve the current status of all sessions, including active, idle, and completed states.",        operationId: "session.status",        responses: {          200: {            description: "Get session status",            content: {              "application/json": {                schema: resolver(z.record(z.string(), SessionStatus.Info)),              },            },          },          ...errors(400),        },      }),      async (c) => {        const result = SessionStatus.list()        return c.json(result)      },    )    .get(      "/:sessionID",      describeRoute({        summary: "Get session",        description: "Retrieve detailed information about a specific OpenScience session.",        tags: ["Session"],        operationId: "session.get",        responses: {          200: {            description: "Get session",            content: {              "application/json": {                schema: resolver(Session.Info),              },            },          },          ...errors(400, 404),        },      }),      validator(        "param",        z.object({          sessionID: Session.get.schema,        }),      ),      async (c) => {        const sessionID = c.req.valid("param").sessionID        log.info("SEARCH", { url: c.req.url })        const session = await Session.get(sessionID)        return c.json(session)      },    )    .get(      "/:sessionID/children",      describeRoute({        summary: "Get session children",        tags: ["Session"],        description: "Retrieve all child sessions that were forked from the specified parent session.",        operationId: "session.children",        responses: {          200: {            description: "List of children",            content: {              "application/json": {                schema: resolver(Session.Info.array()),              },            },          },          ...errors(400, 404),        },      }),      validator(        "param",        z.object({          sessionID: Session.children.schema,        }),      ),      async (c) => {        const sessionID = c.req.valid("param").sessionID        const session = await Session.children(sessionID)        return c.json(session)      },    )    .get(      "/:sessionID/todo",      describeRoute({        summary: "Get session todos",        description: "Retrieve the todo list associated with a specific session, showing tasks and action items.",        operationId: "session.todo",        responses: {          200: {            description: "Todo list",            content: {              "application/json": {                schema: resolver(Todo.Info.array()),              },            },          },          ...errors(400, 404),        },      }),      validator(        "param",        z.object({          sessionID: z.string().meta({ description: "Session ID" }),        }),      ),      async (c) => {        const sessionID = c.req.valid("param").sessionID        const todos = await Todo.get(sessionID)        return c.json(todos)      },    )    .post(      "/",      describeRoute({        summary: "Create session",        description: "Create a new OpenScience session for interacting with AI assistants and managing conversations.",        operationId: "session.create",        responses: {          ...errors(400),          200: {            description: "Successfully created session",            content: {              "application/json": {                schema: resolver(Session.Info),              },            },          },        },      }),      validator("json", Session.create.schema.optional()),      async (c) => {        const body = c.req.valid("json") ?? {}        const session = await Session.create(body)        return c.json(session)      },    )    .delete(      "/:sessionID",      describeRoute({        summary: "Delete session",        description: "Delete a session and permanently remove all associated data, including messages and history.",        operationId: "session.delete",        responses: {          200: {            description: "Successfully deleted session",            content: {              "application/json": {                schema: resolver(z.boolean()),              },            },          },          ...errors(400, 404),        },      }),      validator(        "param",        z.object({          sessionID: Session.remove.schema,        }),      ),      async (c) => {        const sessionID = c.req.valid("param").sessionID        await Session.remove(sessionID)        return c.json(true)      },    )    .patch(      "/:sessionID",      describeRoute({        summary: "Update session",        description: "Update properties of an existing session, such as title or other metadata.",        operationId: "session.update",        responses: {          200: {            description: "Successfully updated session",            content: {              "application/json": {                schema: resolver(Session.Info),              },            },          },          ...errors(400, 404),        },      }),      validator(        "param",        z.object({          sessionID: z.string(),        }),      ),      validator(        "json",        z.object({          title: z.string().optional(),          time: z            .object({              archived: z.number().optional(),            })            .optional(),        }),      ),      async (c) => {        const sessionID = c.req.valid("param").sessionID        const updates = c.req.valid("json")         const updatedSession = await Session.update(          sessionID,          (session) => {            if (updates.title !== undefined) {              session.title = updates.title            }            if (updates.time?.archived !== undefined) session.time.archived = updates.time.archived          },          { touch: false },        )         return c.json(updatedSession)      },    )    .post(      "/:sessionID/init",      describeRoute({        summary: "Initialize session",        description:          "Analyze the current application and create an AGENTS.md file with project-specific agent configurations.",        operationId: "session.init",        responses: {          200: {            description: "200",            content: {              "application/json": {                schema: resolver(z.boolean()),              },            },          },          ...errors(400, 404),        },      }),      validator(        "param",        z.object({          sessionID: z.string().meta({ description: "Session ID" }),        }),      ),      validator("json", Session.initialize.schema.omit({ sessionID: true })),      async (c) => {        const sessionID = c.req.valid("param").sessionID        const body = c.req.valid("json")        await Session.initialize({ ...body, sessionID })        return c.json(true)      },    )    .post(      "/:sessionID/fork",      describeRoute({        summary: "Fork session",        description: "Create a new session by forking an existing session at a specific message point.",        operationId: "session.fork",        responses: {          200: {            description: "200",            content: {              "application/json": {                schema: resolver(Session.Info),              },            },          },        },      }),      validator(        "param",        z.object({          sessionID: Session.fork.schema.shape.sessionID,        }),      ),      validator("json", Session.fork.schema.omit({ sessionID: true })),      async (c) => {        const sessionID = c.req.valid("param").sessionID        const body = c.req.valid("json")        const result = await Session.fork({ ...body, sessionID })        return c.json(result)      },    )    .post(      "/:sessionID/abort",      describeRoute({        summary: "Abort session",        description: "Abort an active session and stop any ongoing AI processing or command execution.",        operationId: "session.abort",        responses: {          200: {            description: "Aborted session",            content: {              "application/json": {                schema: resolver(z.boolean()),              },            },          },          ...errors(400, 404),        },      }),      validator(        "param",        z.object({          sessionID: z.string(),        }),      ),      async (c) => {        SessionPrompt.cancel(c.req.valid("param").sessionID)        return c.json(true)      },    )    .get(      "/:sessionID/diff",      describeRoute({        summary: "Get message diff",        description: "Get the file changes (diff) that resulted from a specific user message in the session.",        operationId: "session.diff",        responses: {          200: {            description: "Successfully retrieved diff",            content: {              "application/json": {                schema: resolver(Snapshot.FileDiff.array()),              },            },          },        },      }),      validator(        "param",        z.object({          sessionID: SessionSummary.diff.schema.shape.sessionID,        }),      ),      validator(        "query",        z.object({          messageID: SessionSummary.diff.schema.shape.messageID,        }),      ),      async (c) => {        const query = c.req.valid("query")        const params = c.req.valid("param")        const result = await SessionSummary.diff({          sessionID: params.sessionID,          messageID: query.messageID,        })        return c.json(result)      },    )    .post(      "/:sessionID/summarize",      describeRoute({        summary: "Summarize session",        description: "Generate a concise summary of the session using AI compaction to preserve key information.",        operationId: "session.summarize",        responses: {          200: {            description: "Summarized session",            content: {              "application/json": {                schema: resolver(z.boolean()),              },            },          },          ...errors(400, 404),        },      }),      validator(        "param",        z.object({          sessionID: z.string().meta({ description: "Session ID" }),        }),      ),      validator(        "json",        z.object({          providerID: z.string(),          modelID: z.string(),          auto: z.boolean().optional().default(false),        }),      ),      async (c) => {        const sessionID = c.req.valid("param").sessionID        const body = c.req.valid("json")        const session = await Session.get(sessionID)        await SessionRevert.cleanup(session)        const msgs = await Session.messages({ sessionID })        let currentAgent = await Agent.defaultAgent()        for (let i = msgs.length - 1; i >= 0; i--) {          const info = msgs[i].info          if (info.role === "user") {            currentAgent = info.agent || (await Agent.defaultAgent())            break          }        }        await SessionCompaction.create({          sessionID,          agent: currentAgent,          model: {            providerID: body.providerID,            modelID: body.modelID,          },          auto: body.auto,        })        await SessionPrompt.loop(sessionID)        return c.json(true)      },    )    .get(      "/:sessionID/message",      describeRoute({        summary: "Get session messages",        description: "Retrieve all messages in a session, including user prompts and AI responses.",        operationId: "session.messages",        responses: {          200: {            description: "List of messages",            content: {              "application/json": {                schema: resolver(MessageV2.WithParts.array()),              },            },          },          ...errors(400, 404),        },

Lines 1–500 of 881.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t