Skip to content

feat: add v3 chat output listener API#1442

Open
yaa19212-droid wants to merge 2 commits into
kwaroran:mainfrom
yaa19212-droid:add-v3-chat-output-listener
Open

feat: add v3 chat output listener API#1442
yaa19212-droid wants to merge 2 commits into
kwaroran:mainfrom
yaa19212-droid:add-v3-chat-output-listener

Conversation

@yaa19212-droid
Copy link
Copy Markdown

@yaa19212-droid yaa19212-droid commented May 11, 2026

PR Checklist

  • Required Checks
    • Have you added type definitions?
    • Have you tested your changes?
    • Have you checked that it won't break any existing features?
  • If your PR uses models, check the following:
    • N/A - this PR does not modify model calls, prompting, or model selection.
  • If your PR is highly AI generated, check the following:
    • Have you understood what the code does?
    • Have you cleaned up any unnecessary or redundant code?
    • Is it not a huge change?

Summary

Adds addRisuChatListener('output', fn) and removeRisuChatListener('output', fn) for v3 plugins.

This lets plugins react after a model output has been committed to the chat, at the same lifecycle point as the existing Lua output trigger.

Related Issues

None.

Changes

  • Added a chatOutput listener set to the plugin API runtime.
  • Added addRisuChatListener and removeRisuChatListener.
  • Exposed the listener API through the v3 plugin API surface.
  • Fired the listener in both streaming and non-streaming output paths, immediately after the existing Lua output trigger.
  • Passed snapshots of the current character/chat plus characterIndex, chatIndex, and messageIndex to the listener.
  • Cleared registered chat output listeners when plugins reload.

Impact

Additive only. Existing plugins are unaffected unless they opt in to the new listener API.

This gives plugin authors a lifecycle point for starting background work after the main chat response is already committed. For example, a plugin can let the user read the generated message immediately, then update related chat data later through existing setter APIs when its background work completes.

The listener receives snapshots of the current character and chat, matching the existing v3 getCharacterFromIndex and getChatFromIndex behavior. Plugins that need to persist changes can use existing setter APIs such as setChatToIndex.

Additional Notes

Listener errors are caught and logged so one plugin listener cannot break the chat flow.

Testing

  • pnpm check
  • pnpm test

@yaa19212-droid yaa19212-droid changed the title Add v3 chat output listener API feat: add v3 chat output listener API May 17, 2026
Copy link
Copy Markdown
Collaborator

@cubicj cubicj left a comment

Choose a reason for hiding this comment

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

The listener API direction looks useful, but I found one streaming-path ordering issue.

In the streaming path, the listener runs before the host applies the inlay result and writes currentChat back to the DB. This means plugins receive the pre-inlay chat snapshot, and any changes persisted with setChatToIndex can be overwritten by that later host-side write.

Could you check the listener ordering there?

@yaa19212-droid yaa19212-droid force-pushed the add-v3-chat-output-listener branch 2 times, most recently from 684965e to cf3ec61 Compare June 2, 2026 06:44
@yaa19212-droid yaa19212-droid force-pushed the add-v3-chat-output-listener branch from cf3ec61 to cab7a8a Compare June 2, 2026 06:45
@yaa19212-droid
Copy link
Copy Markdown
Author

Thanks for the review. I rechecked the listener ordering in the streaming path and updated it in the latest commit.

I moved the v3 output listener in the streaming path to run after both the sync and async runInlayScreen inlay writes have been reflected in the chat. This means the listener now receives a post-inlay chat snapshot in the streaming path as well, and plugin changes saved with setChatToIndex inside the listener are no longer followed by the stale currentChat write from the inlay path.

The non-streaming path already ran the listener after inlay processing and the Lua output trigger, so I kept that flow unchanged. In both paths, the output message position is now resolved by chatId instead of assuming the last message index. If the message is removed during output processing, the listener still runs with messageIndex: -1.

I also updated the API docs to describe the new lifecycle, and wired v3 chat listeners to unregister automatically when the plugin unloads.

Verification:

  • pnpm check
  • pnpm test

Copy link
Copy Markdown
Collaborator

@cubicj cubicj left a comment

Choose a reason for hiding this comment

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

Verified the updated listener lifecycle. The streaming path now runs the listener after Lua output and sync/async inlay writes, so the previous stale write-back concern is addressed.

Check, tests, and build pass locally.

Approving — thanks for the contribution!

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.

2 participants