/** * The `tools` proxy — the only object generated artifact code reaches an * integration through. * * It lives here, apart from the inner renderer, because it now has two callers * with the same shape and different transports: * * - the inner renderer, whose `request` posts the call to the parent frame and * settles when the shell answers; * - the create-time smoke render, whose `request` never settles at all, so * every query stays pending and the component renders its loading state. * * Sharing the proxy rather than writing a stub for the second caller is the * point. Generated code touches far more of this surface than `queryOptions` — * `.queryFilter()`, `.pathFilter()`, `.mutationOptions()` and the infinite * variants all run DURING render — so a hand-rolled stand-in would drift from * the real thing and the smoke render would start rejecting code that works, or * passing code that doesn't. One implementation, two transports. */ import { infiniteQueryOptions, mutationOptions, queryOptions, skipToken, type MutationKey, type QueryFilters, type QueryKey, type UseInfiniteQueryOptions, type UseMutationOptions, type UseQueryOptions,} from "@tanstack/react-query"; /** One tool call, as the proxy emits it. The transport decides what to do with * it; nothing here knows about frames or messages. */export type ToolCallRequest = { readonly path: readonly string[]; readonly args: readonly unknown[]; readonly role?: string;}; /** How a tool call is carried out. Returning a promise that never settles is a * legitimate implementation — it is what the smoke render uses to hold every * query in its pending state. */export type ToolCallTransport = (request: ToolCallRequest) => Promise<unknown>; /** * The role is part of every cache key, not just the wire call. * * `tools.linear("prod").issues.list` and `tools.linear("staging").issues.list` * share a path but not an account, so a key built from the path alone would * serve one role's rows to the other and make a mutation on one invalidate the * other. Undefined for the untagged single-account form, which keeps every key * an artifact wrote before roles existed byte-identical. */const toolScope = (path: readonly string[], role: string | undefined): QueryKey => role === undefined ? ["executor-tool", path] : ["executor-tool", path, { role }]; const toolQueryKey = ( path: readonly string[], role: string | undefined, input?: unknown,): QueryKey => [...toolScope(path, role), { input, type: "query" }]; const toolInfiniteQueryKey = ( path: readonly string[], role: string | undefined, input?: unknown,): QueryKey => [...toolScope(path, role), { input, type: "infinite" }]; const toolPathKey = (path: readonly string[], role: string | undefined): QueryKey => toolScope(path, role); const toolMutationKey = (path: readonly string[], role: string | undefined): MutationKey => [ ...toolScope(path, role), { type: "mutation" },]; const queryFilter = ( path: readonly string[], role: string | undefined, input?: unknown, filters?: Omit<QueryFilters, "queryKey">,): QueryFilters => ({ ...filters, queryKey: toolQueryKey(path, role, input),}); const pathFilter = ( path: readonly string[], role: string | undefined, filters?: Omit<QueryFilters, "queryKey">,): QueryFilters => ({ ...filters, queryKey: toolPathKey(path, role),}); /** * Where a page cursor lands inside a tool's input. * * A dotted path rather than a merge function, deliberately: it is data, so the * paging contract of an artifact can be read statically out of its source the * same way `tools.<integration>.<...>` paths are. Nested inputs — the common * OpenAPI `{ query: { … } }` shape — are reachable as `"query.cursor"`. */const DEFAULT_CURSOR_KEY = "cursor"; /** `input` with `pageParam` written at `cursorKey`, cloning only the spine it * touches so the caller's object is never mutated. A nullish page param writes * nothing at all, which is what makes `initialPageParam: null` mean "the first * request carries no cursor". */const withCursor = (input: unknown, cursorKey: string, pageParam: unknown): unknown => { const base = input === undefined ? {} : input; if (pageParam === null || pageParam === undefined) return base; // Recurse over the remaining segments rather than an index, so "the head // exists" is a fact about the array rather than something to assert. const assign = (target: unknown, [segment, ...rest]: readonly string[]): unknown => { if (segment === undefined) return pageParam; const record: Record<string, unknown> = typeof target === "object" && target !== null && !Array.isArray(target) ? { ...(target as Record<string, unknown>) } : {}; record[segment] = rest.length === 0 ? pageParam : assign(record[segment], rest); return record; }; return assign(base, cursorKey.split("."));}; /** The model-facing half of `.infiniteQueryOptions`: TanStack's own infinite * options (minus the two the proxy owns) plus the cursor placement. */type ToolInfiniteQueryOptions = Omit< UseInfiniteQueryOptions, "queryKey" | "queryFn" | "initialPageParam"> & { readonly initialPageParam?: unknown; readonly cursorKey?: string;}; /** * Build the `tools` proxy over a transport. * * A path under `tools` is an INTEGRATION followed by a tool path — * `tools.vercel.domains.getDomains` — with no tier and no connection name. The * account is chosen by the artifact's stored bindings, server-side. * * At integration depth the proxy is also callable with a string: that is the * ROLE form, `tools.linear("prod").issues.list`, for an artifact that uses two * accounts of one integration. The role rides with every request and every * cache key from that point down. Calling with anything else at that depth is * an ordinary tool invocation of a one-segment path, which is how the system * tools (`tools.search({...})`) are reached — so the two are told apart by the * argument, not by position: exactly one string argument tags a role. */export const createToolsProxy = (request: ToolCallTransport): Record<string, unknown> => { const nest = (path: string[], role: string | undefined): unknown => new Proxy(function () {}, { get(_target, key: string | symbol) { if (key === "then" || key === "toJSON" || key === Symbol.toPrimitive) return undefined; if (typeof key !== "string") return undefined; if (key === "queryOptions") { return (input?: unknown, options?: Omit<UseQueryOptions, "queryKey" | "queryFn">) => queryOptions({ ...options, queryKey: toolQueryKey(path, role, input === skipToken ? undefined : input), queryFn: input === skipToken ? skipToken : () => request({ path, args: [input ?? {}], ...(role === undefined ? {} : { role }), }), }); } if (key === "infiniteQueryOptions") { return (input?: unknown, options?: ToolInfiniteQueryOptions) => { const { cursorKey = DEFAULT_CURSOR_KEY, initialPageParam = null, ...rest } = options ?? {}; return infiniteQueryOptions({ ...rest, initialPageParam, queryKey: toolInfiniteQueryKey(path, role, input === skipToken ? undefined : input), queryFn: input === skipToken ? skipToken : ({ pageParam }: { pageParam: unknown }) => request({ path, args: [withCursor(input, cursorKey, pageParam)], ...(role === undefined ? {} : { role }), }), // oxlint-disable-next-line executor/no-double-cast -- boundary: TanStack's option types are generic over the page-param type the model supplies at runtime } as unknown as UseInfiniteQueryOptions); }; } if (key === "infiniteQueryKey") { return (input?: unknown) => toolInfiniteQueryKey(path, role, input); } if (key === "infiniteQueryFilter") { return (input?: unknown, filters?: Omit<QueryFilters, "queryKey">) => ({ ...filters, queryKey: toolInfiniteQueryKey(path, role, input), }); } if (key === "queryKey") { return (input?: unknown) => toolQueryKey(path, role, input); } if (key === "queryFilter") { return (input?: unknown, filters?: Omit<QueryFilters, "queryKey">) => queryFilter(path, role, input, filters); } if (key === "pathKey") { return () => toolPathKey(path, role); } if (key === "pathFilter") { return (filters?: Omit<QueryFilters, "queryKey">) => pathFilter(path, role, filters); } if (key === "mutationOptions") { return (options?: Omit<UseMutationOptions, "mutationKey" | "mutationFn">) => mutationOptions({ ...options, mutationKey: toolMutationKey(path, role), mutationFn: (input?: unknown) => request({ path, args: [input ?? {}], ...(role === undefined ? {} : { role }), }), }); } if (key === "mutationKey") { return () => toolMutationKey(path, role); } return nest([...path, key], role); }, apply(_target, _thisArg, args: unknown[]) { const [only] = args; if ( path.length === 1 && role === undefined && args.length === 1 && typeof only === "string" ) { return nest(path, only); } return request({ path, args, ...(role === undefined ? {} : { role }), }); }, }); return nest([], undefined) as Record<string, unknown>;};
/**
* The `tools` proxy — the only object generated artifact code reaches an
* integration through.
*
* It lives here, apart from the inner renderer, because it now has two callers
* with the same shape and different transports:
*
* - the inner renderer, whose `request` posts the call to the parent frame and
* settles when the shell answers;
* - the create-time smoke render, whose `request` never settles at all, so
* every query stays pending and the component renders its loading state.
*
* Sharing the proxy rather than writing a stub for the second caller is the
* point. Generated code touches far more of this surface than `queryOptions` —
* `.queryFilter()`, `.pathFilter()`, `.mutationOptions()` and the infinite
* variants all run DURING render — so a hand-rolled stand-in would drift from
* the real thing and the smoke render would start rejecting code that works, or
* passing code that doesn't. One implementation, two transports.
*/
import {
infiniteQueryOptions,
mutationOptions,
queryOptions,
skipToken,
type MutationKey,
type QueryFilters,
type QueryKey,
type UseInfiniteQueryOptions,
type UseMutationOptions,
type UseQueryOptions,
} from "@tanstack/react-query";
/** One tool call, as the proxy emits it. The transport decides what to do with
* it; nothing here knows about frames or messages. */
export type ToolCallRequest = {
readonly path: readonly string[];
readonly args: readonly unknown[];
readonly role?: string;
};
/** How a tool call is carried out. Returning a promise that never settles is a
* legitimate implementation — it is what the smoke render uses to hold every
* query in its pending state. */
export type ToolCallTransport = (request: ToolCallRequest) => Promise<unknown>;
/**
* The role is part of every cache key, not just the wire call.
*
* `tools.linear("prod").issues.list` and `tools.linear("staging").issues.list`
* share a path but not an account, so a key built from the path alone would
* serve one role's rows to the other and make a mutation on one invalidate the
* other. Undefined for the untagged single-account form, which keeps every key
* an artifact wrote before roles existed byte-identical.
*/
const toolScope = (path: readonly string[], role: string | undefined): QueryKey =>
role === undefined ? ["executor-tool", path] : ["executor-tool", path, { role }];
const toolQueryKey = (
path: readonly string[],
role: string | undefined,
input?: unknown,
): QueryKey => [...toolScope(path, role), { input, type: "query" }];
const toolInfiniteQueryKey = (
path: readonly string[],
role: string | undefined,
input?: unknown,
): QueryKey => [...toolScope(path, role), { input, type: "infinite" }];
const toolPathKey = (path: readonly string[], role: string | undefined): QueryKey =>
toolScope(path, role);
const toolMutationKey = (path: readonly string[], role: string | undefined): MutationKey => [
...toolScope(path, role),
{ type: "mutation" },
];
const queryFilter = (
path: readonly string[],
role: string | undefined,
input?: unknown,
filters?: Omit<QueryFilters, "queryKey">,
): QueryFilters => ({
...filters,
queryKey: toolQueryKey(path, role, input),
});
const pathFilter = (
path: readonly string[],
role: string | undefined,
filters?: Omit<QueryFilters, "queryKey">,
): QueryFilters => ({
...filters,
queryKey: toolPathKey(path, role),
});
/**
* Where a page cursor lands inside a tool's input.
*
* A dotted path rather than a merge function, deliberately: it is data, so the
* paging contract of an artifact can be read statically out of its source the
* same way `tools.<integration>.<...>` paths are. Nested inputs — the common
* OpenAPI `{ query: { … } }` shape — are reachable as `"query.cursor"`.
*/
const DEFAULT_CURSOR_KEY = "cursor";
/** `input` with `pageParam` written at `cursorKey`, cloning only the spine it
* touches so the caller's object is never mutated. A nullish page param writes
* nothing at all, which is what makes `initialPageParam: null` mean "the first
* request carries no cursor". */
const withCursor = (input: unknown, cursorKey: string, pageParam: unknown): unknown => {
const base = input === undefined ? {} : input;
if (pageParam === null || pageParam === undefined) return base;
// Recurse over the remaining segments rather than an index, so "the head
// exists" is a fact about the array rather than something to assert.
const assign = (target: unknown, [segment, ...rest]: readonly string[]): unknown => {
if (segment === undefined) return pageParam;
const record: Record<string, unknown> =
typeof target === "object" && target !== null && !Array.isArray(target)
? { ...(target as Record<string, unknown>) }
: {};
record[segment] = rest.length === 0 ? pageParam : assign(record[segment], rest);
return record;
};
return assign(base, cursorKey.split("."));
};
/** The model-facing half of `.infiniteQueryOptions`: TanStack's own infinite
* options (minus the two the proxy owns) plus the cursor placement. */
type ToolInfiniteQueryOptions = Omit<
UseInfiniteQueryOptions,
"queryKey" | "queryFn" | "initialPageParam"
> & {
readonly initialPageParam?: unknown;
readonly cursorKey?: string;
};
/**
* Build the `tools` proxy over a transport.
*
* A path under `tools` is an INTEGRATION followed by a tool path —
* `tools.vercel.domains.getDomains` — with no tier and no connection name. The
* account is chosen by the artifact's stored bindings, server-side.
*
* At integration depth the proxy is also callable with a string: that is the
* ROLE form, `tools.linear("prod").issues.list`, for an artifact that uses two
* accounts of one integration. The role rides with every request and every
* cache key from that point down. Calling with anything else at that depth is
* an ordinary tool invocation of a one-segment path, which is how the system
* tools (`tools.search({...})`) are reached — so the two are told apart by the
* argument, not by position: exactly one string argument tags a role.
*/
export const createToolsProxy = (request: ToolCallTransport): Record<string, unknown> => {
const nest = (path: string[], role: string | undefined): unknown =>
new Proxy(function () {}, {
get(_target, key: string | symbol) {
if (key === "then" || key === "toJSON" || key === Symbol.toPrimitive) return undefined;
if (typeof key !== "string") return undefined;
if (key === "queryOptions") {
return (input?: unknown, options?: Omit<UseQueryOptions, "queryKey" | "queryFn">) =>
queryOptions({
...options,
queryKey: toolQueryKey(path, role, input === skipToken ? undefined : input),
queryFn:
input === skipToken
? skipToken
: () =>
request({
path,
args: [input ?? {}],
...(role === undefined ? {} : { role }),
}),
});
}
if (key === "infiniteQueryOptions") {
return (input?: unknown, options?: ToolInfiniteQueryOptions) => {
const {
cursorKey = DEFAULT_CURSOR_KEY,
initialPageParam = null,
...rest
} = options ?? {};
return infiniteQueryOptions({
...rest,
initialPageParam,
queryKey: toolInfiniteQueryKey(path, role, input === skipToken ? undefined : input),
queryFn:
input === skipToken
? skipToken
: ({ pageParam }: { pageParam: unknown }) =>
request({
path,
args: [withCursor(input, cursorKey, pageParam)],
...(role === undefined ? {} : { role }),
}),
// oxlint-disable-next-line executor/no-double-cast -- boundary: TanStack's option types are generic over the page-param type the model supplies at runtime
} as unknown as UseInfiniteQueryOptions);
};
}
if (key === "infiniteQueryKey") {
return (input?: unknown) => toolInfiniteQueryKey(path, role, input);
}
if (key === "infiniteQueryFilter") {
return (input?: unknown, filters?: Omit<QueryFilters, "queryKey">) => ({
...filters,
queryKey: toolInfiniteQueryKey(path, role, input),
});
}
if (key === "queryKey") {
return (input?: unknown) => toolQueryKey(path, role, input);
}
if (key === "queryFilter") {
return (input?: unknown, filters?: Omit<QueryFilters, "queryKey">) =>
queryFilter(path, role, input, filters);
}
if (key === "pathKey") {
return () => toolPathKey(path, role);
}
if (key === "pathFilter") {
return (filters?: Omit<QueryFilters, "queryKey">) => pathFilter(path, role, filters);
}
if (key === "mutationOptions") {
return (options?: Omit<UseMutationOptions, "mutationKey" | "mutationFn">) =>
mutationOptions({
...options,
mutationKey: toolMutationKey(path, role),
mutationFn: (input?: unknown) =>
request({
path,
args: [input ?? {}],
...(role === undefined ? {} : { role }),
}),
});
}
if (key === "mutationKey") {
return () => toolMutationKey(path, role);
}
return nest([...path, key], role);
},
apply(_target, _thisArg, args: unknown[]) {
const [only] = args;
if (
path.length === 1 &&
role === undefined &&
args.length === 1 &&
typeof only === "string"
) {
return nest(path, only);
}
return request({
path,
args,
...(role === undefined ? {} : { role }),
});
},
});
return nest([], undefined) as Record<string, unknown>;
};