Files
vercel__chat/packages/chat/src/context.ts
Mukund Sarma 85e3d22ba1 fix(chat): follow-up hardening and docs for agent read-tool scoping (#774)
Follow-up hardening and updated docs for the agent read-tool scoping in
`createChatTools`.

## What changed

- Wrap the remaining dispatch paths (modal submit/close,
assistant-thread, assistant-context, app-home, app-context,
member-joined) in `runInConversation` so read tools built inside those
handlers inherit the active conversation.
- Log a warning when a read runs with no resolvable scope, instead of
failing open silently.
- Keep scoping channel-level by default; add opt-in `strictScope: true`
to confine a thread scope to that thread alone (rejects sibling threads
on per-thread-ACL platforms like Discord and GitHub).
- Update the AI SDK tools docs to cover the channel-level default, what
`scope` does and does not do, and the `strictScope` opt-in.

---------

Co-authored-by: dancer <josh@afterima.ge>
2026-08-03 13:50:05 +01:00

29 lines
1014 B
TypeScript

import { AsyncLocalStorage } from "node:async_hooks";
const conversationStorage = new AsyncLocalStorage<string>();
/**
* Run `fn` with `threadId` marked as the conversation currently being handled.
* Chat wraps handler dispatch in this so tools invoked inside a handler can
* tell which conversation they are serving.
*
* An `undefined` id runs `fn` bare, with no active conversation, the same state
* as running outside a handler. This lets dispatch paths whose conversation id
* is optional (e.g. a modal with no related thread or channel) call this
* unconditionally.
*/
export function runInConversation<T>(
threadId: string | undefined,
fn: () => T
): T {
return threadId === undefined ? fn() : conversationStorage.run(threadId, fn);
}
/**
* The conversation being handled on this async call stack, if any.
* Returns `undefined` outside a handler, such as in a cron job or a script.
*/
export function activeConversation(): string | undefined {
return conversationStorage.getStore();
}