session

package
v0.8.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 14, 2026 License: Apache-2.0, MIT Imports: 52 Imported by: 0

Documentation

Overview

Package session provides RFB (VNC) client-stream parsing for the sandbox desktop relay.

The relay is transparent — it forwards bytes without rewriting them — but it still has to know where client messages start and end, because idle detection is by message opcode, not by byte count. Byte counting was rejected: the XFCE panel clock, websockify's --heartbeat 30, and ContinuousUpdates all keep bytes flowing forever, so a sandbox would never be judged idle and its TTL would be refreshed indefinitely.

Everything here is a pure function or a small state machine with no I/O, so the parts that are expensive to debug in production are cheap to test.

Package session provides the sandbox desktop WebSocket relay.

Browser (noVNC, no credentials)

⇄ this handler          ticket auth, 14-byte handshake skip, opcode relay
⇄ sandbox gateway       e2b-traffic-access-token + Authorization: Basic
⇄ websockify :6080      ⇄ x11vnc :5900 (127.0.0.1, -nopw) ⇄ Xvfb ⇄ XFCE

Both credentials exist only on the backend→sandbox hop. The browser holds neither, which is the whole reason traffic is relayed rather than sent direct.

Package session: terminal bridge between the WebSocket and the sandbox PTY (see sandbox_terminal_ws.go for the protocol contract).

Package session: sandbox terminal WebSocket.

GET /api/v1/sessions/:session_id/sandbox/terminal?ticket=<short-lived JWT>

The route is registered before the global auth middleware because a browser WebSocket handshake cannot carry the Authorization header. The browser first POSTs (with a normal Bearer token) for a 2-minute ticket bound to this session and to that access-token's id, then presents the ticket on the handshake. After upgrade the bridge rechecks the bound token, user, workspace membership, and session ownership about once a minute and tears the PTY down on failure.

binary frame  client→server  raw keystrokes into the PTY
binary frame  server→client  raw PTY output
text frame    both ways      JSON control frames (resize/ping/ready/…)

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func DecodeBase64Attachment

func DecodeBase64Attachment(data string) ([]byte, error)

DecodeBase64Attachment decodes a base64 attachment payload, stripping any data URI prefix. Tries Std, URL, RawStd, and RawURL encodings in order.

func MatchApprovedDir

func MatchApprovedDir(raw string, approved []string) (string, bool)

MatchApprovedDir reports whether raw names a directory the user approved through the native picker, returning it cleaned.

This is the authorization boundary for host workspaces, so it lives here rather than in localsandbox: session create runs in every edition, and the desktop-only sandbox must not be imported by cmd/server.

Matching is exact after Clean. A subdirectory of an approved directory is not approved: the user picked a root, and honouring children would let a stored value reach deeper than the picker ever offered.

Types

type AgentStreamHandler

type AgentStreamHandler struct {
	// contains filtered or unexported fields
}

AgentStreamHandler handles agent events for SSE streaming It uses a dedicated EventBus per request to avoid SessionID filtering Events are appended to StreamManager without accumulation

func NewAgentStreamHandler

func NewAgentStreamHandler(
	ctx context.Context,
	sessionID, assistantMessageID, requestID string,
	tenantID uint64,
	receivedAt time.Time,
	assistantMessage *types.Message,
	streamManager interfaces.StreamManager,
	eventBus *event.EventBus,
	artifactCollector *service.ArtifactCollector,
	checkpointer *service.WorkspaceCheckpointer,
	sandboxIDLookup SandboxIDLookup,
) *AgentStreamHandler

NewAgentStreamHandler creates a new handler for agent SSE streaming

func (*AgentStreamHandler) Subscribe

func (h *AgentStreamHandler) Subscribe()

Subscribe subscribes to all agent streaming events on the dedicated EventBus No SessionID filtering needed since we have a dedicated EventBus per request

type AttachmentProcessor

type AttachmentProcessor struct {
	// contains filtered or unexported fields
}

AttachmentProcessor saves uploaded file attachments and extracts their text content for injection into the LLM prompt.

func NewAttachmentProcessor

func NewAttachmentProcessor(
	fileService interfaces.FileService,
	documentReader interfaces.DocumentReader,
	imageResolver *docparser.ImageResolver,
	modelService interfaces.ModelService,
) *AttachmentProcessor

NewAttachmentProcessor creates an AttachmentProcessor with the given dependencies.

func (*AttachmentProcessor) ProcessAttachment

func (p *AttachmentProcessor) ProcessAttachment(
	ctx context.Context,
	data []byte,
	fileName string,
	fileSize int64,
	tenantID uint64,
	asrModelID string,
) (*types.MessageAttachment, error)

ProcessAttachment validates, saves, and extracts content from a single uploaded file. Content extraction is attempted for all supported types; errors are non-fatal (logged as warnings).

type AttachmentUpload

type AttachmentUpload struct {
	Data     string `json:"data"`      // Base64-encoded file content
	FileName string `json:"file_name"` // Original filename
	FileSize int64  `json:"file_size"` // File size in bytes
}

AttachmentUpload represents a file attachment upload from the client

type CreateKnowledgeQARequest

type CreateKnowledgeQARequest struct {
	// Query text; may be empty only when an image or file is attached
	Query string `json:"query"`
	// Selected knowledge base ID for this request
	KnowledgeBaseIDs []string `json:"knowledge_base_ids"`
	// Selected knowledge ID for this request
	KnowledgeIDs []string `json:"knowledge_ids"`
	// Whether agent mode is enabled for this request
	AgentEnabled bool `json:"agent_enabled"`
	// Selected custom agent ID (backend resolves shared agent and its workspace from share relation)
	AgentID string `json:"agent_id"`
	// Optional disambiguator; backend still verifies the share relation
	AgentSourceTenantID uint64 `json:"agent_source_tenant_id,omitempty"`
	// Browser source
	LocalBrowserEnabled bool `json:"local_browser_enabled"`
	// Whether web search is enabled for this request
	WebSearchEnabled bool `json:"web_search_enabled"`
	// Optional summary model ID for this request (overrides session default)
	SummaryModelID string `json:"summary_model_id"`
	// Per-request MCP services selected via @mention
	MCPServiceIDs []string `json:"mcp_service_ids"`
	// Per-request Skills selected via @mention
	SkillNames []string `json:"skill_names"`
	// @mentioned tag IDs (display/debug; scoped via MentionedItems)
	TagIDs []string `json:"tag_ids"`
	// @mentioned knowledge bases and files
	MentionedItems []MentionedItemRequest `json:"mentioned_items"`
	// Whether to disable auto title generation
	DisableTitle bool `json:"disable_title"`
	// Attached images for multimodal chat
	Images []ImageAttachment `json:"images"`
	// Attached files (documents, audio, etc.)
	AttachmentUploads []AttachmentUpload `json:"attachment_uploads,omitempty"`
	// Pre-uploaded session-scoped document IDs
	AttachmentIDs []string `json:"attachment_ids,omitempty"`
	// Source channel: "web", "api", "im", etc.
	Channel               string                       `json:"channel"`
	SuggestionAttribution *types.SuggestionAttribution `json:"suggestion_attribution,omitempty"`
	// QuestionOrigin is the knowledge source of a picked suggested question.
	QuestionOrigin *types.QuestionOrigin `json:"question_origin,omitempty"`

	// ReasoningEffort overrides thinking for this request; empty inherits the agent configuration.
	ReasoningEffort string `json:"reasoning_effort,omitempty"`
}

CreateKnowledgeQARequest defines the request structure for knowledge QA

type CreateSessionRequest

type CreateSessionRequest struct {
	// Title for the session (optional)
	Title string `json:"title"`
	// Description for the session (optional)
	Description string `json:"description"`
	// ProjectDir is an optional Lite host-sandbox binding. When set it must
	// be an absolute path already present in the user-approved ProjectDirs
	// list. Empty means the session gets an auto-allocated workspace.
	ProjectDir string `json:"project_dir,omitempty"`
}

CreateSessionRequest represents a request to create a new session Sessions are now knowledge-base-independent and serve as conversation containers. All configuration (knowledge bases, model settings, etc.) comes from custom agent at query time.

type ForkSessionRequest

type ForkSessionRequest struct {
	// MessageID is the message to branch at. A user message copies history
	// strictly before it (the client prefills that question). An assistant
	// message copies history through that answer so the branch continues after it.
	MessageID string `json:"message_id" binding:"required"`
	// Title is optional. Empty falls back to the source title plus a suffix.
	Title string `json:"title"`
}

ForkSessionRequest is the fork endpoint's body.

type GenerateTitleRequest

type GenerateTitleRequest struct {
	Messages []types.Message `json:"messages" binding:"required"` // Messages to use as context for title generation
}

GenerateTitleRequest defines the request structure for generating a session title

type Handler

type Handler struct {
	// contains filtered or unexported fields
}

Handler handles all HTTP requests related to conversation sessions

func NewHandler

func NewHandler(
	sessionService interfaces.SessionService,
	messageService interfaces.MessageService,
	suggestionService interfaces.MessageSuggestionService,
	streamManager interfaces.StreamManager,
	config *config.Config,
	knowledgebaseService interfaces.KnowledgeBaseService,
	customAgentService interfaces.CustomAgentService,
	tenantService interfaces.TenantService,
	agentShareService interfaces.AgentShareService,
	kbShareService interfaces.KBShareService,
	fileService interfaces.FileService,
	resourceCatalog interfaces.ResourceCatalog,
	storageResolver interfaces.StorageBackendResolver,
	modelService interfaces.ModelService,
	documentReader interfaces.DocumentReader,
	imageResolver *docparser.ImageResolver,
	temporaryDocuments interfaces.TemporaryDocumentService,
	artifactCollector *service.ArtifactCollector,
	workspaceCheckpointer *service.WorkspaceCheckpointer,
	sandboxIDLookup SandboxIDLookup,
	memoryService interfaces.MemoryService,
	userService interfaces.UserService,
	memberService interfaces.TenantMemberService,
	terminalService *service.SandboxTerminalService,
	browserSkill *browserskill.Manager,
	desktopService *service.SandboxDesktopService,
	desktopTickets service.SandboxDesktopTicketStore,
	desktopLast service.SandboxDesktopLastStore,
	rdb *redis.Client,
	forkService *service.SessionForkService,
	rewindService *service.SessionRewindService,
	approvedProjectDirs HostProjectDirsLoader,
) *Handler

NewHandler creates a new instance of Handler with all necessary dependencies

func (*Handler) AgentQA

func (h *Handler) AgentQA(c *gin.Context)

AgentQA godoc @Summary Agent问答 @Description 基于Agent的智能问答,支持多轮对话和SSE流式响应 @Tags 问答 @Accept json @Produce text/event-stream @Param session_id path string true "会话ID" @Param request body CreateKnowledgeQARequest true "问答请求" @Param resource_urls query string false "文件引用形式,public 返回可加载直链" Enums(handle, public) default(handle) @Success 200 {object} map[string]interface{} "问答结果(SSE流)" @Failure 400 {object} errors.AppError "请求参数错误" @Security Bearer @Security ApiKeyAuth @Router /agent-chat/{session_id} [post]

func (*Handler) BatchDeleteSessions

func (h *Handler) BatchDeleteSessions(c *gin.Context)

BatchDeleteSessions godoc @Summary 批量删除会话 @Description 根据ID列表批量删除对话会话,或设置 delete_all=true 删除当前空间的所有会话 @Tags 会话 @Accept json @Produce json @Param request body batchDeleteRequest true "批量删除请求" @Success 200 {object} map[string]interface{} "删除结果" @Failure 400 {object} errors.AppError "请求参数错误" @Security Bearer @Security ApiKeyAuth @Router /sessions/batch [delete]

func (*Handler) BrowserSkillAccount

func (h *Handler) BrowserSkillAccount(c *gin.Context)

BrowserSkillAccount manages this member's shared browser connection independently of any conversation. Normal authentication and tenant membership apply upstream.

func (*Handler) BrowserSkillAuthorize

func (h *Handler) BrowserSkillAuthorize(c *gin.Context)

BrowserSkillAuthorize exchanges one-use pairing and renewable device credentials.

func (*Handler) BrowserSkillConnection

func (h *Handler) BrowserSkillConnection(c *gin.Context)

BrowserSkillConnection exposes task status and user controls for an owned conversation.

func (*Handler) BrowserSkillDownload

func (h *Handler) BrowserSkillDownload(c *gin.Context)

BrowserSkillDownload serves the extension archive for installation.

func (*Handler) BrowserSkillExtension

func (h *Handler) BrowserSkillExtension(c *gin.Context)

BrowserSkillExtension upgrades a device-authenticated extension connection.

func (*Handler) BrowserSkillInternal

func (h *Handler) BrowserSkillInternal(c *gin.Context)

BrowserSkillInternal forwards signed requests to the owning node's browser gateway.

func (*Handler) ClearSessionMessages

func (h *Handler) ClearSessionMessages(c *gin.Context)

ClearSessionMessages godoc @Summary 清空会话消息 @Description 删除会话中的所有消息,同时清除 LLM 上下文和聊天历史知识库条目。会话本身保留。 @Tags 会话 @Accept json @Produce json @Param id path string true "会话ID" @Success 200 {object} map[string]interface{} "清空成功" @Failure 400 {object} errors.AppError "请求参数错误" @Failure 404 {object} errors.AppError "会话不存在" @Security Bearer @Security ApiKeyAuth @Router /sessions/{id}/messages [delete]

func (*Handler) ContinueStream

func (h *Handler) ContinueStream(c *gin.Context)

ContinueStream godoc @Summary 继续流式响应 @Description 继续获取正在进行的流式响应 @Tags 问答 @Accept json @Produce text/event-stream @Param session_id path string true "会话ID" @Param message_id query string true "消息ID" @Param resource_urls query string false "文件引用形式,public 返回可加载直链" Enums(handle, public) default(handle) @Success 200 {object} map[string]interface{} "流式响应" @Failure 404 {object} errors.AppError "会话或消息不存在" @Security Bearer @Security ApiKeyAuth @Router /sessions/continue-stream/{session_id} [get]

func (*Handler) CreateSession

func (h *Handler) CreateSession(c *gin.Context)

CreateSession godoc @Summary 创建会话 @Description 创建新的对话会话 @Tags 会话 @Accept json @Produce json @Param request body CreateSessionRequest true "会话创建请求" @Success 201 {object} map[string]interface{} "创建的会话" @Failure 400 {object} errors.AppError "请求参数错误" @Security Bearer @Security ApiKeyAuth @Router /sessions [post]

func (*Handler) DeleteLibraryArtifact

func (h *Handler) DeleteLibraryArtifact(c *gin.Context)

DeleteLibraryArtifact godoc @Summary 删除产物库中的文件 @Description 删除产物库中的一个文件。产物库一行代表一个文件的最新版本,默认连同该文件在本会话中的所有历史版本一并删除 @Tags 会话 @Produce json @Param session_id query string true "会话ID" @Param message_id query string true "消息ID" @Param index query int true "产物在消息中的位置" @Param all_versions query bool false "是否删除所有版本,默认 true" @Success 200 {object} map[string]interface{} @Failure 404 {object} errors.AppError @Security Bearer @Router /artifacts [delete]

Versions default to being deleted together here: the library row IS the file, with a version count rather than a row per regeneration, so deleting one version would leave the entry in place showing the previous one.

func (*Handler) DeleteMessageArtifact

func (h *Handler) DeleteMessageArtifact(c *gin.Context)

DeleteMessageArtifact godoc @Summary 删除会话中的产物文件 @Description 删除某条 assistant 消息生成的单个文件;all_versions=true 时连同本会话中该文件的所有历史版本一并删除 @Tags 会话 @Produce json @Param session_id path string true "会话ID" @Param message_id path string true "消息ID" @Param index path int true "产物在消息中的位置" @Param all_versions query bool false "是否删除该文件的所有版本" @Success 200 {object} map[string]interface{} @Failure 404 {object} errors.AppError @Security Bearer @Router /sessions/{session_id}/messages/{message_id}/artifacts/{index} [delete]

Addressed by the same (message, index) coordinates as the download endpoint, so a client deletes exactly the row it was showing. The in-chat panel lists every version separately and omits all_versions; the artifact library folds versions into one entry and sets it.

func (*Handler) DeleteSession

func (h *Handler) DeleteSession(c *gin.Context)

DeleteSession godoc @Summary 删除会话 @Description 删除指定的会话 @Tags 会话 @Accept json @Produce json @Param id path string true "会话ID" @Success 200 {object} map[string]interface{} "删除成功" @Failure 404 {object} errors.AppError "会话不存在" @Security Bearer @Security ApiKeyAuth @Router /sessions/{id} [delete]

func (*Handler) DeleteSteerMessage

func (h *Handler) DeleteSteerMessage(c *gin.Context)

DeleteSteerMessage godoc @Summary 删除一条排队中的消息 @Description 从当前运行的排队列表里去掉一条,不再注入也不再作为 follow-up 发出。 @Tags 问答 @Produce json @Param id path string true "会话 ID" @Param steer_id path string true "排队消息 ID" @Success 200 {object} map[string]interface{} @Failure 404 {object} errors.AppError @Failure 503 {object} errors.AppError "活 turn 查询失败,可重试" @Security Bearer @Security ApiKeyAuth @Router /sessions/{id}/steer/{steer_id} [delete]

func (*Handler) DeleteTemporaryDocument

func (h *Handler) DeleteTemporaryDocument(c *gin.Context)

func (*Handler) DownloadMessageArtifact

func (h *Handler) DownloadMessageArtifact(c *gin.Context)

DownloadMessageArtifact streams a single skill-generated file to the client. Clients reference the artifact by its position (:index) in the assistant message's Artifacts array; the storage URL never leaves the server so callers cannot pivot to arbitrary blobs.

@Router /sessions/{session_id}/messages/{message_id}/artifacts/{index}/download [get]

func (*Handler) ForkSession

func (h *Handler) ForkSession(c *gin.Context)

ForkSession godoc @Summary 分叉会话 @Description 从指定的用户或助手消息处分叉出一个新会话。用户消息:复制其之前的历史并预填该问题;助手消息:复制含该回答在内的历史,从该轮沙箱状态继续。 @Tags 会话 @Accept json @Produce json @Param session_id path string true "源会话 ID" @Param request body ForkSessionRequest true "分叉请求" @Success 200 {object} map[string]interface{} "新会话" @Failure 400 {object} errors.AppError "请求参数错误 / 分叉点角色不支持" @Failure 404 {object} errors.AppError "会话或消息不存在" @Failure 409 {object} errors.AppError "源会话正在生成中" @Security Bearer @Security ApiKeyAuth @Router /sessions/{session_id}/fork [post]

func (*Handler) GenerateTitle

func (h *Handler) GenerateTitle(c *gin.Context)

GenerateTitle godoc @Summary 生成会话标题 @Description 根据消息内容自动生成会话标题 @Tags 会话 @Accept json @Produce json @Param session_id path string true "会话ID" @Param request body GenerateTitleRequest true "生成请求" @Success 200 {object} map[string]interface{} "生成的标题" @Failure 400 {object} errors.AppError "请求参数错误" @Security Bearer @Security ApiKeyAuth @Router /sessions/{session_id}/title [post]

func (*Handler) GetSession

func (h *Handler) GetSession(c *gin.Context)

GetSession godoc @Summary 获取会话详情 @Description 根据ID获取会话详情 @Tags 会话 @Accept json @Produce json @Param id path string true "会话ID" @Success 200 {object} map[string]interface{} "会话详情" @Failure 404 {object} errors.AppError "会话不存在" @Security Bearer @Security ApiKeyAuth @Router /sessions/{id} [get]

func (*Handler) GetSessionsByTenant

func (h *Handler) GetSessionsByTenant(c *gin.Context)

GetSessionsByTenant godoc @Summary 获取会话列表 @Description 获取当前空间的会话列表,支持分页、关键字搜索、按来源/Agent 筛选 @Tags 会话 @Accept json @Produce json @Param page query int false "页码" @Param page_size query int false "每页数量" @Param keyword query string false "标题模糊搜索" @Param source query string false "来源过滤:web / embed / api / feishu / wechat / slack / ...(api、embed、IM 渠道需 Admin+)" @Param agent_id query string false "按 Agent 过滤(仅对 IM 会话生效)" @Success 200 {object} map[string]interface{} "会话列表" @Failure 400 {object} errors.AppError "请求参数错误" @Security Bearer @Security ApiKeyAuth @Router /sessions [get]

func (*Handler) GetTemporaryDocument

func (h *Handler) GetTemporaryDocument(c *gin.Context)

func (*Handler) IssueSandboxDesktopTicket

func (h *Handler) IssueSandboxDesktopTicket(c *gin.Context)

IssueSandboxDesktopTicket mints a one-shot handshake ticket. The access JWT stays on this authenticated POST and never reaches the WS URL.

func (*Handler) IssueSandboxTerminalTicket

func (h *Handler) IssueSandboxTerminalTicket(c *gin.Context)

IssueSandboxTerminalTicket mints a short-lived handshake ticket for the WebSocket. The access JWT stays on this authenticated POST and never appears in the WS URL (or nginx access logs of that handshake).

func (*Handler) KnowledgeQA

func (h *Handler) KnowledgeQA(c *gin.Context)

KnowledgeQA godoc @Summary 知识问答 @Description 基于知识库的问答(使用LLM总结),支持SSE流式响应 @Tags 问答 @Accept json @Produce text/event-stream @Param session_id path string true "会话ID" @Param request body CreateKnowledgeQARequest true "问答请求" @Param resource_urls query string false "文件引用形式,public 返回可加载直链" Enums(handle, public) default(handle) @Success 200 {object} map[string]interface{} "问答结果(SSE流)" @Failure 400 {object} errors.AppError "请求参数错误" @Security Bearer @Security ApiKeyAuth @Router /knowledge-chat/{session_id} [post]

func (*Handler) ListArtifactLibrary

func (h *Handler) ListArtifactLibrary(c *gin.Context)

ListArtifactLibrary godoc @Summary 列出我的产物 @Description 跨会话列出当前用户可见会话中技能生成的文件,每个文件只返回最新版本(不含存储 URL) @Tags 会话 @Produce json @Param keyword query string false "按文件名过滤" @Param file_types query string false "逗号分隔的扩展名,如 .pdf,.pptx" @Param page query int false "页码" @Param page_size query int false "每页数量(最大 100)" @Success 200 {object} map[string]interface{} @Security Bearer @Security ApiKeyAuth @Router /artifacts [get]

Items carry session_id, message_id and index so the client downloads through the per-session endpoint, which re-runs the ownership check.

func (*Handler) ListMessageArtifacts

func (h *Handler) ListMessageArtifacts(c *gin.Context)

ListMessageArtifacts returns just the artifacts attached to a single assistant message. Used by the "download files from this reply" button on each bot message.

Same-tenant/same-owner check flows through h.sessionService.GetSession exactly like ListSessionArtifacts.

@Router /sessions/{session_id}/messages/{message_id}/artifacts [get]

func (*Handler) ListSessionArtifacts

func (h *Handler) ListSessionArtifacts(c *gin.Context)

ListSessionArtifacts godoc @Summary 列出会话生成的产物文件 @Description 返回本会话中所有 assistant 消息产生的技能产物元数据(不含 URL) @Tags 会话 @Produce json @Param session_id path string true "会话ID" @Success 200 {object} map[string]interface{} @Failure 404 {object} errors.AppError @Security Bearer @Router /sessions/{session_id}/artifacts [get]

The endpoint powers the drawer that lists every file generated in the session; it does NOT return the storage URL (only names/sizes/mtimes), so clients cannot reach around the download endpoint by reading a provider:// path from the API response.

Deleted artifacts are skipped but still consume their index: the index IS the download address, so renumbering around a tombstone would point old links at the wrong file.

func (*Handler) ListSteerMessages

func (h *Handler) ListSteerMessages(c *gin.Context)

ListSteerMessages godoc @Summary 列出当前运行中尚未消费的排队消息 @Description 刷新页面后用来恢复输入框上方的队列。没有正在运行的 turn 时返回空列表。 @Tags 问答 @Produce json @Param id path string true "会话 ID" @Success 200 {object} map[string]interface{} @Failure 404 {object} errors.AppError @Failure 503 {object} errors.AppError "活 turn 查询失败,可重试" @Security Bearer @Security ApiKeyAuth @Router /sessions/{id}/steer [get]

func (*Handler) ListTemporaryDocuments

func (h *Handler) ListTemporaryDocuments(c *gin.Context)

func (*Handler) PinSession

func (h *Handler) PinSession(c *gin.Context)

PinSession godoc @Summary 置顶会话 @Description 将指定会话置顶(用户维度) @Tags 会话 @Produce json @Param session_id path string true "会话ID" @Success 200 {object} map[string]interface{} "置顶成功" @Failure 404 {object} errors.AppError "会话不存在" @Security Bearer @Security ApiKeyAuth @Router /sessions/{session_id}/pin [post]

func (*Handler) PreviewTemporaryDocument

func (h *Handler) PreviewTemporaryDocument(c *gin.Context)

func (*Handler) PromoteSteerMessage

func (h *Handler) PromoteSteerMessage(c *gin.Context)

PromoteSteerMessage godoc @Summary 将排队消息改为立即注入 @Description 把一条 delivery=after 的排队消息改为 inject,运行中的 agent 会在下一轮边界读到它。 @Tags 问答 @Produce json @Param session_id path string true "会话 ID" @Param steer_id path string true "排队消息 ID" @Success 200 {object} map[string]interface{} "queued | new_run" @Failure 400 {object} errors.AppError @Failure 404 {object} errors.AppError @Failure 503 {object} errors.AppError "活 turn 查询失败,可重试" @Security Bearer @Security ApiKeyAuth @Router /sessions/{session_id}/steer/{steer_id}/inject [post]

func (*Handler) ReportSandboxDesktopActivity

func (h *Handler) ReportSandboxDesktopActivity(c *gin.Context)

ReportSandboxDesktopActivity records browser-side key/mouse activity.

It is the fallback the RFB parser degrades to on an unknown opcode, and a no-op when the parser is healthy. It is deliberately not the primary signal: a client that simply stops posting would keep the sandbox alive for free.

func (*Handler) RewindSession

func (h *Handler) RewindSession(c *gin.Context)

RewindSession godoc @Summary 回滚会话 @Description 回滚当前会话到指定用户或助手消息:删除其后消息,可到达时 git reset 工作区。 @Tags 会话 @Accept json @Produce json @Param session_id path string true "会话 ID" @Param request body RewindSessionRequest true "回滚请求" @Success 200 {object} map[string]interface{} "回滚结果" @Failure 400 {object} errors.AppError "请求参数错误 / 回滚点角色不支持" @Failure 404 {object} errors.AppError "会话或消息不存在" @Failure 409 {object} errors.AppError "会话正在生成中 / 无检查点 / 沙箱已更换" @Failure 500 {object} errors.AppError "工作区回滚失败" @Security Bearer @Security ApiKeyAuth @Router /sessions/{session_id}/rewind [post]

func (*Handler) SandboxDesktopWS

func (h *Handler) SandboxDesktopWS(c *gin.Context)

SandboxDesktopWS relays the browser's RFB stream to the session sandbox.

Ticket, auth, and the exclusive slot stay HTTP because those run before Ensure. Overlay states from Ensure (SANDBOX_NOT_BOUND / SANDBOX_PAUSED / DESKTOP_UNSUPPORTED / DESKTOP_START_FAILED) must upgrade and then close with a reason: the JS WebSocket API never exposes a failed handshake's HTTP status, so a 409 here becomes a generic error overlay instead of the confirm card. Handshake prelude failures stay HTTP — by then the sandbox is already live and the frontend must not show "create sandbox".

func (*Handler) SandboxTerminalWS

func (h *Handler) SandboxTerminalWS(c *gin.Context)

SandboxTerminalWS upgrades a browser connection into a session sandbox terminal. See the file header for the protocol.

func (*Handler) SearchKnowledge

func (h *Handler) SearchKnowledge(c *gin.Context)

SearchKnowledge godoc @Summary 知识搜索 @Description 在知识库中搜索(不使用LLM总结)。与产品内问答使用同一检索流程(召回、rerank、合并),外部检索首选;可覆盖召回参数与 rerank @Tags 问答 @Accept json @Produce json @Param request body SearchKnowledgeRequest true "搜索请求" @Param resource_urls query string false "文件引用形式,public 返回可加载直链" Enums(handle, public) default(handle) @Success 200 {object} map[string]interface{} "搜索结果" @Failure 400 {object} errors.AppError "请求参数错误" @Security Bearer @Security ApiKeyAuth @Router /knowledge-search [post]

func (*Handler) SteerMessage

func (h *Handler) SteerMessage(c *gin.Context)

SteerMessage godoc @Summary 向运行中的对话追加消息 @Description 向运行中的 agent turn 追加用户消息(after 排队 / inject 注入)。无活 turn 时返回 new_run。 @Tags 问答 @Accept json @Produce json @Param session_id path string true "会话 ID" @Param request body SteerMessageRequest true "追加消息" @Success 200 {object} map[string]interface{} "queued | new_run" @Failure 400 {object} errors.AppError "请求参数错误" @Failure 404 {object} errors.AppError "会话不存在" @Failure 503 {object} errors.AppError "活 turn 查询失败,可重试" @Security Bearer @Security ApiKeyAuth @Router /sessions/{session_id}/steer [post]

func (*Handler) StopSession

func (h *Handler) StopSession(c *gin.Context)

StopSession godoc @Summary 停止生成 @Description 停止当前正在进行的生成任务 @Tags 问答 @Accept json @Produce json @Param session_id path string true "会话ID" @Param request body StopSessionRequest true "停止请求" @Success 200 {object} map[string]interface{} "停止成功" @Failure 404 {object} errors.AppError "会话或消息不存在" @Security Bearer @Security ApiKeyAuth @Router /sessions/{session_id}/stop [post]

func (*Handler) UnpinSession

func (h *Handler) UnpinSession(c *gin.Context)

UnpinSession godoc @Summary 取消置顶会话 @Description 取消指定会话的置顶 @Tags 会话 @Produce json @Param id path string true "会话ID" @Success 200 {object} map[string]interface{} "取消置顶成功" @Failure 404 {object} errors.AppError "会话不存在" @Security Bearer @Security ApiKeyAuth @Router /sessions/{id}/pin [delete]

func (*Handler) UpdateSession

func (h *Handler) UpdateSession(c *gin.Context)

UpdateSession godoc @Summary 更新会话 @Description 更新会话属性 @Tags 会话 @Accept json @Produce json @Param id path string true "会话ID" @Param request body types.Session true "会话信息" @Success 200 {object} map[string]interface{} "更新后的会话" @Failure 404 {object} errors.AppError "会话不存在" @Security Bearer @Security ApiKeyAuth @Router /sessions/{id} [put]

func (*Handler) UploadTemporaryDocument

func (h *Handler) UploadTemporaryDocument(c *gin.Context)

UploadTemporaryDocument accepts one multipart file and immediately returns a session-scoped document ID. Parsing continues in the document worker.

type HostProjectDirsLoader

type HostProjectDirsLoader func() []string

HostProjectDirsLoader returns the user-approved project directories from desktop prefs. Nil means no directory is approved.

type ImageAttachment

type ImageAttachment struct {
	Data    string `json:"data,omitempty"`    // base64 data URI from frontend (data:image/png;base64,...)
	URL     string `json:"url,omitempty"`     // serving URL after saving to storage
	Caption string `json:"caption,omitempty"` // VLM analysis result (context-aware, single call)
}

ImageAttachment represents an image in a chat request. Frontend sends base64 data in the Data field; the backend saves, runs VLM analysis, and populates URL/Caption before proceeding with the chat pipeline.

type MentionedItemRequest

type MentionedItemRequest struct {
	ID        string `json:"id"`
	Name      string `json:"name"`
	Type      string `json:"type"`       // "kb", "file", "tag", "mcp", "skill"
	KBType    string `json:"kb_type"`    // "document" or "faq" (only for kb type)
	KBID      string `json:"kb_id"`      // Parent knowledge base for file/tag mentions
	KBName    string `json:"kb_name"`    // Display name for parent KB
	ServiceID string `json:"service_id"` // Parent MCP service for MCP tool mentions
	SkillName string `json:"skill_name"` // Preloaded agent skill name
}

MentionedItemRequest represents a mentioned item in the request

type RewindSessionRequest

type RewindSessionRequest struct {
	// MessageID is the message to rewind to. A user message deletes itself and
	// everything after (the client prefills that question). An assistant
	// message keeps itself and deletes everything after.
	MessageID string `json:"message_id" binding:"required"`
}

RewindSessionRequest is the rewind endpoint's body.

type SandboxIDLookup

type SandboxIDLookup interface {
	BoundSandboxID(ctx context.Context, sessionID string) (string, bool)
}

SandboxIDLookup reports the sandbox currently bound to a session without provisioning one. A session with no live sandbox reports ok=false, which the completion path treats as "nothing to check point".

type SearchKnowledgeRequest

type SearchKnowledgeRequest struct {
	Query            string                 `json:"query"              binding:"required"` // Query text to search for
	KnowledgeBaseID  string                 `json:"knowledge_base_id"`                     // Single knowledge base ID (for backward compatibility)
	KnowledgeBaseIDs []string               `json:"knowledge_base_ids"`                    // IDs of knowledge bases to search (multi-KB support)
	KnowledgeIDs     []string               `json:"knowledge_ids"`                         // IDs of specific knowledge (files) to search
	TagIDs           []string               `json:"tag_ids"`                               // Tag IDs for filtering within a single KB
	MentionedItems   []MentionedItemRequest `json:"mentioned_items"`                       // Optional scoped tag mentions

	// Optional overrides of the tenant retrieval config. Omitted fields keep it.
	VectorThreshold      *float64             `json:"vector_threshold,omitempty"`       // Minimum vector similarity
	KeywordThreshold     *float64             `json:"keyword_threshold,omitempty"`      // Minimum keyword score
	MatchCount           int                  `json:"match_count,omitempty"`            // Number of results to return
	DisableKeywordsMatch bool                 `json:"disable_keywords_match,omitempty"` // Vector recall only
	DisableVectorMatch   bool                 `json:"disable_vector_match,omitempty"`   // Keyword recall only
	Rerank               *types.RerankOptions `json:"rerank,omitempty"`                 // Rerank override
}

SearchKnowledgeRequest defines the request structure for searching knowledge without LLM summarization

type SteerMessageRequest

type SteerMessageRequest struct {
	// Optional for older clients. New clients pin delivery to the run they see
	// and supply a stable ID so a consume event may precede the HTTP response.
	ExpectedAssistantMessageID string                 `json:"expected_assistant_message_id,omitempty"`
	SteerID                    string                 `json:"steer_id,omitempty"`
	Query                      string                 `json:"query" binding:"required"`
	MentionedItems             []MentionedItemRequest `json:"mentioned_items,omitempty"`
	Channel                    string                 `json:"channel,omitempty"`
	// Delivery is "after" (default) or "inject". See the constants above.
	Delivery string `json:"delivery,omitempty"`
}

SteerMessageRequest is the payload of POST /sessions/:session_id/steer.

type StopSessionRequest

type StopSessionRequest struct {
	MessageID string `json:"message_id" binding:"required"`
}

StopSessionRequest represents the stop session request

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL