* Run a /btw side question over the workspace's current conversation. * * Both the user question and the assistant answer are persisted to * chat.jsonl with side-question metadata, and stream lifecycle events * are emitted through the standard chat-event channel so the renderer * anim
(
workspaceId: string,
question: string
)
| 5102 | * versa) without interfering with either. |
| 5103 | */ |
| 5104 | public async askSideQuestion( |
| 5105 | workspaceId: string, |
| 5106 | question: string |
| 5107 | ): Promise<{ success: true; modelUsed: string } | { success: false; error: string }> { |
| 5108 | // Match other workspace ops: refuse on missing/in-flight workspaces so |
| 5109 | // the user gets a clear error instead of a confusing model failure |
| 5110 | // downstream. |
| 5111 | const workspaceConfig = this.config.findWorkspace(workspaceId); |
| 5112 | if (!workspaceConfig) { |
| 5113 | return { success: false, error: "Workspace not found." }; |
| 5114 | } |
| 5115 | |
| 5116 | const liveStreamSnapshot = snapshotSideQuestionLiveStream( |
| 5117 | this.aiService.getStreamInfo(workspaceId) |
| 5118 | ); |
| 5119 | const candidates = await this.getSideQuestionModelCandidates( |
| 5120 | workspaceId, |
| 5121 | liveStreamSnapshot?.model |
| 5122 | ); |
| 5123 | const result = await askSideQuestion({ |
| 5124 | workspaceId, |
| 5125 | question, |
| 5126 | candidates, |
| 5127 | aiService: this.aiService, |
| 5128 | historyService: this.historyService, |
| 5129 | liveStreamSnapshot, |
| 5130 | // Re-use the session's existing chat-event emitter; this is the same |
| 5131 | // path agentSession / streamManager use, so the frontend's onChat |
| 5132 | // subscription handles side-question events identically to a normal |
| 5133 | // agent stream (TypewriterMarkdown, smooth-text, replay all "just |
| 5134 | // work"). |
| 5135 | emitChatEvent: (wsId, message) => { |
| 5136 | this.sessions.get(wsId)?.emitChatEvent(message); |
| 5137 | }, |
| 5138 | }); |
| 5139 | if (!result.success) { |
| 5140 | return { |
| 5141 | success: false, |
| 5142 | error: result.error.raw ?? `Side question failed: ${result.error.type}`, |
| 5143 | }; |
| 5144 | } |
| 5145 | return { success: true, modelUsed: result.data.modelUsed }; |
| 5146 | } |
| 5147 | |
| 5148 | private async maybeRunPendingAutoTitleFromMessage( |
| 5149 | workspaceId: string, |
no test coverage detected