/** * The `execute-action` wire contract. * * `execute-action` is the channel a rendered artifact uses to reach an * integration, and it used to accept arbitrary code — the same surface as * `execute`. That was wider than anything the shell could produce: artifact code * is purely declarative `tools.*`, and the shell's proxy serializes every call * into exactly one shape. * * Wider than necessary is also wider than safe. A hostile or confused iframe * could post arbitrary source down the app channel even though no affordance in * the shell ever writes any. So the server parses `execute-action` against the * one grammar the proxy emits: * * return await tools<segment>("<role>")?<segment>*(<JSON>) * * One awaited tool call, one JSON-literal argument, nothing else — no * statements, no loops, no composition. `execute` (the model-facing codemode * tool) is untouched; this constraint is only for the app-originated channel. * * The leading segment is an INTEGRATION, not a connection: artifact paths * carry no tier and no connection name (see `artifact-bindings.ts`). The * optional string call right after it is the integration ROLE, which is how an * artifact using two accounts of one integration says which it means. Both are * resolved to a full address server-side, against the bindings stored on the * artifact row — so what arrives here is never an address the iframe chose. * * The producing half is `toolCallCode` in * `@executor-js/mcp-apps-shell/shell/proxy`, and `tool-call-grammar.pin.test.ts` * in that package pins the two together so neither can drift. */ import { Option, Schema } from "effect"; const JSON_STRING_LITERAL = String.raw`"(?:[^"\\]|\\.)*"`;const IDENTIFIER = String.raw`[A-Za-z_$][\w$]*`;const SLUG = String.raw`[A-Za-z_$][\w$-]*`;const PATH_SEGMENT = String.raw`(?:\.${IDENTIFIER}|\["${SLUG}"\])`;const TOOL_CALL_CODE = new RegExp( String.raw`^return await tools(${PATH_SEGMENT})(?:\((${JSON_STRING_LITERAL})\))?((?:${PATH_SEGMENT})*)\((.*)\);?$`, "s",);const PATH_SEGMENT_MATCHER = new RegExp(String.raw`(?:\.(${IDENTIFIER})|\["(${SLUG})"\])`, "g"); /** The proxy's argument is always `JSON.stringify` output, so anything that * does not decode is, by construction, not something the proxy emitted. */const decodeArgs = Schema.decodeUnknownOption(Schema.fromJsonString(Schema.Unknown));const decodeRole = Schema.decodeUnknownOption(Schema.fromJsonString(Schema.String)); const decodePath = (serialized: string): readonly string[] => Array.from(serialized.matchAll(PATH_SEGMENT_MATCHER), (match) => match[1] ?? match[2] ?? ""); export type ParsedToolCall = { /** The path segments under `tools`, e.g. `["github", "issues", "create"]`. * The head is an integration slug (or a system-tool root); it is never a * tier or a connection name. */ readonly path: readonly string[]; /** The integration role, when the call was tagged with one. Absent means the * artifact uses a single account of that integration, whose role is the * integration slug itself. */ readonly role?: string; /** The single call argument, already JSON-decoded. */ readonly args: unknown;}; /** The message handed back to the iframe when its code is not a tool call. */export const TOOL_CALL_CONTRACT_MESSAGE = [ "execute-action accepts a single tool call, not arbitrary code.", 'The only accepted form is `return await tools<integration>("<role>")?<path>(<json>)` —', "exactly what the shell's `tools.*` proxy emits.", "Interactive UI reaches integrations declaratively:", "`tools.<integration>.<tool>.queryOptions(...)` / `.infiniteQueryOptions(...)` for reads,", "`.mutationOptions(...)` for writes.",].join(" "); /** * `null` when `code` is not the proxy's emission, otherwise the call it * describes. Callers only need the null check; the parsed value is returned * because it is free here and useful to log. */export const parseToolCallCode = (code: string): ParsedToolCall | null => { const match = TOOL_CALL_CODE.exec(code.trim()); if (!match) return null; const [, root, serializedRole, serializedRest, serializedArgs] = match; if (root === undefined || serializedRest === undefined || serializedArgs === undefined) return null; const args = decodeArgs(serializedArgs); if (Option.isNone(args)) return null; const path = decodePath(`${root}${serializedRest}`); if (serializedRole === undefined) return { path, args: args.value }; // The role is a JSON string literal for the same reason the args are a JSON // literal: it decodes or it was not the proxy's emission. const role = decodeRole(serializedRole); if (Option.isNone(role) || role.value.length === 0) return null; return { path, role: role.value, args: args.value };}; const TOOL_PATH_IDENTIFIER = /^[A-Za-z_$][\w$]*$/;const TOOL_PATH_SEGMENT = /^[A-Za-z_$][\w$-]*$/; const formatToolPathSegment = (segment: string): string => TOOL_PATH_IDENTIFIER.test(segment) ? `.${segment}` : `[${JSON.stringify(segment)}]`; /** * Build the codemode call for a RESOLVED address — the full * `tools.<integration>.<tier>.<connection>.<tool>` path, after bindings have * been applied. * * The app channel never executes the string the iframe sent. It parses that * string into a path, rewrites the path through the artifact's bindings, and * re-emits it here. So this is the only code `execute-action` ever runs, and it * is built from segments the server itself chose. * * Deliberately not shared with the shell's `toolCallCode`: that one produces the * unresolved short form for the wire, this one produces the resolved address for * the engine, and they sit on opposite sides of the trust boundary. The shell * package depends on this one, never the reverse. */export const formatToolCallCode = (path: readonly string[], args: unknown): string => { for (const segment of path) { if (!TOOL_PATH_SEGMENT.test(segment)) { // oxlint-disable-next-line executor/no-try-catch-or-throw, executor/no-error-constructor -- boundary: unreachable by construction (every segment came from a parsed path or a stored binding); a defect here must not become a malformed emission throw new Error("Invalid resolved tool path."); } } return `return await tools${path.map(formatToolPathSegment).join("")}(${JSON.stringify(args ?? {})})`;};
/**
* The `execute-action` wire contract.
*
* `execute-action` is the channel a rendered artifact uses to reach an
* integration, and it used to accept arbitrary code — the same surface as
* `execute`. That was wider than anything the shell could produce: artifact code
* is purely declarative `tools.*`, and the shell's proxy serializes every call
* into exactly one shape.
*
* Wider than necessary is also wider than safe. A hostile or confused iframe
* could post arbitrary source down the app channel even though no affordance in
* the shell ever writes any. So the server parses `execute-action` against the
* one grammar the proxy emits:
*
* return await tools<segment>("<role>")?<segment>*(<JSON>)
*
* One awaited tool call, one JSON-literal argument, nothing else — no
* statements, no loops, no composition. `execute` (the model-facing codemode
* tool) is untouched; this constraint is only for the app-originated channel.
*
* The leading segment is an INTEGRATION, not a connection: artifact paths
* carry no tier and no connection name (see `artifact-bindings.ts`). The
* optional string call right after it is the integration ROLE, which is how an
* artifact using two accounts of one integration says which it means. Both are
* resolved to a full address server-side, against the bindings stored on the
* artifact row — so what arrives here is never an address the iframe chose.
*
* The producing half is `toolCallCode` in
* `@executor-js/mcp-apps-shell/shell/proxy`, and `tool-call-grammar.pin.test.ts`
* in that package pins the two together so neither can drift.
*/
import { Option, Schema } from "effect";
const JSON_STRING_LITERAL = String.raw`"(?:[^"\\]|\\.)*"`;
const IDENTIFIER = String.raw`[A-Za-z_$][\w$]*`;
const SLUG = String.raw`[A-Za-z_$][\w$-]*`;
const PATH_SEGMENT = String.raw`(?:\.${IDENTIFIER}|\["${SLUG}"\])`;
const TOOL_CALL_CODE = new RegExp(
String.raw`^return await tools(${PATH_SEGMENT})(?:\((${JSON_STRING_LITERAL})\))?((?:${PATH_SEGMENT})*)\((.*)\);?$`,
"s",
);
const PATH_SEGMENT_MATCHER = new RegExp(String.raw`(?:\.(${IDENTIFIER})|\["(${SLUG})"\])`, "g");
/** The proxy's argument is always `JSON.stringify` output, so anything that
* does not decode is, by construction, not something the proxy emitted. */
const decodeArgs = Schema.decodeUnknownOption(Schema.fromJsonString(Schema.Unknown));
const decodeRole = Schema.decodeUnknownOption(Schema.fromJsonString(Schema.String));
const decodePath = (serialized: string): readonly string[] =>
Array.from(serialized.matchAll(PATH_SEGMENT_MATCHER), (match) => match[1] ?? match[2] ?? "");
export type ParsedToolCall = {
/** The path segments under `tools`, e.g. `["github", "issues", "create"]`.
* The head is an integration slug (or a system-tool root); it is never a
* tier or a connection name. */
readonly path: readonly string[];
/** The integration role, when the call was tagged with one. Absent means the
* artifact uses a single account of that integration, whose role is the
* integration slug itself. */
readonly role?: string;
/** The single call argument, already JSON-decoded. */
readonly args: unknown;
};
/** The message handed back to the iframe when its code is not a tool call. */
export const TOOL_CALL_CONTRACT_MESSAGE = [
"execute-action accepts a single tool call, not arbitrary code.",
'The only accepted form is `return await tools<integration>("<role>")?<path>(<json>)` —',
"exactly what the shell's `tools.*` proxy emits.",
"Interactive UI reaches integrations declaratively:",
"`tools.<integration>.<tool>.queryOptions(...)` / `.infiniteQueryOptions(...)` for reads,",
"`.mutationOptions(...)` for writes.",
].join(" ");
/**
* `null` when `code` is not the proxy's emission, otherwise the call it
* describes. Callers only need the null check; the parsed value is returned
* because it is free here and useful to log.
*/
export const parseToolCallCode = (code: string): ParsedToolCall | null => {
const match = TOOL_CALL_CODE.exec(code.trim());
if (!match) return null;
const [, root, serializedRole, serializedRest, serializedArgs] = match;
if (root === undefined || serializedRest === undefined || serializedArgs === undefined)
return null;
const args = decodeArgs(serializedArgs);
if (Option.isNone(args)) return null;
const path = decodePath(`${root}${serializedRest}`);
if (serializedRole === undefined) return { path, args: args.value };
// The role is a JSON string literal for the same reason the args are a JSON
// literal: it decodes or it was not the proxy's emission.
const role = decodeRole(serializedRole);
if (Option.isNone(role) || role.value.length === 0) return null;
return { path, role: role.value, args: args.value };
};
const TOOL_PATH_IDENTIFIER = /^[A-Za-z_$][\w$]*$/;
const TOOL_PATH_SEGMENT = /^[A-Za-z_$][\w$-]*$/;
const formatToolPathSegment = (segment: string): string =>
TOOL_PATH_IDENTIFIER.test(segment) ? `.${segment}` : `[${JSON.stringify(segment)}]`;
/**
* Build the codemode call for a RESOLVED address — the full
* `tools.<integration>.<tier>.<connection>.<tool>` path, after bindings have
* been applied.
*
* The app channel never executes the string the iframe sent. It parses that
* string into a path, rewrites the path through the artifact's bindings, and
* re-emits it here. So this is the only code `execute-action` ever runs, and it
* is built from segments the server itself chose.
*
* Deliberately not shared with the shell's `toolCallCode`: that one produces the
* unresolved short form for the wire, this one produces the resolved address for
* the engine, and they sit on opposite sides of the trust boundary. The shell
* package depends on this one, never the reverse.
*/
export const formatToolCallCode = (path: readonly string[], args: unknown): string => {
for (const segment of path) {
if (!TOOL_PATH_SEGMENT.test(segment)) {
// oxlint-disable-next-line executor/no-try-catch-or-throw, executor/no-error-constructor -- boundary: unreachable by construction (every segment came from a parsed path or a stored binding); a defect here must not become a malformed emission
throw new Error("Invalid resolved tool path.");
}
}
return `return await tools${path.map(formatToolPathSegment).join("")}(${JSON.stringify(args ?? {})})`;
};