@superblocksteam/cli 2.0.156 → 2.0.157-next.1

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 (119) hide show
  1. package/README.md +1 -1
  2. package/bin/dev.js +6 -0
  3. package/bin/run.js +6 -0
  4. package/dist/{acorn-KZYRJBVA.js → acorn-374FSKTF.js} +3 -3
  5. package/dist/{angular-WM5LU6FD.js → angular-ZSWIRSL7.js} +3 -3
  6. package/dist/{api-TERRF4V4.js → api-BQ6LCADZ.js} +4 -4
  7. package/dist/{babel-3ORNE6FM.js → babel-H5L6MHE7.js} +3 -3
  8. package/dist/{chunk-4UNLK7GB.js → chunk-23SPDUI6.js} +2 -2
  9. package/dist/{chunk-I5UAWLVZ.js → chunk-BSGDEWZQ.js} +4867 -2870
  10. package/dist/{chunk-I5UAWLVZ.js.map → chunk-BSGDEWZQ.js.map} +1 -1
  11. package/dist/{chunk-BBQHGV6R.js → chunk-CUDVKTYH.js} +3 -3
  12. package/dist/{chunk-XCW3AUST.js → chunk-EB6CJH7V.js} +3 -3
  13. package/dist/{chunk-STRD7EJ5.js → chunk-GH4RCMW5.js} +5 -5
  14. package/dist/{chunk-O6QF4SNO.js → chunk-KOWVZPVA.js} +3758 -157
  15. package/dist/chunk-KOWVZPVA.js.map +1 -0
  16. package/dist/{chunk-H7AUOU4W.js → chunk-L5H7DYO7.js} +3 -3
  17. package/dist/{chunk-B5T5WKNP.js → chunk-N4UPI6V3.js} +3 -3
  18. package/dist/{chunk-U7TCJUOB.js → chunk-OGF6LECY.js} +3 -3
  19. package/dist/{chunk-HGAO2HOW.js → chunk-OHCMPCUL.js} +3 -3
  20. package/dist/{chunk-XX27DB7R.js → chunk-ONLYL2J4.js} +3 -3
  21. package/dist/{chunk-7UYNZ23S.js → chunk-Q5BLWOMS.js} +4 -4
  22. package/dist/{chunk-OK6QICRM.js → chunk-QH7ID45D.js} +4 -4
  23. package/dist/{chunk-KAPL5FDL.js → chunk-QP2YCYHF.js} +4 -4
  24. package/dist/{chunk-QC7LVFFV.js → chunk-RL4R65YP.js} +8 -8
  25. package/dist/{chunk-2CTGMPIU.js → chunk-S26U3GLS.js} +3 -3
  26. package/dist/{chunk-ENQ6K4XH.js → chunk-SJTYSCVR.js} +3 -3
  27. package/dist/{chunk-A6SIMKPR.js → chunk-TROB6FY2.js} +4 -4
  28. package/dist/{chunk-MIE7VILH.js → chunk-TZ6ZWO5X.js} +3 -3
  29. package/dist/{chunk-BLVM23DR.js → chunk-W2NCNWLH.js} +3 -3
  30. package/dist/{cli-truncate-QSJMM2L4.js → cli-truncate-2HLSSSI2.js} +5 -5
  31. package/dist/commands/dev-parent.js +3 -3
  32. package/dist/{dd-trace-GEZRXO5P.js → dd-trace-7UEE44MV.js} +6 -6
  33. package/dist/{dist-P7G6RMCF.js → dist-7EN7U2RU.js} +841 -200
  34. package/dist/dist-7EN7U2RU.js.map +1 -0
  35. package/dist/{dist-GGA2IIFF.js → dist-7I6KWLY3.js} +15 -15
  36. package/dist/{embedded-playwright-mcp-server-5QDLVHBN.js → embedded-playwright-mcp-server-TDTWZVKZ.js} +3 -3
  37. package/dist/{enquirer-BJV23Q6P.js → enquirer-NDCIXNSY.js} +4 -4
  38. package/dist/{estree-WFZOS4H2.js → estree-ABUOMZGX.js} +3 -3
  39. package/dist/{flow-HCW5HOIG.js → flow-PZPC6TPD.js} +3 -3
  40. package/dist/{getMachineId-bsd-APENRPFT.js → getMachineId-bsd-KTJ7DVJE.js} +5 -5
  41. package/dist/{getMachineId-darwin-SCM25SYO.js → getMachineId-darwin-XRRY3CK3.js} +5 -5
  42. package/dist/{getMachineId-linux-ZP45LG2W.js → getMachineId-linux-QDL57GVH.js} +4 -4
  43. package/dist/{getMachineId-unsupported-3T3LBBHH.js → getMachineId-unsupported-MSGEV526.js} +4 -4
  44. package/dist/{getMachineId-win-WMUPYKYI.js → getMachineId-win-ITM6DDEY.js} +5 -5
  45. package/dist/{glimmer-ZXDX6EOO.js → glimmer-WMFPFTLN.js} +3 -3
  46. package/dist/{graphql-UMXWGGZR.js → graphql-HFTQP6IV.js} +3 -3
  47. package/dist/{html-2SZBLK3T.js → html-NCLSIA3Q.js} +3 -3
  48. package/dist/{http-3L4EJ6YP.js → http-3GN25ZNK.js} +14 -14
  49. package/dist/index.js +23 -23
  50. package/dist/{jiti-YWBYU73T.js → jiti-P2ZVYQZF.js} +3 -3
  51. package/dist/{log-update-XOJDFO2A.js → log-update-RXST5UYR.js} +6 -6
  52. package/dist/{markdown-76GI7OP5.js → markdown-LDE2QEHS.js} +3 -3
  53. package/dist/{meriyah-65Z3CGQ6.js → meriyah-CSMBABLZ.js} +3 -3
  54. package/dist/{postcss-RW7LFWNO.js → postcss-4M3R2THY.js} +3 -3
  55. package/dist/{read-pkg-GMF4ZZKW.js → read-pkg-47MQXTJ2.js} +4 -4
  56. package/dist/{spans-QHMLXVJR.js → spans-IX762LIG.js} +4 -4
  57. package/dist/{src-QMR65BY4.js → src-GIXVGP4I.js} +3 -3
  58. package/dist/{token-Z6Q7YMEB.js → token-UJSE5G54.js} +5 -5
  59. package/dist/{token-util-DEN4UW5Y.js → token-util-O5URO43P.js} +5 -5
  60. package/dist/{typescript-S3EJOMRS.js → typescript-6OFSTQJT.js} +3 -3
  61. package/dist/{v4-7CGFD7UI.js → v4-OR3KFUTQ.js} +4 -4
  62. package/dist/{wrap-ansi-F4EVLPXN.js → wrap-ansi-EUQQCFXW.js} +5 -5
  63. package/dist/{yaml-5Q2LTOLJ.js → yaml-F4IRYNR3.js} +3 -3
  64. package/oclif.manifest.json +1 -1
  65. package/package.json +7 -7
  66. package/dist/chunk-O6QF4SNO.js.map +0 -1
  67. package/dist/dist-P7G6RMCF.js.map +0 -1
  68. /package/dist/{acorn-KZYRJBVA.js.map → acorn-374FSKTF.js.map} +0 -0
  69. /package/dist/{angular-WM5LU6FD.js.map → angular-ZSWIRSL7.js.map} +0 -0
  70. /package/dist/{api-TERRF4V4.js.map → api-BQ6LCADZ.js.map} +0 -0
  71. /package/dist/{babel-3ORNE6FM.js.map → babel-H5L6MHE7.js.map} +0 -0
  72. /package/dist/{chunk-4UNLK7GB.js.map → chunk-23SPDUI6.js.map} +0 -0
  73. /package/dist/{chunk-BBQHGV6R.js.map → chunk-CUDVKTYH.js.map} +0 -0
  74. /package/dist/{chunk-XCW3AUST.js.map → chunk-EB6CJH7V.js.map} +0 -0
  75. /package/dist/{chunk-STRD7EJ5.js.map → chunk-GH4RCMW5.js.map} +0 -0
  76. /package/dist/{chunk-H7AUOU4W.js.map → chunk-L5H7DYO7.js.map} +0 -0
  77. /package/dist/{chunk-B5T5WKNP.js.map → chunk-N4UPI6V3.js.map} +0 -0
  78. /package/dist/{chunk-U7TCJUOB.js.map → chunk-OGF6LECY.js.map} +0 -0
  79. /package/dist/{chunk-HGAO2HOW.js.map → chunk-OHCMPCUL.js.map} +0 -0
  80. /package/dist/{chunk-XX27DB7R.js.map → chunk-ONLYL2J4.js.map} +0 -0
  81. /package/dist/{chunk-7UYNZ23S.js.map → chunk-Q5BLWOMS.js.map} +0 -0
  82. /package/dist/{chunk-OK6QICRM.js.map → chunk-QH7ID45D.js.map} +0 -0
  83. /package/dist/{chunk-KAPL5FDL.js.map → chunk-QP2YCYHF.js.map} +0 -0
  84. /package/dist/{chunk-QC7LVFFV.js.map → chunk-RL4R65YP.js.map} +0 -0
  85. /package/dist/{chunk-2CTGMPIU.js.map → chunk-S26U3GLS.js.map} +0 -0
  86. /package/dist/{chunk-ENQ6K4XH.js.map → chunk-SJTYSCVR.js.map} +0 -0
  87. /package/dist/{chunk-A6SIMKPR.js.map → chunk-TROB6FY2.js.map} +0 -0
  88. /package/dist/{chunk-MIE7VILH.js.map → chunk-TZ6ZWO5X.js.map} +0 -0
  89. /package/dist/{chunk-BLVM23DR.js.map → chunk-W2NCNWLH.js.map} +0 -0
  90. /package/dist/{cli-truncate-QSJMM2L4.js.map → cli-truncate-2HLSSSI2.js.map} +0 -0
  91. /package/dist/{dd-trace-GEZRXO5P.js.map → dd-trace-7UEE44MV.js.map} +0 -0
  92. /package/dist/{dist-GGA2IIFF.js.map → dist-7I6KWLY3.js.map} +0 -0
  93. /package/dist/{embedded-playwright-mcp-server-5QDLVHBN.js.map → embedded-playwright-mcp-server-TDTWZVKZ.js.map} +0 -0
  94. /package/dist/{enquirer-BJV23Q6P.js.map → enquirer-NDCIXNSY.js.map} +0 -0
  95. /package/dist/{estree-WFZOS4H2.js.map → estree-ABUOMZGX.js.map} +0 -0
  96. /package/dist/{flow-HCW5HOIG.js.map → flow-PZPC6TPD.js.map} +0 -0
  97. /package/dist/{getMachineId-bsd-APENRPFT.js.map → getMachineId-bsd-KTJ7DVJE.js.map} +0 -0
  98. /package/dist/{getMachineId-darwin-SCM25SYO.js.map → getMachineId-darwin-XRRY3CK3.js.map} +0 -0
  99. /package/dist/{getMachineId-linux-ZP45LG2W.js.map → getMachineId-linux-QDL57GVH.js.map} +0 -0
  100. /package/dist/{getMachineId-unsupported-3T3LBBHH.js.map → getMachineId-unsupported-MSGEV526.js.map} +0 -0
  101. /package/dist/{getMachineId-win-WMUPYKYI.js.map → getMachineId-win-ITM6DDEY.js.map} +0 -0
  102. /package/dist/{glimmer-ZXDX6EOO.js.map → glimmer-WMFPFTLN.js.map} +0 -0
  103. /package/dist/{graphql-UMXWGGZR.js.map → graphql-HFTQP6IV.js.map} +0 -0
  104. /package/dist/{html-2SZBLK3T.js.map → html-NCLSIA3Q.js.map} +0 -0
  105. /package/dist/{http-3L4EJ6YP.js.map → http-3GN25ZNK.js.map} +0 -0
  106. /package/dist/{jiti-YWBYU73T.js.map → jiti-P2ZVYQZF.js.map} +0 -0
  107. /package/dist/{log-update-XOJDFO2A.js.map → log-update-RXST5UYR.js.map} +0 -0
  108. /package/dist/{markdown-76GI7OP5.js.map → markdown-LDE2QEHS.js.map} +0 -0
  109. /package/dist/{meriyah-65Z3CGQ6.js.map → meriyah-CSMBABLZ.js.map} +0 -0
  110. /package/dist/{postcss-RW7LFWNO.js.map → postcss-4M3R2THY.js.map} +0 -0
  111. /package/dist/{read-pkg-GMF4ZZKW.js.map → read-pkg-47MQXTJ2.js.map} +0 -0
  112. /package/dist/{spans-QHMLXVJR.js.map → spans-IX762LIG.js.map} +0 -0
  113. /package/dist/{src-QMR65BY4.js.map → src-GIXVGP4I.js.map} +0 -0
  114. /package/dist/{token-Z6Q7YMEB.js.map → token-UJSE5G54.js.map} +0 -0
  115. /package/dist/{token-util-DEN4UW5Y.js.map → token-util-O5URO43P.js.map} +0 -0
  116. /package/dist/{typescript-S3EJOMRS.js.map → typescript-6OFSTQJT.js.map} +0 -0
  117. /package/dist/{v4-7CGFD7UI.js.map → v4-OR3KFUTQ.js.map} +0 -0
  118. /package/dist/{wrap-ansi-F4EVLPXN.js.map → wrap-ansi-EUQQCFXW.js.map} +0 -0
  119. /package/dist/{yaml-5Q2LTOLJ.js.map → yaml-F4IRYNR3.js.map} +0 -0
@@ -1,7 +1,7 @@
1
1
  if (typeof process === 'object' && process !== null &&
2
2
  process.env !== null && typeof process.env === 'object') {
3
3
  process.env.DD_GIT_REPOSITORY_URL = 'https://token@github.com/superblocksteam/superblocks.git';
4
- process.env.DD_GIT_COMMIT_SHA = 'a5b422b6464068a2d19d8b6b1893a1276d5274f9';
4
+ process.env.DD_GIT_COMMIT_SHA = '26ba1efe16b7d3d563ab105280fd09dd361c3e40';
5
5
  }
6
6
  import { createRequire as $dd_createRequire } from 'module';
7
7
  import { fileURLToPath as $dd_fileURLToPath } from 'url';
@@ -13,7 +13,7 @@ globalThis.__dirname ??= $dd_dirname(globalThis.__filename);
13
13
  import {
14
14
  McpServer,
15
15
  StdioServerTransport
16
- } from "./chunk-QC7LVFFV.js";
16
+ } from "./chunk-RL4R65YP.js";
17
17
  import {
18
18
  CallToolResultSchema,
19
19
  ContentBlockSchema,
@@ -27,30 +27,37 @@ import {
27
27
  executeToolCall,
28
28
  runWithAuthContext,
29
29
  toolDefinitions
30
- } from "./chunk-O6QF4SNO.js";
30
+ } from "./chunk-KOWVZPVA.js";
31
31
  import {
32
32
  external_exports
33
- } from "./chunk-MIE7VILH.js";
34
- import "./chunk-A6SIMKPR.js";
33
+ } from "./chunk-TZ6ZWO5X.js";
34
+ import "./chunk-TROB6FY2.js";
35
35
  import {
36
+ getDefaultPolicy,
37
+ import_shared,
36
38
  import_websocket,
39
+ initNodeTelemetry,
40
+ parseDeploymentType,
37
41
  require_dist
38
- } from "./chunk-I5UAWLVZ.js";
39
- import "./chunk-HGAO2HOW.js";
40
- import "./chunk-2CTGMPIU.js";
41
- import "./chunk-STRD7EJ5.js";
42
- import "./chunk-H7AUOU4W.js";
43
- import "./chunk-B5T5WKNP.js";
44
- import "./chunk-XCW3AUST.js";
42
+ } from "./chunk-BSGDEWZQ.js";
43
+ import "./chunk-OHCMPCUL.js";
44
+ import "./chunk-S26U3GLS.js";
45
+ import "./chunk-GH4RCMW5.js";
46
+ import "./chunk-L5H7DYO7.js";
47
+ import "./chunk-N4UPI6V3.js";
48
+ import "./chunk-EB6CJH7V.js";
45
49
  import {
50
+ SpanStatusCode,
51
+ context,
46
52
  init_esm,
47
- metrics
48
- } from "./chunk-U7TCJUOB.js";
53
+ metrics,
54
+ trace
55
+ } from "./chunk-OGF6LECY.js";
49
56
  import {
50
57
  __require,
51
58
  __toESM,
52
59
  init_cjs_shims
53
- } from "./chunk-4UNLK7GB.js";
60
+ } from "./chunk-23SPDUI6.js";
54
61
 
55
62
  // ../../../gateway/dist/index.js
56
63
  init_cjs_shims();
@@ -232,7 +239,7 @@ function formatCaptureModeSummary(input) {
232
239
 
233
240
  // ../../../gateway/dist/capabilities/lifecycle.js
234
241
  init_cjs_shims();
235
- var import_shared2 = __toESM(require_dist());
242
+ var import_shared3 = __toESM(require_dist());
236
243
  import { randomUUID } from "node:crypto";
237
244
  import { readFile, realpath, stat } from "node:fs/promises";
238
245
  import path2 from "node:path";
@@ -437,7 +444,7 @@ function buildOpenablePreviewUrls(input) {
437
444
 
438
445
  // ../../../gateway/dist/server/client.js
439
446
  init_cjs_shims();
440
- var import_shared = __toESM(require_dist());
447
+ var import_shared2 = __toESM(require_dist());
441
448
 
442
449
  // ../../../gateway/dist/telemetry/metrics.js
443
450
  init_cjs_shims();
@@ -841,7 +848,7 @@ var HttpSuperblocksServerClient = class {
841
848
  }
842
849
  async uploadAttachment(input) {
843
850
  const mimeType = input.mimeType ?? "application/zip";
844
- const response = await (0, import_shared.uploadAttachmentToServerApi)({
851
+ const response = await (0, import_shared2.uploadAttachmentToServerApi)({
845
852
  applicationId: input.applicationId,
846
853
  authorization: `Bearer ${input.apiKey}`,
847
854
  baseUrl: this.baseUrl(),
@@ -888,10 +895,18 @@ var HttpSuperblocksServerClient = class {
888
895
  */
889
896
  async issueScopedToken(input) {
890
897
  const scope = input.scope ?? "apps:update";
891
- const body = {
892
- applicationId: input.applicationId,
893
- scope
894
- };
898
+ const body = { scope };
899
+ if (scope === "integrations:build") {
900
+ if (!input.integrationId?.trim()) {
901
+ throw new Error("integrations:build scoped tokens require an integrationId; the token authorizes exactly one integration");
902
+ }
903
+ body.integrationId = input.integrationId;
904
+ } else {
905
+ if (!input.applicationId?.trim()) {
906
+ throw new Error(`${scope} scoped tokens require an applicationId`);
907
+ }
908
+ body.applicationId = input.applicationId;
909
+ }
895
910
  if (scope === "apps:preview") {
896
911
  if (!input.commitId?.trim()) {
897
912
  throw new Error("apps:preview scoped tokens require a commitId so the viewer can load that commit");
@@ -1831,36 +1846,37 @@ async function captureModeOf(ctx) {
1831
1846
  });
1832
1847
  }
1833
1848
  function libraryCaptureInput(ctx, applicationId) {
1834
- const context = ctx.sessionPeer.libraryBrowserContext?.(applicationId);
1835
- if (!context) {
1849
+ const context2 = ctx.sessionPeer.libraryBrowserContext?.(applicationId);
1850
+ if (!context2) {
1836
1851
  return void 0;
1837
1852
  }
1838
- const auth = context.initData.devServerAuthorization;
1853
+ const auth = context2.initData.devServerAuthorization;
1839
1854
  if (!auth) {
1840
1855
  console.error("gateway live canvas skipped: missing_dev_server_authorization", { applicationId });
1841
1856
  return void 0;
1842
1857
  }
1843
1858
  return {
1844
1859
  bootstrapData: {
1845
- accessToken: context.bootstrapData.accessToken,
1846
- agents: context.bootstrapData.agents ?? [],
1847
- ...context.bootstrapData.agentUrls ? { agentUrls: context.bootstrapData.agentUrls } : {},
1848
- appId: context.bootstrapData.appId,
1849
- ...context.bootstrapData.featureFlags ? { featureFlags: context.bootstrapData.featureFlags } : {},
1850
- ...context.bootstrapData.orgId ? { orgId: context.bootstrapData.orgId } : {},
1851
- ...context.bootstrapData.profiles ? { profiles: context.bootstrapData.profiles } : {},
1852
- token: context.bootstrapData.token,
1853
- ...context.bootstrapData.userEmail ? { userEmail: context.bootstrapData.userEmail } : {},
1854
- userId: context.bootstrapData.userId,
1855
- ...context.bootstrapData.windowOriginUrl ? { windowOriginUrl: context.bootstrapData.windowOriginUrl } : {}
1860
+ accessToken: context2.bootstrapData.accessToken,
1861
+ agents: context2.bootstrapData.agents ?? [],
1862
+ ...context2.bootstrapData.agentUrls ? { agentUrls: context2.bootstrapData.agentUrls } : {},
1863
+ appId: context2.bootstrapData.appId,
1864
+ devServerAuthorization: auth,
1865
+ ...context2.bootstrapData.featureFlags ? { featureFlags: context2.bootstrapData.featureFlags } : {},
1866
+ ...context2.bootstrapData.orgId ? { orgId: context2.bootstrapData.orgId } : {},
1867
+ ...context2.bootstrapData.profiles ? { profiles: context2.bootstrapData.profiles } : {},
1868
+ token: context2.bootstrapData.token,
1869
+ ...context2.bootstrapData.userEmail ? { userEmail: context2.bootstrapData.userEmail } : {},
1870
+ userId: context2.bootstrapData.userId,
1871
+ ...context2.bootstrapData.windowOriginUrl ? { windowOriginUrl: context2.bootstrapData.windowOriginUrl } : {}
1856
1872
  },
1857
- iframeBaseUrl: context.iframeBaseUrl,
1873
+ iframeBaseUrl: context2.iframeBaseUrl,
1858
1874
  initData: {
1859
- appId: context.initData.appId,
1875
+ appId: context2.initData.appId,
1860
1876
  devServerAuthorization: auth,
1861
- peerId: context.initData.peerId,
1862
- ...context.initData.userId ? { userId: context.initData.userId } : {},
1863
- ...context.initData.windowOriginUrl ? { windowOriginUrl: context.initData.windowOriginUrl } : {}
1877
+ peerId: context2.initData.peerId,
1878
+ ...context2.initData.userId ? { userId: context2.initData.userId } : {},
1879
+ ...context2.initData.windowOriginUrl ? { windowOriginUrl: context2.initData.windowOriginUrl } : {}
1864
1880
  }
1865
1881
  };
1866
1882
  }
@@ -1984,13 +2000,15 @@ function defaultAppName(prompt) {
1984
2000
  async function ensurePrincipal(ctx) {
1985
2001
  const { principal } = ctx;
1986
2002
  if (principal.organizationId && principal.userId) {
2003
+ const resolved2 = {
2004
+ ...principal,
2005
+ organizationId: principal.organizationId,
2006
+ userId: principal.userId
2007
+ };
2008
+ ctx.onPrincipalResolved?.(resolved2);
1987
2009
  return {
1988
2010
  kind: "ok",
1989
- value: {
1990
- ...principal,
1991
- organizationId: principal.organizationId,
1992
- userId: principal.userId
1993
- }
2011
+ value: resolved2
1994
2012
  };
1995
2013
  }
1996
2014
  let validated;
@@ -2020,14 +2038,16 @@ async function ensurePrincipal(ctx) {
2020
2038
  message: "The Superblocks API rejected the credential issued for this caller."
2021
2039
  };
2022
2040
  }
2041
+ const resolved = {
2042
+ ...principal,
2043
+ email: validated.email,
2044
+ organizationId: validated.organizationId,
2045
+ userId: validated.userId
2046
+ };
2047
+ ctx.onPrincipalResolved?.(resolved);
2023
2048
  return {
2024
2049
  kind: "ok",
2025
- value: {
2026
- ...principal,
2027
- email: validated.email,
2028
- organizationId: validated.organizationId,
2029
- userId: validated.userId
2030
- }
2050
+ value: resolved
2031
2051
  };
2032
2052
  }
2033
2053
  async function nameWithClark(ctx, input) {
@@ -2220,7 +2240,7 @@ function turnSummary(turn) {
2220
2240
  return "Superblocks is still working, but only the editor can show it \u2014 open the edit URL";
2221
2241
  }
2222
2242
  if (turn.generationStatus === "live_edit_terminated") {
2223
- return "The Superblocks live-edit session terminated before anything was built \u2014 send the prompt again with edit_app";
2243
+ return turn.retryAction === "retry_import" ? "The Superblocks live-edit session terminated before anything was built \u2014 send the archive again with import_app" : "The Superblocks live-edit session terminated before anything was built \u2014 send the prompt again with edit_app";
2224
2244
  }
2225
2245
  if (turn.checkpointCommitId) {
2226
2246
  return `Application ready \u2014 checkpoint saved. ${NEXT_STEPS}`;
@@ -2346,6 +2366,7 @@ async function forwardClarkPrompt(ctx, input) {
2346
2366
  generationStatus: forward.generationStatus,
2347
2367
  ...forward.liveEditId ? { liveEditId: forward.liveEditId } : {},
2348
2368
  ...forward.recentRuns && forward.recentRuns.length > 0 ? { recentRuns: forward.recentRuns } : {},
2369
+ ...forward.retryAction ? { retryAction: forward.retryAction } : {},
2349
2370
  viewStatus: forward.viewStatus
2350
2371
  }
2351
2372
  };
@@ -2391,7 +2412,7 @@ function namingCreatedApplication(applicationId, capability, result) {
2391
2412
  return namingApplication(applicationId, result);
2392
2413
  }
2393
2414
  function retryIfReplayable(attachments) {
2394
- return attachments && attachments.length > 0 ? void 0 : { pendingAction: "retry_build" };
2415
+ return attachments && attachments.length > 0 ? { pendingAction: "retry_import" } : { pendingAction: "retry_build" };
2395
2416
  }
2396
2417
  function liveEditUnavailable(error, attachments) {
2397
2418
  return {
@@ -2412,7 +2433,8 @@ function rememberFailedStart(ctx, input) {
2412
2433
  organizationId: input.principal.organizationId,
2413
2434
  // Reaches the caller as the dead start's reply on its next poll, so it
2414
2435
  // has to stand on its own there rather than lean on this call's error.
2415
- reason: `The Superblocks live-edit session for application ${input.applicationId} never opened, so the prompt never reached Superblocks and nothing was built. Nothing is running for that application; send the same instruction again to retry it. (${cause})`,
2436
+ reason: input.reason ?? `The Superblocks live-edit session for application ${input.applicationId} never opened, so the prompt never reached Superblocks and nothing was built. Nothing is running for that application; send the same instruction again to retry it. (${cause})`,
2437
+ ...input.retryAction ? { retryAction: input.retryAction } : {},
2416
2438
  userId: input.principal.userId
2417
2439
  });
2418
2440
  } catch (error) {
@@ -2442,7 +2464,12 @@ async function promptNewApplication(ctx, args) {
2442
2464
  organizationId: principal.organizationId
2443
2465
  });
2444
2466
  } catch (error) {
2445
- rememberFailedStart(ctx, { applicationId, error, principal });
2467
+ rememberFailedStart(ctx, {
2468
+ applicationId,
2469
+ error,
2470
+ principal,
2471
+ ...args.attachments?.length ? { retryAction: "retry_import" } : {}
2472
+ });
2446
2473
  return liveEditUnavailable(error, args.attachments);
2447
2474
  }
2448
2475
  ctx.onProgress?.({
@@ -2637,7 +2664,7 @@ async function resolveImportArchive(input, searchDirs) {
2637
2664
  if (bytes.byteLength > IMPORT_ZIP_MAX_BYTES) {
2638
2665
  return tooLargeError(bytes.byteLength);
2639
2666
  }
2640
- const archiveType = (0, import_shared2.detectAttachmentArchiveType)({ fileName });
2667
+ const archiveType = (0, import_shared3.detectAttachmentArchiveType)({ fileName });
2641
2668
  if (archiveType === null) {
2642
2669
  return {
2643
2670
  kind: "error",
@@ -2679,14 +2706,15 @@ function normalizeImportSource(source) {
2679
2706
  }
2680
2707
  }
2681
2708
  async function importApp(ctx, input) {
2709
+ const resumedApplicationId = input.applicationId?.trim();
2682
2710
  const archiveResult = await resolveImportArchive(input, ctx.config.importSearchDirs);
2683
2711
  if (archiveResult.kind !== "ok") {
2684
- return archiveResult;
2712
+ return resumedApplicationId ? namingApplication(resumedApplicationId, archiveResult) : archiveResult;
2685
2713
  }
2686
2714
  const archive = archiveResult.value;
2687
2715
  const principalResult = await ensurePrincipal(ctx);
2688
2716
  if (principalResult.kind !== "ok") {
2689
- return principalResult;
2717
+ return resumedApplicationId ? namingApplication(resumedApplicationId, principalResult) : principalResult;
2690
2718
  }
2691
2719
  const principal = principalResult.value;
2692
2720
  const source = normalizeImportSource(input.source);
@@ -2697,23 +2725,26 @@ async function importApp(ctx, input) {
2697
2725
  });
2698
2726
  const explicitName = input.name?.trim();
2699
2727
  const name = explicitName || defaultAppName(migrationPrompt);
2700
- ctx.onProgress?.({
2701
- message: "Creating application for import",
2702
- type: "progress"
2703
- });
2704
- const created = await createApplicationForPrompt(ctx, {
2705
- apiKey: principal.apiKey,
2706
- name,
2707
- organizationId: principal.organizationId,
2708
- templateName: "app-fullstack"
2709
- });
2710
- if (created.kind !== "ok") {
2711
- return created;
2728
+ let applicationId = resumedApplicationId;
2729
+ if (!applicationId) {
2730
+ ctx.onProgress?.({
2731
+ message: "Creating application for import",
2732
+ type: "progress"
2733
+ });
2734
+ const created = await createApplicationForPrompt(ctx, {
2735
+ apiKey: principal.apiKey,
2736
+ name,
2737
+ organizationId: principal.organizationId,
2738
+ templateName: "app-fullstack"
2739
+ });
2740
+ if (created.kind !== "ok") {
2741
+ return created;
2742
+ }
2743
+ applicationId = created.value.id;
2712
2744
  }
2713
- const app = created.value;
2714
- await rememberApplication(ctx, principal, app.id);
2715
- return namingCreatedApplication(app.id, "import_app", await migrateArchiveIntoApplication(ctx, {
2716
- applicationId: app.id,
2745
+ await rememberApplication(ctx, principal, applicationId);
2746
+ const result = await migrateArchiveIntoApplication(ctx, {
2747
+ applicationId,
2717
2748
  archive,
2718
2749
  explicitName,
2719
2750
  input,
@@ -2721,7 +2752,8 @@ async function importApp(ctx, input) {
2721
2752
  name,
2722
2753
  principal,
2723
2754
  source
2724
- }));
2755
+ });
2756
+ return resumedApplicationId ? namingApplication(applicationId, result) : namingCreatedApplication(applicationId, "import_app", result);
2725
2757
  }
2726
2758
  async function migrateArchiveIntoApplication(ctx, args) {
2727
2759
  const { applicationId, archive, principal } = args;
@@ -2740,10 +2772,18 @@ async function migrateArchiveIntoApplication(ctx, args) {
2740
2772
  });
2741
2773
  } catch (error) {
2742
2774
  const message = error instanceof Error ? error.message : String(error);
2775
+ rememberFailedStart(ctx, {
2776
+ applicationId,
2777
+ error,
2778
+ principal,
2779
+ reason: `The archive for application ${applicationId} could not be uploaded, so the migration never started and nothing was built. Send the same archive again with import_app to retry it. (${message})`,
2780
+ retryAction: "retry_import"
2781
+ });
2743
2782
  return {
2744
2783
  kind: "error",
2745
2784
  code: "import_upload_failed",
2746
- message
2785
+ message,
2786
+ pendingAction: "retry_import"
2747
2787
  };
2748
2788
  }
2749
2789
  return promptNewApplication(ctx, {
@@ -2828,7 +2868,7 @@ async function checkAppProgress(ctx, input) {
2828
2868
  }
2829
2869
  const applicationId = resolved.value;
2830
2870
  if (ctx.signal?.aborted) {
2831
- return cancelled("check_app_progress");
2871
+ return namingApplication(applicationId, cancelled("check_app_progress"));
2832
2872
  }
2833
2873
  const cursorKey2 = cursorKeyFor(ctx, principal, applicationId);
2834
2874
  const stored = cursorKey2 ? await ctx.progressCursors?.getCursor(cursorKey2) : void 0;
@@ -2840,7 +2880,7 @@ async function checkAppProgress(ctx, input) {
2840
2880
  waitMs: hostCallBudgetMs(ctx, ctx.config.clarkProgressWaitMs)
2841
2881
  });
2842
2882
  if (ctx.signal?.aborted) {
2843
- return cancelled("check_app_progress");
2883
+ return namingApplication(applicationId, cancelled("check_app_progress"));
2844
2884
  }
2845
2885
  const snapshot = await readSnapshot(ctx, {
2846
2886
  applicationId,
@@ -2914,6 +2954,7 @@ async function checkAppProgress(ctx, input) {
2914
2954
  reply: update.reply,
2915
2955
  ...update.decision ? { decision: update.decision } : {},
2916
2956
  generationStatus: update.generationStatus,
2957
+ ...update.retryAction && emptyTurnStatus(update.generationStatus) ? { pendingAction: update.retryAction } : {},
2917
2958
  status,
2918
2959
  viewStatus: update.viewStatus
2919
2960
  }
@@ -3335,7 +3376,11 @@ function localUnbuiltReason(turn, pending) {
3335
3376
  return void 0;
3336
3377
  }
3337
3378
  const status = emptyTurnStatus(turn.generationStatus);
3338
- return status ? { kind: "empty_turn", status } : void 0;
3379
+ return status ? {
3380
+ kind: "empty_turn",
3381
+ retryAction: turn.retryAction ?? "retry_build",
3382
+ status
3383
+ } : void 0;
3339
3384
  }
3340
3385
  async function persistedUnbuiltReason(ctx, principal, applicationId) {
3341
3386
  let snapshot;
@@ -3378,6 +3423,8 @@ function persistedTurnEnd(status) {
3378
3423
  // plain question and cannot be approved.
3379
3424
  case "needs_decision":
3380
3425
  return { kind: "awaiting_decision" };
3426
+ // The stream never recorded whether the prompt carried an archive, so
3427
+ // this path cannot pick retry_build vs retry_import. Omit rather than guess.
3381
3428
  case "live_edit_terminated":
3382
3429
  return { kind: "empty_turn", status };
3383
3430
  // A `cancelled` in the tail may still have a real app behind it: the
@@ -3436,7 +3483,11 @@ async function unbuiltApp(ctx, principal, applicationId) {
3436
3483
  if (reason.kind === "empty_turn") {
3437
3484
  return {
3438
3485
  kind: "ok",
3439
- value: { ...app, pendingAction: "retry_build", status: reason.status }
3486
+ value: {
3487
+ ...app,
3488
+ ...reason.retryAction ? { pendingAction: reason.retryAction } : {},
3489
+ status: reason.status
3490
+ }
3440
3491
  };
3441
3492
  }
3442
3493
  return {
@@ -3538,7 +3589,7 @@ async function waitForBuild(ctx, input) {
3538
3589
  let started = false;
3539
3590
  for (; ; ) {
3540
3591
  if (ctx.signal?.aborted) {
3541
- return cancelled("get_app");
3592
+ return namingApplication(input.applicationId, cancelled("get_app"));
3542
3593
  }
3543
3594
  if (nowMs(ctx) >= deadline) {
3544
3595
  return { kind: "ok", value: { built: false } };
@@ -4192,7 +4243,7 @@ async function createStores() {
4192
4243
  // ../../../gateway/dist/sabs/websocket-session-peer.js
4193
4244
  init_cjs_shims();
4194
4245
  import { randomUUID as randomUUID3 } from "node:crypto";
4195
- var import_shared5 = __toESM(require_dist());
4246
+ var import_shared6 = __toESM(require_dist());
4196
4247
 
4197
4248
  // ../../../gateway/dist/agents/resolve-orchestrator-url.js
4198
4249
  init_cjs_shims();
@@ -4513,15 +4564,15 @@ var HttpOrchestratorClient = class {
4513
4564
 
4514
4565
  // ../../../gateway/dist/sabs/browser-context.js
4515
4566
  init_cjs_shims();
4516
- var import_shared3 = __toESM(require_dist());
4567
+ var import_shared4 = __toESM(require_dist());
4517
4568
  function toProfile(row) {
4518
- return new import_shared3.Profile({
4569
+ return new import_shared4.Profile({
4519
4570
  allowedInEditor: row.allowedInEditor,
4520
4571
  description: "",
4521
4572
  displayName: row.displayName ?? row.key,
4522
4573
  id: row.id ?? row.key,
4523
4574
  key: row.key,
4524
- type: import_shared3.ProfileType.RESERVED
4575
+ type: import_shared4.ProfileType.RESERVED
4525
4576
  });
4526
4577
  }
4527
4578
  function selectProfiles(input) {
@@ -4558,6 +4609,7 @@ function buildBrowserContext(input) {
4558
4609
  agents: [],
4559
4610
  agentUrls: [input.orchestratorUrl],
4560
4611
  appId: input.applicationId,
4612
+ devServerAuthorization: `Bearer ${input.sessionToken}`,
4561
4613
  featureFlags: {
4562
4614
  "clark.sdk-api.enabled": true
4563
4615
  },
@@ -4584,7 +4636,7 @@ function buildBrowserContext(input) {
4584
4636
 
4585
4637
  // ../../../gateway/dist/sabs/editor-client-methods.js
4586
4638
  init_cjs_shims();
4587
- var import_shared4 = __toESM(require_dist());
4639
+ var import_shared5 = __toESM(require_dist());
4588
4640
 
4589
4641
  // ../../../gateway/dist/integrations/map.js
4590
4642
  init_cjs_shims();
@@ -4739,7 +4791,7 @@ function sanitizeAgentFacingValue(value) {
4739
4791
  }
4740
4792
 
4741
4793
  // ../../../gateway/dist/sabs/editor-client-methods.js
4742
- var ADMIN_MCP_SETUP_MESSAGE = "Integrations CRUD is available on this Superblocks MCP via create_integration, update_integration, and list_integrations (same connector as start_app). Use those Admin tools, then continue with edit_app. The headless editor session cannot open the Integrations UI.";
4794
+ var ADMIN_MCP_SETUP_MESSAGE = "Integrations CRUD is available on this Superblocks MCP via get_integration_config_schema, create_integration, update_integration, and list_integrations (same connector as start_app). Call get_integration_config_schema before create_integration. Use those Admin tools, then continue with edit_app. The headless editor session cannot open the Integrations UI.";
4743
4795
  var HEADLESS_AUTO_APPROVED_TOOLS = /* @__PURE__ */ new Set([
4744
4796
  "build_deleteFile",
4745
4797
  "build_installPackages",
@@ -4825,27 +4877,27 @@ function clarifySdkExecutionErrorMessage(message) {
4825
4877
  }
4826
4878
  return sanitizeAgentFacingText(clarified);
4827
4879
  }
4828
- async function executeRequestToIntegrationViaOrchestrator(context, payload) {
4880
+ async function executeRequestToIntegrationViaOrchestrator(context2, payload) {
4829
4881
  let pluginId = payload.integrationHint?.type;
4830
4882
  let integrationName = payload.integrationHint?.displayName ?? payload.integrationHint?.name ?? "Integration";
4831
4883
  if (!pluginId) {
4832
4884
  let row;
4833
4885
  try {
4834
- row = await context.server.getIntegration({
4835
- apiKey: context.apiKey,
4886
+ row = await context2.server.getIntegration({
4887
+ apiKey: context2.apiKey,
4836
4888
  integrationId: payload.integrationId
4837
4889
  });
4838
4890
  if (!row) {
4839
- const rows = await context.server.getSupersetIntegrations({
4840
- apiKey: context.apiKey,
4841
- applicationId: context.applicationId,
4842
- organizationId: context.organizationId
4891
+ const rows = await context2.server.getSupersetIntegrations({
4892
+ apiKey: context2.apiKey,
4893
+ applicationId: context2.applicationId,
4894
+ organizationId: context2.organizationId
4843
4895
  });
4844
4896
  row = rows.find((candidate) => candidate.id === payload.integrationId);
4845
4897
  }
4846
4898
  } catch (error) {
4847
4899
  console.warn("gateway failed to resolve integration for execute", {
4848
- applicationId: context.applicationId,
4900
+ applicationId: context2.applicationId,
4849
4901
  integrationId: payload.integrationId,
4850
4902
  message: error instanceof Error ? error.message : String(error)
4851
4903
  });
@@ -4858,7 +4910,7 @@ async function executeRequestToIntegrationViaOrchestrator(context, payload) {
4858
4910
  pluginId = row.pluginId;
4859
4911
  integrationName = row.name || integrationName;
4860
4912
  }
4861
- const pluginIdForStep = (0, import_shared4.getBasePluginId)(pluginId);
4913
+ const pluginIdForStep = (0, import_shared5.getBasePluginId)(pluginId);
4862
4914
  const now = (/* @__PURE__ */ new Date()).toISOString();
4863
4915
  const inlineApiId = `ai-inline-${payload.integrationId}-${Date.now()}`;
4864
4916
  const definition = {
@@ -4874,14 +4926,14 @@ async function executeRequestToIntegrationViaOrchestrator(context, payload) {
4874
4926
  metadata: {
4875
4927
  id: inlineApiId,
4876
4928
  name: `${integrationName} AI Query`,
4877
- organization: context.organizationId,
4929
+ organization: context2.organizationId,
4878
4930
  timestamps: { created: now, updated: now }
4879
4931
  },
4880
4932
  trigger: { application: {} }
4881
4933
  };
4882
4934
  try {
4883
- const result = await context.orchestrator.executeV2Api({
4884
- apiKey: context.apiKey,
4935
+ const result = await context2.orchestrator.executeV2Api({
4936
+ apiKey: context2.apiKey,
4885
4937
  body: {
4886
4938
  definition: { api: definition },
4887
4939
  inputs: {},
@@ -4892,10 +4944,10 @@ async function executeRequestToIntegrationViaOrchestrator(context, payload) {
4892
4944
  isAiTriggered: true,
4893
4945
  suppressTracking: true
4894
4946
  },
4895
- ...context.profileKey ? { profile: { name: context.profileKey } } : {}
4947
+ ...context2.profileKey ? { profile: { name: context2.profileKey } } : {}
4896
4948
  },
4897
- profileKey: context.profileKey,
4898
- scopedToken: context.sessionToken
4949
+ profileKey: context2.profileKey,
4950
+ scopedToken: context2.sessionToken
4899
4951
  });
4900
4952
  if (result && typeof result === "object" && "systemError" in result) {
4901
4953
  const systemError = String(result.systemError ?? "Integration execution failed");
@@ -4931,11 +4983,11 @@ async function executeRequestToIntegrationViaOrchestrator(context, payload) {
4931
4983
  return { error: clarifySdkExecutionErrorMessage(message) };
4932
4984
  }
4933
4985
  }
4934
- async function loadIntegrationsForPrompt(context) {
4935
- const rows = await context.server.getSupersetIntegrations({
4936
- apiKey: context.apiKey,
4937
- applicationId: context.applicationId,
4938
- organizationId: context.organizationId
4986
+ async function loadIntegrationsForPrompt(context2) {
4987
+ const rows = await context2.server.getSupersetIntegrations({
4988
+ apiKey: context2.apiKey,
4989
+ applicationId: context2.applicationId,
4990
+ organizationId: context2.organizationId
4939
4991
  });
4940
4992
  return listIntegrationsForClark(rows);
4941
4993
  }
@@ -5080,10 +5132,9 @@ function createEditorClientMethods(getListener, onInbound, deps) {
5080
5132
  ],
5081
5133
  updateApiInputs: ack,
5082
5134
  updateScope: ack,
5083
- // No browser tab means no viewport to photograph. The flag matters more
5084
- // than the message: it tells the ai-service this was a refusal, not a
5085
- // failed capture, so Clark can fall back to the sandbox's own browser
5086
- // instead of reporting that it cannot see the app it just built.
5135
+ // No browser tab means no viewport to photograph. viewportUnavailable
5136
+ // marks this as a refusal, not a failed capture. The screenshot tool
5137
+ // does not read that flag; MCP turns omit the tool via disabledTools.
5087
5138
  aiCaptureScreenshot: [
5088
5139
  async () => ({
5089
5140
  error: "the headless gateway session has no browser viewport",
@@ -5095,13 +5146,13 @@ function createEditorClientMethods(getListener, onInbound, deps) {
5095
5146
  aiDeleteIntegration: setupUnavailable,
5096
5147
  aiExecuteRequestToIntegration: [
5097
5148
  async (payload) => {
5098
- const context = deps?.getContext();
5099
- if (!context) {
5149
+ const context2 = deps?.getContext();
5150
+ if (!context2) {
5100
5151
  return {
5101
5152
  error: "the headless gateway session cannot execute integration requests without an active prompt session"
5102
5153
  };
5103
5154
  }
5104
- const result = await executeRequestToIntegrationViaOrchestrator(context, payload);
5155
+ const result = await executeRequestToIntegrationViaOrchestrator(context2, payload);
5105
5156
  const name = payload.integrationHint?.name || payload.integrationId || "integration";
5106
5157
  deps?.onToolRun?.({
5107
5158
  detail: result,
@@ -5114,8 +5165,8 @@ function createEditorClientMethods(getListener, onInbound, deps) {
5114
5165
  ],
5115
5166
  aiExecuteSdkApi: [
5116
5167
  async (payload) => {
5117
- const context = deps?.getContext();
5118
- if (!context) {
5168
+ const context2 = deps?.getContext();
5169
+ if (!context2) {
5119
5170
  throw missingSessionError("execute SDK APIs");
5120
5171
  }
5121
5172
  if (!payload.entryPoint) {
@@ -5135,7 +5186,7 @@ function createEditorClientMethods(getListener, onInbound, deps) {
5135
5186
  return missing;
5136
5187
  }
5137
5188
  try {
5138
- const inSync = await context.waitUntilFilesSynced();
5189
+ const inSync = await context2.waitUntilFilesSynced();
5139
5190
  if (!inSync) {
5140
5191
  const syncing = {
5141
5192
  success: false,
@@ -5152,15 +5203,15 @@ function createEditorClientMethods(getListener, onInbound, deps) {
5152
5203
  });
5153
5204
  return syncing;
5154
5205
  }
5155
- const response = await context.orchestrator.executeSdkApiV3({
5156
- apiKey: context.apiKey,
5157
- applicationId: context.applicationId,
5158
- branchName: context.branchName,
5206
+ const response = await context2.orchestrator.executeSdkApiV3({
5207
+ apiKey: context2.apiKey,
5208
+ applicationId: context2.applicationId,
5209
+ branchName: context2.branchName,
5159
5210
  entryPoint: payload.entryPoint,
5160
5211
  exportName: payload.exportName,
5161
5212
  inputs: payload.input ?? {},
5162
- profileKey: context.profileKey,
5163
- scopedToken: context.sessionToken
5213
+ profileKey: context2.profileKey,
5214
+ scopedToken: context2.sessionToken
5164
5215
  });
5165
5216
  const mapped = mapSdkExecutionResponse(response);
5166
5217
  deps?.onToolRun?.({
@@ -5190,8 +5241,8 @@ function createEditorClientMethods(getListener, onInbound, deps) {
5190
5241
  ],
5191
5242
  aiExecuteV2Api: [
5192
5243
  async (payload) => {
5193
- const context = deps?.getContext();
5194
- if (!context) {
5244
+ const context2 = deps?.getContext();
5245
+ if (!context2) {
5195
5246
  throw missingSessionError("execute V2 APIs");
5196
5247
  }
5197
5248
  if (payload.api.type === "fetch") {
@@ -5204,14 +5255,14 @@ function createEditorClientMethods(getListener, onInbound, deps) {
5204
5255
  ...payload.api.definition,
5205
5256
  metadata: {
5206
5257
  ...payload.api.definition.metadata ?? {},
5207
- organization: context.organizationId,
5258
+ organization: context2.organizationId,
5208
5259
  timestamps: { created: now, updated: now }
5209
5260
  },
5210
5261
  trigger: { application: {} }
5211
5262
  };
5212
5263
  try {
5213
- const result = await context.orchestrator.executeV2Api({
5214
- apiKey: context.apiKey,
5264
+ const result = await context2.orchestrator.executeV2Api({
5265
+ apiKey: context2.apiKey,
5215
5266
  body: {
5216
5267
  definition: { api: definition },
5217
5268
  inputs: payload.inputs ?? {},
@@ -5222,10 +5273,10 @@ function createEditorClientMethods(getListener, onInbound, deps) {
5222
5273
  isAiTriggered: true,
5223
5274
  suppressTracking: true
5224
5275
  },
5225
- ...context.profileKey ? { profile: { name: context.profileKey } } : {}
5276
+ ...context2.profileKey ? { profile: { name: context2.profileKey } } : {}
5226
5277
  },
5227
- profileKey: context.profileKey,
5228
- scopedToken: context.sessionToken
5278
+ profileKey: context2.profileKey,
5279
+ scopedToken: context2.sessionToken
5229
5280
  });
5230
5281
  if (result && typeof result === "object" && "systemError" in result) {
5231
5282
  return result;
@@ -5247,27 +5298,27 @@ function createEditorClientMethods(getListener, onInbound, deps) {
5247
5298
  ],
5248
5299
  aiGetIntegration: [
5249
5300
  async (payload) => {
5250
- const context = deps?.getContext();
5251
- if (!context) {
5301
+ const context2 = deps?.getContext();
5302
+ if (!context2) {
5252
5303
  throw missingSessionError("provide integration metadata");
5253
5304
  }
5254
5305
  let row;
5255
5306
  try {
5256
- row = await context.server.getIntegration({
5257
- apiKey: context.apiKey,
5307
+ row = await context2.server.getIntegration({
5308
+ apiKey: context2.apiKey,
5258
5309
  integrationId: payload.integrationId
5259
5310
  });
5260
5311
  if (!row) {
5261
- const rows = await context.server.getSupersetIntegrations({
5262
- apiKey: context.apiKey,
5263
- applicationId: context.applicationId,
5264
- organizationId: context.organizationId
5312
+ const rows = await context2.server.getSupersetIntegrations({
5313
+ apiKey: context2.apiKey,
5314
+ applicationId: context2.applicationId,
5315
+ organizationId: context2.organizationId
5265
5316
  });
5266
5317
  row = rows.find((candidate) => candidate.id === payload.integrationId);
5267
5318
  }
5268
5319
  } catch (error) {
5269
5320
  console.warn("gateway failed to load integration for Clark", {
5270
- applicationId: context.applicationId,
5321
+ applicationId: context2.applicationId,
5271
5322
  integrationId: payload.integrationId,
5272
5323
  message: error instanceof Error ? error.message : String(error)
5273
5324
  });
@@ -5283,11 +5334,11 @@ function createEditorClientMethods(getListener, onInbound, deps) {
5283
5334
  }
5284
5335
  let metadata = {};
5285
5336
  try {
5286
- metadata = await context.orchestrator.getMetadata({
5287
- apiKey: context.apiKey,
5337
+ metadata = await context2.orchestrator.getMetadata({
5338
+ apiKey: context2.apiKey,
5288
5339
  integrationId: payload.integrationId,
5289
- profileKey: context.profileKey,
5290
- scopedToken: context.sessionToken
5340
+ profileKey: context2.profileKey,
5341
+ scopedToken: context2.sessionToken
5291
5342
  });
5292
5343
  } catch {
5293
5344
  metadata = {};
@@ -5309,15 +5360,15 @@ function createEditorClientMethods(getListener, onInbound, deps) {
5309
5360
  ],
5310
5361
  aiGetIntegrations: [
5311
5362
  async () => {
5312
- const context = deps?.getContext();
5313
- if (!context) {
5363
+ const context2 = deps?.getContext();
5364
+ if (!context2) {
5314
5365
  return [];
5315
5366
  }
5316
5367
  try {
5317
- return await loadIntegrationsForPrompt(context);
5368
+ return await loadIntegrationsForPrompt(context2);
5318
5369
  } catch (error) {
5319
5370
  console.warn("gateway failed to list integrations for Clark", {
5320
- applicationId: context.applicationId,
5371
+ applicationId: context2.applicationId,
5321
5372
  message: error instanceof Error ? error.message : String(error)
5322
5373
  });
5323
5374
  return [];
@@ -5418,6 +5469,7 @@ function truncate(text, max = RECENT_TOOL_RUN_SUMMARY_MAX) {
5418
5469
  var GENERATE_METHOD = "editor.aiGenerateWithQueue";
5419
5470
  var ACCEPT_DRAFT_METHOD = "editor.aiAcceptDraft";
5420
5471
  var RELOAD_CHAT_METHOD = "editor.aiChatGetMessages";
5472
+ var RELEASE_TURN_LOCK_METHOD = "editor.releaseTurnLock";
5421
5473
  var RESPOND_TOOL_PERMISSION_METHOD = "editor.aiRespondToToolPermission";
5422
5474
  var PING_METHOD = "editor.ping";
5423
5475
  var DEFAULT_TIMEOUT_MS3 = 12e4;
@@ -5759,6 +5811,7 @@ var WebSocketSessionPeer = class {
5759
5811
  this.failedStarts.set(input, {
5760
5812
  generationStatus: "live_edit_terminated",
5761
5813
  reply: input.reason,
5814
+ ...input.retryAction ? { retryAction: input.retryAction } : {},
5762
5815
  // Nothing ran, so nothing is viewable. Reporting "ready" here is how a
5763
5816
  // caller comes to show the untouched starter template as the app.
5764
5817
  viewStatus: "unknown"
@@ -5791,7 +5844,11 @@ var WebSocketSessionPeer = class {
5791
5844
  }
5792
5845
  const failed = this.failedStarts.get(input);
5793
5846
  if (failed) {
5794
- return { generationStatus: failed.generationStatus, phase: "finished" };
5847
+ return {
5848
+ generationStatus: failed.generationStatus,
5849
+ phase: "finished",
5850
+ ...failed.retryAction ? { retryAction: failed.retryAction } : {}
5851
+ };
5795
5852
  }
5796
5853
  if (!turn?.finished) {
5797
5854
  return void 0;
@@ -5799,7 +5856,8 @@ var WebSocketSessionPeer = class {
5799
5856
  return {
5800
5857
  ...turn.finished.checkpointCommitId ? { checkpointCommitId: turn.finished.checkpointCommitId } : {},
5801
5858
  generationStatus: turn.finished.generationStatus,
5802
- phase: "finished"
5859
+ phase: "finished",
5860
+ ...turn.retryAction ? { retryAction: turn.retryAction } : {}
5803
5861
  };
5804
5862
  }
5805
5863
  clearPendingPlan(applicationId) {
@@ -5945,6 +6003,7 @@ var WebSocketSessionPeer = class {
5945
6003
  normalized: void 0,
5946
6004
  recentRuns: [],
5947
6005
  reconnects: 0,
6006
+ ...input.attachments?.length ? { retryAction: "retry_import" } : {},
5948
6007
  startedAt: Date.now(),
5949
6008
  turnId: randomUUID3(),
5950
6009
  unsubscribe: () => void 0,
@@ -6005,6 +6064,7 @@ var WebSocketSessionPeer = class {
6005
6064
  jwt: input.sessionToken,
6006
6065
  llmConfig: {
6007
6066
  coreModel: this.options.coreModel ?? DEFAULT_LLM.coreModel,
6067
+ disabledTools: ["build_captureScreenshot", "build_navigatePreview"],
6008
6068
  // The ai-service gates Clark's testing tools on this flag *and* a
6009
6069
  // browser context, so the two travel together or not at all.
6010
6070
  ...browserContext ? { flags: { isTestingEnabled: true } } : {},
@@ -6110,6 +6170,7 @@ var WebSocketSessionPeer = class {
6110
6170
  elapsedMs,
6111
6171
  status: "finished",
6112
6172
  ...turn.finished,
6173
+ ...turn.retryAction ? { retryAction: turn.retryAction } : {},
6113
6174
  ...recentRuns ? { recentRuns } : {}
6114
6175
  };
6115
6176
  }
@@ -6250,6 +6311,15 @@ var WebSocketSessionPeer = class {
6250
6311
  collector.dispose();
6251
6312
  const stillMapped = this.turns.get(input.applicationId) === turn;
6252
6313
  if (stillMapped) {
6314
+ try {
6315
+ await turn.held.client.requestWithTracing(RELEASE_TURN_LOCK_METHOD, this.turnCorrelator(turn.turnId), {}, `Bearer ${input.sessionToken}`);
6316
+ } catch (error) {
6317
+ console.warn("gateway could not release the live-edit turn lock", {
6318
+ applicationId: input.applicationId,
6319
+ message: describeSocketError(error),
6320
+ turnId: turn.turnId
6321
+ });
6322
+ }
6253
6323
  if (turn.finished?.generationStatus === "needs_decision") {
6254
6324
  this.armIdle(turn.held);
6255
6325
  } else {
@@ -6666,7 +6736,7 @@ var WebSocketSessionPeer = class {
6666
6736
  const heldRef = {
6667
6737
  current: void 0
6668
6738
  };
6669
- const socket = new import_shared5.ISocket(ws, createEditorClientMethods(() => {
6739
+ const socket = new import_shared6.ISocket(ws, createEditorClientMethods(() => {
6670
6740
  const listeners = heldRef.current?.listeners;
6671
6741
  if (!listeners || listeners.size === 0) {
6672
6742
  return void 0;
@@ -6743,7 +6813,7 @@ var WebSocketSessionPeer = class {
6743
6813
  noResponseTimeoutInSeconds: (this.options.timeoutMs ?? DEFAULT_TIMEOUT_MS3) / 1e3
6744
6814
  }
6745
6815
  });
6746
- const client = (0, import_shared5.createISocketClient)(socket);
6816
+ const client = (0, import_shared6.createISocketClient)(socket);
6747
6817
  const heartbeat = this.buildHeartbeat(heldRef);
6748
6818
  const held = {
6749
6819
  applicationId: input.applicationId,
@@ -6779,7 +6849,7 @@ var WebSocketSessionPeer = class {
6779
6849
  * who to tell.
6780
6850
  */
6781
6851
  buildHeartbeat(heldRef) {
6782
- return new import_shared5.SocketHeartbeat({
6852
+ return new import_shared6.SocketHeartbeat({
6783
6853
  beat: async () => {
6784
6854
  const held = heldRef.current;
6785
6855
  if (!held?.sessionToken) {
@@ -7306,6 +7376,233 @@ var WebSocketSessionPeer = class {
7306
7376
  }
7307
7377
  };
7308
7378
 
7379
+ // ../../../gateway/dist/telemetry/runtime.js
7380
+ init_cjs_shims();
7381
+
7382
+ // ../../../gateway/dist/telemetry/mcp-client.js
7383
+ init_cjs_shims();
7384
+ init_esm();
7385
+ function bounded(value, maxLength) {
7386
+ return value.slice(0, maxLength);
7387
+ }
7388
+ function stringProperty(value, key2) {
7389
+ if (typeof value !== "object" || value === null || !(key2 in value)) {
7390
+ return void 0;
7391
+ }
7392
+ const property = Reflect.get(value, key2);
7393
+ return typeof property === "string" ? property : void 0;
7394
+ }
7395
+ var McpClientTelemetry = class {
7396
+ tracer;
7397
+ ended = false;
7398
+ hadError = false;
7399
+ organizationId;
7400
+ sessionContext;
7401
+ sessionSpan;
7402
+ constructor(tracer) {
7403
+ this.tracer = tracer;
7404
+ this.sessionSpan = tracer.startSpan("gen_ai.request", {
7405
+ attributes: {
7406
+ "gen_ai.operation.name": "invoke_agent"
7407
+ }
7408
+ });
7409
+ this.sessionContext = trace.setSpan(context.active(), this.sessionSpan);
7410
+ }
7411
+ setOrganizationId(organizationId) {
7412
+ this.organizationId = organizationId;
7413
+ this.sessionSpan.setAttribute("organization.id", organizationId);
7414
+ }
7415
+ async runTool(call, operation) {
7416
+ const attributes = {
7417
+ "gen_ai.operation.name": "execute_tool",
7418
+ ...call.client ? {
7419
+ "mcp.client.name": bounded(call.client.name, 80),
7420
+ ...call.client.version ? {
7421
+ "mcp.client.version": bounded(call.client.version, 40)
7422
+ } : {}
7423
+ } : {},
7424
+ ...this.organizationId ? { "organization.id": this.organizationId } : {},
7425
+ "tool.name": bounded(call.toolName, 80)
7426
+ };
7427
+ if (call.client) {
7428
+ this.sessionSpan.setAttributes({
7429
+ "mcp.client.name": bounded(call.client.name, 80),
7430
+ ...call.client.version ? { "mcp.client.version": bounded(call.client.version, 40) } : {}
7431
+ });
7432
+ }
7433
+ let span;
7434
+ try {
7435
+ span = this.tracer.startSpan("gen_ai.tool_call", { attributes }, this.sessionContext);
7436
+ } catch {
7437
+ return operation();
7438
+ }
7439
+ const startedAt = Date.now();
7440
+ try {
7441
+ const result = await context.with(trace.setSpan(this.sessionContext, span), operation);
7442
+ span.setAttribute("tool.duration_ms", Date.now() - startedAt);
7443
+ if (this.organizationId) {
7444
+ span.setAttribute("organization.id", this.organizationId);
7445
+ }
7446
+ if (result.kind === "error") {
7447
+ this.hadError = true;
7448
+ span.setAttributes({
7449
+ ...result.applicationId ? { "application.id": result.applicationId } : {},
7450
+ "mcp.error.code": bounded(result.code, 80),
7451
+ "tool.error_category": result.code === "cancelled" ? "timeout" : "execution_error",
7452
+ "tool.status": "error"
7453
+ });
7454
+ span.setStatus({ code: SpanStatusCode.ERROR });
7455
+ } else if (result.kind === "auth_required") {
7456
+ this.hadError = true;
7457
+ span.setAttributes({
7458
+ "mcp.result.status": "auth_required",
7459
+ "tool.error_category": "permission_denied",
7460
+ "tool.status": "error"
7461
+ });
7462
+ } else {
7463
+ const applicationId = stringProperty(result.value, "applicationId");
7464
+ const resultStatus = stringProperty(result.value, "status");
7465
+ span.setAttributes({
7466
+ ...applicationId ? { "application.id": applicationId } : {},
7467
+ ...resultStatus ? { "mcp.result.status": bounded(resultStatus, 80) } : {},
7468
+ "tool.error_category": "none",
7469
+ "tool.status": "success"
7470
+ });
7471
+ }
7472
+ return result;
7473
+ } catch (error) {
7474
+ this.hadError = true;
7475
+ span.setAttributes({
7476
+ "tool.duration_ms": Date.now() - startedAt,
7477
+ "tool.error_category": "execution_error",
7478
+ "tool.status": "error"
7479
+ });
7480
+ span.setStatus({ code: SpanStatusCode.ERROR });
7481
+ throw error;
7482
+ } finally {
7483
+ span.end();
7484
+ }
7485
+ }
7486
+ endSession() {
7487
+ if (this.ended) {
7488
+ return;
7489
+ }
7490
+ this.ended = true;
7491
+ this.sessionSpan.setAttribute("gen_ai.request.status", this.hadError ? "error" : "success");
7492
+ this.sessionSpan.end();
7493
+ }
7494
+ };
7495
+
7496
+ // ../../../gateway/dist/telemetry/runtime.js
7497
+ var TELEMETRY_SHUTDOWN_MS = 2e3;
7498
+ function logToStderr(first, message) {
7499
+ if (message === void 0) {
7500
+ console.error(first);
7501
+ return;
7502
+ }
7503
+ console.error(first, message);
7504
+ }
7505
+ var stderrLogger = {
7506
+ error: logToStderr,
7507
+ info: logToStderr,
7508
+ warn: logToStderr
7509
+ };
7510
+ function telemetryBaseUrl(serverUrl) {
7511
+ try {
7512
+ const url = new URL(serverUrl);
7513
+ url.hash = "";
7514
+ url.pathname = `${url.pathname.replace(/\/$/u, "")}/api`;
7515
+ url.search = "";
7516
+ return url.toString().replace(/\/$/u, "");
7517
+ } catch {
7518
+ console.error("gateway telemetry skipped: server URL is not parseable");
7519
+ return void 0;
7520
+ }
7521
+ }
7522
+ function environmentName(config) {
7523
+ if (config.deploymentEnvironmentName !== "unknown") {
7524
+ return config.deploymentEnvironmentName;
7525
+ }
7526
+ try {
7527
+ return environmentFromHostname(new URL(config.serverUrl).hostname);
7528
+ } catch {
7529
+ return "unknown";
7530
+ }
7531
+ }
7532
+ function environmentFromHostname(hostname) {
7533
+ if (hostname.match(/^app\.superblocks(?:hq)?\.com$/u)) {
7534
+ return "prod";
7535
+ }
7536
+ if (hostname.match(/^eu\.superblocks(?:hq)?\.com$/u)) {
7537
+ return "prod-eu";
7538
+ }
7539
+ if (hostname.match(/^staging\.superblocks(?:hq)?\.com$/u)) {
7540
+ return "staging";
7541
+ }
7542
+ if (hostname.match(/^dev\.superblocks(?:hq)?\.com$/u)) {
7543
+ return "dev";
7544
+ }
7545
+ if (hostname.match(/^pr-[0-9]+\.superblocks(?:hq)?\.dev$/u)) {
7546
+ return "ephemeral";
7547
+ }
7548
+ if (hostname === "localhost" || hostname === "127.0.0.1") {
7549
+ return "local";
7550
+ }
7551
+ return "other";
7552
+ }
7553
+ function deploymentTypeFor(config) {
7554
+ if (config.localAgentMode) {
7555
+ return import_shared.DeploymentTypeEnum.CLOUD_PREM;
7556
+ }
7557
+ try {
7558
+ return parseDeploymentType(process.env.SUPERBLOCKS_DEPLOYMENT_TYPE);
7559
+ } catch {
7560
+ return import_shared.DeploymentTypeEnum.CLOUD;
7561
+ }
7562
+ }
7563
+ async function settleWithin(work, timeoutMs) {
7564
+ let timer;
7565
+ try {
7566
+ await Promise.race([
7567
+ work,
7568
+ new Promise((resolve) => {
7569
+ timer = setTimeout(resolve, timeoutMs);
7570
+ })
7571
+ ]);
7572
+ } finally {
7573
+ if (timer !== void 0) {
7574
+ clearTimeout(timer);
7575
+ }
7576
+ }
7577
+ }
7578
+ function startGatewayTelemetry(input) {
7579
+ const otlpUrl = telemetryBaseUrl(input.config.serverUrl);
7580
+ const telemetry = initNodeTelemetry({
7581
+ environment: environmentName(input.config),
7582
+ logger: stderrLogger,
7583
+ logsEnabled: false,
7584
+ // Counters in metrics.ts are created at import time, before this SDK
7585
+ // starts, so they stay no-ops. Do not turn metrics on until those
7586
+ // instruments are created after init.
7587
+ metricsEnabled: false,
7588
+ otlpHeaders: { Authorization: `Bearer ${input.apiKey}` },
7589
+ ...otlpUrl ? { otlpUrl } : {},
7590
+ serviceName: "gateway",
7591
+ serviceVersion: "0.0.1"
7592
+ }, getDefaultPolicy(deploymentTypeFor(input.config)));
7593
+ const mcpTelemetry = new McpClientTelemetry(telemetry.getTracer("gateway.mcp"));
7594
+ return {
7595
+ mcpTelemetry,
7596
+ shutdown: async () => {
7597
+ try {
7598
+ mcpTelemetry.endSession();
7599
+ } finally {
7600
+ await settleWithin(telemetry.shutdown(), TELEMETRY_SHUTDOWN_MS);
7601
+ }
7602
+ }
7603
+ };
7604
+ }
7605
+
7309
7606
  // ../../../gateway/dist/transports/mcp/mount.js
7310
7607
  init_cjs_shims();
7311
7608
  import { randomUUID as randomUUID4 } from "node:crypto";
@@ -7405,6 +7702,150 @@ function N3(Z, $, J, X, V) {
7405
7702
  return Z.registerResource($, J, { mimeType: p, ...X }, V);
7406
7703
  }
7407
7704
 
7705
+ // ../../../gateway/dist/capabilities/integration-metadata.js
7706
+ init_cjs_shims();
7707
+ var DEFAULT_LIMIT = 100;
7708
+ function labelPart(value) {
7709
+ return typeof value === "string" ? value.trim().replaceAll(/[.[\]]/g, "_") : "";
7710
+ }
7711
+ function arrayEntryLabel(value, index) {
7712
+ if (value && typeof value === "object") {
7713
+ const row = value;
7714
+ const name = labelPart(row.name);
7715
+ if (name) {
7716
+ const schema = labelPart(row.schema);
7717
+ return schema ? `${schema}.${name}` : name;
7718
+ }
7719
+ }
7720
+ return String(index);
7721
+ }
7722
+ function metadataEntries(value, path3 = "", entries = []) {
7723
+ if (value === null || typeof value === "boolean" || typeof value === "number" || typeof value === "string") {
7724
+ entries.push({ path: path3, value });
7725
+ return entries;
7726
+ }
7727
+ if (Array.isArray(value)) {
7728
+ const seen = /* @__PURE__ */ new Set();
7729
+ value.forEach((entry, index) => {
7730
+ const label = arrayEntryLabel(entry, index);
7731
+ const unique = seen.has(label) ? `${label}#${index}` : label;
7732
+ seen.add(unique);
7733
+ metadataEntries(entry, `${path3}[${unique}]`, entries);
7734
+ });
7735
+ return entries;
7736
+ }
7737
+ if (value && typeof value === "object") {
7738
+ for (const [key2, entry] of Object.entries(value)) {
7739
+ metadataEntries(entry, path3 ? `${path3}.${key2}` : key2, entries);
7740
+ }
7741
+ }
7742
+ return entries;
7743
+ }
7744
+ async function getIntegrationMetadata(context2, input) {
7745
+ const principal = await ensurePrincipal(context2);
7746
+ if (principal.kind !== "ok") {
7747
+ return principal;
7748
+ }
7749
+ let applicationId;
7750
+ let label = sanitizeAgentFacingText(input.integrationId);
7751
+ try {
7752
+ const resolved = await resolveApplicationId(context2, principal.value, input.applicationId);
7753
+ applicationId = resolved.kind === "ok" ? resolved.value : void 0;
7754
+ const rows = await context2.server.getSupersetIntegrations({
7755
+ apiKey: principal.value.apiKey,
7756
+ ...applicationId && { applicationId },
7757
+ organizationId: principal.value.organizationId
7758
+ });
7759
+ const integration = rows.find((row) => row.id === input.integrationId);
7760
+ if (!integration) {
7761
+ return {
7762
+ ...applicationId && { applicationId },
7763
+ kind: "error",
7764
+ code: "integration_not_accessible",
7765
+ message: `Integration ${label} was not found or is not accessible.`
7766
+ };
7767
+ }
7768
+ const integrationName = sanitizeAgentFacingText(integration.name);
7769
+ const pluginId = sanitizeAgentFacingText(integration.pluginId);
7770
+ label = integrationName;
7771
+ if (!hasBuildPermission(integration)) {
7772
+ return {
7773
+ ...applicationId && { applicationId },
7774
+ kind: "error",
7775
+ code: "integration_not_permitted",
7776
+ message: `You do not have build permission on ${integrationName}.`
7777
+ };
7778
+ }
7779
+ if (!integrationIsToolSupported(integration.pluginId)) {
7780
+ return {
7781
+ ...applicationId && { applicationId },
7782
+ kind: "error",
7783
+ code: "integration_not_supported",
7784
+ message: `${integrationName} is a ${pluginId} integration, which this tool cannot inspect.`
7785
+ };
7786
+ }
7787
+ const profiles = await context2.server.getOrganizationProfiles({
7788
+ apiKey: principal.value.apiKey,
7789
+ organizationId: principal.value.organizationId
7790
+ });
7791
+ const profileKey = pickEditorProfileKey(context2.config.profileKey, profiles);
7792
+ const inventory = await context2.server.getAgentInventory({
7793
+ apiKey: principal.value.apiKey,
7794
+ organizationId: principal.value.organizationId
7795
+ });
7796
+ const orchestratorUrl = pickOrchestratorBaseUrl({
7797
+ agentType: inventory.agentType,
7798
+ agents: inventory.agents,
7799
+ profileKey
7800
+ });
7801
+ const scopedToken = await context2.server.issueScopedToken(applicationId ? {
7802
+ apiKey: principal.value.apiKey,
7803
+ applicationId,
7804
+ scope: "apps:update"
7805
+ } : {
7806
+ apiKey: principal.value.apiKey,
7807
+ integrationId: input.integrationId,
7808
+ scope: "integrations:build"
7809
+ });
7810
+ const metadata = await new HttpOrchestratorClient(orchestratorUrl).getMetadata({
7811
+ apiKey: principal.value.apiKey,
7812
+ integrationId: input.integrationId,
7813
+ profileKey,
7814
+ scopedToken
7815
+ });
7816
+ const sanitized = sanitizeAgentFacingValue(metadata);
7817
+ const search = input.search?.trim().toLowerCase();
7818
+ const matched = metadataEntries(sanitized).filter((entry) => !search || entry.path.toLowerCase().includes(search) || String(entry.value).toLowerCase().includes(search));
7819
+ const limit = input.limit ?? DEFAULT_LIMIT;
7820
+ const offset = input.offset ?? 0;
7821
+ const entries = matched.slice(offset, offset + limit);
7822
+ const nextOffset = offset + entries.length < matched.length ? offset + entries.length : null;
7823
+ return {
7824
+ kind: "ok",
7825
+ value: {
7826
+ ...applicationId && { applicationId },
7827
+ entries,
7828
+ hasMore: nextOffset !== null,
7829
+ integrationId: integration.id,
7830
+ integrationName,
7831
+ nextOffset,
7832
+ offset,
7833
+ pluginId,
7834
+ returnedCount: entries.length,
7835
+ totalCount: matched.length
7836
+ }
7837
+ };
7838
+ } catch (error) {
7839
+ const detail = sanitizeAgentFacingText(error instanceof Error ? error.message : "Unknown metadata error").replaceAll("\u2014", "-");
7840
+ return {
7841
+ ...applicationId && { applicationId },
7842
+ kind: "error",
7843
+ code: "integration_metadata_unavailable",
7844
+ message: `Could not load metadata for ${label}: ${detail}`
7845
+ };
7846
+ }
7847
+ }
7848
+
7408
7849
  // ../../../gateway/dist/capture/browser-instructions.js
7409
7850
  init_cjs_shims();
7410
7851
  var BROWSER_DRIVER_INSTRUCTIONS = `Browser capture (MUST):
@@ -8066,6 +8507,16 @@ function resolveToken(options, auth) {
8066
8507
  function superblocksProductCopy(text) {
8067
8508
  return text.replaceAll(/\bClark AI Apps\b/g, "Superblocks AI Apps").replaceAll(/\bClark\b/g, "Superblocks");
8068
8509
  }
8510
+ function completeToolAnnotations(annotations) {
8511
+ const readOnlyHint = annotations?.readOnlyHint ?? false;
8512
+ return {
8513
+ ...annotations,
8514
+ destructiveHint: annotations?.destructiveHint ?? (readOnlyHint ? false : true),
8515
+ idempotentHint: annotations?.idempotentHint ?? readOnlyHint,
8516
+ openWorldHint: annotations?.openWorldHint ?? true,
8517
+ readOnlyHint
8518
+ };
8519
+ }
8069
8520
  function registerAdminTools(server, options) {
8070
8521
  const registry = buildToolRegistry(toolDefinitions, "customer", {
8071
8522
  requireEntitledPlan: options.localAgentMode !== true
@@ -8076,7 +8527,7 @@ function registerAdminTools(server, options) {
8076
8527
  }
8077
8528
  const definition = toolDefinitions.find((d2) => d2.name === tool.name);
8078
8529
  server.registerTool(tool.name, {
8079
- annotations: tool.annotations,
8530
+ annotations: completeToolAnnotations(tool.annotations),
8080
8531
  description: superblocksProductCopy(tool.description ?? ""),
8081
8532
  inputSchema: gatewayAdminInputShape(tool.inputSchema),
8082
8533
  title: tool.title ?? definition?.title
@@ -8117,7 +8568,7 @@ function registerAdminTools(server, options) {
8117
8568
 
8118
8569
  // ../../../gateway/dist/transports/mcp/app-status-html.js
8119
8570
  init_cjs_shims();
8120
- var APP_STATUS_RESOURCE_URI = "ui://gateway/app-status-v6.html";
8571
+ var APP_STATUS_RESOURCE_URI = "ui://gateway/app-status-v8.html";
8121
8572
  var APP_STATUS_HTML = `<!DOCTYPE html>
8122
8573
  <html lang="en">
8123
8574
  <head>
@@ -8231,11 +8682,29 @@ var APP_STATUS_HTML = `<!DOCTYPE html>
8231
8682
  <script>
8232
8683
  (() => {
8233
8684
  const PROTOCOL_VERSION = "2026-01-26";
8685
+ // Statuses get_app reports when the app is still the empty starter
8686
+ // template. "building" is deliberately absent: the card only hears a
8687
+ // build finished if a new result arrives, so a stale badge would strand
8688
+ // the button.
8689
+ const UNBUILT_STATUSES = [
8690
+ "cancelled",
8691
+ "live_edit_terminated",
8692
+ "needs_decision",
8693
+ "no_changes",
8694
+ "timeout",
8695
+ ];
8696
+ const BLOCKED_ACTIONS = [
8697
+ "answer_question",
8698
+ "approve_plan",
8699
+ "retry_build",
8700
+ "retry_import",
8701
+ ];
8234
8702
  let nextId = 1;
8235
8703
  const pending = new Map();
8236
8704
  let applicationId = "";
8237
8705
  let appName = "";
8238
8706
  let commitId = "";
8707
+ let pendingAction = "";
8239
8708
  let editUrl = "";
8240
8709
  let previewUrl = "";
8241
8710
  let publishUrl = "";
@@ -8243,6 +8712,13 @@ var APP_STATUS_HTML = `<!DOCTYPE html>
8243
8712
  let canCallTools = false;
8244
8713
  let publishing = false;
8245
8714
  let lastHeight = 0;
8715
+ let refreshInFlight = false;
8716
+ let refreshTimer = 0;
8717
+ let refreshGeneration = 0;
8718
+ let refreshAttempts = 0;
8719
+ let pollAfterMs = 2000;
8720
+ const MIN_POLL_MS = 2000;
8721
+ const MAX_REFRESH_ATTEMPTS = 40;
8246
8722
 
8247
8723
  function post(message) {
8248
8724
  window.parent.postMessage(message, "*");
@@ -8332,6 +8808,31 @@ var APP_STATUS_HTML = `<!DOCTYPE html>
8332
8808
  }
8333
8809
  }
8334
8810
 
8811
+ /**
8812
+ * True when publishing would deploy the empty starter template.
8813
+ * pendingAction is the server's own "nothing to preview" signal;
8814
+ * wait_for_build is the building exception. Status is only a fallback
8815
+ * when the payload omitted pendingAction. loading/waiting with no
8816
+ * preview fail closed so Publish is not clickable before get_app
8817
+ * returns.
8818
+ */
8819
+ function nothingToPublish() {
8820
+ if (previewUrl || publishUrl) {
8821
+ return false;
8822
+ }
8823
+ if (pendingAction === "wait_for_build") {
8824
+ return false;
8825
+ }
8826
+ if (BLOCKED_ACTIONS.indexOf(pendingAction) !== -1) {
8827
+ return true;
8828
+ }
8829
+ const badge = element("status").textContent;
8830
+ if (!badge || badge === "loading" || badge === "waiting") {
8831
+ return true;
8832
+ }
8833
+ return UNBUILT_STATUSES.indexOf(badge) !== -1;
8834
+ }
8835
+
8335
8836
  /**
8336
8837
  * Keep URLs in memory for openLink \u2014 never print them (preview JWTs are
8337
8838
  * huge and ugly in the card).
@@ -8346,7 +8847,10 @@ var APP_STATUS_HTML = `<!DOCTYPE html>
8346
8847
  : "Publish app";
8347
8848
  element("publishApp").disabled = publishUrl
8348
8849
  ? !canOpenLinks
8349
- : !applicationId || !canCallTools || publishing;
8850
+ : !applicationId ||
8851
+ !canCallTools ||
8852
+ publishing ||
8853
+ nothingToPublish();
8350
8854
  // Only warn about what is actually broken. Hosts that do open links
8351
8855
  // handle local http fine; if one refuses, refused() says so then.
8352
8856
  if (!canOpenLinks && (editUrl || previewUrl) && !publishUrl) {
@@ -8418,6 +8922,11 @@ var APP_STATUS_HTML = `<!DOCTYPE html>
8418
8922
  if (typeof value.commitId === "string") {
8419
8923
  commitId = value.commitId;
8420
8924
  }
8925
+ if (typeof value.pendingAction === "string") {
8926
+ pendingAction = value.pendingAction;
8927
+ } else if (typeof value.status === "string") {
8928
+ pendingAction = "";
8929
+ }
8421
8930
  if (typeof value.screenshotBase64 === "string") {
8422
8931
  showScreenshot(value.screenshotBase64, "image/jpeg");
8423
8932
  }
@@ -8430,6 +8939,9 @@ var APP_STATUS_HTML = `<!DOCTYPE html>
8430
8939
  if (typeof value.publishUrl === "string") {
8431
8940
  publishUrl = webUrl(value.publishUrl);
8432
8941
  }
8942
+ if (typeof value.nextPollAfterMs === "number") {
8943
+ pollAfterMs = value.nextPollAfterMs;
8944
+ }
8433
8945
  // Hosts that deliver URLs without a status left the badge on the HTML
8434
8946
  // default ("loading") even after the app was built.
8435
8947
  const badge = element("status").textContent;
@@ -8444,29 +8956,91 @@ var APP_STATUS_HTML = `<!DOCTYPE html>
8444
8956
  }
8445
8957
 
8446
8958
  /**
8447
- * When the host opened the card with tool input (applicationId) but never
8448
- * pushed a tool result, refresh ourselves so the badge is not stuck on
8449
- * "loading" after the build finished.
8959
+ * Re-ask get_app when the host gave us no result, or while status is
8960
+ * still building. wait:false and ensurePreview:false so polls only
8961
+ * ask for status. Honor nextPollAfterMs; 0 means stop.
8450
8962
  */
8963
+ function clearRefreshTimer() {
8964
+ if (refreshTimer) {
8965
+ clearTimeout(refreshTimer);
8966
+ refreshTimer = 0;
8967
+ }
8968
+ }
8969
+
8970
+ function retryDelay() {
8971
+ return Math.max(MIN_POLL_MS, pollAfterMs || MIN_POLL_MS);
8972
+ }
8973
+
8974
+ function scheduleRefresh(delayMs) {
8975
+ clearRefreshTimer();
8976
+ if (refreshAttempts >= MAX_REFRESH_ATTEMPTS) {
8977
+ note("Stopped checking whether the app is still building.");
8978
+ return;
8979
+ }
8980
+ refreshTimer = setTimeout(() => {
8981
+ refreshTimer = 0;
8982
+ refreshFromServer();
8983
+ }, delayMs);
8984
+ }
8985
+
8451
8986
  function refreshFromServer() {
8452
- if (!canCallTools || !applicationId || editUrl) {
8987
+ clearRefreshTimer();
8988
+ if (!canCallTools || !applicationId || refreshInFlight) {
8989
+ return;
8990
+ }
8991
+ if (typeof document.hidden === "boolean" && document.hidden) {
8992
+ scheduleRefresh(retryDelay());
8453
8993
  return;
8454
8994
  }
8995
+ const badge = element("status").textContent;
8996
+ if (editUrl && badge !== "building") {
8997
+ return;
8998
+ }
8999
+ if (refreshAttempts >= MAX_REFRESH_ATTEMPTS) {
9000
+ note("Stopped checking whether the app is still building.");
9001
+ return;
9002
+ }
9003
+ refreshAttempts += 1;
9004
+ const generation = refreshGeneration;
9005
+ refreshInFlight = true;
8455
9006
  request("tools/call", {
8456
9007
  name: "get_app",
8457
- arguments: { applicationId },
9008
+ arguments: {
9009
+ applicationId,
9010
+ ensurePreview: false,
9011
+ wait: false,
9012
+ },
8458
9013
  })
8459
9014
  .then((result) => {
9015
+ refreshInFlight = false;
9016
+ if (generation !== refreshGeneration) {
9017
+ if (element("status").textContent === "building") {
9018
+ scheduleRefresh(retryDelay());
9019
+ }
9020
+ return;
9021
+ }
8460
9022
  if (!result || result.isError) {
9023
+ scheduleRefresh(retryDelay());
8461
9024
  return;
8462
9025
  }
8463
9026
  applyPayload(result);
8464
9027
  if (result.structuredContent) {
8465
9028
  applyPayload(result.structuredContent);
8466
9029
  }
9030
+ if (element("status").textContent !== "building") {
9031
+ return;
9032
+ }
9033
+ if (pollAfterMs === 0) {
9034
+ return;
9035
+ }
9036
+ scheduleRefresh(retryDelay());
8467
9037
  })
8468
9038
  .catch(() => {
8469
- // Host declined; stay on whatever we have.
9039
+ refreshInFlight = false;
9040
+ if (generation !== refreshGeneration) {
9041
+ return;
9042
+ }
9043
+ scheduleRefresh(retryDelay());
8470
9044
  });
8471
9045
  }
8472
9046
 
@@ -8519,6 +9093,10 @@ var APP_STATUS_HTML = `<!DOCTYPE html>
8519
9093
  if (!applicationId || !canCallTools || publishing) {
8520
9094
  return;
8521
9095
  }
9096
+ if (nothingToPublish()) {
9097
+ note("Nothing to publish yet: this turn did not build the app.");
9098
+ return;
9099
+ }
8522
9100
  publishing = true;
8523
9101
  renderActions();
8524
9102
  note("Publishing\u2026");
@@ -8602,6 +9180,10 @@ var APP_STATUS_HTML = `<!DOCTYPE html>
8602
9180
  if (params.structuredContent) {
8603
9181
  applyPayload(params.structuredContent);
8604
9182
  }
9183
+ refreshGeneration += 1;
9184
+ if (element("status").textContent === "building") {
9185
+ refreshFromServer();
9186
+ }
8605
9187
  }
8606
9188
  if (method === "ui/notifications/tool-input" && params.arguments) {
8607
9189
  applyPayload(params.arguments);
@@ -8931,6 +9513,24 @@ function createProgressNotifier(extra) {
8931
9513
  }
8932
9514
 
8933
9515
  // ../../../gateway/dist/transports/mcp/mount.js
9516
+ var DESTRUCTIVE_TOOL_ANNOTATIONS = {
9517
+ destructiveHint: true,
9518
+ idempotentHint: false,
9519
+ openWorldHint: true,
9520
+ readOnlyHint: false
9521
+ };
9522
+ var NON_DESTRUCTIVE_WRITE_TOOL_ANNOTATIONS = {
9523
+ destructiveHint: false,
9524
+ idempotentHint: false,
9525
+ openWorldHint: true,
9526
+ readOnlyHint: false
9527
+ };
9528
+ var READ_ONLY_TOOL_ANNOTATIONS = {
9529
+ destructiveHint: false,
9530
+ idempotentHint: true,
9531
+ openWorldHint: true,
9532
+ readOnlyHint: true
9533
+ };
8934
9534
  var eventCursorInputSchema = external_exports.object({
8935
9535
  streams: external_exports.record(external_exports.string(), external_exports.unknown()).optional(),
8936
9536
  version: external_exports.unknown().optional()
@@ -8951,10 +9551,11 @@ Name the builder "Superblocks" in every word you write. Users sometimes call it
8951
9551
  Use these tools whenever the user asks to build, change, import, or ship an app, whichever of those names they reach for.
8952
9552
 
8953
9553
  - start_app: create a new application and hand a prompt to Superblocks. Returns as soon as Superblocks accepts, with status "building". Superblocks names it unless a name is given.
8954
- - import_app: import an existing app from a ZIP (or .tgz/.gz) into Superblocks and start a Superblocks migration. Pass the archive's file name as zipPath \u2014 the gateway opens the file on this machine itself. Archives larger than 1 MB are rejected \u2014 they almost certainly include node_modules; re-export without install artifacts. Optional prompt carries extra intent (e.g. "make it more secure"). Returns like start_app with status "building".
9554
+ - import_app: import an existing app from a ZIP (or .tgz/.gz) into Superblocks and start a Superblocks migration. Pass the archive's file name as zipPath \u2014 the gateway opens the file on this machine itself. Archives larger than 1 MB are rejected \u2014 they almost certainly include node_modules; re-export without install artifacts. Optional prompt carries extra intent (e.g. "make it more secure"). Returns like start_app with status "building". Pass applicationId only when retrying a failed import into the application that call already created.
8955
9555
  - edit_app: send a follow-up prompt (including the answer when Superblocks asked a question) for an existing application. Also returns at status "building". Defaults to the most recent app, so an applicationId is only needed to target a different app.
8956
9556
  - check_app_progress: wait for the next thing Superblocks does and return it. It returns the moment Superblocks says anything, and after ~10 seconds of silence at the latest. "activity" holds Superblocks' own words since the previous call, in order; a quiet poll still returns a short heartbeat with elapsedSeconds.
8957
- - get_app: return editor and preview URLs for the recent or specified app (builds a private preview when needed). Prefer this after status "ready" so the user can open the live app. Hosts that support MCP Apps show the status card (Open editor / Open preview) on this call \u2014 not on check_app_progress or preview_app \u2014 so only one card opens. When Superblocks has written code the tool includes a screenshot: the live Vite canvas (library peer) if a held session exists, otherwise the signed-in preview. Never open canvas.doNotOpen (the edit URL) to take that screenshot. It refuses to build a preview of an app Superblocks has not written yet: while a plan or question is outstanding it returns status "needs_decision" with "pendingAction", while a turn is running it returns status "building", and when the last turn ended without writing anything it returns that turn's status ("timeout", "live_edit_terminated", "no_changes" or "cancelled") with pendingAction "retry_build". In each case there are no preview URLs and no screenshot \u2014 do the pendingAction first rather than calling get_app again.
9557
+ - get_app: return editor and preview URLs for the recent or specified app (builds a private preview when needed). Prefer this after status "ready" so the user can open the live app. Hosts that support MCP Apps show the status card (Open editor / Open preview) on this call \u2014 not on check_app_progress or preview_app \u2014 so only one card opens. When Superblocks has written code the tool includes a screenshot: the live Vite canvas (library peer) if a held session exists, otherwise the signed-in preview. Never open canvas.doNotOpen (the edit URL) to take that screenshot. It refuses to build a preview of an app Superblocks has not written yet: while a plan or question is outstanding it returns status "needs_decision" with "pendingAction", while a turn is running it returns status "building", and when the last turn ended without writing anything it returns that turn's status ("timeout", "live_edit_terminated", "no_changes" or "cancelled") with pendingAction "retry_build" for ordinary prompts or pendingAction "retry_import" for imports. In each case there are no preview URLs and no screenshot \u2014 do the pendingAction first rather than calling get_app again.
9558
+ - get_integration_metadata: inspect the tables, columns, and types in a connected integration. It needs no application and never executes an integration action, so do not call start_app or hunt through list_applications to satisfy it. Pass applicationId only for an integration that belongs to one application, such as a Native DB. Use search, limit, and offset to page large results. get_integration_config_schema is the JSON form for create_integration; it does not inspect connected data.
8958
9559
  - preview_app: build the application's current work and return a URL that runs it, without deploying it \u2014 the editor's Preview button. Nobody else can reach a preview. Text/markdown only (no MCP Apps widget); use get_app instead when you want the status card.
8959
9560
  - publish_app: deploy an application and wait until it is live. It commits the current work first, so a commitId is only needed to publish an earlier commit.
8960
9561
 
@@ -8962,21 +9563,25 @@ A build takes two to four minutes, and start_app/edit_app/import_app return with
8962
9563
 
8963
9564
  The user sees nothing at all while a tool call is outstanding, so between two calls always write out the new "activity" lines as a short message in your own reply \u2014 quote or paraphrase them, oldest first, before calling check_app_progress again. Do not batch them up for a summary at the end, and do not go two calls in a row without writing something: the point of the loop is that the user watches the build happen. When a call comes back with only a heartbeat (no new Superblocks words), say Superblocks is still working and how long it has been going, then poll again.
8964
9565
 
8965
- status "ready" means the turn finished: report the "reply" field, then call get_app (not preview_app as well \u2014 calling both used to open two widgets) so the user gets Open preview / an https link or a screenshot. status "needs_decision" means Superblocks is waiting \u2014 often with a plan \u2014 so stop polling and put the decision to the user. When "decision.kind" is "plan", show the plan markdown, then invite them to build it as written or say what to change \u2014 the same fork the editor's Build-it button offers. Ask that as an ordinary closing question in your own words; do not turn it into a numbered or bulleted menu, and do not wrap the choices in quotation marks. When they say "Build it", "approve", "looks good", or "go ahead", you MUST call edit_app with planAction "approve" and omit prompt (do not re-send the plan, and do not put "approve" or "Build it" in the prompt field \u2014 that is refine feedback to Superblocks, not the editor Build-it path). When they want changes, call edit_app with planAction "refine" and their feedback as prompt. When "decision.kind" is "multi_choice", relay the question and options and send their answer with edit_app. Do not claim the app is built while status is "needs_decision". status "no_changes" means the Superblocks job ended without building anything: relay reply, say no app was built, and do not present the edit URL as a finished app. status "live_edit_terminated" means the live-edit session that was to run the turn died before it built anything, or never opened at all \u2014 the prompt was fine and nothing timed out, so relay reply, tell the user the session did not survive, and offer to send the same instruction again with edit_app. status "detached" means Superblocks is still working but the gateway can no longer see it \u2014 usually because the app is open in a browser editor, which takes over Superblocks updates. Stop polling, say so, and point the user at the edit URL. status "unknown" means nothing is in flight for that app. A checkpointCommitId means Superblocks work is saved and the edit URL opens the finished app; when viewStatus is unknown, say the app may still be finishing in the editor.
9566
+ status "ready" means the turn finished: report the "reply" field, then call get_app (not preview_app as well \u2014 calling both used to open two widgets) so the user gets Open preview / an https link or a screenshot. status "needs_decision" means Superblocks is waiting \u2014 often with a plan \u2014 so stop polling and put the decision to the user. When "decision.kind" is "plan", show the plan markdown, then invite them to build it as written or say what to change \u2014 the same fork the editor's Build-it button offers. Ask that as an ordinary closing question in your own words; do not turn it into a numbered or bulleted menu, and do not wrap the choices in quotation marks. When they say "Build it", "approve", "looks good", or "go ahead", you MUST call edit_app with planAction "approve" and omit prompt (do not re-send the plan, and do not put "approve" or "Build it" in the prompt field \u2014 that is refine feedback to Superblocks, not the editor Build-it path). When they want changes, call edit_app with planAction "refine" and their feedback as prompt. When "decision.kind" is "multi_choice", relay the question and options and send their answer with edit_app. Do not claim the app is built while status is "needs_decision". status "no_changes" means the Superblocks job ended without building anything: relay reply, say no app was built, and do not present the edit URL as a finished app. status "live_edit_terminated" means the live-edit session that was to run the turn died before it built anything, or never opened at all \u2014 relay reply, tell the user the session did not survive, and follow its pendingAction to retry with edit_app or import_app. status "detached" means Superblocks is still working but the gateway can no longer see it \u2014 usually because the app is open in a browser editor, which takes over Superblocks updates. Stop polling, say so, and point the user at the edit URL. status "unknown" means nothing is in flight for that app. A checkpointCommitId means Superblocks work is saved and the edit URL opens the finished app; when viewStatus is unknown, say the app may still be finishing in the editor.
9567
+
9568
+ When get_app returns a screenshot, inspect it yourself before reporting that the app looks correct. If it shows skeletons, spinners, empty tables or charts, or "Loading...", call get_app again a few seconds later instead of treating that as a defect. If it shows a problem the user would plainly recognize as wrong, call edit_app once with a concrete correction, follow that turn to completion, and inspect the next get_app screenshot. Otherwise describe what you see and ask the user whether to fix it.
8966
9569
 
8967
9570
  start_app, edit_app, and import_app default to Plan mode (Superblocks proposes a plan before writing). Pass mode "BUILD" only when the user asks to skip planning and build immediately. Approving a plan after start_app or import_app is always a follow-up edit_app with planAction "approve" \u2014 never a second start_app/import_app with prompt "approve". Once the app has run in Build mode, later edit_app calls stay in Build mode on their own: you do not need to re-send mode "BUILD", and a new instruction is not a fresh planning round. Send mode "PLAN" only when the user explicitly asks to plan again before building. Every start_app / edit_app / import_app result reports the "mode" the turn ran in, so you can tell the user whether Superblocks is planning or building rather than guessing \u2014 and if a result comes back "PLAN" after a long gap, the build session was recycled rather than the user asking to plan, so pass mode "BUILD" again to carry on building. editUrl / previewUrl / publishUrl may appear in structured JSON for openLink buttons \u2014 do not dump raw URLs (especially preview JWTs) into the chat. Prefer the MCP Apps Open editor / Open preview buttons from get_app. Claude Desktop can only openLink https URLs; when previewUrl is http, say so briefly and rely on any screenshot image the tool returned rather than pasting the URL.
8968
9571
 
8969
9572
  Once a build reaches "ready", call get_app so the user can open the app, then offer publish_app to deploy for their organization when they ask. Prefer get_app over preview_app after a build \u2014 get_app already ensures a private preview and is the only tool that opens the MCP Apps status card. Only publish when the user asks. If preview_app or get_app comes back with status "building", the URL is already correct; tell the user it is still building and call again to check.
8970
9573
 
8971
- There is nothing to preview until Superblocks has written code, so get_app is not the way out of a plan, a question, or a turn that failed. When it answers status "needs_decision" (pendingAction "approve_plan" or "answer_question"), the app is still the empty starter template: put the decision to the user, send their answer with edit_app, and only call get_app once a turn has finished. When it answers "timeout", "live_edit_terminated", "no_changes" or "cancelled" with pendingAction "retry_build", that turn built nothing: tell the user so plainly, and send the instruction again with edit_app if they want to retry. Repeating get_app instead opens another status card and tells the user nothing new.
9574
+ There is nothing to preview until Superblocks has written code, so get_app is not the way out of a plan, a question, or a turn that failed. When it answers status "needs_decision" (pendingAction "approve_plan" or "answer_question"), the app is still the empty starter template: put the decision to the user, send their answer with edit_app, and only call get_app once a turn has finished. When it answers "timeout", "live_edit_terminated", "no_changes" or "cancelled" with pendingAction "retry_build", that turn built nothing: tell the user so plainly, and send the instruction again with edit_app if they want to retry. For pendingAction "retry_import", call import_app again with the same archive and applicationId. One of those statuses can arrive with no pendingAction at all, which means the gateway knows the turn wrote nothing but not how it was started: say so, and pick the retry from what this conversation already knows \u2014 import_app with the archive and applicationId for an app you imported, edit_app with the same prompt otherwise. Repeating get_app instead opens another status card and tells the user nothing new.
8972
9575
 
8973
9576
  Superblocks tests what it builds: it opens the app in a browser and runs its APIs, and the test run arrives as ordinary "activity" lines ("Testing the orders page", "Test passed: \u2026", "Tests failed: \u2026"). Relay those lines like any other activity, and say which cases passed and which failed \u2014 a test run is the only evidence the user has that the app actually works. A failing test is a build signal, not an auth problem: it means the app has a bug Superblocks is about to work on, so keep polling and never tell the user to sign in again or re-enter credentials over one.
8974
9577
 
8975
- A failed call still names the application it was working on. start_app and import_app create the application before the build can start, so an error from either one leaves an application that exists and belongs to this conversation, and the error says which: read the "applicationId" field, never a UUID out of the message text. Never take an error to mean nothing happened. Do not answer a failed start_app with a second start_app \u2014 that abandons the app you already have and creates a duplicate. When the error carries pendingAction "retry_build" \u2014 a "live_edit_unavailable" session that would not open, or a turn that ran out of budget \u2014 send the same prompt again with edit_app and that applicationId. A failed import_app carries no such recovery, because edit_app cannot resupply the archive the migration prompt reads: that application is empty and nothing can continue it in place, so name it to the user and let them decide whether to import again. Every edit_app error names its applicationId too, which is the one place to see which app a call that omitted the id actually ran on.
9578
+ A failed call still names the application it was working on. start_app and import_app create the application before the build can start, so an error from either one leaves an application that exists and belongs to this conversation, and the error says which: read the "applicationId" field, never a UUID out of the message text. Never take an error to mean nothing happened. Do not answer a failed start_app with a second start_app \u2014 that abandons the app you already have and creates a duplicate. When the error carries pendingAction "retry_build" \u2014 a "live_edit_unavailable" session that would not open, or a turn that ran out of budget \u2014 send the same prompt again with edit_app and that applicationId. When import_app returns pendingAction "retry_import", call import_app again with the same archive and applicationId so it resumes the existing app instead of creating another one. Every edit_app error names its applicationId too, which is the one place to see which app a call that omitted the id actually ran on.
9579
+
9580
+ A cancelled call does not stop the build, and it is not permission to start over. Cancelling only ends the waiting \u2014 Superblocks carries on, and the "cancelled" error names the application it was driving in "applicationId". Read that id, call check_app_progress or get_app on it, and act on what comes back. When the user then asks to redo, retry, or resume an import, that is a request to finish the application you already have: never answer it with an import_app that omits applicationId, which builds a second app and strands the first. The same holds when no pendingAction is in front of you \u2014 after a cancel, or in a later session. If the app came from an import, the way back in is import_app with the same archive and that applicationId; if you cannot tell which application the user means, ask rather than create one.
8976
9581
 
8977
9582
  Prompts are forwarded to Superblocks verbatim \u2014 pass the user's wording rather than a summary.
8978
9583
 
8979
- This connector is the single Superblocks MCP: Builder tools (start_app, import_app, edit_app, check_app_progress, get_app, preview_app, publish_app) and Admin tools (integrations, apps, users, folders, deploy_application, and the rest of the customer Admin surface) share this endpoint. Prefer create_integration / update_integration / list_integrations here when Superblocks needs a missing Postgres, REST, or other datasource \u2014 do not send the user to a second MCP. create_integration is Admin CRUD for org datasources; start_app creates a new application and hands a prompt to Superblocks. deploy_application is Admin deploy of an existing commit; publish_app is the Builder path that commits current work and waits until the app is live. Omit base_url on Admin tools unless targeting a different control plane \u2014 the gateway injects its configured Server URL. checkout_application is not exposed on this network surface (host filesystem writes); use the CLI/workspace checkout path instead.
9584
+ This connector is the single Superblocks MCP: Builder tools (start_app, import_app, edit_app, check_app_progress, get_app, get_integration_metadata, preview_app, publish_app) and Admin tools (integrations, apps, users, folders, deploy_application, and the rest of the customer Admin surface) share this endpoint. Prefer create_integration / update_integration / list_integrations here when Superblocks needs a missing Postgres, REST, or other datasource \u2014 do not send the user to a second MCP. Call get_integration_config_schema with the plugin_id before create_integration, then pass name, plugin_id, and configuration \u2014 never the raw integrations API envelope. create_integration is Admin CRUD for org datasources; start_app creates a new application and hands a prompt to Superblocks. deploy_application is Admin deploy of an existing commit; publish_app is the Builder path that commits current work and waits until the app is live. Omit base_url on Admin tools unless targeting a different control plane \u2014 the gateway injects its configured Server URL. checkout_application is not exposed on this network surface (host filesystem writes); use the CLI/workspace checkout path instead.
8980
9585
 
8981
9586
  This connection is already authenticated and already pointed at one Superblocks Server, so there is no setup, login, or auth-status call to make first. Call the tool that does the job \u2014 list_integrations to see the org's datasources, start_app to create an application \u2014 and read its error if something is genuinely wrong. Do not look for a tool that reports credentials or environments; there is none, and asking for one costs the user a turn.
8982
9587
 
@@ -8986,7 +9591,7 @@ For import_app, the archive never travels through you. A file the user attached
8986
9591
 
8987
9592
  ${BROWSER_DRIVER_INSTRUCTIONS}`;
8988
9593
  function createMcpServer(deps) {
8989
- const context = async (progress, notify, signal) => ({
9594
+ const context2 = async (progress, notify, signal) => ({
8990
9595
  caller: callerFrom(),
8991
9596
  captureLibraryScreenshot: captureLibraryScreenshotWithPlaywright,
8992
9597
  capturePreviewScreenshot: capturePreviewScreenshotWithPlaywright,
@@ -8996,6 +9601,9 @@ function createMcpServer(deps) {
8996
9601
  progress.push(event);
8997
9602
  notify?.(event.message);
8998
9603
  },
9604
+ onPrincipalResolved: (principal) => {
9605
+ deps.mcpTelemetry?.setOrganizationId(principal.organizationId);
9606
+ },
8999
9607
  principal: principalFrom(deps),
9000
9608
  progressCursors: deps.progressCursors,
9001
9609
  recentApps: deps.recentApps,
@@ -9008,22 +9616,22 @@ function createMcpServer(deps) {
9008
9616
  gatewayDebugStack("tool started", { "tool.name": toolName });
9009
9617
  const progress = [];
9010
9618
  const notify = createProgressNotifier(extra);
9011
- let result;
9012
- if (!deps.cliApiKey) {
9013
- gatewayDebug("tool short-circuited", {
9014
- "tool.name": toolName,
9015
- "tool.reason": "missing_cli_session"
9016
- });
9017
- result = {
9018
- kind: "error",
9019
- code: "unauthenticated",
9020
- message: "This Gateway has no CLI session. Run: superblocks login"
9021
- };
9022
- } else {
9619
+ const execute = async () => {
9620
+ if (!deps.cliApiKey) {
9621
+ gatewayDebug("tool short-circuited", {
9622
+ "tool.name": toolName,
9623
+ "tool.reason": "missing_cli_session"
9624
+ });
9625
+ return {
9626
+ kind: "error",
9627
+ code: "unauthenticated",
9628
+ message: "This Gateway has no CLI session. Run: superblocks login"
9629
+ };
9630
+ }
9023
9631
  try {
9024
9632
  const signal = extra?.signal;
9025
- const capabilityContext = await context(progress, notify, signal);
9026
- result = await capability(capabilityContext, () => {
9633
+ const capabilityContext = await context2(progress, notify, signal);
9634
+ return await capability(capabilityContext, () => {
9027
9635
  for (let index = progress.length - 1; index >= 0; index -= 1) {
9028
9636
  if (progress[index]?.type === "completed") {
9029
9637
  progress.splice(index, 1);
@@ -9032,13 +9640,23 @@ function createMcpServer(deps) {
9032
9640
  });
9033
9641
  } catch (error) {
9034
9642
  console.error("gateway capability failed", error);
9035
- result = {
9643
+ return {
9036
9644
  kind: "error",
9037
9645
  code: "capability_failed",
9038
9646
  message: error instanceof Error ? error.message : "The gateway failed for an unknown reason."
9039
9647
  };
9040
9648
  }
9041
- }
9649
+ };
9650
+ const client = server.server.getClientVersion();
9651
+ const result = deps.mcpTelemetry ? await deps.mcpTelemetry.runTool({
9652
+ ...client ? {
9653
+ client: {
9654
+ name: client.name,
9655
+ version: client.version
9656
+ }
9657
+ } : {},
9658
+ toolName
9659
+ }, execute) : await execute();
9042
9660
  const resultStatus = result.kind === "ok" && result.value && typeof result.value === "object" && "status" in result.value && typeof result.value.status === "string" ? result.value.status : void 0;
9043
9661
  gatewayDebug("tool finished", {
9044
9662
  "progress.event_count": progress.length,
@@ -9064,12 +9682,12 @@ function createMcpServer(deps) {
9064
9682
  title: "Superblocks",
9065
9683
  version: "0.0.1"
9066
9684
  }, { instructions: SERVER_INSTRUCTIONS });
9067
- const progressWithDecisionForm = async (context2, input, extra, forgetFinishedTurn) => {
9685
+ const progressWithDecisionForm = async (context3, input, extra, forgetFinishedTurn) => {
9068
9686
  gatewayDebug("progress decision flow started", {
9069
9687
  "application.id": input.applicationId,
9070
9688
  "progress.cursor_provided": Boolean(input.cursor)
9071
9689
  });
9072
- const result = await checkAppProgress(context2, input);
9690
+ const result = await checkAppProgress(context3, input);
9073
9691
  const decision = pendingDecision(result);
9074
9692
  if (!decision) {
9075
9693
  gatewayDebug("progress decision flow finished", {
@@ -9089,7 +9707,7 @@ function createMcpServer(deps) {
9089
9707
  const resolved = await resolveDecisionByElicitation({
9090
9708
  apply: (answer) => {
9091
9709
  forgetFinishedTurn();
9092
- return editApp(context2, {
9710
+ return editApp(context3, {
9093
9711
  applicationId: input.applicationId,
9094
9712
  idempotencyKey: `mcp-decision:${randomUUID4()}`,
9095
9713
  wait: "ack",
@@ -9121,7 +9739,20 @@ function createMcpServer(deps) {
9121
9739
  });
9122
9740
  }
9123
9741
  function registerBuilderTools() {
9742
+ server.registerTool("get_integration_metadata", {
9743
+ annotations: READ_ONLY_TOOL_ANNOTATIONS,
9744
+ description: "Inspect the tables, columns, and types in a connected Superblocks integration. Needs no application and never executes an integration action. Pass integrationId, which list_integrations returns. A large result comes back paged: narrow it with search, take more or fewer entries with limit, and continue from nextOffset with offset. Requires build permission on the integration. Do not use this to learn how to create an integration; that is get_integration_config_schema.",
9745
+ inputSchema: {
9746
+ applicationId: external_exports.string().uuid().optional().describe("Only for an integration owned by a single application, such as a Native DB. Leave unset otherwise; do not go looking for an application to supply."),
9747
+ integrationId: external_exports.string().uuid(),
9748
+ limit: external_exports.number().int().min(1).max(200).optional(),
9749
+ offset: external_exports.number().int().min(0).optional(),
9750
+ search: external_exports.string().min(1).optional()
9751
+ },
9752
+ title: "Get Integration Metadata"
9753
+ }, async (input, extra) => run("get_integration_metadata", (context3) => getIntegrationMetadata(context3, input), extra));
9124
9754
  server.registerTool("start_app", {
9755
+ annotations: NON_DESTRUCTIVE_WRITE_TOOL_ANNOTATIONS,
9125
9756
  description: 'Create a Superblocks application and hand a prompt to Superblocks in Plan mode by default. Returns as soon as Superblocks accepts, with status "building" and an edit URL; follow with check_app_progress until status is no longer "building". When Superblocks returns needs_decision with a plan and the user says "Build it", call edit_app with planAction "approve" (omit prompt) \u2014 do not call start_app again with prompt "approve". Pass mode "BUILD" to skip planning. Superblocks names the application unless name is given. An error from this tool does not mean nothing happened: the application is created before the build starts, so the error names it in "applicationId". Retry that app with edit_app rather than calling start_app another time, which would leave the first one abandoned.',
9126
9757
  inputSchema: {
9127
9758
  branch: external_exports.string().optional(),
@@ -9134,7 +9765,7 @@ function createMcpServer(deps) {
9134
9765
  // the tool name: a host that ranks tools by title has to be able to find
9135
9766
  // this one from the request.
9136
9767
  title: "Create Superblocks App"
9137
- }, async ({ branch, idempotencyKey, mode, name, prompt }, extra) => run("start_app", (context2) => startApp(context2, {
9768
+ }, async ({ branch, idempotencyKey, mode, name, prompt }, extra) => run("start_app", (context3) => startApp(context3, {
9138
9769
  branch,
9139
9770
  idempotencyKey: idempotencyKey ?? `mcp-start:${randomUUID4()}`,
9140
9771
  mode,
@@ -9146,8 +9777,10 @@ function createMcpServer(deps) {
9146
9777
  wait: "ack"
9147
9778
  }), extra));
9148
9779
  server.registerTool("import_app", {
9149
- description: `Import an existing app from a ZIP/.tgz/.gz archive into Superblocks and start a Superblocks migration. Pass the archive's file name (or its full path on this machine) as zipPath: the gateway reads the file itself, so a path from your own sandbox is fine as long as the name matches the file the user has. Never encode the archive as base64 \u2014 zipBase64 only carries test fixtures. Archives larger than 1 MB are rejected \u2014 exclude node_modules. Optional prompt adds intent such as hardening security. Returns status "building"; follow with check_app_progress. Defaults to Plan mode \u2014 when Superblocks returns a plan and the user says "Build it", call edit_app with planAction "approve" (omit prompt); do not call import_app again with prompt "approve". An error from this tool does not mean nothing happened: the application is created before the migration starts, so the error names it in "applicationId". That application is empty \u2014 the migration never ran \u2014 and nothing can continue it in place, so say it exists and let the user decide whether to import again rather than silently retrying.`,
9780
+ annotations: DESTRUCTIVE_TOOL_ANNOTATIONS,
9781
+ description: `Import an existing app from a ZIP/.tgz/.gz archive into Superblocks and start a Superblocks migration. Pass the archive's file name (or its full path on this machine) as zipPath: the gateway reads the file itself, so a path from your own sandbox is fine as long as the name matches the file the user has. Never encode the archive as base64 \u2014 zipBase64 only carries test fixtures. Archives larger than 1 MB are rejected \u2014 exclude node_modules. Optional prompt adds intent such as hardening security. Returns status "building"; follow with check_app_progress. Defaults to Plan mode \u2014 when Superblocks returns a plan and the user says "Build it", call edit_app with planAction "approve" (omit prompt); do not call import_app again with prompt "approve". An error from this tool does not mean nothing happened: the application is created before the migration starts, so the error names it in "applicationId". When it returns pendingAction "retry_import", call import_app again with the same archive and applicationId to resume that application instead of creating another one.`,
9150
9782
  inputSchema: {
9783
+ applicationId: external_exports.string().uuid().optional(),
9151
9784
  branch: external_exports.string().optional(),
9152
9785
  idempotencyKey: external_exports.string().optional(),
9153
9786
  mode: external_exports.enum(["BUILD", "PLAN"]).optional(),
@@ -9167,7 +9800,8 @@ function createMcpServer(deps) {
9167
9800
  zipPath: external_exports.string().min(1).optional()
9168
9801
  },
9169
9802
  title: "Import App"
9170
- }, async ({ branch, idempotencyKey, mode, name, prompt, source, zipBase64, zipPath }, extra) => run("import_app", (context2) => importApp(context2, {
9803
+ }, async ({ applicationId, branch, idempotencyKey, mode, name, prompt, source, zipBase64, zipPath }, extra) => run("import_app", (context3) => importApp(context3, {
9804
+ applicationId,
9171
9805
  branch,
9172
9806
  idempotencyKey: idempotencyKey ?? `mcp-import:${randomUUID4()}`,
9173
9807
  mode,
@@ -9179,6 +9813,7 @@ function createMcpServer(deps) {
9179
9813
  zipPath
9180
9814
  }), extra));
9181
9815
  server.registerTool("edit_app", {
9816
+ annotations: DESTRUCTIVE_TOOL_ANNOTATIONS,
9182
9817
  description: 'Forward a prompt to Superblocks for the recent or specified application. Defaults to Plan mode until the app has run in Build mode; after that later edits keep building directly \u2014 you do not need to pass mode again. Pass mode "PLAN" only when the user explicitly asks to plan before building. The result reports the "mode" the turn ran in; a "PLAN" result after a long gap means the build session was recycled, so pass mode "BUILD" again rather than treating it as a new planning round. When status was needs_decision with a plan and the user says "Build it" / approve / go ahead, call edit_app with planAction "approve" and omit prompt \u2014 do not put "approve" or "Build it" in prompt (that is refine feedback, not the Build-it path). Use planAction "refine" with their feedback as prompt to change the plan. Also use edit_app to answer multi_choice questions. Returns status "building"; follow with check_app_progress.',
9183
9818
  inputSchema: {
9184
9819
  applicationId: external_exports.string().uuid().optional(),
@@ -9189,7 +9824,7 @@ function createMcpServer(deps) {
9189
9824
  prompt: external_exports.string().min(1).optional()
9190
9825
  },
9191
9826
  title: "Edit App"
9192
- }, async ({ applicationId, branch, idempotencyKey, mode, planAction, prompt }, extra) => run("edit_app", (context2) => editApp(context2, {
9827
+ }, async ({ applicationId, branch, idempotencyKey, mode, planAction, prompt }, extra) => run("edit_app", (context3) => editApp(context3, {
9193
9828
  applicationId,
9194
9829
  branch,
9195
9830
  idempotencyKey: idempotencyKey ?? `mcp-edit:${randomUUID4()}`,
@@ -9199,24 +9834,26 @@ function createMcpServer(deps) {
9199
9834
  wait: "ack"
9200
9835
  }), extra));
9201
9836
  server.registerTool("check_app_progress", {
9837
+ annotations: NON_DESTRUCTIVE_WRITE_TOOL_ANNOTATIONS,
9202
9838
  description: `Wait for the next thing Superblocks does on the recent or specified application and return it: "activity" carries Superblocks' words since the previous call (or a short heartbeat when Superblocks is quiet), and status is "building" while the app is still being generated. Returns as soon as Superblocks says anything, and after ~10 seconds of silence at the latest. Call within about 10 seconds of start_app, import_app, or edit_app so a plan or question surfaces quickly. Once the plan is approved and Superblocks is building, space later polls about 30 seconds apart (honor "nextPollAfterMs" when present) until status is no longer "building" \u2014 a 10-second cadence on a multi-minute build burns through the host's tool-call budget. Relay each new activity line to the user between calls. When status becomes "needs_decision", stop polling and put the decision to the user \u2014 for a plan, show it and ask whether to build it or say what to change; for multi_choice, relay the question and options. Hosts that support form elicitation get a native picker instead, and this tool then returns the answered turn. When status becomes "ready", follow with get_app so the user can open the preview. Progress is text-only (no MCP Apps widget) so each poll does not reopen a blank card. Optional "cursor" from a prior result resumes from that composite position (per producer, no global sequence); omit it to use the gateway's stored place for this caller.`,
9203
9839
  inputSchema: {
9204
9840
  applicationId: external_exports.string().uuid().optional(),
9205
9841
  cursor: eventCursorInputSchema.optional()
9206
9842
  },
9207
9843
  title: "Check App Progress"
9208
- }, async ({ applicationId, cursor }, extra) => run("check_app_progress", (context2, forgetFinishedTurn) => {
9844
+ }, async ({ applicationId, cursor }, extra) => run("check_app_progress", (context3, forgetFinishedTurn) => {
9209
9845
  const parsed = cursor === void 0 ? void 0 : parseEventCursor(cursor);
9210
9846
  gatewayDebug("progress cursor parsed", {
9211
9847
  "progress.cursor_provided": cursor !== void 0,
9212
9848
  "progress.cursor_valid": Boolean(parsed)
9213
9849
  });
9214
- return progressWithDecisionForm(context2, {
9850
+ return progressWithDecisionForm(context3, {
9215
9851
  applicationId,
9216
9852
  ...parsed ? { cursor: parsed } : {}
9217
9853
  }, extra, forgetFinishedTurn);
9218
9854
  }, extra));
9219
9855
  K3(server, "get_app", {
9856
+ annotations: READ_ONLY_TOOL_ANNOTATIONS,
9220
9857
  description: 'Return editor and preview URLs for the recent or specified Superblocks application. Ensures a private preview build by default (same as preview_app), but only once Superblocks has something to show: while a plan or question is outstanding it returns status "needs_decision" with the "decision" and a "pendingAction" of "approve_plan" or "answer_question", while a turn is running it returns status "building", and when the last turn ended without writing anything it returns "timeout", "live_edit_terminated", "no_changes" or "cancelled" with pendingAction "retry_build" \u2014 no preview URLs and no screenshot in any of those cases, because the app is still the empty starter template. Act on "pendingAction" through edit_app instead of calling this again. Prefer this after check_app_progress reaches status "ready". Also the right call when you do not know where a build stands \u2014 after a reconnect, when no turn of yours is running, or when the conversation moved to another channel: pass wait false and read "status" and "cursor" from the result rather than guessing. Hosts that support MCP Apps show Open editor / Open preview buttons. When Superblocks has written code the result includes a screenshot of the live Vite canvas if a held session exists, otherwise the signed-in preview \u2014 never the edit URL.',
9221
9858
  inputSchema: {
9222
9859
  applicationId: external_exports.string().uuid().optional(),
@@ -9228,15 +9865,16 @@ function createMcpServer(deps) {
9228
9865
  _meta: {
9229
9866
  ui: { resourceUri: APP_STATUS_RESOURCE_URI }
9230
9867
  }
9231
- }, async ({ applicationId, branch, ensurePreview, wait }, extra) => run("get_app", (context2) => getApp(context2, { applicationId, branch, ensurePreview, wait }), extra));
9868
+ }, async ({ applicationId, branch, ensurePreview, wait }, extra) => run("get_app", (context3) => getApp(context3, { applicationId, branch, ensurePreview, wait }), extra));
9232
9869
  server.registerTool("preview_app", {
9870
+ annotations: READ_ONLY_TOOL_ANNOTATIONS,
9233
9871
  description: `Build the recent or specified Superblocks application's current work and return a URL that runs it, without deploying it \u2014 the same thing the editor's Preview button does. Only the user can open it. Status "ready" means the URL works now; "building" means the build is still running and calling again resumes watching it. Prefer get_app when you want the MCP Apps status card (Open editor / Open preview); this tool returns the same URLs as text/markdown without opening a second widget.`,
9234
9872
  inputSchema: {
9235
9873
  applicationId: external_exports.string().uuid().optional(),
9236
9874
  branch: external_exports.string().optional()
9237
9875
  },
9238
9876
  title: "Preview App"
9239
- }, async ({ applicationId, branch }, extra) => run("preview_app", (context2) => previewApp(context2, { applicationId, branch }), extra));
9877
+ }, async ({ applicationId, branch }, extra) => run("preview_app", (context3) => previewApp(context3, { applicationId, branch }), extra));
9240
9878
  const uiCsp = { csp: { frameDomains: [] } };
9241
9879
  N3(server, "Gateway app status", APP_STATUS_RESOURCE_URI, {
9242
9880
  description: "Status card with Open editor / Open preview / Publish app",
@@ -9253,6 +9891,7 @@ function createMcpServer(deps) {
9253
9891
  ]
9254
9892
  }));
9255
9893
  server.registerTool("publish_app", {
9894
+ annotations: DESTRUCTIVE_TOOL_ANNOTATIONS,
9256
9895
  description: "Publish/deploy the recent or specified Superblocks application and wait until it is live. Commits the application's current work first, so commitId is only needed to publish an earlier commit.",
9257
9896
  inputSchema: {
9258
9897
  applicationId: external_exports.string().uuid().optional(),
@@ -9262,7 +9901,7 @@ function createMcpServer(deps) {
9262
9901
  prompt: external_exports.string().min(1)
9263
9902
  },
9264
9903
  title: "Publish App"
9265
- }, async ({ applicationId, branch, commitId, idempotencyKey, prompt }, extra) => run("publish_app", (context2) => publishApp(context2, {
9904
+ }, async ({ applicationId, branch, commitId, idempotencyKey, prompt }, extra) => run("publish_app", (context3) => publishApp(context3, {
9266
9905
  applicationId,
9267
9906
  branch,
9268
9907
  commitId,
@@ -9282,6 +9921,7 @@ function createMcpServer(deps) {
9282
9921
  async function createRuntime(apiKey) {
9283
9922
  const config = loadConfig();
9284
9923
  const stores = await createStores();
9924
+ const telemetry = startGatewayTelemetry({ apiKey, config });
9285
9925
  const serverClient = new HttpSuperblocksServerClient(config.serverUrl);
9286
9926
  const sessionPeer = new WebSocketSessionPeer({
9287
9927
  beatLostGraceMs: config.beatLostGraceMs,
@@ -9299,6 +9939,7 @@ async function createRuntime(apiKey) {
9299
9939
  cliApiKey: apiKey,
9300
9940
  config,
9301
9941
  events: stores.events,
9942
+ mcpTelemetry: telemetry.mcpTelemetry,
9302
9943
  progressCursors: stores.progressCursors,
9303
9944
  recentApps: stores.recentApps,
9304
9945
  server: serverClient,
@@ -9310,7 +9951,7 @@ async function createRuntime(apiKey) {
9310
9951
  deps,
9311
9952
  close: async () => {
9312
9953
  sessionPeer.closeAll();
9313
- await stores.close();
9954
+ await Promise.allSettled([stores.close(), telemetry.shutdown()]);
9314
9955
  }
9315
9956
  };
9316
9957
  }
@@ -9356,4 +9997,4 @@ export {
9356
9997
  startApp,
9357
9998
  startStdioGateway
9358
9999
  };
9359
- //# sourceMappingURL=dist-P7G6RMCF.js.map
10000
+ //# sourceMappingURL=dist-7EN7U2RU.js.map