119 lines · 5.6 KB · TypeScript
RawDownload
/** * 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.<ident>("<role>")?(.<ident>)*(<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 identifier 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 TOOL_CALL_CODE =  /^return await tools\.([A-Za-z_$][\w$]*)(?:\((("(?:[^"\\]|\\.)*"))\))?((?:\.[A-Za-z_$][\w$]*)*)\((.*)\);?$/s; /** 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)); export type ParsedToolCall = {  /** The dotted 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, , dottedRest, serializedArgs] = match;  if (root === undefined || dottedRest === undefined || serializedArgs === undefined) return null;   const args = decodeArgs(serializedArgs);  if (Option.isNone(args)) return null;   const rest = dottedRest.length > 0 ? dottedRest.slice(1).split(".") : [];  const path = [root, ...rest];   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_SEGMENT = /^[A-Za-z_$][\w$]*$/; /** * 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.join(".")}(${JSON.stringify(args ?? {})})`;};