Skip to content

feat: hand off artifact chats after AI artifact changes - #747

Merged
ilyabo merged 8 commits into
mainfrom
codex/artifact-chat-handoff
Jun 21, 2026
Merged

ilyabo merged 8 commits into
mainfrom
codex/artifact-chat-handoff

Conversation

@ilyabo

@ilyabo ilyabo commented Jun 21, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

  • Add a reusable ArtifactTargetChange metadata shape and include it in artifact create/select command results.
  • Pass AI session metadata through execute_command and add an AI-core onChatFinish hook for host-owned completed-turn follow-up behavior.
  • Add CLI artifact chat handoff middleware that forks the completed source chat, maps the fork to the new target artifact, updates run context, and keeps manual navigation/forks unchanged.
  • Add focused tests for command metadata propagation and completed-turn chat finish hooks.

Validation

  • corepack pnpm install --frozen-lockfile
  • corepack pnpm build
  • corepack pnpm --filter @sqlrooms/ai test -- commandTools.test.ts --runInBand
  • corepack pnpm --filter @sqlrooms/ai-core test -- AiSlice.onChatFinish.test.ts --runInBand
  • corepack pnpm --filter @sqlrooms/artifacts test -- artifactAiSessions.test.ts --runInBand
  • corepack pnpm --filter @sqlrooms/ai typecheck
  • corepack pnpm --filter @sqlrooms/ai-core typecheck
  • corepack pnpm --filter sqlrooms-cli-app build
  • Dev server smoke: Vite served http://127.0.0.1:4174/ with HTTP 200.

Note: the in-app browser connector failed with a session metadata error, so full UI automation was not available. Live AI handoff interaction also depends on configured model/API credentials.

Summary by CodeRabbit

  • New Features

    • AI chat now supports seamless transitions between artifacts during conversations. When creating or selecting artifacts, the system intelligently hands off the conversation to the target artifact while preserving chat context.
    • Added onChatFinish callback to enable app-level logic for handling completed AI turns, supporting artifact-scoped follow-up behaviors.
  • Documentation

    • Updated guides to document the new artifact chat handoff capability and onChatFinish configuration option.

@coderabbitai

coderabbitai Bot commented Jun 21, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@ilyabo, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 22 minutes and 49 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, the refill rate gradually slows as usage increases. The highest same-day bursts are limited more strictly.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: fbca7e7e-e808-4dab-8d10-7452877ff214

📥 Commits

Reviewing files that changed from the base of the PR and between aee4c91 and 583b57a.

📒 Files selected for processing (3)
  • apps/sqlrooms-cli-ui/src/artifactChatHandoff.ts
  • apps/sqlrooms-cli-ui/src/createDashboardCommands.ts
  • apps/sqlrooms-cli-ui/src/store.ts
📝 Walkthrough

Walkthrough

Introduces an artifact chat handoff system: new ArtifactTargetChange types signal artifact creation/selection in command results; the command tool forwards aiSessionId through invocation metadata; AiSlice gains an onChatFinish hook wired through chatTransport; and the CLI-UI createArtifactChatHandoffController validates handoff state, forks a new AI session, and routes it to the target artifact.

Changes

Artifact Chat Handoff Flow

Layer / File(s) Summary
ArtifactTargetChange type contract and docs
packages/artifacts/src/ai/artifactTargetChange.ts, packages/artifacts/src/ai/index.ts, packages/artifacts/README.md
Defines ArtifactTargetChange and ArtifactTargetChangeData types and re-exports them from the artifacts barrel. README documents the field and clarifies it does not automatically trigger chat changes.
Command responses enriched with artifactTargetChange
apps/sqlrooms-cli-ui/src/createDashboardCommands.ts, packages/documents/src/BlockDocumentCommands.ts, packages/documents/src/documentCommands.ts
Dashboard create, dashboard artifact create, artifact select, block document create, and document create commands extend their success data payload with an artifactTargetChange object describing the created or selected artifact.
Session ID forwarding through command tool
packages/ai/src/tools/commandTools.ts, packages/ai/__tests__/commandTools.test.ts, packages/ai/package.json
commandTools.ts adds CommandToolExecutionContext, updates execute to accept executionOptions, and passes aiSessionId into invokeCommand metadata. Test verifies propagation. Jest config updated for ESM module mocking.
onChatFinish extension point in ai-core
packages/ai-core/src/AiSlice.ts, packages/ai-core/src/chatTransport.ts, packages/ai-core/__tests__/AiSlice.onChatFinish.test.ts, packages/ai-core/README.md, packages/ai/README.md
AiSliceOptions gains an optional onChatFinish callback threaded through createChatHandlers into chatTransport, which invokes it after shouldEndAnalysis triggers. Tests cover invocation and aborted-turn guard. READMEs document the new option.
Artifact chat handoff controller and CLI-UI store wiring
apps/sqlrooms-cli-ui/src/artifactChatHandoff.ts, apps/sqlrooms-cli-ui/src/store.ts
createArtifactChatHandoffController validates artifactTargetChange payloads in command middleware and records pending handoffs; onChatFinish verifies the pending state and forks a new AI session onto the target artifact. Store wires both hooks into the room shell and AI slice configuration.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • sqlrooms/sqlrooms#716: Both PRs use the ability to fork an AI session from the last assistant message — the retrieved PR introduces that general forking capability, while this PR builds artifact handoff directly on top of it.

Suggested reviewers

  • dmitriy-kostianetskiy

🐇 A command whispers its artifact name,
The session hops and starts a new game.
Through middleware, metadata flows with care,
onChatFinish fires — a new session's there!
From document to dashboard, the rabbit leaps free,
Forking through context most cleverly. 🌟

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 13.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title directly summarizes the main change: implementing artifact chat handoff when AI commands change artifacts.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/artifact-chat-handoff

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@ilyabo
ilyabo marked this pull request as ready for review June 21, 2026 06:14

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9e867389c5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

return result;
}

pendingHandoff = {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Key pending artifact handoffs by session

When two artifact-scoped AI chats are running at the same time, the second command that reaches this assignment overwrites the first session's pending handoff before its onChatFinish fires. Since the app keeps providers mounted for every session.isRunning session, concurrent turns are supported; in that case the first session finishes, sees a pending handoff for a different sourceSessionId, and never forks its continuation chat. Store pending handoffs per source session/turn instead of a single shared slot.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: aee4c91f3f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

return result;
}

pendingHandoffs.set(sourceSessionId, {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Clear pending handoffs when a turn is aborted

When this records a handoff, the only cleanup is in the app onChatFinish callback, but the AI slice deliberately skips that callback for aborted turns and errors. If a user cancels or a turn fails after an artifact-select/create command, the stale entry remains; a later run of the same source session can fork into that old target whenever that artifact happens to be current, even if the later turn did not change artifacts. Clear the pending entry on abort/error or tie it to the specific turn/message before forking.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
apps/sqlrooms-cli-ui/src/artifactChatHandoff.ts (1)

64-178: ⚡ Quick win

Add TSDoc to exported function.

The exported createArtifactChatHandoffController function should have TSDoc documentation describing its purpose, parameters, and return value. As per coding guidelines, exported functions in TypeScript should include TSDoc.

📝 Suggested documentation
+/**
+ * Creates a chat handoff controller that automatically continues AI conversations
+ * when commands change the active artifact.
+ *
+ * `@param` store - The room Zustand store
+ * `@returns` An object containing commandMiddleware and onChatFinish hooks
+ */
 export function createArtifactChatHandoffController(
   store: StoreApi<RoomState>,
 ) {
🤖 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 `@apps/sqlrooms-cli-ui/src/artifactChatHandoff.ts` around lines 64 - 178, Add
TSDoc documentation to the `createArtifactChatHandoffController` exported
function by placing a JSDoc comment block above the function declaration. The
documentation should describe the function's purpose (that it creates a
controller for handling artifact chat handoffs), document the store parameter
and its type (StoreApi<RoomState>), and document the return value object
containing the commandMiddleware and onChatFinish properties with their types
and purposes.

Source: Coding guidelines

apps/sqlrooms-cli-ui/src/createDashboardCommands.ts (1)

134-160: ⚡ Quick win

Avoid redundant getState() call.

At line 152, getState() is called to retrieve the artifact title, but state was already obtained at line 136. Use state.artifacts.getArtifact(artifactId)?.title instead to avoid the redundant state getter invocation.

♻️ Suggested fix
         title:
-          getState().artifacts.getArtifact(artifactId)?.title ??
+          state.artifacts.getArtifact(artifactId)?.title ??
           title ??
           'Dashboard',
🤖 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 `@apps/sqlrooms-cli-ui/src/createDashboardCommands.ts` around lines 134 - 160,
In the execute function, there is a redundant call to getState() within the
return statement when retrieving the artifact title. The state variable has
already been captured at the beginning of the function. Replace the
getState().artifacts.getArtifact(artifactId)?.title expression in the title
field with state.artifacts.getArtifact(artifactId)?.title to use the
already-captured state variable instead of calling getState() again.
🤖 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 `@apps/sqlrooms-cli-ui/src/store.ts`:
- Around line 541-543: The `as any` cast on
`artifactChatHandoff.commandMiddleware` in the `createCommandProps` middleware
array is unnecessary and should be removed. Since `commandMiddleware` is already
properly typed as `RoomCommandMiddleware<RoomState>` in the artifactChatHandoff
definition, which matches the expected middleware type, TypeScript can correctly
validate the type without the cast. Simply remove the `as any` cast to let the
type system work as intended.

---

Nitpick comments:
In `@apps/sqlrooms-cli-ui/src/artifactChatHandoff.ts`:
- Around line 64-178: Add TSDoc documentation to the
`createArtifactChatHandoffController` exported function by placing a JSDoc
comment block above the function declaration. The documentation should describe
the function's purpose (that it creates a controller for handling artifact chat
handoffs), document the store parameter and its type (StoreApi<RoomState>), and
document the return value object containing the commandMiddleware and
onChatFinish properties with their types and purposes.

In `@apps/sqlrooms-cli-ui/src/createDashboardCommands.ts`:
- Around line 134-160: In the execute function, there is a redundant call to
getState() within the return statement when retrieving the artifact title. The
state variable has already been captured at the beginning of the function.
Replace the getState().artifacts.getArtifact(artifactId)?.title expression in
the title field with state.artifacts.getArtifact(artifactId)?.title to use the
already-captured state variable instead of calling getState() again.
🪄 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: 7d20b42d-1413-4cc1-bddd-ed3b37950daa

📥 Commits

Reviewing files that changed from the base of the PR and between 3b3bb0a and aee4c91.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (16)
  • apps/sqlrooms-cli-ui/src/artifactChatHandoff.ts
  • apps/sqlrooms-cli-ui/src/createDashboardCommands.ts
  • apps/sqlrooms-cli-ui/src/store.ts
  • packages/ai-core/README.md
  • packages/ai-core/__tests__/AiSlice.onChatFinish.test.ts
  • packages/ai-core/src/AiSlice.ts
  • packages/ai-core/src/chatTransport.ts
  • packages/ai/README.md
  • packages/ai/__tests__/commandTools.test.ts
  • packages/ai/package.json
  • packages/ai/src/tools/commandTools.ts
  • packages/artifacts/README.md
  • packages/artifacts/src/ai/artifactTargetChange.ts
  • packages/artifacts/src/ai/index.ts
  • packages/documents/src/BlockDocumentCommands.ts
  • packages/documents/src/documentCommands.ts

Comment thread apps/sqlrooms-cli-ui/src/store.ts

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1dbf69e603

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

sourceSessionId,
sourceArtifactId,
sourceUserMessageId: getLastUserMessage(
nextState.ai.getSession(sourceSessionId)?.uiMessages ?? [],

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Use an existing AI session lookup

When an AI execute_command creates or selects a different artifact, this line runs after the command has already switched artifacts, but AiSliceState does not expose getSession (sessions are stored under ai.config.sessions, with only getCurrentSession as a helper). In that scenario the middleware throws nextState.ai.getSession is not a function, so the tool call is reported as failed and no handoff is recorded even though the artifact change already happened.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant