@superblocksteam/gateway 2.0.166 → 2.0.168-next.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/README.md +163 -28
  2. package/dist/agents/resolve-orchestrator-url.js +1 -2
  3. package/dist/agents/resolve-orchestrator-url.js.map +1 -1
  4. package/dist/capabilities/lifecycle.d.ts +3 -0
  5. package/dist/capabilities/lifecycle.js +330 -128
  6. package/dist/capabilities/lifecycle.js.map +1 -1
  7. package/dist/capabilities/live-state-attaches.d.ts +26 -0
  8. package/dist/capabilities/live-state-attaches.js +41 -0
  9. package/dist/capabilities/live-state-attaches.js.map +1 -0
  10. package/dist/capabilities/tool-permission-reply.d.ts +28 -0
  11. package/dist/capabilities/tool-permission-reply.js +55 -0
  12. package/dist/capabilities/tool-permission-reply.js.map +1 -0
  13. package/dist/capabilities/types.d.ts +19 -1
  14. package/dist/capabilities/types.js.map +1 -1
  15. package/dist/deadline.d.ts +6 -0
  16. package/dist/deadline.js +23 -0
  17. package/dist/deadline.js.map +1 -0
  18. package/dist/deps.d.ts +7 -0
  19. package/dist/health/readiness.js +1 -17
  20. package/dist/health/readiness.js.map +1 -1
  21. package/dist/integrations/filters.js +2 -2
  22. package/dist/integrations/filters.js.map +1 -1
  23. package/dist/orchestrator/client.d.ts +2 -1
  24. package/dist/orchestrator/client.js +4 -2
  25. package/dist/orchestrator/client.js.map +1 -1
  26. package/dist/sabs/app-state.d.ts +1 -0
  27. package/dist/sabs/app-state.js +9 -0
  28. package/dist/sabs/app-state.js.map +1 -1
  29. package/dist/sabs/awaited-decision.d.ts +24 -1
  30. package/dist/sabs/awaited-decision.js +40 -9
  31. package/dist/sabs/awaited-decision.js.map +1 -1
  32. package/dist/sabs/editor-client-methods.d.ts +21 -10
  33. package/dist/sabs/editor-client-methods.js +48 -24
  34. package/dist/sabs/editor-client-methods.js.map +1 -1
  35. package/dist/sabs/session-peer.d.ts +47 -14
  36. package/dist/sabs/session-peer.js.map +1 -1
  37. package/dist/sabs/turn-collector.d.ts +27 -1
  38. package/dist/sabs/turn-collector.js +38 -2
  39. package/dist/sabs/turn-collector.js.map +1 -1
  40. package/dist/sabs/websocket-session-peer.d.ts +27 -3
  41. package/dist/sabs/websocket-session-peer.js +466 -66
  42. package/dist/sabs/websocket-session-peer.js.map +1 -1
  43. package/dist/server/client.d.ts +3 -0
  44. package/dist/server/client.js +7 -3
  45. package/dist/server/client.js.map +1 -1
  46. package/dist/start.d.ts +13 -2
  47. package/dist/start.js +112 -12
  48. package/dist/start.js.map +1 -1
  49. package/dist/telemetry/metrics.js +8 -21
  50. package/dist/telemetry/metrics.js.map +1 -1
  51. package/dist/telemetry/runtime.d.ts +5 -0
  52. package/dist/telemetry/runtime.js +22 -8
  53. package/dist/telemetry/runtime.js.map +1 -1
  54. package/dist/transports/mcp/admin-tools.d.ts +2 -1
  55. package/dist/transports/mcp/admin-tools.js +19 -2
  56. package/dist/transports/mcp/admin-tools.js.map +1 -1
  57. package/dist/transports/mcp/client-presentation.d.ts +9 -1
  58. package/dist/transports/mcp/client-presentation.js +10 -0
  59. package/dist/transports/mcp/client-presentation.js.map +1 -1
  60. package/dist/transports/mcp/decision-card-html.d.ts +4 -2
  61. package/dist/transports/mcp/decision-card-html.js +107 -25
  62. package/dist/transports/mcp/decision-card-html.js.map +1 -1
  63. package/dist/transports/mcp/decision-elicitation.d.ts +3 -2
  64. package/dist/transports/mcp/decision-elicitation.js +67 -2
  65. package/dist/transports/mcp/decision-elicitation.js.map +1 -1
  66. package/dist/transports/mcp/format-tool-content.d.ts +7 -0
  67. package/dist/transports/mcp/format-tool-content.js +3 -3
  68. package/dist/transports/mcp/format-tool-content.js.map +1 -1
  69. package/dist/transports/mcp/instructions/result.js +1 -0
  70. package/dist/transports/mcp/instructions/result.js.map +1 -1
  71. package/dist/transports/mcp/instructions/tools/ask-user.d.ts +2 -1
  72. package/dist/transports/mcp/instructions/tools/ask-user.js +9 -2
  73. package/dist/transports/mcp/instructions/tools/ask-user.js.map +1 -1
  74. package/dist/transports/mcp/instructions/tools/copy.d.ts +5 -2
  75. package/dist/transports/mcp/instructions/tools/copy.js +20 -5
  76. package/dist/transports/mcp/instructions/tools/copy.js.map +1 -1
  77. package/dist/transports/mcp/instructions/tools/edit-app.d.ts +1 -1
  78. package/dist/transports/mcp/instructions/tools/edit-app.js +11 -1
  79. package/dist/transports/mcp/instructions/tools/edit-app.js.map +1 -1
  80. package/dist/transports/mcp/mount.d.ts +2 -3
  81. package/dist/transports/mcp/mount.js +395 -112
  82. package/dist/transports/mcp/mount.js.map +1 -1
  83. package/dist/transports/mcp/plan-approval.d.ts +23 -13
  84. package/dist/transports/mcp/plan-approval.js +39 -26
  85. package/dist/transports/mcp/plan-approval.js.map +1 -1
  86. package/package.json +5 -5
@@ -1 +1 @@
1
- {"version":3,"file":"admin-tools.js","sourceRoot":"","sources":["../../../src/transports/mcp/admin-tools.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"admin-tools.js","sourceRoot":"","sources":["../../../src/transports/mcp/admin-tools.ts"],"names":[],"mappings":"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"}
@@ -7,10 +7,18 @@ export type ClientPresentationCapabilities = {
7
7
  hasNativeAskForm: boolean;
8
8
  hasNativeBrowser: boolean;
9
9
  };
10
- /** Presentation surfaces plus the one capability MCP does advertise. */
10
+ /** Presentation surfaces plus the capabilities MCP does advertise. */
11
11
  export type ClientProfile = ClientPresentationCapabilities & {
12
+ /** Renders the MCP Apps decision card, whose confirm step attests an approval. */
13
+ supportsDecisionCard: boolean;
12
14
  supportsFormElicitation: boolean;
13
15
  };
16
+ /**
17
+ * Whether the user can answer a decision through a surface the Gateway trusts
18
+ * as theirs: Cowork's native form, the decision card, or form elicitation. An
19
+ * approval the agent sends on its own is never accepted.
20
+ */
21
+ export declare function collectsUserApproval(profile: ClientProfile): boolean;
14
22
  export declare function clientPresentationCapabilities(client: {
15
23
  name: string;
16
24
  } | undefined): ClientPresentationCapabilities;
@@ -2,6 +2,16 @@
2
2
  * What surfaces a host has. MCP advertises no capability for a host's own
3
3
  * browser or ask/choice UI, so `clientInfo.name` is the only signal.
4
4
  */
5
+ /**
6
+ * Whether the user can answer a decision through a surface the Gateway trusts
7
+ * as theirs: Cowork's native form, the decision card, or form elicitation. An
8
+ * approval the agent sends on its own is never accepted.
9
+ */
10
+ export function collectsUserApproval(profile) {
11
+ return (profile.hasNativeAskForm ||
12
+ profile.supportsDecisionCard ||
13
+ profile.supportsFormElicitation);
14
+ }
5
15
  export function clientPresentationCapabilities(client) {
6
16
  const isCowork = client?.name === "local-agent-mode-superblocks" ||
7
17
  client?.name.startsWith("local-agent-mode-plugin:") === true;
@@ -1 +1 @@
1
- {"version":3,"file":"client-presentation.js","sourceRoot":"","sources":["../../../src/transports/mcp/client-presentation.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAaH,MAAM,UAAU,8BAA8B,CAC5C,MAAoC;IAEpC,MAAM,QAAQ,GACZ,MAAM,EAAE,IAAI,KAAK,8BAA8B;QAC/C,MAAM,EAAE,IAAI,CAAC,UAAU,CAAC,0BAA0B,CAAC,KAAK,IAAI,CAAC;IAC/D,OAAO;QACL,gBAAgB,EAAE,QAAQ;QAC1B,gBAAgB,EAAE,QAAQ;KAC3B,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"client-presentation.js","sourceRoot":"","sources":["../../../src/transports/mcp/client-presentation.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAeH;;;;GAIG;AACH,MAAM,UAAU,oBAAoB,CAAC,OAAsB;IACzD,OAAO,CACL,OAAO,CAAC,gBAAgB;QACxB,OAAO,CAAC,oBAAoB;QAC5B,OAAO,CAAC,uBAAuB,CAChC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,8BAA8B,CAC5C,MAAoC;IAEpC,MAAM,QAAQ,GACZ,MAAM,EAAE,IAAI,KAAK,8BAA8B;QAC/C,MAAM,EAAE,IAAI,CAAC,UAAU,CAAC,0BAA0B,CAAC,KAAK,IAAI,CAAC;IAC/D,OAAO;QACL,gBAAgB,EAAE,QAAQ;QAC1B,gBAAgB,EAAE,QAAQ;KAC3B,CAAC;AACJ,CAAC"}
@@ -21,6 +21,8 @@
21
21
  * resource URI when the HTML changes so Claude Desktop refreshes its cache.
22
22
  */
23
23
  export declare const PLAN_APPROVAL_TOKEN_META_KEY = "superblocks/plan-approval-token";
24
+ /** The plan with its `@Name[id=...]` mentions, kept out of model-visible fields. */
25
+ export declare const PLAN_MARKUP_META_KEY = "superblocks/plan-markup";
24
26
  export declare const CONFIRM_PLAN_APPROVAL_TOOL = "confirm_plan_approval";
25
- export declare const APP_DECISION_RESOURCE_URI = "ui://gateway/decision-v20.html";
26
- export declare const APP_DECISION_HTML = "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n <title>Superblocks needs an answer</title>\n <style>\n h1 {\n margin: 0;\n font-size: var(--font-text-md-size, 14px);\n font-weight: var(--font-weight-semibold, 600);\n }\n .meta {\n color: var(--sb-app-text-tertiary);\n font-size: var(--font-text-xs-size, 12px);\n }\n .reply {\n display: none;\n color: var(--sb-app-text-secondary);\n }\n .reply.shown {\n display: block;\n }\n .plan {\n display: none;\n max-height: 340px;\n margin: 0;\n overflow: auto;\n padding: 10px;\n border: var(--border-width-regular, 1px) solid var(--sb-app-border);\n border-radius: var(--border-radius-md, 8px);\n background: var(--sb-app-background-subtle);\n font-size: var(--font-text-xs-size, 12px);\n word-break: break-word;\n }\n .plan.shown {\n display: block;\n }\n .plan > :first-child,\n .question > :first-child {\n margin-top: 0;\n }\n .plan > :last-child,\n .question > :last-child {\n margin-bottom: 0;\n }\n .plan h1,\n .plan h2,\n .plan h3,\n .plan p,\n .question h1,\n .question h2,\n .question h3,\n .question p {\n margin: 10px 0;\n }\n .plan h1,\n .plan h2,\n .plan h3,\n .question h1,\n .question h2,\n .question h3 {\n font-size: inherit;\n font-weight: var(--font-weight-semibold, 600);\n }\n .plan ul,\n .plan ol,\n .question ul,\n .question ol {\n margin: 10px 0;\n padding-left: 20px;\n }\n .plan blockquote,\n .question blockquote {\n margin: 10px 0;\n padding-left: 10px;\n border-left: 3px solid var(--sb-app-border);\n color: var(--sb-app-text-secondary);\n }\n .plan pre,\n .question pre {\n overflow: auto;\n padding: 8px;\n border-radius: var(--border-radius-md, 8px);\n background: var(--sb-app-background);\n }\n .plan a,\n .question a {\n color: var(--sb-app-info-text);\n }\n .entity-tag {\n display: inline-flex;\n align-items: center;\n padding: 0 4px;\n max-width: 100%;\n overflow-wrap: anywhere;\n border: var(--border-width-regular, 1px) solid var(--sb-app-border);\n border-radius: 4px;\n background: var(--sb-app-background);\n color: var(--sb-app-info-text);\n font-weight: 400;\n line-height: 18px;\n vertical-align: middle;\n white-space: normal;\n }\n .task {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n }\n .task input {\n margin: 0;\n }\n .plan code,\n .question code,\n button code {\n font-family: var(--font-mono, ui-monospace, SFMono-Regular, monospace);\n }\n .question {\n display: none;\n overflow-wrap: anywhere;\n font-weight: var(--font-weight-medium, 500);\n }\n .question.shown {\n display: block;\n }\n .actions {\n display: none;\n flex-wrap: wrap;\n gap: 8px;\n }\n .actions.shown {\n display: flex;\n }\n #options {\n flex-direction: column;\n flex-wrap: nowrap;\n }\n #options > .option-button {\n width: 100%;\n overflow-wrap: anywhere;\n text-align: left;\n }\n .refine {\n display: none;\n flex-direction: column;\n gap: 8px;\n }\n .refine.shown {\n display: flex;\n }\n textarea {\n width: 100%;\n box-sizing: border-box;\n min-height: 64px;\n padding: 8px;\n border: var(--border-width-regular, 1px) solid var(--sb-app-border);\n border-radius: var(--border-radius-md, 8px);\n background: var(--sb-app-background);\n color: var(--sb-app-text);\n font: inherit;\n resize: vertical;\n }\n .note {\n color: var(--sb-app-text-secondary);\n font-size: var(--font-text-xs-size, 12px);\n }\n\n :root {\n color-scheme: light dark;\n --sb-app-background: var(\n --color-background-primary,\n light-dark(#ffffff, #1b1c1f)\n );\n --sb-app-background-subtle: var(\n --color-background-secondary,\n light-dark(#f9fafb, #24272b)\n );\n --sb-app-background-hover: var(\n --color-background-tertiary,\n light-dark(#f3f4f6, #2f3437)\n );\n --sb-app-background-inverse: var(\n --color-background-inverse,\n light-dark(#121517, #f9fafb)\n );\n --sb-app-text: var(\n --color-text-primary,\n light-dark(#121517, #f9fafb)\n );\n --sb-app-text-secondary: var(\n --color-text-secondary,\n light-dark(#5a6272, #c6cad2)\n );\n --sb-app-text-tertiary: var(\n --color-text-tertiary,\n light-dark(#6b7280, #a4aab7)\n );\n --sb-app-text-inverse: var(\n --color-text-inverse,\n light-dark(#ffffff, #121517)\n );\n --sb-app-border: var(\n --color-border-primary,\n light-dark(#e8eaed, #454d5f)\n );\n --sb-app-ring: var(\n --color-ring-primary,\n light-dark(#0077b6, #27bbff)\n );\n --sb-app-info-background: var(\n --color-background-info,\n light-dark(#e6f5ff, #083a50)\n );\n --sb-app-info-text: var(\n --color-text-info,\n light-dark(#00679e, #7dd3fc)\n );\n --sb-app-info-border: var(\n --color-border-info,\n light-dark(#c2e7ff, #075985)\n );\n --sb-app-success-background: var(\n --color-background-success,\n light-dark(#e8f8ef, #0a3926)\n );\n --sb-app-success-text: var(\n --color-text-success,\n light-dark(#087a43, #6ee7b7)\n );\n --sb-app-success-border: var(\n --color-border-success,\n light-dark(#b8e8cf, #087a43)\n );\n --sb-app-warning-background: var(\n --color-background-warning,\n light-dark(#fff7e6, #49350a)\n );\n --sb-app-warning-text: var(\n --color-text-warning,\n light-dark(#946200, #fcd34d)\n );\n --sb-app-warning-border: var(\n --color-border-warning,\n light-dark(#f5d996, #946200)\n );\n --sb-app-danger-background: var(\n --color-background-danger,\n light-dark(#fff1f1, #4a1d1d)\n );\n --sb-app-danger-text: var(\n --color-text-danger,\n light-dark(#c54141, #fca5a5)\n );\n --sb-app-danger-border: var(\n --color-border-danger,\n light-dark(#ffcaca, #c54141)\n );\n }\n * {\n box-sizing: border-box;\n }\n body {\n min-width: 0;\n margin: 0;\n overflow-x: hidden;\n background: transparent;\n color: var(--sb-app-text);\n font-family: var(--font-sans, system-ui, -apple-system, sans-serif);\n font-size: var(--font-text-sm-size, 13px);\n line-height: var(--font-text-sm-line-height, 1.45);\n }\n .card {\n display: flex;\n min-width: 0;\n flex-direction: column;\n gap: 12px;\n padding: 16px;\n border: var(--border-width-regular, 1px) solid var(--sb-app-border);\n border-radius: var(--border-radius-lg, 12px);\n background: var(--sb-app-background);\n box-shadow: var(--shadow-sm, 0 1px 2px rgba(18, 21, 23, 0.08));\n }\n h1,\n .meta,\n .note {\n min-width: 0;\n overflow-wrap: anywhere;\n }\n .note:empty {\n position: absolute;\n }\n button,\n textarea {\n font: inherit;\n }\n button {\n appearance: none;\n min-width: 0;\n padding: 8px 12px;\n border: var(--border-width-regular, 1px) solid var(--sb-app-border);\n border-radius: var(--border-radius-md, 8px);\n background: var(--sb-app-background-subtle);\n color: var(--sb-app-text);\n font-weight: var(--font-weight-medium, 500);\n line-height: var(--font-text-sm-line-height, 1.45);\n cursor: pointer;\n }\n button:hover:not(:disabled) {\n background: var(--sb-app-background-hover);\n }\n .button-primary {\n border-color: var(--sb-app-background-inverse);\n background: var(--sb-app-background-inverse);\n color: var(--sb-app-text-inverse);\n }\n .button-primary:hover:not(:disabled) {\n border-color: var(--sb-app-background-inverse);\n background: var(--sb-app-background-inverse);\n }\n button:focus-visible,\n textarea:focus-visible,\n a:focus-visible {\n outline: 2px solid var(--sb-app-ring);\n outline-offset: 2px;\n }\n button:disabled {\n opacity: 0.48;\n cursor: not-allowed;\n }\n .actions {\n min-width: 0;\n }\n .actions > button {\n overflow-wrap: anywhere;\n }\n @media (max-width: 360px) {\n .actions {\n flex-direction: column;\n flex-wrap: nowrap;\n }\n .actions > button {\n width: 100%;\n }\n }\n\n </style>\n</head>\n<body>\n <div class=\"card\">\n <h1 id=\"heading\">Superblocks</h1>\n <div id=\"appId\" class=\"meta\"></div>\n <div id=\"reply\" class=\"reply\"></div>\n <div id=\"plan\" class=\"plan\"></div>\n <div id=\"question\" class=\"question\"></div>\n <div id=\"options\" class=\"actions\"></div>\n <div id=\"planActions\" class=\"actions\">\n <button id=\"approve\" class=\"button-primary\" type=\"button\">Build it</button>\n <button id=\"refine\" class=\"button-secondary\" type=\"button\">Change something</button>\n </div>\n <div id=\"refineBox\" class=\"refine\">\n <textarea id=\"feedback\" placeholder=\"What should change?\"></textarea>\n <button id=\"sendRefine\" class=\"button-primary\" type=\"button\">Send</button>\n </div>\n <div id=\"note\" class=\"note\" aria-live=\"polite\"></div>\n </div>\n <script>\n (() => {\n const PROTOCOL_VERSION = \"2026-01-26\";\n let nextId = 1;\n const pending = new Map();\n let applicationId = \"\";\n let appName = \"\";\n let reply = \"\";\n let decision = null;\n let status = \"\";\n let refining = false;\n let sending = false;\n let answered = false;\n let approvalToken = \"\";\n let optionButtons = [];\n let lastHeight = 0;\n\n function post(message) {\n window.parent.postMessage(message, \"*\");\n }\n\n function notify(method, params) {\n post({ jsonrpc: \"2.0\", method, params });\n }\n\n function request(method, params) {\n const id = nextId++;\n return new Promise((resolve, reject) => {\n pending.set(id, { resolve, reject });\n post({ jsonrpc: \"2.0\", id, method, params });\n });\n }\n\n function element(id) {\n return document.getElementById(id);\n }\n\n function show(id, on) {\n const node = element(id);\n if (node && node.classList) {\n node.classList.toggle(\"shown\", on);\n }\n }\n\n // Hosts size the card from this notification alone; without it the card\n // is laid out at zero height and never appears.\n function reportSize() {\n const height = Math.ceil(document.documentElement.scrollHeight || 0);\n if (height <= 0 || height === lastHeight) {\n return;\n }\n lastHeight = height;\n notify(\"ui/notifications/size-changed\", {\n height,\n width: Math.ceil(window.innerWidth || 0),\n });\n }\n\n function note(text) {\n element(\"note\").textContent = text;\n show(\"note\", Boolean(text));\n reportSize();\n }\n\n function applyHostContext(context) {\n if (!context || typeof context !== \"object\") {\n return;\n }\n const root = document.documentElement;\n if (context.theme === \"light\" || context.theme === \"dark\") {\n root.setAttribute(\"data-theme\", context.theme);\n root.style.colorScheme = context.theme;\n }\n const variables = context.styles && context.styles.variables;\n if (variables && root.style) {\n for (const name of Object.keys(variables)) {\n const value = variables[name];\n if (typeof value === \"string\") {\n root.style.setProperty(name, value);\n }\n }\n }\n const fonts = context.styles && context.styles.css?.fonts;\n if (typeof fonts === \"string\") {\n let fontStyle = document.getElementById(\"__mcp-host-fonts\");\n if (!fontStyle) {\n fontStyle = document.createElement(\"style\");\n fontStyle.id = \"__mcp-host-fonts\";\n document.head.appendChild(fontStyle);\n }\n fontStyle.textContent = fonts;\n }\n reportSize();\n }\n\n function setBusy(on) {\n element(\"approve\").disabled = on;\n element(\"refine\").disabled = on;\n element(\"sendRefine\").disabled = on;\n for (const button of optionButtons) {\n button.disabled = on;\n }\n }\n\n /**\n * Posts the answer as the user's own turn.\n *\n * Not tools/call: calling edit_app from in here starts a build the host\n * agent never learns about, so nothing polls it and nothing is relayed.\n */\n function answer(text) {\n if (!text || sending || answered) {\n return;\n }\n sending = true;\n setBusy(true);\n note(\"Sending your answer\u2026\");\n request(\"ui/message\", {\n content: [{ text: text, type: \"text\" }],\n role: \"user\",\n })\n .then((result) => {\n sending = false;\n if (result && result.isError) {\n refused();\n return;\n }\n answered = true;\n setBusy(true);\n // The host taking the message is not the same as the user's turn\n // going anywhere: Claude Desktop leaves it in the composer as a\n // draft. Claiming it was sent strands the user on a build that is\n // waiting for their enter key.\n note(\"Your answer is in the chat \u2014 press enter there to send it.\");\n })\n .catch(refused);\n }\n\n function refused() {\n sending = false;\n setBusy(false);\n note(\n \"This host would not take your answer from the card \u2014 reply in the chat instead.\",\n );\n }\n\n function approvalRefused() {\n sending = false;\n setBusy(false);\n note(\n \"This host could not verify your approval - approve this plan in the Superblocks editor instead.\",\n );\n }\n\n function approve() {\n if (sending || answered) {\n return;\n }\n if (!approvalToken) {\n approvalRefused();\n return;\n }\n sending = true;\n setBusy(true);\n note(\"Confirming your approval...\");\n request(\"tools/call\", {\n arguments: { approvalToken: approvalToken },\n name: \"confirm_plan_approval\",\n })\n .then((result) => {\n sending = false;\n if (result && result.isError) {\n approvalRefused();\n return;\n }\n answer(\"Build it \u2014 approve the Superblocks plan as written.\");\n })\n .catch(approvalRefused);\n }\n\n function renderOptions(options) {\n const host = element(\"options\");\n host.replaceChildren();\n optionButtons = [];\n options.forEach((option, index) => {\n const button = document.createElement(\"button\");\n button.id = \"option-\" + index;\n button.className = \"button-secondary option-button\";\n button.type = \"button\";\n button.innerHTML = markdownInline(option, false);\n // Verbatim: an option Superblocks did not offer reaches it as a\n // prompt answering a question it never asked.\n button.addEventListener(\"click\", () => answer(option));\n host.appendChild(button);\n optionButtons.push(button);\n });\n show(\"options\", options.length > 0);\n }\n\n function waitingNote() {\n if (status === \"building\") {\n return \"Superblocks is still building \u2014 nothing to answer yet.\";\n }\n // Something opened this card, so \"nothing is waiting on you\" is a dead\n // end: say where the answer to that actually is.\n return \"Nothing to answer here \u2014 the chat has the latest.\";\n }\n\n function escapeHtml(text) {\n return String(text).replace(/[&<>\"']/g, (character) => {\n return {\n \"&\": \"&amp;\",\n \"<\": \"&lt;\",\n \">\": \"&gt;\",\n '\"': \"&quot;\",\n \"'\": \"&#39;\",\n }[character];\n });\n }\n\n function markdownInline(text, allowLinks = true) {\n const codeDelimiter = String.fromCharCode(96);\n const emphasis = (text) =>\n text\n .replace(/\\*\\*([^*]+)\\*\\*/g, \"<strong>$1</strong>\")\n .replace(/__([^_]+)__/g, \"<strong>$1</strong>\")\n .replace(/(^|[^*])\\*([^*]+)\\*(?!\\*)/g, \"$1<em>$2</em>\")\n .replace(\n /(^|[^A-Za-z0-9_])_([^_]+)_(?![A-Za-z0-9_])/g,\n \"$1<em>$2</em>\",\n );\n const safeLink = (url) => {\n try {\n const parsed = new URL(url);\n return parsed.protocol === \"https:\" && Boolean(parsed.hostname);\n } catch {\n return false;\n }\n };\n const entities = /@((?:(?!\\[id=)[^@\\r\\n])+?(?<!\\s))\\[id=[^\\]\\r\\n]+\\]/gu;\n const links = /\\[([^\\]]+)\\]\\(([^\\s)]+)\\)/g;\n const renderLinks = (value, allowLinks) => {\n const output = [];\n let offset = 0;\n const text = String(value);\n for (const match of text.matchAll(links)) {\n const index = match.index ?? 0;\n if (text[index - 1] === \"!\") {\n continue;\n }\n output.push(emphasis(escapeHtml(text.slice(offset, index))));\n const label = match[1];\n const url = match[2];\n if (safeLink(url)) {\n output.push(\n allowLinks\n ? '<a href=\"' +\n escapeHtml(url) +\n '\" target=\"_blank\" rel=\"noopener noreferrer\">' +\n markdownInline(label, false) +\n \"</a>\"\n : markdownInline(label, false),\n );\n } else {\n output.push(emphasis(escapeHtml(match[0])));\n }\n offset = index + match[0].length;\n }\n output.push(emphasis(escapeHtml(text.slice(offset))));\n return output.join(\"\");\n };\n const renderText = (value, allowLinks) => {\n const text = String(value);\n let placeholderDelimiter = String.fromCharCode(0);\n while (text.includes(placeholderDelimiter)) {\n placeholderDelimiter += String.fromCharCode(0);\n }\n const linkDestinations = Array.from(text.matchAll(links), (match) => {\n const start = (match.index ?? 0) + match[1].length + 3;\n return { start, end: start + match[2].length };\n });\n const entityTags = [];\n const tokenized = text.replace(entities, (match, label, index) => {\n if (\n linkDestinations.some(\n ({ start, end }) => index >= start && index < end,\n )\n ) {\n return match;\n }\n const placeholder =\n placeholderDelimiter + entityTags.length + placeholderDelimiter;\n entityTags.push(\n '<span class=\"entity-tag\">' + escapeHtml(label) + \"</span>\",\n );\n return placeholder;\n });\n return entityTags.reduce(\n (html, tag, index) =>\n html.replace(\n placeholderDelimiter + index + placeholderDelimiter,\n () => tag,\n ),\n renderLinks(tokenized, allowLinks),\n );\n };\n return String(text)\n .split(/(\\x60[^\\x60]+\\x60)/)\n .map((part) =>\n part.startsWith(codeDelimiter) && part.endsWith(codeDelimiter)\n ? \"<code>\" + escapeHtml(part.slice(1, -1)) + \"</code>\"\n : renderText(part, allowLinks),\n )\n .join(\"\");\n }\n\n function markdownHtml(markdown) {\n const lines = String(markdown).replace(/\\r/g, \"\").split(\"\\n\");\n const output = [];\n const lists = [];\n let paragraph = [];\n const closeParagraph = () => {\n if (paragraph.length > 0) {\n output.push(\"<p>\" + markdownInline(paragraph.join(\" \")) + \"</p>\");\n paragraph = [];\n }\n };\n const closeList = () => {\n const list = lists.pop();\n if (!list) {\n return;\n }\n if (list.open) {\n output.push(\"</li>\");\n }\n output.push(\"</\" + list.tag + \">\");\n };\n const closeLists = () => {\n while (lists.length > 0) {\n closeList();\n }\n };\n const listItem = (line) => {\n const match = /^(\\s*)([-*+]|\\d+\\.)\\s+(.+)$/.exec(line);\n if (!match) {\n return undefined;\n }\n return {\n content: match[3],\n indent: match[1].replace(/\\t/g, \" \").length,\n tag: /\\d+\\./.test(match[2]) ? \"ol\" : \"ul\",\n };\n };\n const renderListItem = (item) => {\n while (\n lists.length > 0 &&\n item.indent < lists[lists.length - 1].indent\n ) {\n closeList();\n }\n let list = lists[lists.length - 1];\n if (\n !list ||\n item.indent > list.indent ||\n (item.indent === list.indent && item.tag !== list.tag)\n ) {\n if (list && item.indent === list.indent) {\n closeList();\n }\n output.push(\"<\" + item.tag + \">\");\n list = { indent: item.indent, open: false, tag: item.tag };\n lists.push(list);\n }\n if (list.open) {\n output.push(\"</li>\");\n }\n const task = /^\\[([ xX])\\]\\s+(.+)$/.exec(item.content);\n output.push(\n \"<li>\" +\n (task\n ? '<label class=\"task\"><input type=\"checkbox\" disabled' +\n (task[1].toLowerCase() === \"x\" ? \" checked\" : \"\") +\n \">\" +\n markdownInline(task[2]) +\n \"</label>\"\n : markdownInline(item.content)),\n );\n list.open = true;\n };\n\n for (let index = 0; index < lines.length; index += 1) {\n const line = lines[index];\n const fence = /^\\x60{3}[^\\x60]*\\s*$/.exec(line);\n if (fence) {\n closeParagraph();\n closeLists();\n const code = [];\n while (\n index + 1 < lines.length &&\n !/^\\x60{3}\\s*$/.test(lines[index + 1])\n ) {\n index += 1;\n code.push(lines[index]);\n }\n if (index + 1 < lines.length) {\n index += 1;\n }\n output.push(\n \"<pre><code>\" + escapeHtml(code.join(\"\\n\")) + \"</code></pre>\",\n );\n continue;\n }\n const heading = /^(#{1,3})\\s+(.+?)\\s*$/.exec(line);\n const quote = /^>\\s?(.*)$/.exec(line);\n const item = listItem(line);\n const rule = /^(---+|\\*\\*\\*+|___+)$/.test(line.trim());\n if (heading) {\n closeParagraph();\n closeLists();\n const level = heading[1].length;\n output.push(\"<h\" + level + \">\" + markdownInline(heading[2]) + \"</h\" + level + \">\");\n } else if (quote) {\n closeParagraph();\n closeLists();\n const quoted = [];\n while (index < lines.length) {\n const next = /^>\\s?(.*)$/.exec(lines[index]);\n if (!next) {\n break;\n }\n quoted.push(next[1]);\n index += 1;\n }\n index -= 1;\n output.push(\"<blockquote>\" + markdownHtml(quoted.join(\"\\n\")) + \"</blockquote>\");\n } else if (item) {\n closeParagraph();\n renderListItem(item);\n } else if (rule) {\n closeParagraph();\n closeLists();\n output.push(\"<hr>\");\n } else if (line.trim()) {\n closeLists();\n paragraph.push(line.trim());\n } else {\n closeParagraph();\n closeLists();\n }\n }\n closeParagraph();\n closeLists();\n return output.join(\"\");\n }\n\n function render() {\n const waiting = status === \"needs_decision\";\n const plan =\n waiting && decision && decision.kind === \"plan\" ? decision : null;\n const choice =\n waiting && decision && decision.kind === \"multi_choice\"\n ? decision\n : null;\n\n element(\"heading\").textContent = plan\n ? \"Superblocks proposed a plan\"\n : choice\n ? \"Superblocks has a question\"\n : waiting\n ? \"Superblocks is waiting on you\"\n : \"Superblocks\";\n element(\"appId\").textContent = appName || applicationId;\n // Superblocks narrates its whole working session into the reply. Once\n // there is a plan or a question, that narration is preamble in front\n // of the thing being asked, so it is dropped: the ask is the ask. It\n // survives only when there is nothing structured, where the reply is\n // itself the whole question and hiding it leaves a blank card.\n const narrationOnly = Boolean(plan || choice);\n element(\"reply\").textContent = narrationOnly ? \"\" : reply;\n show(\"reply\", Boolean(reply) && !narrationOnly);\n element(\"plan\").innerHTML = plan ? markdownHtml(plan.plan) : \"\";\n show(\"plan\", Boolean(plan));\n element(\"question\").innerHTML = choice\n ? markdownHtml(choice.question)\n : \"\";\n show(\"question\", Boolean(choice));\n show(\"planActions\", Boolean(plan));\n renderOptions(choice ? choice.options : []);\n // A turn can stop waiting on the user having streamed its plan or\n // question as chat text, leaving nothing structured to lay out. The\n // reply is then the whole ask, so free text is the only honest form.\n show(\"refineBox\", plan ? refining : waiting && !choice);\n setBusy(answered || sending);\n if (!answered && !sending) {\n note(waiting ? \"\" : waitingNote());\n }\n reportSize();\n }\n\n function applyPayload(payload) {\n let value = payload;\n if (typeof value === \"string\") {\n try {\n value = JSON.parse(value);\n } catch (error) {\n return;\n }\n }\n if (!value || typeof value !== \"object\") {\n return;\n }\n if (Array.isArray(value.content)) {\n applyContent(value.content);\n if (value.structuredContent) {\n applyPayload(value.structuredContent);\n }\n return;\n }\n if (value.result && typeof value.result === \"object\") {\n applyPayload(value.result);\n return;\n }\n if (value.kind === \"ok\" && value.value) {\n value = value.value;\n }\n if (typeof value.applicationId === \"string\") {\n applicationId = value.applicationId;\n }\n if (typeof value.name === \"string\") {\n appName = value.name;\n }\n if (typeof value.reply === \"string\") {\n reply = value.reply;\n }\n if (typeof value.status === \"string\") {\n status = value.status;\n }\n if (\"decision\" in value) {\n decision =\n value.decision && typeof value.decision === \"object\"\n ? value.decision\n : null;\n }\n render();\n }\n\n function applyContent(content) {\n for (const part of content || []) {\n if (part && part.type === \"text\" && typeof part.text === \"string\") {\n applyPayload(part.text);\n }\n }\n }\n\n function applyApprovalMetadata(value) {\n const metadata = value && typeof value === \"object\" ? value._meta : null;\n if (\n metadata &&\n typeof metadata[\"superblocks/plan-approval-token\"] === \"string\"\n ) {\n approvalToken = metadata[\"superblocks/plan-approval-token\"];\n }\n }\n\n function handleHostMessage(event) {\n // App documents are opaque-origin, so the host window is the only\n // identity available to authenticate a message against.\n if (event.source !== window.parent) {\n return;\n }\n const data = event.data;\n if (!data || typeof data !== \"object\") {\n return;\n }\n if (data.method === \"ping\") {\n post({ jsonrpc: \"2.0\", id: data.id, result: {} });\n return;\n }\n if (data.id != null && pending.has(data.id)) {\n const waiter = pending.get(data.id);\n pending.delete(data.id);\n if (data.error) {\n waiter.reject(data.error);\n } else {\n waiter.resolve(data.result);\n }\n return;\n }\n const method = data.method;\n const params = data.params || {};\n if (\n method === \"ui/notifications/tool-result\" ||\n method === \"ui/tool-result\" ||\n method === \"tools/call\"\n ) {\n approvalToken = \"\";\n applyApprovalMetadata(params);\n applyApprovalMetadata(params.result);\n applyContent(params.content || (params.result || {}).content || []);\n if (params.structuredContent) {\n applyPayload(params.structuredContent);\n }\n }\n if (method === \"ui/notifications/tool-input\" && params.arguments) {\n applyPayload(params.arguments);\n }\n if (method === \"ui/notifications/host-context-changed\") {\n applyHostContext(params);\n }\n }\n\n window.addEventListener(\"message\", handleHostMessage);\n\n element(\"approve\").addEventListener(\"click\", approve);\n element(\"refine\").addEventListener(\"click\", () => {\n refining = true;\n element(\"approve\").className = \"button-secondary\";\n show(\"refineBox\", true);\n element(\"feedback\").focus();\n reportSize();\n });\n element(\"sendRefine\").addEventListener(\"click\", () => {\n const feedback = (element(\"feedback\").value || \"\").trim();\n if (!feedback) {\n return;\n }\n answer(\n decision && decision.kind === \"plan\"\n ? \"Change the Superblocks plan: \" + feedback\n : feedback,\n );\n });\n\n if (typeof ResizeObserver === \"function\") {\n new ResizeObserver(reportSize).observe(document.documentElement);\n }\n\n void request(\"ui/initialize\", {\n appCapabilities: { availableDisplayModes: [\"inline\"] },\n appInfo: { name: \"gateway-decision\", version: \"0.0.1\" },\n protocolVersion: PROTOCOL_VERSION,\n })\n .then((result) => {\n // Hosts hold back tool results until the app confirms it is ready.\n notify(\"ui/notifications/initialized\", {});\n applyHostContext(result && result.hostContext);\n if (result && result.toolResult) {\n approvalToken = \"\";\n applyApprovalMetadata(result.toolResult);\n applyPayload(result.toolResult);\n }\n })\n .catch(() => {\n // Hosts that push tool results via notification still work.\n })\n .finally(reportSize);\n })();\n </script>\n</body>\n</html>\n";
27
+ export declare const APP_DECISION_RESOURCE_URI = "ui://gateway/decision-v26.html";
28
+ export declare const APP_DECISION_HTML = "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n <title>Superblocks needs an answer</title>\n <style>\n h1 {\n margin: 0;\n font-size: var(--font-text-md-size, 14px);\n font-weight: var(--font-weight-semibold, 600);\n }\n .meta {\n color: var(--sb-app-text-tertiary);\n font-size: var(--font-text-xs-size, 12px);\n }\n .reply {\n display: none;\n color: var(--sb-app-text-secondary);\n }\n .reply.shown {\n display: block;\n }\n .plan {\n display: none;\n max-height: 340px;\n margin: 0;\n overflow: auto;\n padding: 10px;\n border: var(--border-width-regular, 1px) solid var(--sb-app-border);\n border-radius: var(--border-radius-md, 8px);\n background: var(--sb-app-background-subtle);\n font-size: var(--font-text-xs-size, 12px);\n word-break: break-word;\n }\n .plan.shown {\n display: block;\n }\n .plan > :first-child,\n .question > :first-child {\n margin-top: 0;\n }\n .plan > :last-child,\n .question > :last-child {\n margin-bottom: 0;\n }\n .plan h1,\n .plan h2,\n .plan h3,\n .plan p,\n .question h1,\n .question h2,\n .question h3,\n .question p {\n margin: 10px 0;\n }\n .plan h1,\n .plan h2,\n .plan h3,\n .question h1,\n .question h2,\n .question h3 {\n font-size: inherit;\n font-weight: var(--font-weight-semibold, 600);\n }\n .plan ul,\n .plan ol,\n .question ul,\n .question ol {\n margin: 10px 0;\n padding-left: 20px;\n }\n .plan blockquote,\n .question blockquote {\n margin: 10px 0;\n padding-left: 10px;\n border-left: 3px solid var(--sb-app-border);\n color: var(--sb-app-text-secondary);\n }\n .plan pre,\n .question pre {\n overflow: auto;\n padding: 8px;\n border-radius: var(--border-radius-md, 8px);\n background: var(--sb-app-background);\n }\n .plan a,\n .question a {\n color: var(--sb-app-info-text);\n }\n .entity-tag {\n display: inline-flex;\n align-items: center;\n padding: 0 4px;\n max-width: 100%;\n overflow-wrap: anywhere;\n border: var(--border-width-regular, 1px) solid var(--sb-app-border);\n border-radius: 4px;\n background: var(--sb-app-background);\n color: var(--sb-app-info-text);\n font-weight: 400;\n line-height: 18px;\n vertical-align: middle;\n white-space: normal;\n }\n .task {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n }\n .task input {\n margin: 0;\n }\n .plan code,\n .question code,\n button code {\n font-family: var(--font-mono, ui-monospace, SFMono-Regular, monospace);\n }\n .question {\n display: none;\n overflow-wrap: anywhere;\n font-weight: var(--font-weight-medium, 500);\n }\n .question.shown {\n display: block;\n }\n .actions {\n display: none;\n flex-wrap: wrap;\n gap: 8px;\n }\n .actions.shown {\n display: flex;\n }\n #options {\n flex-direction: column;\n flex-wrap: nowrap;\n }\n #options > .option-button {\n width: 100%;\n overflow-wrap: anywhere;\n text-align: left;\n }\n .refine {\n display: none;\n flex-direction: column;\n gap: 8px;\n }\n .refine.shown {\n display: flex;\n }\n textarea {\n width: 100%;\n box-sizing: border-box;\n min-height: 64px;\n padding: 8px;\n border: var(--border-width-regular, 1px) solid var(--sb-app-border);\n border-radius: var(--border-radius-md, 8px);\n background: var(--sb-app-background);\n color: var(--sb-app-text);\n font: inherit;\n resize: vertical;\n }\n .note {\n color: var(--sb-app-text-secondary);\n font-size: var(--font-text-xs-size, 12px);\n }\n\n :root {\n color-scheme: light dark;\n --sb-app-background: var(\n --color-background-primary,\n light-dark(#ffffff, #1b1c1f)\n );\n --sb-app-background-subtle: var(\n --color-background-secondary,\n light-dark(#f9fafb, #24272b)\n );\n --sb-app-background-hover: var(\n --color-background-tertiary,\n light-dark(#f3f4f6, #2f3437)\n );\n --sb-app-background-inverse: var(\n --color-background-inverse,\n light-dark(#121517, #f9fafb)\n );\n --sb-app-text: var(\n --color-text-primary,\n light-dark(#121517, #f9fafb)\n );\n --sb-app-text-secondary: var(\n --color-text-secondary,\n light-dark(#5a6272, #c6cad2)\n );\n --sb-app-text-tertiary: var(\n --color-text-tertiary,\n light-dark(#6b7280, #a4aab7)\n );\n --sb-app-text-inverse: var(\n --color-text-inverse,\n light-dark(#ffffff, #121517)\n );\n --sb-app-border: var(\n --color-border-primary,\n light-dark(#e8eaed, #454d5f)\n );\n --sb-app-ring: var(\n --color-ring-primary,\n light-dark(#0077b6, #27bbff)\n );\n --sb-app-info-background: var(\n --color-background-info,\n light-dark(#e6f5ff, #083a50)\n );\n --sb-app-info-text: var(\n --color-text-info,\n light-dark(#00679e, #7dd3fc)\n );\n --sb-app-info-border: var(\n --color-border-info,\n light-dark(#c2e7ff, #075985)\n );\n --sb-app-success-background: var(\n --color-background-success,\n light-dark(#e8f8ef, #0a3926)\n );\n --sb-app-success-text: var(\n --color-text-success,\n light-dark(#087a43, #6ee7b7)\n );\n --sb-app-success-border: var(\n --color-border-success,\n light-dark(#b8e8cf, #087a43)\n );\n --sb-app-warning-background: var(\n --color-background-warning,\n light-dark(#fff7e6, #49350a)\n );\n --sb-app-warning-text: var(\n --color-text-warning,\n light-dark(#946200, #fcd34d)\n );\n --sb-app-warning-border: var(\n --color-border-warning,\n light-dark(#f5d996, #946200)\n );\n --sb-app-danger-background: var(\n --color-background-danger,\n light-dark(#fff1f1, #4a1d1d)\n );\n --sb-app-danger-text: var(\n --color-text-danger,\n light-dark(#c54141, #fca5a5)\n );\n --sb-app-danger-border: var(\n --color-border-danger,\n light-dark(#ffcaca, #c54141)\n );\n }\n * {\n box-sizing: border-box;\n }\n body {\n min-width: 0;\n margin: 0;\n overflow-x: hidden;\n background: transparent;\n color: var(--sb-app-text);\n font-family: var(--font-sans, system-ui, -apple-system, sans-serif);\n font-size: var(--font-text-sm-size, 13px);\n line-height: var(--font-text-sm-line-height, 1.45);\n }\n .card {\n display: flex;\n min-width: 0;\n flex-direction: column;\n gap: 12px;\n padding: 16px;\n border: var(--border-width-regular, 1px) solid var(--sb-app-border);\n border-radius: var(--border-radius-lg, 12px);\n background: var(--sb-app-background);\n box-shadow: var(--shadow-sm, 0 1px 2px rgba(18, 21, 23, 0.08));\n }\n h1,\n .meta,\n .note {\n min-width: 0;\n overflow-wrap: anywhere;\n }\n .note:empty {\n position: absolute;\n }\n button,\n textarea {\n font: inherit;\n }\n button {\n appearance: none;\n min-width: 0;\n padding: 8px 12px;\n border: var(--border-width-regular, 1px) solid var(--sb-app-border);\n border-radius: var(--border-radius-md, 8px);\n background: var(--sb-app-background-subtle);\n color: var(--sb-app-text);\n font-weight: var(--font-weight-medium, 500);\n line-height: var(--font-text-sm-line-height, 1.45);\n cursor: pointer;\n }\n button:hover:not(:disabled) {\n background: var(--sb-app-background-hover);\n }\n .button-primary {\n border-color: var(--sb-app-background-inverse);\n background: var(--sb-app-background-inverse);\n color: var(--sb-app-text-inverse);\n }\n .button-primary:hover:not(:disabled) {\n border-color: var(--sb-app-background-inverse);\n background: var(--sb-app-background-inverse);\n }\n button:focus-visible,\n textarea:focus-visible,\n a:focus-visible {\n outline: 2px solid var(--sb-app-ring);\n outline-offset: 2px;\n }\n button:disabled {\n opacity: 0.48;\n cursor: not-allowed;\n }\n .actions {\n min-width: 0;\n }\n .actions > button {\n overflow-wrap: anywhere;\n }\n @media (max-width: 360px) {\n .actions {\n flex-direction: column;\n flex-wrap: nowrap;\n }\n .actions > button {\n width: 100%;\n }\n }\n\n </style>\n</head>\n<body>\n <div class=\"card\">\n <h1 id=\"heading\">Superblocks</h1>\n <div id=\"appId\" class=\"meta\"></div>\n <div id=\"reply\" class=\"reply\"></div>\n <div id=\"plan\" class=\"plan\"></div>\n <div id=\"question\" class=\"question\"></div>\n <div id=\"options\" class=\"actions\"></div>\n <div id=\"planActions\" class=\"actions\">\n <button id=\"approve\" class=\"button-primary\" type=\"button\">Build it</button>\n <button id=\"refine\" class=\"button-secondary\" type=\"button\">Change something</button>\n </div>\n <div id=\"refineBox\" class=\"refine\">\n <textarea id=\"feedback\" placeholder=\"What should change?\"></textarea>\n <button id=\"sendRefine\" class=\"button-primary\" type=\"button\">Send</button>\n </div>\n <div id=\"note\" class=\"note\" aria-live=\"polite\"></div>\n </div>\n <script>\n (() => {\n const PROTOCOL_VERSION = \"2026-01-26\";\n let nextId = 1;\n const pending = new Map();\n let applicationId = \"\";\n let branch;\n let appName = \"\";\n let reply = \"\";\n let decision = null;\n let status = \"\";\n let refining = false;\n let sending = false;\n let answered = false;\n let approvalToken = \"\";\n let planMarkup = \"\";\n let confirmedApprovalToken = \"\";\n let optionButtons = [];\n let lastHeight = 0;\n\n function post(message) {\n window.parent.postMessage(message, \"*\");\n }\n\n function notify(method, params) {\n post({ jsonrpc: \"2.0\", method, params });\n }\n\n function request(method, params) {\n const id = nextId++;\n return new Promise((resolve, reject) => {\n pending.set(id, { resolve, reject });\n post({ jsonrpc: \"2.0\", id, method, params });\n });\n }\n\n function element(id) {\n return document.getElementById(id);\n }\n\n function show(id, on) {\n const node = element(id);\n if (node && node.classList) {\n node.classList.toggle(\"shown\", on);\n }\n }\n\n // Hosts size the card from this notification alone; without it the card\n // is laid out at zero height and never appears.\n function reportSize() {\n const height = Math.ceil(document.documentElement.scrollHeight || 0);\n if (height <= 0 || height === lastHeight) {\n return;\n }\n lastHeight = height;\n notify(\"ui/notifications/size-changed\", {\n height,\n width: Math.ceil(window.innerWidth || 0),\n });\n }\n\n function note(text) {\n element(\"note\").textContent = text;\n show(\"note\", Boolean(text));\n reportSize();\n }\n\n function applyHostContext(context) {\n if (!context || typeof context !== \"object\") {\n return;\n }\n const root = document.documentElement;\n if (context.theme === \"light\" || context.theme === \"dark\") {\n root.setAttribute(\"data-theme\", context.theme);\n root.style.colorScheme = context.theme;\n }\n const variables = context.styles && context.styles.variables;\n if (variables && root.style) {\n for (const name of Object.keys(variables)) {\n const value = variables[name];\n if (typeof value === \"string\") {\n root.style.setProperty(name, value);\n }\n }\n }\n const fonts = context.styles && context.styles.css?.fonts;\n if (typeof fonts === \"string\") {\n let fontStyle = document.getElementById(\"__mcp-host-fonts\");\n if (!fontStyle) {\n fontStyle = document.createElement(\"style\");\n fontStyle.id = \"__mcp-host-fonts\";\n document.head.appendChild(fontStyle);\n }\n fontStyle.textContent = fonts;\n }\n reportSize();\n }\n\n function setBusy(on) {\n element(\"approve\").disabled = on;\n element(\"refine\").disabled = on;\n element(\"sendRefine\").disabled = on;\n for (const button of optionButtons) {\n button.disabled = on;\n }\n }\n\n /**\n * Posts the answer as the user's own turn.\n *\n * The host must receive a user turn so it continues following the build.\n */\n function answer(text) {\n if (!text || sending || answered) {\n return;\n }\n sending = true;\n setBusy(true);\n note(\"Sending your answer\u2026\");\n request(\"ui/message\", {\n content: [{ text: text, type: \"text\" }],\n role: \"user\",\n })\n .then((result) => {\n sending = false;\n if (result && result.isError) {\n refused();\n return;\n }\n answered = true;\n setBusy(true);\n // The host taking the message is not the same as the user's turn\n // going anywhere: Claude Desktop leaves it in the composer as a\n // draft. Claiming it was sent strands the user on a build that is\n // waiting for their enter key.\n note(\"Your answer is in the chat \u2014 press enter there to send it.\");\n })\n .catch(refused);\n }\n\n function refused() {\n sending = false;\n setBusy(false);\n note(\n \"This host would not take your answer from the card \u2014 reply in the chat instead.\",\n );\n }\n\n function approvalRefused() {\n sending = false;\n setBusy(false);\n note(\n \"This host could not verify your approval - approve this plan in the Superblocks editor instead.\",\n );\n }\n\n /**\n * Confirms the user's approval of the current challenge, once per token:\n * a confirmed challenge stays confirmed while a send that never went out\n * is retried, and confirming it again is refused.\n */\n async function confirmApproval() {\n if (!approvalToken) {\n return false;\n }\n if (confirmedApprovalToken === approvalToken) {\n return true;\n }\n const token = approvalToken;\n const confirmation = await request(\"tools/call\", {\n arguments: { approvalToken: token },\n name: \"confirm_plan_approval\",\n });\n if (confirmation && confirmation.isError) {\n return false;\n }\n confirmedApprovalToken = token;\n return true;\n }\n\n async function approve() {\n if (sending || answered) {\n return;\n }\n if (!approvalToken) {\n approvalRefused();\n return;\n }\n sending = true;\n setBusy(true);\n note(\"Confirming your approval...\");\n try {\n const confirmed = await confirmApproval();\n sending = false;\n if (!confirmed) {\n approvalRefused();\n return;\n }\n answer(\"Build it \u2014 approve the Superblocks plan as written.\");\n } catch {\n approvalRefused();\n }\n }\n\n function renderOptions(options) {\n const host = element(\"options\");\n host.replaceChildren();\n optionButtons = [];\n const permissionMessageId = decision && decision.kind === \"tool_permission\"\n ? decision.messageId\n : undefined;\n options.forEach((option, index) => {\n const button = document.createElement(\"button\");\n button.id = \"option-\" + index;\n button.className = \"button-secondary option-button\";\n button.type = \"button\";\n button.innerHTML = markdownInline(option, false);\n // Verbatim: an option Superblocks did not offer reaches it as a\n // prompt answering a question it never asked.\n button.addEventListener(\"click\", () => permissionMessageId\n ? answerPermission(option, permissionMessageId)\n : answer(option));\n host.appendChild(button);\n optionButtons.push(button);\n });\n show(\"options\", options.length > 0);\n }\n\n async function answerPermission(text, permissionMessageId) {\n if (sending || answered) {\n return;\n }\n sending = true;\n setBusy(true);\n note(\"Submitting your response...\");\n const refused = () => {\n sending = false;\n setBusy(false);\n note(\"This request could not be submitted. Ask the chat to show the current approval request again.\");\n };\n try {\n if (text === \"Approve\" && !(await confirmApproval())) {\n refused();\n return;\n }\n const result = await request(\"tools/call\", {\n name: \"edit_app\",\n arguments: { applicationId, ...(branch ? { branch } : {}), permissionMessageId, prompt: text },\n });\n if (result?.isError || result?.structuredContent?.status !== \"building\") {\n refused();\n return;\n }\n sending = false;\n answer(\"My response to the approval request is queued. Please check the build's progress to confirm it was accepted.\");\n } catch {\n refused();\n }\n }\n\n function waitingNote() {\n if (status === \"building\") {\n return \"Superblocks is still building \u2014 nothing to answer yet.\";\n }\n // Something opened this card, so \"nothing is waiting on you\" is a dead\n // end: say where the answer to that actually is.\n return \"Nothing to answer here \u2014 the chat has the latest.\";\n }\n\n function escapeHtml(text) {\n return String(text).replace(/[&<>\"']/g, (character) => {\n return {\n \"&\": \"&amp;\",\n \"<\": \"&lt;\",\n \">\": \"&gt;\",\n '\"': \"&quot;\",\n \"'\": \"&#39;\",\n }[character];\n });\n }\n\n function markdownInline(text, allowLinks = true) {\n const codeDelimiter = String.fromCharCode(96);\n const emphasis = (text) =>\n text\n .replace(/\\*\\*([^*]+)\\*\\*/g, \"<strong>$1</strong>\")\n .replace(/__([^_]+)__/g, \"<strong>$1</strong>\")\n .replace(/(^|[^*])\\*([^*]+)\\*(?!\\*)/g, \"$1<em>$2</em>\")\n .replace(\n /(^|[^A-Za-z0-9_])_([^_]+)_(?![A-Za-z0-9_])/g,\n \"$1<em>$2</em>\",\n );\n const safeLink = (url) => {\n try {\n const parsed = new URL(url);\n return parsed.protocol === \"https:\" && Boolean(parsed.hostname);\n } catch {\n return false;\n }\n };\n const entities = /@((?:(?!\\[id=)[^@\\r\\n])+?(?<!\\s))\\[id=[^\\]\\r\\n]+\\]/gu;\n const links = /\\[([^\\]]+)\\]\\(([^\\s)]+)\\)/g;\n const renderLinks = (value, allowLinks) => {\n const output = [];\n let offset = 0;\n const text = String(value);\n for (const match of text.matchAll(links)) {\n const index = match.index ?? 0;\n if (text[index - 1] === \"!\") {\n continue;\n }\n output.push(emphasis(escapeHtml(text.slice(offset, index))));\n const label = match[1];\n const url = match[2];\n if (safeLink(url)) {\n output.push(\n allowLinks\n ? '<a href=\"' +\n escapeHtml(url) +\n '\" target=\"_blank\" rel=\"noopener noreferrer\">' +\n markdownInline(label, false) +\n \"</a>\"\n : markdownInline(label, false),\n );\n } else {\n output.push(emphasis(escapeHtml(match[0])));\n }\n offset = index + match[0].length;\n }\n output.push(emphasis(escapeHtml(text.slice(offset))));\n return output.join(\"\");\n };\n const renderText = (value, allowLinks) => {\n const text = String(value);\n let placeholderDelimiter = String.fromCharCode(0);\n while (text.includes(placeholderDelimiter)) {\n placeholderDelimiter += String.fromCharCode(0);\n }\n const linkDestinations = Array.from(text.matchAll(links), (match) => {\n const start = (match.index ?? 0) + match[1].length + 3;\n return { start, end: start + match[2].length };\n });\n const entityTags = [];\n const tokenized = text.replace(entities, (match, label, index) => {\n if (\n linkDestinations.some(\n ({ start, end }) => index >= start && index < end,\n )\n ) {\n return match;\n }\n const placeholder =\n placeholderDelimiter + entityTags.length + placeholderDelimiter;\n entityTags.push(\n '<span class=\"entity-tag\">' + escapeHtml(label) + \"</span>\",\n );\n return placeholder;\n });\n return entityTags.reduce(\n (html, tag, index) =>\n html.replace(\n placeholderDelimiter + index + placeholderDelimiter,\n () => tag,\n ),\n renderLinks(tokenized, allowLinks),\n );\n };\n return String(text)\n .split(/(\\x60[^\\x60]+\\x60)/)\n .map((part) =>\n part.startsWith(codeDelimiter) && part.endsWith(codeDelimiter)\n ? \"<code>\" + escapeHtml(part.slice(1, -1)) + \"</code>\"\n : renderText(part, allowLinks),\n )\n .join(\"\");\n }\n\n function markdownHtml(markdown) {\n const lines = String(markdown).replace(/\\r/g, \"\").split(\"\\n\");\n const output = [];\n const lists = [];\n let paragraph = [];\n const closeParagraph = () => {\n if (paragraph.length > 0) {\n output.push(\"<p>\" + markdownInline(paragraph.join(\" \")) + \"</p>\");\n paragraph = [];\n }\n };\n const closeList = () => {\n const list = lists.pop();\n if (!list) {\n return;\n }\n if (list.open) {\n output.push(\"</li>\");\n }\n output.push(\"</\" + list.tag + \">\");\n };\n const closeLists = () => {\n while (lists.length > 0) {\n closeList();\n }\n };\n const listItem = (line) => {\n const match = /^(\\s*)([-*+]|\\d+\\.)\\s+(.+)$/.exec(line);\n if (!match) {\n return undefined;\n }\n return {\n content: match[3],\n indent: match[1].replace(/\\t/g, \" \").length,\n tag: /\\d+\\./.test(match[2]) ? \"ol\" : \"ul\",\n };\n };\n const renderListItem = (item) => {\n while (\n lists.length > 0 &&\n item.indent < lists[lists.length - 1].indent\n ) {\n closeList();\n }\n let list = lists[lists.length - 1];\n if (\n !list ||\n item.indent > list.indent ||\n (item.indent === list.indent && item.tag !== list.tag)\n ) {\n if (list && item.indent === list.indent) {\n closeList();\n }\n output.push(\"<\" + item.tag + \">\");\n list = { indent: item.indent, open: false, tag: item.tag };\n lists.push(list);\n }\n if (list.open) {\n output.push(\"</li>\");\n }\n const task = /^\\[([ xX])\\]\\s+(.+)$/.exec(item.content);\n output.push(\n \"<li>\" +\n (task\n ? '<label class=\"task\"><input type=\"checkbox\" disabled' +\n (task[1].toLowerCase() === \"x\" ? \" checked\" : \"\") +\n \">\" +\n markdownInline(task[2]) +\n \"</label>\"\n : markdownInline(item.content)),\n );\n list.open = true;\n };\n\n for (let index = 0; index < lines.length; index += 1) {\n const line = lines[index];\n const fence = /^\\x60{3}[^\\x60]*\\s*$/.exec(line);\n if (fence) {\n closeParagraph();\n closeLists();\n const code = [];\n while (\n index + 1 < lines.length &&\n !/^\\x60{3}\\s*$/.test(lines[index + 1])\n ) {\n index += 1;\n code.push(lines[index]);\n }\n if (index + 1 < lines.length) {\n index += 1;\n }\n output.push(\n \"<pre><code>\" + escapeHtml(code.join(\"\\n\")) + \"</code></pre>\",\n );\n continue;\n }\n const heading = /^(#{1,3})\\s+(.+?)\\s*$/.exec(line);\n const quote = /^>\\s?(.*)$/.exec(line);\n const item = listItem(line);\n const rule = /^(---+|\\*\\*\\*+|___+)$/.test(line.trim());\n if (heading) {\n closeParagraph();\n closeLists();\n const level = heading[1].length;\n output.push(\"<h\" + level + \">\" + markdownInline(heading[2]) + \"</h\" + level + \">\");\n } else if (quote) {\n closeParagraph();\n closeLists();\n const quoted = [];\n while (index < lines.length) {\n const next = /^>\\s?(.*)$/.exec(lines[index]);\n if (!next) {\n break;\n }\n quoted.push(next[1]);\n index += 1;\n }\n index -= 1;\n output.push(\"<blockquote>\" + markdownHtml(quoted.join(\"\\n\")) + \"</blockquote>\");\n } else if (item) {\n closeParagraph();\n renderListItem(item);\n } else if (rule) {\n closeParagraph();\n closeLists();\n output.push(\"<hr>\");\n } else if (line.trim()) {\n closeLists();\n paragraph.push(line.trim());\n } else {\n closeParagraph();\n closeLists();\n }\n }\n closeParagraph();\n closeLists();\n return output.join(\"\");\n }\n\n function render() {\n const waiting = status === \"needs_decision\";\n const plan =\n waiting && decision && decision.kind === \"plan\" ? decision : null;\n const permission =\n waiting && decision && decision.kind === \"tool_permission\"\n ? decision\n : null;\n const choice = waiting\n ? (decision &&\n decision.kind === \"multi_choice\" &&\n decision) ||\n (permission && {\n options: [\"Approve\", \"Decline\"],\n question: permission.question,\n })\n : null;\n\n element(\"heading\").textContent = plan\n ? \"Superblocks proposed a plan\"\n : permission\n ? \"Superblocks needs your approval\"\n : choice\n ? \"Superblocks has a question\"\n : waiting\n ? \"Superblocks is waiting on you\"\n : \"Superblocks\";\n element(\"appId\").textContent = appName || applicationId;\n // Superblocks narrates its whole working session into the reply. Once\n // there is a plan or a question, that narration is preamble in front\n // of the thing being asked, so it is dropped: the ask is the ask. It\n // survives only when there is nothing structured, where the reply is\n // itself the whole question and hiding it leaves a blank card.\n const narrationOnly = Boolean(plan || choice);\n element(\"reply\").textContent = narrationOnly ? \"\" : reply;\n show(\"reply\", Boolean(reply) && !narrationOnly);\n element(\"plan\").innerHTML = plan\n ? markdownHtml(planMarkup || plan.plan)\n : \"\";\n show(\"plan\", Boolean(plan));\n element(\"question\").innerHTML = choice\n ? markdownHtml(choice.question)\n : \"\";\n show(\"question\", Boolean(choice));\n show(\"planActions\", Boolean(plan));\n renderOptions(choice ? choice.options : []);\n // A turn can stop waiting on the user having streamed its plan or\n // question as chat text, leaving nothing structured to lay out. The\n // reply is then the whole ask, so free text is the only honest form.\n show(\"refineBox\", plan ? refining : waiting && !choice);\n setBusy(answered || sending);\n if (!answered && !sending) {\n note(waiting ? \"\" : waitingNote());\n }\n reportSize();\n }\n\n function applyPayload(payload) {\n let value = payload;\n if (typeof value === \"string\") {\n try {\n value = JSON.parse(value);\n } catch (error) {\n return;\n }\n }\n if (!value || typeof value !== \"object\") {\n return;\n }\n if (Array.isArray(value.content)) {\n applyContent(value.content);\n if (value.structuredContent) {\n applyPayload(value.structuredContent);\n }\n return;\n }\n if (value.result && typeof value.result === \"object\") {\n applyPayload(value.result);\n return;\n }\n if (value.kind === \"ok\" && value.value) {\n value = value.value;\n }\n if (typeof value.applicationId === \"string\") {\n applicationId = value.applicationId;\n branch = typeof value.branch === \"string\" ? value.branch : undefined;\n }\n if (typeof value.name === \"string\") {\n appName = value.name;\n }\n if (typeof value.reply === \"string\") {\n reply = value.reply;\n }\n if (typeof value.status === \"string\") {\n status = value.status;\n }\n if (\"decision\" in value) {\n decision =\n value.decision && typeof value.decision === \"object\"\n ? value.decision\n : null;\n }\n render();\n }\n\n function applyContent(content) {\n for (const part of content || []) {\n if (part && part.type === \"text\" && typeof part.text === \"string\") {\n applyPayload(part.text);\n }\n }\n }\n\n function applyResultMetadata(value) {\n const metadata = value && typeof value === \"object\" ? value._meta : null;\n if (\n metadata &&\n typeof metadata[\"superblocks/plan-approval-token\"] === \"string\"\n ) {\n approvalToken = metadata[\"superblocks/plan-approval-token\"];\n }\n if (metadata && typeof metadata[\"superblocks/plan-markup\"] === \"string\") {\n planMarkup = metadata[\"superblocks/plan-markup\"];\n }\n }\n\n function handleHostMessage(event) {\n // App documents are opaque-origin, so the host window is the only\n // identity available to authenticate a message against.\n if (event.source !== window.parent) {\n return;\n }\n const data = event.data;\n if (!data || typeof data !== \"object\") {\n return;\n }\n if (data.method === \"ping\") {\n post({ jsonrpc: \"2.0\", id: data.id, result: {} });\n return;\n }\n if (data.id != null && pending.has(data.id)) {\n const waiter = pending.get(data.id);\n pending.delete(data.id);\n if (data.error) {\n waiter.reject(data.error);\n } else {\n waiter.resolve(data.result);\n }\n return;\n }\n const method = data.method;\n const params = data.params || {};\n if (\n method === \"ui/notifications/tool-result\" ||\n method === \"ui/tool-result\" ||\n method === \"tools/call\"\n ) {\n approvalToken = \"\";\n planMarkup = \"\";\n applyResultMetadata(params);\n applyResultMetadata(params.result);\n applyContent(params.content || (params.result || {}).content || []);\n if (params.structuredContent) {\n applyPayload(params.structuredContent);\n }\n }\n if (method === \"ui/notifications/tool-input\" && params.arguments) {\n applyPayload(params.arguments);\n }\n if (method === \"ui/notifications/host-context-changed\") {\n applyHostContext(params);\n }\n }\n\n window.addEventListener(\"message\", handleHostMessage);\n\n element(\"approve\").addEventListener(\"click\", approve);\n element(\"refine\").addEventListener(\"click\", () => {\n refining = true;\n element(\"approve\").className = \"button-secondary\";\n show(\"refineBox\", true);\n element(\"feedback\").focus();\n reportSize();\n });\n element(\"sendRefine\").addEventListener(\"click\", () => {\n const feedback = (element(\"feedback\").value || \"\").trim();\n if (!feedback) {\n return;\n }\n answer(\n decision && decision.kind === \"plan\"\n ? \"Change the Superblocks plan: \" + feedback\n : feedback,\n );\n });\n\n if (typeof ResizeObserver === \"function\") {\n new ResizeObserver(reportSize).observe(document.documentElement);\n }\n\n void request(\"ui/initialize\", {\n appCapabilities: { availableDisplayModes: [\"inline\"] },\n appInfo: { name: \"gateway-decision\", version: \"0.0.1\" },\n protocolVersion: PROTOCOL_VERSION,\n })\n .then((result) => {\n // Hosts hold back tool results until the app confirms it is ready.\n notify(\"ui/notifications/initialized\", {});\n applyHostContext(result && result.hostContext);\n if (result && result.toolResult) {\n approvalToken = \"\";\n planMarkup = \"\";\n applyResultMetadata(result.toolResult);\n applyPayload(result.toolResult);\n }\n })\n .catch(() => {\n // Hosts that push tool results via notification still work.\n })\n .finally(reportSize);\n })();\n </script>\n</body>\n</html>\n";
@@ -22,8 +22,10 @@ import { MCP_APP_BRAND_CSS } from "./mcp-app-brand-css.js";
22
22
  * resource URI when the HTML changes so Claude Desktop refreshes its cache.
23
23
  */
24
24
  export const PLAN_APPROVAL_TOKEN_META_KEY = "superblocks/plan-approval-token";
25
+ /** The plan with its `@Name[id=...]` mentions, kept out of model-visible fields. */
26
+ export const PLAN_MARKUP_META_KEY = "superblocks/plan-markup";
25
27
  export const CONFIRM_PLAN_APPROVAL_TOOL = "confirm_plan_approval";
26
- export const APP_DECISION_RESOURCE_URI = "ui://gateway/decision-v20.html";
28
+ export const APP_DECISION_RESOURCE_URI = "ui://gateway/decision-v26.html";
27
29
  export const APP_DECISION_HTML = `<!DOCTYPE html>
28
30
  <html lang="en">
29
31
  <head>
@@ -218,6 +220,7 @@ ${MCP_APP_BRAND_CSS}
218
220
  let nextId = 1;
219
221
  const pending = new Map();
220
222
  let applicationId = "";
223
+ let branch;
221
224
  let appName = "";
222
225
  let reply = "";
223
226
  let decision = null;
@@ -226,6 +229,8 @@ ${MCP_APP_BRAND_CSS}
226
229
  let sending = false;
227
230
  let answered = false;
228
231
  let approvalToken = "";
232
+ let planMarkup = "";
233
+ let confirmedApprovalToken = "";
229
234
  let optionButtons = [];
230
235
  let lastHeight = 0;
231
236
 
@@ -319,8 +324,7 @@ ${MCP_APP_BRAND_CSS}
319
324
  /**
320
325
  * Posts the answer as the user's own turn.
321
326
  *
322
- * Not tools/call: calling edit_app from in here starts a build the host
323
- * agent never learns about, so nothing polls it and nothing is relayed.
327
+ * The host must receive a user turn so it continues following the build.
324
328
  */
325
329
  function answer(text) {
326
330
  if (!text || sending || answered) {
@@ -366,7 +370,31 @@ ${MCP_APP_BRAND_CSS}
366
370
  );
367
371
  }
368
372
 
369
- function approve() {
373
+ /**
374
+ * Confirms the user's approval of the current challenge, once per token:
375
+ * a confirmed challenge stays confirmed while a send that never went out
376
+ * is retried, and confirming it again is refused.
377
+ */
378
+ async function confirmApproval() {
379
+ if (!approvalToken) {
380
+ return false;
381
+ }
382
+ if (confirmedApprovalToken === approvalToken) {
383
+ return true;
384
+ }
385
+ const token = approvalToken;
386
+ const confirmation = await request("tools/call", {
387
+ arguments: { approvalToken: token },
388
+ name: "${CONFIRM_PLAN_APPROVAL_TOOL}",
389
+ });
390
+ if (confirmation && confirmation.isError) {
391
+ return false;
392
+ }
393
+ confirmedApprovalToken = token;
394
+ return true;
395
+ }
396
+
397
+ async function approve() {
370
398
  if (sending || answered) {
371
399
  return;
372
400
  }
@@ -377,25 +405,26 @@ ${MCP_APP_BRAND_CSS}
377
405
  sending = true;
378
406
  setBusy(true);
379
407
  note("Confirming your approval...");
380
- request("tools/call", {
381
- arguments: { approvalToken: approvalToken },
382
- name: "${CONFIRM_PLAN_APPROVAL_TOOL}",
383
- })
384
- .then((result) => {
385
- sending = false;
386
- if (result && result.isError) {
387
- approvalRefused();
388
- return;
389
- }
390
- answer("Build it — approve the Superblocks plan as written.");
391
- })
392
- .catch(approvalRefused);
408
+ try {
409
+ const confirmed = await confirmApproval();
410
+ sending = false;
411
+ if (!confirmed) {
412
+ approvalRefused();
413
+ return;
414
+ }
415
+ answer("Build it — approve the Superblocks plan as written.");
416
+ } catch {
417
+ approvalRefused();
418
+ }
393
419
  }
394
420
 
395
421
  function renderOptions(options) {
396
422
  const host = element("options");
397
423
  host.replaceChildren();
398
424
  optionButtons = [];
425
+ const permissionMessageId = decision && decision.kind === "tool_permission"
426
+ ? decision.messageId
427
+ : undefined;
399
428
  options.forEach((option, index) => {
400
429
  const button = document.createElement("button");
401
430
  button.id = "option-" + index;
@@ -404,13 +433,47 @@ ${MCP_APP_BRAND_CSS}
404
433
  button.innerHTML = markdownInline(option, false);
405
434
  // Verbatim: an option Superblocks did not offer reaches it as a
406
435
  // prompt answering a question it never asked.
407
- button.addEventListener("click", () => answer(option));
436
+ button.addEventListener("click", () => permissionMessageId
437
+ ? answerPermission(option, permissionMessageId)
438
+ : answer(option));
408
439
  host.appendChild(button);
409
440
  optionButtons.push(button);
410
441
  });
411
442
  show("options", options.length > 0);
412
443
  }
413
444
 
445
+ async function answerPermission(text, permissionMessageId) {
446
+ if (sending || answered) {
447
+ return;
448
+ }
449
+ sending = true;
450
+ setBusy(true);
451
+ note("Submitting your response...");
452
+ const refused = () => {
453
+ sending = false;
454
+ setBusy(false);
455
+ note("This request could not be submitted. Ask the chat to show the current approval request again.");
456
+ };
457
+ try {
458
+ if (text === "Approve" && !(await confirmApproval())) {
459
+ refused();
460
+ return;
461
+ }
462
+ const result = await request("tools/call", {
463
+ name: "edit_app",
464
+ arguments: { applicationId, ...(branch ? { branch } : {}), permissionMessageId, prompt: text },
465
+ });
466
+ if (result?.isError || result?.structuredContent?.status !== "building") {
467
+ refused();
468
+ return;
469
+ }
470
+ sending = false;
471
+ answer("My response to the approval request is queued. Please check the build's progress to confirm it was accepted.");
472
+ } catch {
473
+ refused();
474
+ }
475
+ }
476
+
414
477
  function waitingNote() {
415
478
  if (status === "building") {
416
479
  return "Superblocks is still building — nothing to answer yet.";
@@ -671,13 +734,24 @@ ${MCP_APP_BRAND_CSS}
671
734
  const waiting = status === "needs_decision";
672
735
  const plan =
673
736
  waiting && decision && decision.kind === "plan" ? decision : null;
674
- const choice =
675
- waiting && decision && decision.kind === "multi_choice"
737
+ const permission =
738
+ waiting && decision && decision.kind === "tool_permission"
676
739
  ? decision
677
740
  : null;
741
+ const choice = waiting
742
+ ? (decision &&
743
+ decision.kind === "multi_choice" &&
744
+ decision) ||
745
+ (permission && {
746
+ options: ["Approve", "Decline"],
747
+ question: permission.question,
748
+ })
749
+ : null;
678
750
 
679
751
  element("heading").textContent = plan
680
752
  ? "Superblocks proposed a plan"
753
+ : permission
754
+ ? "Superblocks needs your approval"
681
755
  : choice
682
756
  ? "Superblocks has a question"
683
757
  : waiting
@@ -692,7 +766,9 @@ ${MCP_APP_BRAND_CSS}
692
766
  const narrationOnly = Boolean(plan || choice);
693
767
  element("reply").textContent = narrationOnly ? "" : reply;
694
768
  show("reply", Boolean(reply) && !narrationOnly);
695
- element("plan").innerHTML = plan ? markdownHtml(plan.plan) : "";
769
+ element("plan").innerHTML = plan
770
+ ? markdownHtml(planMarkup || plan.plan)
771
+ : "";
696
772
  show("plan", Boolean(plan));
697
773
  element("question").innerHTML = choice
698
774
  ? markdownHtml(choice.question)
@@ -739,6 +815,7 @@ ${MCP_APP_BRAND_CSS}
739
815
  }
740
816
  if (typeof value.applicationId === "string") {
741
817
  applicationId = value.applicationId;
818
+ branch = typeof value.branch === "string" ? value.branch : undefined;
742
819
  }
743
820
  if (typeof value.name === "string") {
744
821
  appName = value.name;
@@ -766,7 +843,7 @@ ${MCP_APP_BRAND_CSS}
766
843
  }
767
844
  }
768
845
 
769
- function applyApprovalMetadata(value) {
846
+ function applyResultMetadata(value) {
770
847
  const metadata = value && typeof value === "object" ? value._meta : null;
771
848
  if (
772
849
  metadata &&
@@ -774,6 +851,9 @@ ${MCP_APP_BRAND_CSS}
774
851
  ) {
775
852
  approvalToken = metadata["${PLAN_APPROVAL_TOKEN_META_KEY}"];
776
853
  }
854
+ if (metadata && typeof metadata["${PLAN_MARKUP_META_KEY}"] === "string") {
855
+ planMarkup = metadata["${PLAN_MARKUP_META_KEY}"];
856
+ }
777
857
  }
778
858
 
779
859
  function handleHostMessage(event) {
@@ -808,8 +888,9 @@ ${MCP_APP_BRAND_CSS}
808
888
  method === "tools/call"
809
889
  ) {
810
890
  approvalToken = "";
811
- applyApprovalMetadata(params);
812
- applyApprovalMetadata(params.result);
891
+ planMarkup = "";
892
+ applyResultMetadata(params);
893
+ applyResultMetadata(params.result);
813
894
  applyContent(params.content || (params.result || {}).content || []);
814
895
  if (params.structuredContent) {
815
896
  applyPayload(params.structuredContent);
@@ -860,7 +941,8 @@ ${MCP_APP_BRAND_CSS}
860
941
  applyHostContext(result && result.hostContext);
861
942
  if (result && result.toolResult) {
862
943
  approvalToken = "";
863
- applyApprovalMetadata(result.toolResult);
944
+ planMarkup = "";
945
+ applyResultMetadata(result.toolResult);
864
946
  applyPayload(result.toolResult);
865
947
  }
866
948
  })
@@ -1 +1 @@
1
- {"version":3,"file":"decision-card-html.js","sourceRoot":"","sources":["../../../src/transports/mcp/decision-card-html.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAE3D;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,CAAC,MAAM,4BAA4B,GAAG,iCAAiC,CAAC;AAC9E,MAAM,CAAC,MAAM,0BAA0B,GAAG,uBAAuB,CAAC;AAClE,MAAM,CAAC,MAAM,yBAAyB,GAAG,gCAAgC,CAAC;AAE1E,MAAM,CAAC,MAAM,iBAAiB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAuK/B,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mBA4LA,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;6BAuYhB,4BAA4B;;sCAEnB,4BAA4B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoGjE,CAAC"}
1
+ {"version":3,"file":"decision-card-html.js","sourceRoot":"","sources":["../../../src/transports/mcp/decision-card-html.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAE3D;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,CAAC,MAAM,4BAA4B,GAAG,iCAAiC,CAAC;AAC9E,oFAAoF;AACpF,MAAM,CAAC,MAAM,oBAAoB,GAAG,yBAAyB,CAAC;AAC9D,MAAM,CAAC,MAAM,0BAA0B,GAAG,uBAAuB,CAAC;AAClE,MAAM,CAAC,MAAM,yBAAyB,GAAG,gCAAgC,CAAC;AAE1E,MAAM,CAAC,MAAM,iBAAiB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAuK/B,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mBAgMA,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;6BA8chB,4BAA4B;;sCAEnB,4BAA4B;;2CAEvB,oBAAoB;mCAC5B,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsGtD,CAAC"}
@@ -1,6 +1,6 @@
1
1
  /**
2
- * MCP form elicitation for Superblocks decisions (plan approve/refine and
3
- * multi_choice questions).
2
+ * MCP form elicitation for Superblocks decisions (plan approve/refine,
3
+ * multi_choice questions, and tool-permission approvals).
4
4
  *
5
5
  * Chat is the baseline: every decision is also returned as needs_decision +
6
6
  * decision so a host without elicitation can ask the user in its own words. A
@@ -12,6 +12,7 @@ import { ElicitResultSchema } from "@modelcontextprotocol/sdk/types.js";
12
12
  import type { CapabilityResult, DecisionPayload } from "../../capabilities/types.js";
13
13
  export type DecisionAnswer = {
14
14
  kind: "answer";
15
+ permissionMessageId?: string;
15
16
  prompt: string;
16
17
  } | {
17
18
  kind: "approve";
@@ -1,6 +1,6 @@
1
1
  /**
2
- * MCP form elicitation for Superblocks decisions (plan approve/refine and
3
- * multi_choice questions).
2
+ * MCP form elicitation for Superblocks decisions (plan approve/refine,
3
+ * multi_choice questions, and tool-permission approvals).
4
4
  *
5
5
  * Chat is the baseline: every decision is also returned as needs_decision +
6
6
  * decision so a host without elicitation can ask the user in its own words. A
@@ -128,6 +128,36 @@ export function decisionElicitationRequest(decision) {
128
128
  },
129
129
  };
130
130
  }
131
+ if (decision.kind === "tool_permission") {
132
+ gatewayDebug("decision request created", {
133
+ "decision.kind": decision.kind,
134
+ });
135
+ return {
136
+ message: decision.question,
137
+ mode: "form",
138
+ requestedSchema: {
139
+ properties: {
140
+ action: {
141
+ description: "Allow this action, or decline it",
142
+ enum: ["approve", "decline"],
143
+ enumNames: [
144
+ "Approve: allow this action",
145
+ "Decline: do not allow this action",
146
+ ],
147
+ title: "Approval",
148
+ type: "string",
149
+ },
150
+ feedback: {
151
+ description: "Optional explanation to pass back with a declined response.",
152
+ title: "Reason",
153
+ type: "string",
154
+ },
155
+ },
156
+ required: ["action"],
157
+ type: "object",
158
+ },
159
+ };
160
+ }
131
161
  if (decision.options.length === 0) {
132
162
  gatewayDebug("decision request skipped", {
133
163
  "decision.kind": decision.kind,
@@ -199,6 +229,41 @@ export function readDecisionAnswer(decision, outcome) {
199
229
  });
200
230
  return feedback ? { feedback, kind: "refine" } : undefined;
201
231
  }
232
+ if (decision.kind === "tool_permission") {
233
+ const action = outcome.content.action;
234
+ if (action === "approve") {
235
+ gatewayDebug("decision answer read", {
236
+ "decision.answer_kind": "answer",
237
+ "decision.kind": decision.kind,
238
+ });
239
+ return {
240
+ kind: "answer",
241
+ permissionMessageId: decision.messageId,
242
+ prompt: "Approve",
243
+ };
244
+ }
245
+ if (action === "decline") {
246
+ const feedback = typeof outcome.content.feedback === "string"
247
+ ? outcome.content.feedback.trim()
248
+ : "";
249
+ gatewayDebug("decision answer read", {
250
+ "decision.answer_kind": "answer",
251
+ "decision.kind": decision.kind,
252
+ ...(feedback ? { "decision.feedback": true } : {}),
253
+ });
254
+ return {
255
+ kind: "answer",
256
+ permissionMessageId: decision.messageId,
257
+ prompt: feedback ? `Decline: ${feedback}` : "Decline",
258
+ };
259
+ }
260
+ gatewayDebug("decision answer read", {
261
+ "decision.answer_kind": "none",
262
+ "decision.kind": decision.kind,
263
+ "decision.reason": "invalid_tool_permission_action",
264
+ });
265
+ return undefined;
266
+ }
202
267
  const choice = outcome.content.choice;
203
268
  // Only an option Superblocks offered: a free-typed answer would reach Clark
204
269
  // as a prompt for a question it did not ask.