feat(agents): launch Superset Chat sessions from desktop / CLI / SDK / MCP#4116
feat(agents): launch Superset Chat sessions from desktop / CLI / SDK / MCP#4116saddlepaddle merged 5 commits intomainfrom
Conversation
…/ MCP Folds the built-in Superset Chat agent into the existing `agents` sugar so the same one-call surface spawns terminal *and* chat sessions across every entry point. Picker today shows only the host's terminal-config rows; submitting `superset-chat` 404s on the host because `runAgentInWorkspace` only knows how to spawn PTYs. Plumbing changes: - `agents.ts runAgentInWorkspace` branches on chat-builtin ids: mints a sessionId, registers the cloud `chat_sessions` row, and fires the first turn through `ChatRuntimeManager.startTurn`. Returns a `kind`-tagged result so callers materialize the right pane. - `ChatRuntimeManager.startTurn` boots the runtime and queues the message, but returns as soon as streaming begins instead of awaiting the assistant's full reply (~30s -> ~5-8s for headless callers). - `getSnapshot` cold-start fast path: returns an empty skeleton immediately when no runtime is in memory while creation runs in the background. Renderer's "Loading conversation..." clears on the first poll instead of hanging on the harness boot. - Host's API client carries the bound organizationId via x-superset-organization-id. The session-exchanged JWT only ships organizationIds[]; without the header protectedProcedure middleware can't resolve activeOrganizationId and any host->cloud call hits "No active organization selected". Surface updates: - Desktop v2 picker: new useV2AgentChoices hook overlays builtin chat agents on top of the host's terminal rows. Wired into the new workspace modal and the two task launch pickers. - appendLaunchesToPaneLayout seeds a kind: "chat" pane for chat results. - CLI workspaces create --agent --prompt --attachment and agents run --attachment accept local file paths and upload them to the host's attachment store before launch. - agents list includes Superset Chat. - SDK + MCP-v2 result types gain the kind discriminator. JSDoc and tool descriptions document superset-chat as a valid agent value.
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (23)
📝 WalkthroughWalkthroughThis PR standardizes the built-in Superset chat preset id/label to ChangesSuperset Agent + Attachment & Result Shape Integration
Sequence DiagramsequenceDiagram
participant User
participant Desktop as Desktop App
participant Hook as useV2AgentChoices
participant HostAPI as Host API
participant HostService as Host Service
rect rgba(200, 150, 255, 0.5)
Note over User,HostService: Agent Selection Flow
User->>Desktop: Open agent selector
Desktop->>Hook: useV2AgentChoices(hostUrl)
Hook->>HostAPI: Fetch host agent configs
HostAPI-->>Hook: Terminal agent configs
Hook->>Hook: Map configs → AgentSelectAgent[]
Hook->>Hook: Append built-in Superset agent
Hook-->>Desktop: { agents, isFetched }
Desktop->>Desktop: Render agent options
User->>Desktop: Select agent + prompt
end
rect rgba(100, 200, 150, 0.5)
Note over User,HostService: Agent Execution Flow
User->>Desktop: Submit agent + prompt + attachments
Desktop->>HostService: runAgentInWorkspace(ctx, { agent, prompt, attachmentIds })
alt Agent is "superset"
HostService->>HostService: runChatAgent()
HostService->>HostService: Create chat session, attach files, send prompt
HostService-->>Desktop: { kind: "chat", sessionId, label }
else Terminal agent
HostService->>HostService: runTerminalAgent()
HostService-->>Desktop: { kind: "terminal", sessionId, label }
end
Desktop->>Desktop: Open appropriate pane (chat or terminal)
Desktop-->>User: Session launched
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Greptile SummaryThis PR wires Superset Chat sessions into the existing
Confidence Score: 3/5The new chat launch path works end-to-end under the happy path, but two code paths can leave users stuck without actionable feedback. The
|
| Filename | Overview |
|---|---|
| packages/host-service/src/runtime/chat/chat.ts | Adds peekRuntime (non-blocking cold-start) and startTurn (fire-and-forget headless launch). The peekRuntime path silently swallows creation errors, leaving the renderer stuck on 'Loading conversation...' with no error surfaced. |
| apps/desktop/src/renderer/hooks/useV2AgentChoices/useV2AgentChoices.ts | New hook merges terminal configs with builtin chat agents. Returns isFetched: query.isFetched which is permanently false when hostUrl is null, incorrectly gating chat builtins on the terminal query. |
| packages/host-service/src/trpc/router/agents/agents.ts | Splits runAgentInWorkspace into chat and terminal paths, adds resolveAttachmentsAsFiles for base64-inlining files into chat messages. Logic is sound; attachment resolution could block on large files. |
| packages/host-service/src/api/createApiClient/createApiClient.ts | Adds organizationId parameter and pins it as x-superset-organization-id header on every host→cloud request, fixing the missing activeOrganizationId for session-JWT callers. |
| packages/cli/src/lib/upload-attachments.ts | New helper uploads local files to the host attachment store. Uses readFileSync which blocks the event loop for large files; otherwise logic is correct. |
| apps/desktop/src/renderer/stores/workspace-creates/appendLaunchesToPaneLayout.ts | Extends pane layout builder to handle kind: 'chat' agent results, creating a ChatPaneData pane instead of a terminal pane. Change is clean and additive. |
| packages/cli/src/commands/agents/run/command.ts | Adds --attachment (local file path) option and updates output message to discriminate between terminal and chat results. Clean changes. |
| packages/cli/src/commands/workspaces/create/command.ts | Adds --agent, --prompt, --attachment options with mutual-dependency validation guards. Logic is thorough and error messages are helpful. |
| packages/mcp-v2/src/tools/agents/run.ts | Updates tool description and result type to include kind discriminator. Documentation changes are accurate and the type update is correct. |
| packages/sdk/src/resources/agents.ts | Converts AgentRunResult from an interface to a discriminated union with kind field. JSDoc is accurate and complete. |
Sequence Diagram
sequenceDiagram
participant C as CLI / SDK / MCP
participant A as agentsRouter (host)
participant Chat as ChatRuntimeManager
participant Cloud as Cloud API
C->>A: agents.run { agent: superset-chat, prompt }
A->>Cloud: chat.createSession { sessionId, v2WorkspaceId }
Cloud-->>A: ok
A->>Chat: startTurn { sessionId, workspaceId, payload }
Chat->>Chat: getOrCreateRuntime (3-8s boot)
Chat-->>Chat: fire sendMessage (fire-and-forget)
Chat-->>A: returns
A-->>C: { kind: chat, sessionId, label }
Note over C: CLI exits / Desktop opens chat pane
loop Renderer polling
C->>Chat: getSnapshot { sessionId, workspaceId }
alt runtime in memory
Chat-->>C: { displayState, messages }
else runtime evicted or not yet booted
Chat->>Chat: peekRuntime kicks off background creation
Chat-->>C: empty skeleton { isRunning:false, messages:[] }
end
end
Comments Outside Diff (1)
-
apps/desktop/src/renderer/hooks/useV2AgentChoices/useV2AgentChoices.ts, line 64 (link)isFetchedgates builtin chat agents on the host query unnecessarily.isFetchedreflectsquery.isFetched— the terminal-config fetch — not whether the chat builtins are ready (they're synchronously available). WhenhostUrlisnull(query disabled), React Query keepsisFetched: falsepermanently, so callers likeOpenInWorkspaceV2show "Checking agents…" forever even ifsuperset-chatis already invalidAgentIds. The returned value should betrueas soon as the builtins are known, regardless of the host query state.Prompt To Fix With AI
This is a comment left during a code review. Path: apps/desktop/src/renderer/hooks/useV2AgentChoices/useV2AgentChoices.ts Line: 64 Comment: `isFetched` gates builtin chat agents on the host query unnecessarily. `isFetched` reflects `query.isFetched` — the terminal-config fetch — not whether the chat builtins are ready (they're synchronously available). When `hostUrl` is `null` (query disabled), React Query keeps `isFetched: false` permanently, so callers like `OpenInWorkspaceV2` show "Checking agents…" forever even if `superset-chat` is already in `validAgentIds`. The returned value should be `true` as soon as the builtins are known, regardless of the host query state. How can I resolve this? If you propose a fix, please make it concise.
Prompt To Fix All With AI
Fix the following 3 code review issues. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 3
packages/host-service/src/runtime/chat/chat.ts:735-744
**Cold-start skeleton silently hides runtime creation errors**
`peekRuntime` kicks off `getOrCreateRuntime` as a fire-and-forget and swallows the rejection entirely. If creation fails (workspace directory deleted, harness binary missing, permissions error), the inflight entry is removed from `runtimeCreations` via `.finally()`, the next `getSnapshot` call starts a fresh background attempt, and the cycle repeats — all while the skeleton returned here permanently sets `errorMessage: null`. The renderer's "Loading conversation…" spinner never clears and no error is surfaced to the user.
Before this PR, `getSnapshot` awaited `getOrCreateRuntime` directly, so creation failures propagated as tRPC errors that the client could catch and display. The new fast path loses that signal. At minimum, the empty skeleton should carry a sentinel flag (e.g., `isLoading: true`) so the renderer can distinguish "loading" from "error", or the retry loop needs a bounded attempt count before surfacing a synthetic error message.
### Issue 2 of 3
apps/desktop/src/renderer/hooks/useV2AgentChoices/useV2AgentChoices.ts:64
`isFetched` gates builtin chat agents on the host query unnecessarily. `isFetched` reflects `query.isFetched` — the terminal-config fetch — not whether the chat builtins are ready (they're synchronously available). When `hostUrl` is `null` (query disabled), React Query keeps `isFetched: false` permanently, so callers like `OpenInWorkspaceV2` show "Checking agents…" forever even if `superset-chat` is already in `validAgentIds`. The returned value should be `true` as soon as the builtins are known, regardless of the host query state.
```suggestion
// Chat builtins are synchronously available; only block on the host query
// if terminal agents were actually fetched. This avoids a permanent
// "Checking agents…" gate when hostUrl is null or the query is disabled.
const isFetched = !hostUrl || query.isFetched;
return { agents, isFetched };
```
### Issue 3 of 3
packages/cli/src/lib/upload-attachments.ts:28-30
`readFileSync` loads each attachment entirely into memory before the next upload starts. On large files this blocks the Node/Bun event loop for the duration of the read. Switching to `fs.promises.readFile` avoids the block without changing upload ordering.
```suggestion
const bytes = await import("node:fs/promises").then((fs) =>
fs.readFile(path),
);
const result = await client.attachments.upload.mutate({
data: { kind: "base64", data: bytes.toString("base64") },
```
Reviews (1): Last reviewed commit: "feat(agents): launch Superset Chat sessi..." | Re-trigger Greptile
| if (!runtime) { | ||
| return { | ||
| displayState: { | ||
| isRunning: false, | ||
| currentMessage: null, | ||
| pendingQuestion: null, | ||
| errorMessage: null, | ||
| } as unknown as ChatDisplayState, | ||
| messages: [] as unknown as RuntimeMessages, | ||
| }; |
There was a problem hiding this comment.
Cold-start skeleton silently hides runtime creation errors
peekRuntime kicks off getOrCreateRuntime as a fire-and-forget and swallows the rejection entirely. If creation fails (workspace directory deleted, harness binary missing, permissions error), the inflight entry is removed from runtimeCreations via .finally(), the next getSnapshot call starts a fresh background attempt, and the cycle repeats — all while the skeleton returned here permanently sets errorMessage: null. The renderer's "Loading conversation…" spinner never clears and no error is surfaced to the user.
Before this PR, getSnapshot awaited getOrCreateRuntime directly, so creation failures propagated as tRPC errors that the client could catch and display. The new fast path loses that signal. At minimum, the empty skeleton should carry a sentinel flag (e.g., isLoading: true) so the renderer can distinguish "loading" from "error", or the retry loop needs a bounded attempt count before surfacing a synthetic error message.
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/host-service/src/runtime/chat/chat.ts
Line: 735-744
Comment:
**Cold-start skeleton silently hides runtime creation errors**
`peekRuntime` kicks off `getOrCreateRuntime` as a fire-and-forget and swallows the rejection entirely. If creation fails (workspace directory deleted, harness binary missing, permissions error), the inflight entry is removed from `runtimeCreations` via `.finally()`, the next `getSnapshot` call starts a fresh background attempt, and the cycle repeats — all while the skeleton returned here permanently sets `errorMessage: null`. The renderer's "Loading conversation…" spinner never clears and no error is surfaced to the user.
Before this PR, `getSnapshot` awaited `getOrCreateRuntime` directly, so creation failures propagated as tRPC errors that the client could catch and display. The new fast path loses that signal. At minimum, the empty skeleton should carry a sentinel flag (e.g., `isLoading: true`) so the renderer can distinguish "loading" from "error", or the retry loop needs a bounded attempt count before surfacing a synthetic error message.
How can I resolve this? If you propose a fix, please make it concise.| const bytes = readFileSync(path); | ||
| const result = await client.attachments.upload.mutate({ | ||
| data: { kind: "base64", data: bytes.toString("base64") }, |
There was a problem hiding this comment.
readFileSync loads each attachment entirely into memory before the next upload starts. On large files this blocks the Node/Bun event loop for the duration of the read. Switching to fs.promises.readFile avoids the block without changing upload ordering.
| const bytes = readFileSync(path); | |
| const result = await client.attachments.upload.mutate({ | |
| data: { kind: "base64", data: bytes.toString("base64") }, | |
| const bytes = await import("node:fs/promises").then((fs) => | |
| fs.readFile(path), | |
| ); | |
| const result = await client.attachments.upload.mutate({ | |
| data: { kind: "base64", data: bytes.toString("base64") }, |
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/cli/src/lib/upload-attachments.ts
Line: 28-30
Comment:
`readFileSync` loads each attachment entirely into memory before the next upload starts. On large files this blocks the Node/Bun event loop for the duration of the read. Switching to `fs.promises.readFile` avoids the block without changing upload ordering.
```suggestion
const bytes = await import("node:fs/promises").then((fs) =>
fs.readFile(path),
);
const result = await client.attachments.upload.mutate({
data: { kind: "base64", data: bytes.toString("base64") },
```
How can I resolve this? If you propose a fix, please make it concise.
🧹 Preview Cleanup CompleteThe following preview resources have been cleaned up:
Thank you for your contribution! 🎉 |
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/sdk/src/resources/workspaces.ts (1)
149-158:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winUpdate
attachmentIdsdocs to cover chat launches too.This type now advertises
"superset-chat", butattachmentIdsstill says the host always resolves attachments to filesystem paths in the prompt. That is no longer true for chat sessions, so the SDK docs are contradictory.Suggested doc update
- /** Host-scoped attachment ids; host resolves to absolute paths in the prompt. */ + /** + * Host-scoped attachment ids. For terminal agents the host appends + * absolute paths to the prompt; for `"superset-chat"` it inlines the + * file bytes as base64 data URLs on the chat message. + */ attachmentIds?: string[];🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/sdk/src/resources/workspaces.ts` around lines 149 - 158, The JSDoc for WorkspaceAgentLaunch.attachmentIds is incorrect for chat launches; update the comment on the attachmentIds field in the WorkspaceAgentLaunch interface so it explains that for host-scoped agents the host resolves IDs to filesystem paths in the prompt, but for the "superset-chat" agent IDs are treated as chat attachments (not necessarily filesystem paths) and are resolved differently; reference the agent field and the special "superset-chat" value in the comment so consumers understand the distinction.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/cli/src/lib/upload-attachments.ts`:
- Around line 28-33: Wrap the file read and upload calls in a try/catch and
rethrow failures as CLIError annotated with the filename/path to provide
actionable CLI output: around the readFileSync(...) and
client.attachments.upload.mutate(...) calls in upload-attachments.ts, catch any
error and throw new CLIError(`Failed to read/upload attachment "${filename}"
(path: ${path}): ${err.message}`, { cause: err }) or similar so the original
error is preserved; ensure you import/use CLIError and reference the
functions/variables bytes, readFileSync, client.attachments.upload.mutate,
filename and path in the error message.
In `@packages/host-service/src/runtime/chat/chat.ts`:
- Around line 582-584: peekRuntime() currently swallows getOrCreateRuntime()
failures (called via void this.getOrCreateRuntime(...).catch(() => {})), causing
getSnapshot() to return a silent empty running state; instead propagate and
record the boot error so callers see an error snapshot. Change the void-catch
pattern in the getOrCreateRuntime invocation(s) (the call sites that use
this.getOrCreateRuntime(sessionId, workspaceId) around the peekRuntime flow and
the similar block at lines ~734-744) to capture the thrown error and set the
runtime entry/state used by peekRuntime/getSnapshot with an errorMessage and
isRunning=false (or otherwise mark it failed) so getSnapshot() returns an
error-containing snapshot rather than an empty success; ensure the same behavior
for both call sites and reference peekRuntime, getOrCreateRuntime, and
getSnapshot when updating the runtime state/metadata.
In `@packages/host-service/src/trpc/router/agents/agents.ts`:
- Around line 208-224: The createSession call
(ctx.api.chat.createSession.mutate) is committed before starting the runtime
turn (ctx.runtime.chat.startTurn), so if startTurn throws you must roll back the
persisted session to avoid orphaned/duplicate chat_sessions rows; wrap the
startTurn invocation in a try/catch and on any error call the session delete
mutation (e.g., ctx.api.chat.deleteSession.mutate or the existing delete API)
with the same sessionId (and workspaceId) before rethrowing the error, ensuring
delete is awaited so the DB row is removed when startup fails.
---
Outside diff comments:
In `@packages/sdk/src/resources/workspaces.ts`:
- Around line 149-158: The JSDoc for WorkspaceAgentLaunch.attachmentIds is
incorrect for chat launches; update the comment on the attachmentIds field in
the WorkspaceAgentLaunch interface so it explains that for host-scoped agents
the host resolves IDs to filesystem paths in the prompt, but for the
"superset-chat" agent IDs are treated as chat attachments (not necessarily
filesystem paths) and are resolved differently; reference the agent field and
the special "superset-chat" value in the comment so consumers understand the
distinction.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 5c56d5dd-96bd-4778-9e0f-e638639d1636
⛔ Files ignored due to path filters (1)
bun.lockis excluded by!**/*.lock
📒 Files selected for processing (20)
apps/desktop/src/renderer/components/AgentSelect/index.tsapps/desktop/src/renderer/hooks/useV2AgentChoices/index.tsapps/desktop/src/renderer/hooks/useV2AgentChoices/useV2AgentChoices.tsapps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/$taskId/components/PropertiesSidebar/components/OpenInWorkspaceV2/OpenInWorkspaceV2.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/components/TasksView/components/TasksTopBar/components/RunInWorkspacePopoverV2/RunInWorkspacePopoverV2.tsxapps/desktop/src/renderer/routes/_authenticated/components/DashboardNewWorkspaceModal/components/DashboardNewWorkspaceForm/PromptGroup/PromptGroup.tsxapps/desktop/src/renderer/stores/workspace-creates/appendLaunchesToPaneLayout.tspackages/cli/package.jsonpackages/cli/src/commands/agents/list/command.tspackages/cli/src/commands/agents/run/command.tspackages/cli/src/commands/workspaces/create/command.tspackages/cli/src/lib/upload-attachments.tspackages/host-service/src/api/createApiClient/createApiClient.tspackages/host-service/src/app.tspackages/host-service/src/runtime/chat/chat.tspackages/host-service/src/trpc/router/agents/agents.tspackages/mcp-v2/src/tools/agents/run.tspackages/mcp-v2/src/tools/workspaces/create.tspackages/sdk/src/resources/agents.tspackages/sdk/src/resources/workspaces.ts
| const bytes = readFileSync(path); | ||
| const result = await client.attachments.upload.mutate({ | ||
| data: { kind: "base64", data: bytes.toString("base64") }, | ||
| mediaType, | ||
| originalFilename: filename, | ||
| }); |
There was a problem hiding this comment.
Wrap read/upload failures in CLIError with file context.
Line 28 and Line 29 can throw raw errors; surfacing them as CLIError keeps CLI output actionable and consistent.
Suggested patch
const filename = basename(path);
const mediaType = mimeTypes.lookup(filename);
if (!mediaType) {
throw new CLIError(
`Could not determine media type for attachment: ${path}`,
"Use a recognizable file extension (e.g. .png, .pdf, .md)",
);
}
- const bytes = readFileSync(path);
- const result = await client.attachments.upload.mutate({
- data: { kind: "base64", data: bytes.toString("base64") },
- mediaType,
- originalFilename: filename,
- });
- ids.push(result.attachmentId);
+ try {
+ const bytes = readFileSync(path);
+ const result = await client.attachments.upload.mutate({
+ data: { kind: "base64", data: bytes.toString("base64") },
+ mediaType,
+ originalFilename: filename,
+ });
+ ids.push(result.attachmentId);
+ } catch (error) {
+ throw new CLIError(
+ `Failed to upload attachment: ${path}`,
+ error instanceof Error ? error.message : "Unknown attachment upload error",
+ );
+ }
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const bytes = readFileSync(path); | |
| const result = await client.attachments.upload.mutate({ | |
| data: { kind: "base64", data: bytes.toString("base64") }, | |
| mediaType, | |
| originalFilename: filename, | |
| }); | |
| try { | |
| const bytes = readFileSync(path); | |
| const result = await client.attachments.upload.mutate({ | |
| data: { kind: "base64", data: bytes.toString("base64") }, | |
| mediaType, | |
| originalFilename: filename, | |
| }); | |
| ids.push(result.attachmentId); | |
| } catch (error) { | |
| throw new CLIError( | |
| `Failed to upload attachment: ${path}`, | |
| error instanceof Error ? error.message : "Unknown attachment upload error", | |
| ); | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/cli/src/lib/upload-attachments.ts` around lines 28 - 33, Wrap the
file read and upload calls in a try/catch and rethrow failures as CLIError
annotated with the filename/path to provide actionable CLI output: around the
readFileSync(...) and client.attachments.upload.mutate(...) calls in
upload-attachments.ts, catch any error and throw new CLIError(`Failed to
read/upload attachment "${filename}" (path: ${path}): ${err.message}`, { cause:
err }) or similar so the original error is preserved; ensure you import/use
CLIError and reference the functions/variables bytes, readFileSync,
client.attachments.upload.mutate, filename and path in the error message.
| await ctx.api.chat.createSession.mutate({ | ||
| sessionId, | ||
| v2WorkspaceId: input.workspaceId, | ||
| }); | ||
|
|
||
| // Fire-and-forget the turn: the caller (CLI/SDK/MCP) gets the sessionId | ||
| // back as soon as the runtime is ready, instead of waiting ~30s for the | ||
| // assistant stream to finish. The renderer materializes the streaming | ||
| // state via `chat.getSnapshot` when a chat pane opens. | ||
| await ctx.runtime.chat.startTurn({ | ||
| sessionId, | ||
| workspaceId: input.workspaceId, | ||
| payload: { | ||
| content: input.prompt, | ||
| ...(files.length > 0 ? { files } : {}), | ||
| }, | ||
| }); |
There was a problem hiding this comment.
Rollback the persisted chat session when startup fails.
chat.createSession is committed before startTurn(). If runtime boot, model selection, or initial-turn setup throws here, the mutation returns an error but leaves a chat_sessions row behind, so retries create duplicate empty chats.
Suggested direction
await ctx.api.chat.createSession.mutate({
sessionId,
v2WorkspaceId: input.workspaceId,
});
- await ctx.runtime.chat.startTurn({
- sessionId,
- workspaceId: input.workspaceId,
- payload: {
- content: input.prompt,
- ...(files.length > 0 ? { files } : {}),
- },
- });
+ try {
+ await ctx.runtime.chat.startTurn({
+ sessionId,
+ workspaceId: input.workspaceId,
+ payload: {
+ content: input.prompt,
+ ...(files.length > 0 ? { files } : {}),
+ },
+ });
+ } catch (error) {
+ await ctx.api.chat.deleteSession.mutate({ sessionId });
+ throw error;
+ }If there is no delete mutation yet, this path should still roll back the row before returning the launch error.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| await ctx.api.chat.createSession.mutate({ | |
| sessionId, | |
| v2WorkspaceId: input.workspaceId, | |
| }); | |
| // Fire-and-forget the turn: the caller (CLI/SDK/MCP) gets the sessionId | |
| // back as soon as the runtime is ready, instead of waiting ~30s for the | |
| // assistant stream to finish. The renderer materializes the streaming | |
| // state via `chat.getSnapshot` when a chat pane opens. | |
| await ctx.runtime.chat.startTurn({ | |
| sessionId, | |
| workspaceId: input.workspaceId, | |
| payload: { | |
| content: input.prompt, | |
| ...(files.length > 0 ? { files } : {}), | |
| }, | |
| }); | |
| await ctx.api.chat.createSession.mutate({ | |
| sessionId, | |
| v2WorkspaceId: input.workspaceId, | |
| }); | |
| // Fire-and-forget the turn: the caller (CLI/SDK/MCP) gets the sessionId | |
| // back as soon as the runtime is ready, instead of waiting ~30s for the | |
| // assistant stream to finish. The renderer materializes the streaming | |
| // state via `chat.getSnapshot` when a chat pane opens. | |
| try { | |
| await ctx.runtime.chat.startTurn({ | |
| sessionId, | |
| workspaceId: input.workspaceId, | |
| payload: { | |
| content: input.prompt, | |
| ...(files.length > 0 ? { files } : {}), | |
| }, | |
| }); | |
| } catch (error) { | |
| await ctx.api.chat.deleteSession.mutate({ sessionId }); | |
| throw error; | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/host-service/src/trpc/router/agents/agents.ts` around lines 208 -
224, The createSession call (ctx.api.chat.createSession.mutate) is committed
before starting the runtime turn (ctx.runtime.chat.startTurn), so if startTurn
throws you must roll back the persisted session to avoid orphaned/duplicate
chat_sessions rows; wrap the startTurn invocation in a try/catch and on any
error call the session delete mutation (e.g., ctx.api.chat.deleteSession.mutate
or the existing delete API) with the same sessionId (and workspaceId) before
rethrowing the error, ensuring delete is awaited so the DB row is removed when
startup fails.
…rset - Drop ChatRuntimeManager.startTurn and the cold-start workarounds (peekRuntime, skeleton snapshot). runChatAgent now just void-fires chat.sendMessage and returns the sessionId. CLI agents run goes from ~18s to ~2s; runtime boot finishes async on the host. - Rename superset-chat -> superset (id, type literals, tests, mcp v1, icon map). Label "Superset Chat" -> "Superset" everywhere it appeared (mcp / cli / sdk / agent-card / orchestrator title). - Trim function-level JSDocs that restated the code.
There's exactly one chat builtin, so iterating BUILTIN_AGENT_DEFINITIONS to filter for chat-kinds is busywork — the id and label are known at the call site. Drops the catalog import + type predicate in CLI agents list, the v2 picker hook, and runAgentInWorkspace.
Restore the original MCP and SDK descriptions and just add `superset` to the e.g. lists rather than spelling out the chat-vs-terminal split in every docstring.
…/ MCP (#4116) * feat(agents): launch Superset Chat sessions from desktop / CLI / SDK / MCP Folds the built-in Superset Chat agent into the existing `agents` sugar so the same one-call surface spawns terminal *and* chat sessions across every entry point. Picker today shows only the host's terminal-config rows; submitting `superset-chat` 404s on the host because `runAgentInWorkspace` only knows how to spawn PTYs. Plumbing changes: - `agents.ts runAgentInWorkspace` branches on chat-builtin ids: mints a sessionId, registers the cloud `chat_sessions` row, and fires the first turn through `ChatRuntimeManager.startTurn`. Returns a `kind`-tagged result so callers materialize the right pane. - `ChatRuntimeManager.startTurn` boots the runtime and queues the message, but returns as soon as streaming begins instead of awaiting the assistant's full reply (~30s -> ~5-8s for headless callers). - `getSnapshot` cold-start fast path: returns an empty skeleton immediately when no runtime is in memory while creation runs in the background. Renderer's "Loading conversation..." clears on the first poll instead of hanging on the harness boot. - Host's API client carries the bound organizationId via x-superset-organization-id. The session-exchanged JWT only ships organizationIds[]; without the header protectedProcedure middleware can't resolve activeOrganizationId and any host->cloud call hits "No active organization selected". Surface updates: - Desktop v2 picker: new useV2AgentChoices hook overlays builtin chat agents on top of the host's terminal rows. Wired into the new workspace modal and the two task launch pickers. - appendLaunchesToPaneLayout seeds a kind: "chat" pane for chat results. - CLI workspaces create --agent --prompt --attachment and agents run --attachment accept local file paths and upload them to the host's attachment store before launch. - agents list includes Superset Chat. - SDK + MCP-v2 result types gain the kind discriminator. JSDoc and tool descriptions document superset-chat as a valid agent value. * refactor(agents): trim chat launch path, rename superset-chat -> superset - Drop ChatRuntimeManager.startTurn and the cold-start workarounds (peekRuntime, skeleton snapshot). runChatAgent now just void-fires chat.sendMessage and returns the sessionId. CLI agents run goes from ~18s to ~2s; runtime boot finishes async on the host. - Rename superset-chat -> superset (id, type literals, tests, mcp v1, icon map). Label "Superset Chat" -> "Superset" everywhere it appeared (mcp / cli / sdk / agent-card / orchestrator title). - Trim function-level JSDocs that restated the code. * refactor(agents): hardcode 'superset' instead of filtering catalog There's exactly one chat builtin, so iterating BUILTIN_AGENT_DEFINITIONS to filter for chat-kinds is busywork — the id and label are known at the call site. Drops the catalog import + type predicate in CLI agents list, the v2 picker hook, and runAgentInWorkspace. * docs(agents): trim verbose mcp / sdk descriptions Restore the original MCP and SDK descriptions and just add `superset` to the e.g. lists rather than spelling out the chat-vs-terminal split in every docstring. * docs(agents): trim fire-and-forget comment to just the WHY
Changes since v0.2.9: - New `superset` chat agent surfaced in `agents list`. (#4116) - `workspaces create` accepts `--agent`, `--prompt`, and `--attachment` to spawn a session at create-time. (#4116) - `agents run` accepts `--attachment` to upload local files alongside a prompt. (#4116) - `agents run --agent superset` returns immediately once streaming begins instead of waiting on the full assistant reply (~18s -> ~2s). (#4116) Push cli-v0.2.10 after this lands to fire the release pipeline.
Changes since v0.2.9: - New `superset` chat agent surfaced in `agents list`. (#4116) - `workspaces create` accepts `--agent`, `--prompt`, and `--attachment` to spawn a session at create-time. (#4116) - `agents run` accepts `--attachment` to upload local files alongside a prompt. (#4116) - `agents run --agent superset` returns immediately once streaming begins instead of waiting on the full assistant reply (~18s -> ~2s). (#4116) Push cli-v0.2.10 after this lands to fire the release pipeline.
npm has alpha.6 as the most recent published; alpha.7 was bumped in 71bf008 but never `npm publish`-ed. Skip alpha.7 on the registry and ship the current repo state as alpha.8. Changes since alpha.6: - workspaces.create adopts the canonical host-service shape (#3893) - automations.list accepts --name filter (#3952) - automations.prompt split into automations.prompt.get / .set (#3959) - agents.list (presets demoted to UI-only configuration) (#4097) - agents.run / workspaces.create gain `superset-chat` agent + `kind` discriminator on launch results (terminal vs chat) (#4116) - type adjustments for v2 workspace render path (#4141) - redact x-api-key in debug-log header dumps (#3956, was alpha.7) After merge: `cd packages/sdk && bun run build && cd dist && npm publish --access public`.
Summary
Folds the built-in Superset Chat agent into the existing
agentssugar so the same one-call surface spawns terminal and chat sessions across desktop, CLI, SDK, and MCP-v2. Before this PR, the v2 agent picker only showed the host's terminal-config rows, and even if you forcedsuperset-chatthrough, the host'srunAgentInWorkspaceonly knew how to spawn PTYs.Plumbing
runAgentInWorkspacebranches on chat-builtin ids: mints a sessionId, registers the cloudchat_sessionsrow, and fires the first turn throughChatRuntimeManager.startTurn. Returns akind-tagged result so callers materialize the right pane.ChatRuntimeManager.startTurnboots the runtime and queues the message, but returns as soon as streaming begins (agents.runfrom CLI/SDK/MCP no longer waits ~30s for the assistant's full reply).getSnapshotcold-start fast path returns an empty skeleton when no runtime is in memory while creation runs in the background — renderer's "Loading conversation..." clears on the first poll instead of perma-spinning on the harness boot.organizationIdviax-superset-organization-id. The session-exchanged JWT only shipsorganizationIds[]; without the headerprotectedProceduremiddleware can't resolveactiveOrganizationIdand any host→cloud call hits "No active organization selected".Surface updates
useV2AgentChoiceshook overlays builtin chat agents on top of the host's terminal rows. Wired into the new workspace modal,OpenInWorkspaceV2, andRunInWorkspacePopoverV2.appendLaunchesToPaneLayoutseeds akind: "chat"pane for chat results.workspaces create --agent --prompt --attachmentandagents run --attachmentaccept local file paths and upload them to the host's attachment store before launch.agents listincludes Superset Chat.kinddiscriminator. JSDoc and tool descriptions documentsuperset-chatas a valid agent value.Test plan
bun run lintcleanbun run typecheckcleanbun cli agents run --workspace <id> --agent superset-chat --prompt "..."returns{ kind: "chat", sessionId, label }end-to-endchat_sessionsrow inserted with correctv2WorkspaceIdandorganizationIdkind: "terminal"on the resultagents list --localshows Superset Chat alongside terminal rowsmcp__superset-v2__workspaces_createwithagents: [{ agent: "superset-chat", prompt: ... }](not exercised in this session)Summary by cubic
Launch Superset sessions via the
agentssurface across desktop, CLI, SDK, and MCP, returning akindso UIs open the right pane. Headless launches return faster, and host→cloud requests include the org header.New Features
supersetfrom desktop v2 picker (useV2AgentChoices), CLI (agents run,workspaces create --agent --prompt --attachment), SDK, and MCP;agents listnow includes Superset.--attachment; outputs includekind: "chat" | "terminal".appendLaunchesToPaneLayoutseeds akind: "chat"pane; chat tab title is “Superset”.Refactors
supersetid and “Superset” label (dropsuperset-chat).runAgentInWorkspacereturns{ kind, sessionId, label }and void‑fireschat.sendMessagefor Superset; terminal path unchanged.x-superset-organization-id; SDK/MCP result types includekind, and docs now listsupersetin examples.Written for commit b909aa3. Summary will update on new commits.
Summary by CodeRabbit
New Features
Improvements