@agent-native/core 0.49.11 → 0.49.13

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 (77) hide show
  1. package/dist/cli/pr-visual-recap-workflow.d.ts +1 -1
  2. package/dist/cli/pr-visual-recap-workflow.d.ts.map +1 -1
  3. package/dist/cli/pr-visual-recap-workflow.js +1 -1
  4. package/dist/cli/pr-visual-recap-workflow.js.map +1 -1
  5. package/dist/cli/recap.d.ts +37 -0
  6. package/dist/cli/recap.d.ts.map +1 -1
  7. package/dist/cli/recap.js +240 -0
  8. package/dist/cli/recap.js.map +1 -1
  9. package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
  10. package/dist/client/MultiTabAssistantChat.js +5 -10
  11. package/dist/client/MultiTabAssistantChat.js.map +1 -1
  12. package/dist/client/blocks/library/question-form.js +1 -1
  13. package/dist/client/blocks/library/question-form.js.map +1 -1
  14. package/dist/client/extensions/EmbeddedExtension.d.ts.map +1 -1
  15. package/dist/client/extensions/EmbeddedExtension.js +4 -0
  16. package/dist/client/extensions/EmbeddedExtension.js.map +1 -1
  17. package/dist/client/extensions/ExtensionViewer.d.ts.map +1 -1
  18. package/dist/client/extensions/ExtensionViewer.js +12 -4
  19. package/dist/client/extensions/ExtensionViewer.js.map +1 -1
  20. package/dist/client/extensions/ExtensionsListPage.d.ts.map +1 -1
  21. package/dist/client/extensions/ExtensionsListPage.js +14 -9
  22. package/dist/client/extensions/ExtensionsListPage.js.map +1 -1
  23. package/dist/client/extensions/ExtensionsSidebarSection.d.ts.map +1 -1
  24. package/dist/client/extensions/ExtensionsSidebarSection.js +6 -4
  25. package/dist/client/extensions/ExtensionsSidebarSection.js.map +1 -1
  26. package/dist/client/extensions/iframe-bridge.d.ts +8 -0
  27. package/dist/client/extensions/iframe-bridge.d.ts.map +1 -1
  28. package/dist/client/extensions/iframe-bridge.js +54 -0
  29. package/dist/client/extensions/iframe-bridge.js.map +1 -1
  30. package/dist/client/progress/RunsTray.d.ts.map +1 -1
  31. package/dist/client/progress/RunsTray.js +12 -3
  32. package/dist/client/progress/RunsTray.js.map +1 -1
  33. package/dist/client/resources/ResourceEditor.d.ts +1 -3
  34. package/dist/client/resources/ResourceEditor.d.ts.map +1 -1
  35. package/dist/client/resources/ResourceEditor.js +8 -23
  36. package/dist/client/resources/ResourceEditor.js.map +1 -1
  37. package/dist/client/resources/ResourcesPanel.d.ts.map +1 -1
  38. package/dist/client/resources/ResourcesPanel.js +4 -9
  39. package/dist/client/resources/ResourcesPanel.js.map +1 -1
  40. package/dist/client/settings/VoiceTranscriptionSection.d.ts.map +1 -1
  41. package/dist/client/settings/VoiceTranscriptionSection.js +1 -1
  42. package/dist/client/settings/VoiceTranscriptionSection.js.map +1 -1
  43. package/dist/client/sharing/ShareButton.d.ts +5 -1
  44. package/dist/client/sharing/ShareButton.d.ts.map +1 -1
  45. package/dist/client/sharing/ShareButton.js +15 -7
  46. package/dist/client/sharing/ShareButton.js.map +1 -1
  47. package/dist/client/sharing/ShareDialog.d.ts.map +1 -1
  48. package/dist/client/sharing/ShareDialog.js +6 -2
  49. package/dist/client/sharing/ShareDialog.js.map +1 -1
  50. package/dist/extensions/actions.d.ts.map +1 -1
  51. package/dist/extensions/actions.js +70 -2
  52. package/dist/extensions/actions.js.map +1 -1
  53. package/dist/extensions/html-shell.d.ts +12 -0
  54. package/dist/extensions/html-shell.d.ts.map +1 -1
  55. package/dist/extensions/html-shell.js.map +1 -1
  56. package/dist/extensions/local.d.ts +35 -0
  57. package/dist/extensions/local.d.ts.map +1 -0
  58. package/dist/extensions/local.js +334 -0
  59. package/dist/extensions/local.js.map +1 -0
  60. package/dist/extensions/routes.d.ts.map +1 -1
  61. package/dist/extensions/routes.js +92 -12
  62. package/dist/extensions/routes.js.map +1 -1
  63. package/dist/extensions/slots/store.d.ts.map +1 -1
  64. package/dist/extensions/slots/store.js +72 -4
  65. package/dist/extensions/slots/store.js.map +1 -1
  66. package/dist/local-artifacts/index.d.ts +4 -0
  67. package/dist/local-artifacts/index.d.ts.map +1 -1
  68. package/dist/local-artifacts/index.js +60 -35
  69. package/dist/local-artifacts/index.js.map +1 -1
  70. package/dist/vite/client.d.ts +11 -1
  71. package/dist/vite/client.d.ts.map +1 -1
  72. package/dist/vite/client.js +26 -1
  73. package/dist/vite/client.js.map +1 -1
  74. package/docs/content/extensions.md +65 -0
  75. package/docs/content/local-file-mode.md +378 -0
  76. package/docs/content/template-content.md +53 -4
  77. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"iframe-bridge.js","sourceRoot":"","sources":["../../../src/client/extensions/iframe-bridge.ts"],"names":[],"mappings":"AAAA,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC;IAC9B,KAAK;IACL,MAAM;IACN,KAAK;IACL,OAAO;IACP,QAAQ;IACR,MAAM;CACP,CAAC,CAAC;AAEH,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC;IAC9B,YAAY;IACZ,gBAAgB;IAChB,QAAQ;IACR,WAAW;IACX,MAAM;IACN,YAAY;IACZ,QAAQ;IACR,oBAAoB;IACpB,qBAAqB;IACrB,SAAS;IACT,YAAY;IACZ,IAAI;IACJ,SAAS;IACT,mBAAmB;IACnB,SAAS;IACT,iBAAiB;IACjB,kBAAkB;IAClB,mBAAmB;CACpB,CAAC,CAAC;AAEH,MAAM,cAAc,GAAG,+BAA+B,CAAC;AAEvD;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,sBAAsB,CACpC,IAAY,EACZ,WAAmB;IAEnB,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IACjE,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IAE7D,IAAI,QAAgB,CAAC;IACrB,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,IAAI,EAAE,2BAA2B,CAAC,CAAC;QAC1D,IAAI,MAAM,CAAC,MAAM,KAAK,2BAA2B;YAAE,OAAO,KAAK,CAAC;QAChE,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;IAC7B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACrD,IAAI,QAAQ,KAAK,WAAW,IAAI,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IAEtE,IAAI,QAAQ,CAAC,UAAU,CAAC,yBAAyB,CAAC;QAAE,OAAO,IAAI,CAAC;IAChE,IAAI,QAAQ,CAAC,UAAU,CAAC,mCAAmC,CAAC;QAAE,OAAO,IAAI,CAAC;IAE1E,IAAI,QAAQ,KAAK,iCAAiC;QAAE,OAAO,IAAI,CAAC;IAChE,IAAI,QAAQ,KAAK,qCAAqC;QAAE,OAAO,IAAI,CAAC;IACpE,IAAI,QAAQ,KAAK,oCAAoC;QAAE,OAAO,IAAI,CAAC;IAEnE,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAClC,IACE,KAAK,CAAC,MAAM,IAAI,CAAC;QACjB,KAAK,CAAC,MAAM,IAAI,CAAC;QACjB,KAAK,CAAC,CAAC,CAAC,KAAK,eAAe;QAC5B,KAAK,CAAC,CAAC,CAAC,KAAK,YAAY;QACzB,KAAK,CAAC,CAAC,CAAC,KAAK,MAAM,EACnB,CAAC;QACD,IAAI,CAAC;YACH,OAAO,kBAAkB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,WAAW,CAAC;QACtD,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,+BAA+B,CAAC,KAAc;IAC5D,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,EAAE,CAAC;IACnD,MAAM,GAAG,GAAG,KAAgC,CAAC;IAC7C,MAAM,MAAM,GACV,OAAO,GAAG,CAAC,MAAM,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE;QACjD,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,WAAW,EAAE;QAC1B,CAAC,CAAC,KAAK,CAAC;IACZ,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;QACjC,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;IAC7D,CAAC;IAED,MAAM,OAAO,GACX,GAAG,CAAC,OAAO,IAAI,OAAO,GAAG,CAAC,OAAO,KAAK,QAAQ;QAC5C,CAAC,CAAC,MAAM,CAAC,WAAW,CAChB,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,OAAkC,CAAC;aACnD,MAAM,CAAC,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,GAAG,CAAC,IAAI,GAAG,KAAK,SAAS,CAAC;aACjE,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAC3C;QACH,CAAC,CAAC,SAAS,CAAC;IAChB,MAAM,IAAI,GACR,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ;QAC5B,GAAG,CAAC,IAAI,YAAY,IAAI;QACxB,GAAG,CAAC,IAAI,YAAY,QAAQ;QAC1B,CAAC,CAAC,GAAG,CAAC,IAAI;QACV,CAAC,CAAC,GAAG,CAAC,IAAI,KAAK,SAAS;YACtB,CAAC,CAAC,SAAS;YACX,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAEjC,OAAO;QACL,MAAM;QACN,OAAO;QACP,IAAI,EAAE,MAAM,KAAK,KAAK,IAAI,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI;KAC/D,CAAC;AACJ,CAAC;AAED,SAAS,eAAe,CAAC,IAAY;IACnC,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;IACjC,OAAO,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;AAClE,CAAC;AAoCD,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC;AAgB9C;;;;;;;GAOG;AACH,MAAM,UAAU,iBAAiB,CAC/B,IAAY,EACZ,MAAc,EACd,GAAwB;IAExB,uEAAuE;IACvE,IAAI,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,IAAI,KAAK,OAAO,IAAI,GAAG,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;QACjE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;IACtB,CAAC;IAED,sEAAsE;IACtE,oEAAoE;IACpE,oCAAoC;IACpC,IAAI,GAAG,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC1B,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;IACtB,CAAC;IAED,sEAAsE;IACtE,MAAM,WAAW,GAAG,MAAM,CAAC,WAAW,EAAE,CAAC;IAEzC,0EAA0E;IAC1E,gCAAgC;IAChC,IAAI,IAAI,KAAK,qCAAqC,EAAE,CAAC;QACnD,OAAO;YACL,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,aAAa,CAAC,SAAS,EAAE,GAAG,CAAC,IAAI,CAAC;SAC1C,CAAC;IACJ,CAAC;IACD,IAAI,IAAI,KAAK,oCAAoC,EAAE,CAAC;QAClD,OAAO;YACL,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,aAAa,CAAC,QAAQ,EAAE,GAAG,CAAC,IAAI,CAAC;SACzC,CAAC;IACJ,CAAC;IAED,wEAAwE;IACxE,4EAA4E;IAC5E,6EAA6E;IAC7E,mDAAmD;IACnD,IAAI,IAAI,CAAC,UAAU,CAAC,yBAAyB,CAAC,EAAE,CAAC;QAC/C,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;IACtB,CAAC;IAED,0EAA0E;IAC1E,8EAA8E;IAC9E,IAAI,IAAI,CAAC,UAAU,CAAC,iCAAiC,CAAC,EAAE,CAAC;QACvD,IAAI,YAAY,CAAC,GAAG,CAAC,WAAW,CAAC;YAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;QACvD,OAAO;YACL,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,aAAa,CAAC,0BAA0B,EAAE,GAAG,CAAC,IAAI,CAAC;SAC3D,CAAC;IACJ,CAAC;IAED,iFAAiF;IACjF,wEAAwE;IACxE,yEAAyE;IACzE,yEAAyE;IACzE,0EAA0E;IAC1E,mFAAmF;IACnF,wEAAwE;IACxE,IAAI,IAAI,KAAK,iCAAiC,EAAE,CAAC;QAC/C,OAAO;YACL,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,aAAa,CAAC,gBAAgB,EAAE,GAAG,CAAC,IAAI,CAAC;SACjD,CAAC;IACJ,CAAC;IAED,sDAAsD;IACtD,IAAI,IAAI,CAAC,UAAU,CAAC,mCAAmC,CAAC,EAAE,CAAC;QACzD,IAAI,YAAY,CAAC,GAAG,CAAC,WAAW,CAAC;YAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;QACvD,OAAO;YACL,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,aAAa,CAAC,qBAAqB,EAAE,GAAG,CAAC,IAAI,CAAC;SACtD,CAAC;IACJ,CAAC;IAED,6CAA6C;IAC7C,IAAI,YAAY,CAAC,GAAG,CAAC,WAAW,CAAC;QAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;IACvD,OAAO;QACL,EAAE,EAAE,KAAK;QACT,KAAK,EAAE,aAAa,CAAC,UAAU,EAAE,GAAG,CAAC,IAAI,CAAC;KAC3C,CAAC;AACJ,CAAC;AAED,SAAS,aAAa,CAAC,MAAc,EAAE,IAAyB;IAC9D,OAAO,WAAW,MAAM,8BAA8B,IAAI,qBAAqB,CAAC;AAClF,CAAC","sourcesContent":["const ALLOWED_METHODS = new Set([\n \"GET\",\n \"POST\",\n \"PUT\",\n \"PATCH\",\n \"DELETE\",\n \"HEAD\",\n]);\n\nconst BLOCKED_HEADERS = new Set([\n \"connection\",\n \"content-length\",\n \"cookie\",\n \"forwarded\",\n \"host\",\n \"keep-alive\",\n \"origin\",\n \"proxy-authenticate\",\n \"proxy-authorization\",\n \"referer\",\n \"set-cookie\",\n \"te\",\n \"trailer\",\n \"transfer-encoding\",\n \"upgrade\",\n \"x-forwarded-for\",\n \"x-forwarded-host\",\n \"x-forwarded-proto\",\n]);\n\nconst HEADER_NAME_RE = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;\n\n/**\n * Path allowlist for the extension postMessage bridge.\n *\n * Extensions can only call paths under `/_agent-native/*` (the framework's own\n * namespace). Template-defined `/api/*` routes are intentionally rejected:\n * those routes are written by app authors who may not consistently apply the\n * `accessFilter`/`assertAccess` access scoping helpers. A shared/org extension\n * running with the viewer's session should not be able to reach surfaces\n * outside the framework's own well-audited namespace.\n *\n * If a template needs a extension to reach a custom route, expose it via an\n * action (`defineAction` auto-mounts under `/_agent-native/actions/<name>`).\n */\nexport function isAllowedExtensionPath(\n path: string,\n extensionId: string,\n): boolean {\n if (!path.startsWith(\"/\") || path.startsWith(\"//\")) return false;\n if (path.includes(\"\\\\\") || path.includes(\"\\0\")) return false;\n\n let pathname: string;\n try {\n const parsed = new URL(path, \"http://agent-native.local\");\n if (parsed.origin !== \"http://agent-native.local\") return false;\n pathname = parsed.pathname;\n } catch {\n return false;\n }\n\n const rawPathname = path.split(\"?\")[0].split(\"#\")[0];\n if (pathname !== rawPathname || pathname.includes(\"..\")) return false;\n\n if (pathname.startsWith(\"/_agent-native/actions/\")) return true;\n if (pathname.startsWith(\"/_agent-native/application-state/\")) return true;\n\n if (pathname === \"/_agent-native/extensions/proxy\") return true;\n if (pathname === \"/_agent-native/extensions/sql/query\") return true;\n if (pathname === \"/_agent-native/extensions/sql/exec\") return true;\n\n const parts = pathname.split(\"/\");\n if (\n parts.length >= 6 &&\n parts.length <= 7 &&\n parts[1] === \"_agent-native\" &&\n parts[2] === \"extensions\" &&\n parts[3] === \"data\"\n ) {\n try {\n return decodeURIComponent(parts[4]) === extensionId;\n } catch {\n return false;\n }\n }\n\n return false;\n}\n\nexport function sanitizeExtensionRequestOptions(value: unknown): RequestInit {\n if (!value || typeof value !== \"object\") return {};\n const raw = value as Record<string, unknown>;\n const method =\n typeof raw.method === \"string\" && raw.method.trim()\n ? raw.method.toUpperCase()\n : \"GET\";\n if (!ALLOWED_METHODS.has(method)) {\n throw new Error(\"Extension request method is not allowed\");\n }\n\n const headers =\n raw.headers && typeof raw.headers === \"object\"\n ? Object.fromEntries(\n Object.entries(raw.headers as Record<string, unknown>)\n .filter(([key, val]) => isAllowedHeader(key) && val !== undefined)\n .map(([key, val]) => [key, String(val)]),\n )\n : undefined;\n const body =\n typeof raw.body === \"string\" ||\n raw.body instanceof Blob ||\n raw.body instanceof FormData\n ? raw.body\n : raw.body === undefined\n ? undefined\n : JSON.stringify(raw.body);\n\n return {\n method,\n headers,\n body: method === \"GET\" || method === \"HEAD\" ? undefined : body,\n };\n}\n\nfunction isAllowedHeader(name: string): boolean {\n const lower = name.toLowerCase();\n return HEADER_NAME_RE.test(name) && !BLOCKED_HEADERS.has(lower);\n}\n\n// ---------------------------------------------------------------------------\n// Role-aware bridge gating (audit H4)\n// ---------------------------------------------------------------------------\n//\n// The host bridge dispatches every iframe postMessage request with the\n// viewer's session cookie. That means a non-author viewer's session can be\n// used to call mutating actions, write SQL, and resolve secret references —\n// the very capabilities that motivate the C1 consent step. After consent has\n// been granted, we still want defense-in-depth: a viewer whose role is\n// \"viewer\" should not be able to (e.g.) chain `appAction('share-resource')`\n// or run `dbExec` writes against their own data through someone else's extension.\n//\n// Role table (lowest tier first):\n//\n// role | appFetch | extensionFetch | extensionData | dbQuery | dbExec | appAction\n// ---------|-----------------|-----------------|----------------|---------|--------|----------\n// viewer | GET only | GET only | get/list only | deny | deny | allow*\n// editor | all methods | all methods | get/list/set/ | allow | allow* | allow\n// | | | remove | | |\n// admin | all methods | all methods | all | allow | allow* | allow\n// owner | all methods | all methods | all | allow | allow* | allow\n//\n// * dbExec destructive operations are independently blocked by the SQL\n// blocklist on the server (DROP / TRUNCATE / DELETE without WHERE etc).\n// The role gate sits in front of that — viewers can't reach the SQL\n// surface at all; editors and above hit the SQL gate as well.\n//\n// The SQL helpers are denied entirely for viewers (not just dbExec) because\n// the dbQuery surface in dev mode bypasses the production scoping shim and\n// can leak other users' rows in template tables that aren't in\n// SENSITIVE_SQL_RE.\n\nexport type ExtensionBridgeRole = \"owner\" | \"admin\" | \"editor\" | \"viewer\";\n\nconst READ_METHODS = new Set([\"GET\", \"HEAD\"]);\n\nexport interface BridgePolicyContext {\n /** Resolved role of the viewer on this extension. */\n role: ExtensionBridgeRole;\n /** True when viewer is the extension's owner_email — equivalent to role \"owner\"\n * but cheaper to plumb through from the render binding. */\n isAuthor: boolean;\n}\n\nexport interface BridgePolicyResult {\n ok: boolean;\n /** Human-readable error to send back to the iframe when ok=false. */\n error?: string;\n}\n\n/**\n * Decide whether the iframe is allowed to proxy this request given the\n * viewer's role on the extension. Authors (and owner/admin/editor in general)\n * keep the full bridge surface; viewers get a strictly read-only subset.\n *\n * Called BEFORE the request leaves the parent — so a denial is local-only\n * and never reveals server state to the iframe.\n */\nexport function checkBridgePolicy(\n path: string,\n method: string,\n ctx: BridgePolicyContext,\n): BridgePolicyResult {\n // Authors and the highest non-owner roles get the unrestricted bridge.\n if (ctx.isAuthor || ctx.role === \"owner\" || ctx.role === \"admin\") {\n return { ok: true };\n }\n\n // Editors get write access EXCEPT for the helper-specific destructive\n // operations the server still gates (the SQL blocklist + per-action\n // toolCallable flag, see audit H5).\n if (ctx.role === \"editor\") {\n return { ok: true };\n }\n\n // From here on: role === \"viewer\". Lock down everything beyond reads.\n const upperMethod = method.toUpperCase();\n\n // SQL is denied for viewers entirely (defense-in-depth: dev mode bypasses\n // the production scoping shim).\n if (path === \"/_agent-native/extensions/sql/query\") {\n return {\n ok: false,\n error: deniedMessage(\"dbQuery\", ctx.role),\n };\n }\n if (path === \"/_agent-native/extensions/sql/exec\") {\n return {\n ok: false,\n error: deniedMessage(\"dbExec\", ctx.role),\n };\n }\n\n // Actions are allowed for viewers; action-routes enforces each action's\n // `toolCallable` opt-out server-side. This keeps read-oriented widgets like\n // inbox stats usable when shared while still blocking sensitive actions such\n // as share/unshare through their per-action flags.\n if (path.startsWith(\"/_agent-native/actions/\")) {\n return { ok: true };\n }\n\n // Extension-data writes/deletes are denied; reads (GET/HEAD) are allowed.\n // Match /_agent-native/extensions/data/<extensionId>/<collection>[/<itemId>].\n if (path.startsWith(\"/_agent-native/extensions/data/\")) {\n if (READ_METHODS.has(upperMethod)) return { ok: true };\n return {\n ok: false,\n error: deniedMessage(\"extensionData.set/remove\", ctx.role),\n };\n }\n\n // extensionFetch — outbound proxy. POSTed JSON body carries the upstream method.\n // The bridge can only see the path here, not the upstream method, so we\n // restrict by REQUEST method (POST to /proxy carries the actual upstream\n // method as { method: 'GET' | ... } in body). For viewers we pre-flight-\n // deny the proxy unless a future code path emits a GET to /proxy/preview.\n // In practice, extensionFetch always POSTs to /proxy, so a viewer's extensionFetch\n // is denied entirely. Adapt this if /proxy gains a GET preview surface.\n if (path === \"/_agent-native/extensions/proxy\") {\n return {\n ok: false,\n error: deniedMessage(\"extensionFetch\", ctx.role),\n };\n }\n\n // application-state — viewers can read but not write.\n if (path.startsWith(\"/_agent-native/application-state/\")) {\n if (READ_METHODS.has(upperMethod)) return { ok: true };\n return {\n ok: false,\n error: deniedMessage(\"appFetch (mutation)\", ctx.role),\n };\n }\n\n // Generic appFetch — reads only for viewers.\n if (READ_METHODS.has(upperMethod)) return { ok: true };\n return {\n ok: false,\n error: deniedMessage(\"appFetch\", ctx.role),\n };\n}\n\nfunction deniedMessage(helper: string, role: ExtensionBridgeRole): string {\n return `Helper '${helper}' is not allowed for role '${role}' on this extension`;\n}\n"]}
1
+ {"version":3,"file":"iframe-bridge.js","sourceRoot":"","sources":["../../../src/client/extensions/iframe-bridge.ts"],"names":[],"mappings":"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","sourcesContent":["const ALLOWED_METHODS = new Set([\n \"GET\",\n \"POST\",\n \"PUT\",\n \"PATCH\",\n \"DELETE\",\n \"HEAD\",\n]);\n\nconst BLOCKED_HEADERS = new Set([\n \"connection\",\n \"content-length\",\n \"cookie\",\n \"forwarded\",\n \"host\",\n \"keep-alive\",\n \"origin\",\n \"proxy-authenticate\",\n \"proxy-authorization\",\n \"referer\",\n \"set-cookie\",\n \"te\",\n \"trailer\",\n \"transfer-encoding\",\n \"upgrade\",\n \"x-forwarded-for\",\n \"x-forwarded-host\",\n \"x-forwarded-proto\",\n]);\n\nconst HEADER_NAME_RE = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;\n\n/**\n * Path allowlist for the extension postMessage bridge.\n *\n * Extensions can only call paths under `/_agent-native/*` (the framework's own\n * namespace). Template-defined `/api/*` routes are intentionally rejected:\n * those routes are written by app authors who may not consistently apply the\n * `accessFilter`/`assertAccess` access scoping helpers. A shared/org extension\n * running with the viewer's session should not be able to reach surfaces\n * outside the framework's own well-audited namespace.\n *\n * If a template needs a extension to reach a custom route, expose it via an\n * action (`defineAction` auto-mounts under `/_agent-native/actions/<name>`).\n */\nexport function isAllowedExtensionPath(\n path: string,\n extensionId: string,\n): boolean {\n if (!path.startsWith(\"/\") || path.startsWith(\"//\")) return false;\n if (path.includes(\"\\\\\") || path.includes(\"\\0\")) return false;\n\n let pathname: string;\n try {\n const parsed = new URL(path, \"http://agent-native.local\");\n if (parsed.origin !== \"http://agent-native.local\") return false;\n pathname = parsed.pathname;\n } catch {\n return false;\n }\n\n const rawPathname = path.split(\"?\")[0].split(\"#\")[0];\n if (pathname !== rawPathname || pathname.includes(\"..\")) return false;\n\n if (pathname.startsWith(\"/_agent-native/actions/\")) return true;\n if (pathname.startsWith(\"/_agent-native/application-state/\")) return true;\n\n if (pathname === \"/_agent-native/extensions/proxy\") return true;\n if (pathname === \"/_agent-native/extensions/sql/query\") return true;\n if (pathname === \"/_agent-native/extensions/sql/exec\") return true;\n\n const parts = pathname.split(\"/\");\n if (\n parts.length >= 6 &&\n parts.length <= 7 &&\n parts[1] === \"_agent-native\" &&\n parts[2] === \"extensions\" &&\n parts[3] === \"data\"\n ) {\n try {\n return decodeURIComponent(parts[4]) === extensionId;\n } catch {\n return false;\n }\n }\n\n return false;\n}\n\nexport function sanitizeExtensionRequestOptions(value: unknown): RequestInit {\n if (!value || typeof value !== \"object\") return {};\n const raw = value as Record<string, unknown>;\n const method =\n typeof raw.method === \"string\" && raw.method.trim()\n ? raw.method.toUpperCase()\n : \"GET\";\n if (!ALLOWED_METHODS.has(method)) {\n throw new Error(\"Extension request method is not allowed\");\n }\n\n const headers =\n raw.headers && typeof raw.headers === \"object\"\n ? Object.fromEntries(\n Object.entries(raw.headers as Record<string, unknown>)\n .filter(([key, val]) => isAllowedHeader(key) && val !== undefined)\n .map(([key, val]) => [key, String(val)]),\n )\n : undefined;\n const body =\n typeof raw.body === \"string\" ||\n raw.body instanceof Blob ||\n raw.body instanceof FormData\n ? raw.body\n : raw.body === undefined\n ? undefined\n : JSON.stringify(raw.body);\n\n return {\n method,\n headers,\n body: method === \"GET\" || method === \"HEAD\" ? undefined : body,\n };\n}\n\nfunction isAllowedHeader(name: string): boolean {\n const lower = name.toLowerCase();\n return HEADER_NAME_RE.test(name) && !BLOCKED_HEADERS.has(lower);\n}\n\n// ---------------------------------------------------------------------------\n// Role-aware bridge gating (audit H4)\n// ---------------------------------------------------------------------------\n//\n// The host bridge dispatches every iframe postMessage request with the\n// viewer's session cookie. That means a non-author viewer's session can be\n// used to call mutating actions, write SQL, and resolve secret references —\n// the very capabilities that motivate the C1 consent step. After consent has\n// been granted, we still want defense-in-depth: a viewer whose role is\n// \"viewer\" should not be able to (e.g.) chain `appAction('share-resource')`\n// or run `dbExec` writes against their own data through someone else's extension.\n//\n// Role table (lowest tier first):\n//\n// role | appFetch | extensionFetch | extensionData | dbQuery | dbExec | appAction\n// ---------|-----------------|-----------------|----------------|---------|--------|----------\n// viewer | GET only | GET only | get/list only | deny | deny | allow*\n// editor | all methods | all methods | get/list/set/ | allow | allow* | allow\n// | | | remove | | |\n// admin | all methods | all methods | all | allow | allow* | allow\n// owner | all methods | all methods | all | allow | allow* | allow\n//\n// * dbExec destructive operations are independently blocked by the SQL\n// blocklist on the server (DROP / TRUNCATE / DELETE without WHERE etc).\n// The role gate sits in front of that — viewers can't reach the SQL\n// surface at all; editors and above hit the SQL gate as well.\n//\n// The SQL helpers are denied entirely for viewers (not just dbExec) because\n// the dbQuery surface in dev mode bypasses the production scoping shim and\n// can leak other users' rows in template tables that aren't in\n// SENSITIVE_SQL_RE.\n\nexport type ExtensionBridgeRole = \"owner\" | \"admin\" | \"editor\" | \"viewer\";\n\nconst READ_METHODS = new Set([\"GET\", \"HEAD\"]);\n\nexport interface BridgePolicyContext {\n /** Resolved role of the viewer on this extension. */\n role: ExtensionBridgeRole;\n /** True when viewer is the extension's owner_email — equivalent to role \"owner\"\n * but cheaper to plumb through from the render binding. */\n isAuthor: boolean;\n /** Database-backed extensions use role gates; local-file extensions use manifest gates. */\n source?: \"database\" | \"local-files\";\n permissions?: {\n appActions?: string[];\n extensionData?: boolean;\n sql?: boolean;\n externalFetch?: boolean;\n };\n}\n\nexport interface BridgePolicyResult {\n ok: boolean;\n /** Human-readable error to send back to the iframe when ok=false. */\n error?: string;\n}\n\n/**\n * Decide whether the iframe is allowed to proxy this request given the\n * viewer's role on the extension. Authors (and owner/admin/editor in general)\n * keep the full bridge surface; viewers get a strictly read-only subset.\n *\n * Called BEFORE the request leaves the parent — so a denial is local-only\n * and never reveals server state to the iframe.\n */\nexport function checkBridgePolicy(\n path: string,\n method: string,\n ctx: BridgePolicyContext,\n): BridgePolicyResult {\n if (ctx.source === \"local-files\") {\n return checkLocalFileBridgePolicy(path, method, ctx);\n }\n\n // Authors and the highest non-owner roles get the unrestricted bridge.\n if (ctx.isAuthor || ctx.role === \"owner\" || ctx.role === \"admin\") {\n return { ok: true };\n }\n\n // Editors get write access EXCEPT for the helper-specific destructive\n // operations the server still gates (the SQL blocklist + per-action\n // toolCallable flag, see audit H5).\n if (ctx.role === \"editor\") {\n return { ok: true };\n }\n\n // From here on: role === \"viewer\". Lock down everything beyond reads.\n const upperMethod = method.toUpperCase();\n\n // SQL is denied for viewers entirely (defense-in-depth: dev mode bypasses\n // the production scoping shim).\n if (path === \"/_agent-native/extensions/sql/query\") {\n return {\n ok: false,\n error: deniedMessage(\"dbQuery\", ctx.role),\n };\n }\n if (path === \"/_agent-native/extensions/sql/exec\") {\n return {\n ok: false,\n error: deniedMessage(\"dbExec\", ctx.role),\n };\n }\n\n // Actions are allowed for viewers; action-routes enforces each action's\n // `toolCallable` opt-out server-side. This keeps read-oriented widgets like\n // inbox stats usable when shared while still blocking sensitive actions such\n // as share/unshare through their per-action flags.\n if (path.startsWith(\"/_agent-native/actions/\")) {\n return { ok: true };\n }\n\n // Extension-data writes/deletes are denied; reads (GET/HEAD) are allowed.\n // Match /_agent-native/extensions/data/<extensionId>/<collection>[/<itemId>].\n if (path.startsWith(\"/_agent-native/extensions/data/\")) {\n if (READ_METHODS.has(upperMethod)) return { ok: true };\n return {\n ok: false,\n error: deniedMessage(\"extensionData.set/remove\", ctx.role),\n };\n }\n\n // extensionFetch — outbound proxy. POSTed JSON body carries the upstream method.\n // The bridge can only see the path here, not the upstream method, so we\n // restrict by REQUEST method (POST to /proxy carries the actual upstream\n // method as { method: 'GET' | ... } in body). For viewers we pre-flight-\n // deny the proxy unless a future code path emits a GET to /proxy/preview.\n // In practice, extensionFetch always POSTs to /proxy, so a viewer's extensionFetch\n // is denied entirely. Adapt this if /proxy gains a GET preview surface.\n if (path === \"/_agent-native/extensions/proxy\") {\n return {\n ok: false,\n error: deniedMessage(\"extensionFetch\", ctx.role),\n };\n }\n\n // application-state — viewers can read but not write.\n if (path.startsWith(\"/_agent-native/application-state/\")) {\n if (READ_METHODS.has(upperMethod)) return { ok: true };\n return {\n ok: false,\n error: deniedMessage(\"appFetch (mutation)\", ctx.role),\n };\n }\n\n // Generic appFetch — reads only for viewers.\n if (READ_METHODS.has(upperMethod)) return { ok: true };\n return {\n ok: false,\n error: deniedMessage(\"appFetch\", ctx.role),\n };\n}\n\nfunction deniedMessage(helper: string, role: ExtensionBridgeRole): string {\n return `Helper '${helper}' is not allowed for role '${role}' on this extension`;\n}\n\nfunction localDeniedMessage(helper: string): string {\n return `Helper '${helper}' is not allowed by this local extension's extension.json permissions`;\n}\n\nfunction actionNameFromPath(path: string): string | null {\n try {\n const pathname = new URL(path, \"http://agent-native.local\").pathname;\n const prefix = \"/_agent-native/actions/\";\n if (!pathname.startsWith(prefix)) return null;\n return decodeURIComponent(pathname.slice(prefix.length));\n } catch {\n return null;\n }\n}\n\nfunction localAllowsAction(ctx: BridgePolicyContext, path: string): boolean {\n const action = actionNameFromPath(path);\n if (!action) return false;\n const actions = ctx.permissions?.appActions ?? [];\n return actions.includes(\"*\") || actions.includes(action);\n}\n\nfunction checkLocalFileBridgePolicy(\n path: string,\n method: string,\n ctx: BridgePolicyContext,\n): BridgePolicyResult {\n const upperMethod = method.toUpperCase();\n\n if (path.startsWith(\"/_agent-native/actions/\")) {\n if (localAllowsAction(ctx, path)) return { ok: true };\n return { ok: false, error: localDeniedMessage(\"appAction\") };\n }\n\n if (\n path === \"/_agent-native/extensions/sql/query\" ||\n path === \"/_agent-native/extensions/sql/exec\"\n ) {\n return { ok: false, error: localDeniedMessage(\"dbQuery/dbExec\") };\n }\n\n if (path === \"/_agent-native/extensions/proxy\") {\n return { ok: false, error: localDeniedMessage(\"extensionFetch\") };\n }\n\n if (path.startsWith(\"/_agent-native/extensions/data/\")) {\n if (ctx.permissions?.extensionData === false) {\n return { ok: false, error: localDeniedMessage(\"extensionData\") };\n }\n return { ok: true };\n }\n\n if (path.startsWith(\"/_agent-native/application-state/\")) {\n if (READ_METHODS.has(upperMethod)) return { ok: true };\n return { ok: false, error: localDeniedMessage(\"applicationState\") };\n }\n\n if (READ_METHODS.has(upperMethod)) return { ok: true };\n return { ok: false, error: localDeniedMessage(\"appFetch\") };\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"RunsTray.d.ts","sourceRoot":"","sources":["../../../src/client/progress/RunsTray.tsx"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,QAAQ,EAAkB,MAAM,yBAAyB,CAAC;AAmBxE,KAAK,WAAW,GAAG,QAAQ,CAAC;AAC5B,KAAK,sBAAsB,GAAG,MAAM,GAAG,MAAM,CAAC;AAE9C,UAAU,aAAa;IACrB,qDAAqD;IACrD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,mDAAmD;IACnD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,mEAAmE;IACnE,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,2FAA2F;IAC3F,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,2EAA2E;IAC3E,cAAc,CAAC,EAAE,sBAAsB,CAAC;IACxC,8DAA8D;IAC9D,YAAY,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,WAAW,KAAK,IAAI,CAAC;IAC5D,KAAK,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAC;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AA2ID;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,EACvB,MAAa,EACb,KAAS,EACT,YAAmB,EACnB,UAAU,EACV,cAAuB,EACvB,YAAY,EACZ,KAAa,EACb,SAAS,GACV,EAAE,aAAa,kDAiGf;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,MAAa,EACb,KAAS,EACT,YAAoB,EACpB,UAAiB,EACjB,YAAY,GACb,EAAE,IAAI,CACL,aAAa,EACb,QAAQ,GAAG,OAAO,GAAG,cAAc,GAAG,YAAY,GAAG,cAAc,CACpE,kDA8DA"}
1
+ {"version":3,"file":"RunsTray.d.ts","sourceRoot":"","sources":["../../../src/client/progress/RunsTray.tsx"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,QAAQ,EAAkB,MAAM,yBAAyB,CAAC;AAmBxE,KAAK,WAAW,GAAG,QAAQ,CAAC;AAC5B,KAAK,sBAAsB,GAAG,MAAM,GAAG,MAAM,CAAC;AAE9C,UAAU,aAAa;IACrB,qDAAqD;IACrD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,mDAAmD;IACnD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,mEAAmE;IACnE,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,2FAA2F;IAC3F,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,2EAA2E;IAC3E,cAAc,CAAC,EAAE,sBAAsB,CAAC;IACxC,8DAA8D;IAC9D,YAAY,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,WAAW,KAAK,IAAI,CAAC;IAC5D,KAAK,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAC;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AA2ID;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,EACvB,MAAa,EACb,KAAS,EACT,YAAmB,EACnB,UAAU,EACV,cAAuB,EACvB,YAAY,EACZ,KAAa,EACb,SAAS,GACV,EAAE,aAAa,kDAiGf;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,MAAa,EACb,KAAS,EACT,YAAoB,EACpB,UAAiB,EACjB,YAAY,GACb,EAAE,IAAI,CACL,aAAa,EACb,QAAQ,GAAG,OAAO,GAAG,cAAc,GAAG,YAAY,GAAG,cAAc,CACpE,kDA4EA"}
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { agentNativePath } from "../api-path.js";
3
3
  import { useCallback, useEffect, useMemo, useState } from "react";
4
- import { IconAlertCircle, IconCheck, IconClock, IconExternalLink, IconLoader2, IconPlayerStop, IconX, } from "@tabler/icons-react";
4
+ import { IconAlertCircle, IconCheck, IconClock, IconExternalLink, IconLoader2, IconPlayerStop, IconSubtask, IconX, } from "@tabler/icons-react";
5
5
  import { usePausingInterval } from "../use-pausing-interval.js";
6
6
  import { cn } from "../utils.js";
7
7
  import { Popover, PopoverContent, PopoverTrigger, } from "../components/ui/popover.js";
@@ -75,7 +75,7 @@ function useRunsTrayState({ pollMs = 3000, limit = 5, hideWhenIdle = true, showR
75
75
  ? IconLoader2
76
76
  : failedCount > 0
77
77
  ? IconAlertCircle
78
- : IconClock;
78
+ : IconSubtask;
79
79
  const triggerTone = activeCount > 0
80
80
  ? "text-primary"
81
81
  : failedCount > 0
@@ -120,6 +120,7 @@ export function RunsTray({ pollMs = 3000, limit = 5, hideWhenIdle = true, showRe
120
120
  : "absolute -right-0.5 -top-0.5 px-1"), children: failedCount > 9 ? "9+" : failedCount })) : null] }) }) }), _jsx(TooltipContent, { children: triggerLabel })] }) }), _jsx(PopoverContent, { align: align, sideOffset: 8, className: "an-runs-tray__menu w-80 max-w-[calc(100vw-24px)] p-0", children: _jsx(RunsTrayContent, { runs: runs, hasRuns: hasRuns, activeCount: activeCount, terminalCount: terminalCount, onDismiss: dismissRun, onStop: stopRun, onOpenThread: onOpenThread }) })] }));
121
121
  }
122
122
  export function RunsTrayMenuItem({ pollMs = 3000, limit = 5, hideWhenIdle = false, showRecent = true, onOpenThread, }) {
123
+ const [submenuOpen, setSubmenuOpen] = useState(false);
123
124
  const { runs, hasRuns, activeCount, failedCount, terminalCount, triggerLabel, TriggerIcon, triggerTone, dismissRun, stopRun, } = useRunsTrayState({
124
125
  pollMs,
125
126
  limit,
@@ -128,7 +129,15 @@ export function RunsTrayMenuItem({ pollMs = 3000, limit = 5, hideWhenIdle = fals
128
129
  });
129
130
  if (!hasRuns && hideWhenIdle)
130
131
  return null;
131
- return (_jsxs(DropdownMenuSub, { children: [_jsxs(DropdownMenuSubTrigger, { className: "gap-2", children: [_jsx(TriggerIcon, { size: 14, className: cn(triggerTone, activeCount > 0 && "animate-spin"), "aria-hidden": true }), _jsx("span", { className: "min-w-0 flex-1 truncate", children: "Agent runs" }), activeCount > 0 ? (_jsx("span", { "aria-label": triggerLabel, className: "rounded-full bg-primary px-1.5 text-[10px] font-medium leading-4 text-primary-foreground", children: activeCount > 9 ? "9+" : activeCount })) : failedCount > 0 ? (_jsx("span", { "aria-label": triggerLabel, className: "rounded-full bg-destructive px-1.5 text-[10px] font-medium leading-4 text-destructive-foreground", children: failedCount > 9 ? "9+" : failedCount })) : null] }), _jsx(DropdownMenuSubContent, { sideOffset: 6, className: "an-runs-tray__menu w-80 max-w-[calc(100vw-24px)] p-0", children: _jsx(RunsTrayContent, { runs: runs, hasRuns: hasRuns, activeCount: activeCount, terminalCount: terminalCount, onDismiss: dismissRun, onStop: stopRun, onOpenThread: onOpenThread }) })] }));
132
+ return (_jsxs(DropdownMenuSub, { open: submenuOpen, onOpenChange: setSubmenuOpen, children: [_jsxs(DropdownMenuSubTrigger, { "aria-label": `Agent runs, ${triggerLabel}`, className: "cursor-pointer gap-2", onClick: (event) => {
133
+ event.preventDefault();
134
+ setSubmenuOpen(true);
135
+ }, onKeyDown: (event) => {
136
+ if (event.key === "Enter" || event.key === " ") {
137
+ event.preventDefault();
138
+ setSubmenuOpen(true);
139
+ }
140
+ }, children: [_jsx(TriggerIcon, { size: 14, className: cn(triggerTone, activeCount > 0 && "animate-spin"), "aria-hidden": true }), _jsx("span", { className: "min-w-0 flex-1 truncate", children: "Agent runs" }), activeCount > 0 ? (_jsx("span", { "aria-label": triggerLabel, className: "rounded-full bg-primary px-1.5 text-[10px] font-medium leading-4 text-primary-foreground", children: activeCount > 9 ? "9+" : activeCount })) : failedCount > 0 ? (_jsx("span", { "aria-label": triggerLabel, className: "rounded-full bg-destructive px-1.5 text-[10px] font-medium leading-4 text-destructive-foreground", children: failedCount > 9 ? "9+" : failedCount })) : null] }), _jsx(DropdownMenuSubContent, { sideOffset: 6, className: "an-runs-tray__menu w-80 max-w-[calc(100vw-24px)] p-0", children: _jsx(RunsTrayContent, { runs: runs, hasRuns: hasRuns, activeCount: activeCount, terminalCount: terminalCount, onDismiss: dismissRun, onStop: stopRun, onOpenThread: onOpenThread }) })] }));
132
141
  }
133
142
  function RunsTrayContent({ runs, hasRuns, activeCount, terminalCount, onDismiss, onStop, onOpenThread, }) {
134
143
  return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "border-b border-border px-3 py-2", children: [_jsx("div", { className: "text-sm font-medium text-foreground", children: "Agent runs" }), _jsx("div", { className: "mt-0.5 text-[11px] text-muted-foreground", children: activeCount > 0
@@ -1 +1 @@
1
- {"version":3,"file":"RunsTray.js","sourceRoot":"","sources":["../../../src/client/progress/RunsTray.tsx"],"names":[],"mappings":"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","sourcesContent":["import { agentNativePath } from \"../api-path.js\";\nimport { useCallback, useEffect, useMemo, useState } from \"react\";\nimport {\n IconAlertCircle,\n IconCheck,\n IconClock,\n IconExternalLink,\n IconLoader2,\n IconPlayerStop,\n IconX,\n} from \"@tabler/icons-react\";\nimport { usePausingInterval } from \"../use-pausing-interval.js\";\nimport type { AgentRun, ProgressStatus } from \"../../progress/types.js\";\nimport { cn } from \"../utils.js\";\nimport {\n Popover,\n PopoverContent,\n PopoverTrigger,\n} from \"../components/ui/popover.js\";\nimport {\n DropdownMenuSub,\n DropdownMenuSubContent,\n DropdownMenuSubTrigger,\n} from \"../components/ui/dropdown-menu.js\";\nimport {\n Tooltip,\n TooltipContent,\n TooltipProvider,\n TooltipTrigger,\n} from \"../components/ui/tooltip.js\";\n\ntype AgentRunDto = AgentRun;\ntype RunsTrayTriggerVariant = \"icon\" | \"pill\";\n\ninterface RunsTrayProps {\n /** Poll interval in ms. 0 disables. Default 3000. */\n pollMs?: number;\n /** Max runs to show in the dropdown. Default 5. */\n limit?: number;\n /** Hide the trigger entirely when no active runs. Default true. */\n hideWhenIdle?: boolean;\n /** Include recent terminal runs instead of active runs only. Defaults to !hideWhenIdle. */\n showRecent?: boolean;\n /** Compact icon for app headers, or a labeled pill for the agent panel. */\n triggerVariant?: RunsTrayTriggerVariant;\n /** Called when a run can open a related agent chat thread. */\n onOpenThread?: (threadId: string, run: AgentRunDto) => void;\n align?: \"start\" | \"center\" | \"end\";\n className?: string;\n}\n\ninterface RunsTrayState {\n runs: AgentRunDto[];\n hasRuns: boolean;\n activeCount: number;\n failedCount: number;\n terminalCount: number;\n triggerLabel: string;\n TriggerIcon: typeof IconLoader2;\n triggerTone: string;\n dismissRun: (runId: string) => void;\n stopRun: (runId: string) => void;\n}\n\nfunction useRunsTrayState({\n pollMs = 3000,\n limit = 5,\n hideWhenIdle = true,\n showRecent,\n}: Pick<\n RunsTrayProps,\n \"pollMs\" | \"limit\" | \"hideWhenIdle\" | \"showRecent\"\n>): RunsTrayState {\n const [runs, setRuns] = useState<AgentRunDto[]>([]);\n const includeRecent = showRecent ?? !hideWhenIdle;\n\n const refresh = useCallback(async () => {\n try {\n const query = new URLSearchParams({ limit: String(limit) });\n if (!includeRecent) query.set(\"active\", \"true\");\n const res = await fetch(\n agentNativePath(`/_agent-native/runs?${query.toString()}`),\n );\n if (!res.ok) return;\n const rows = (await res.json()) as AgentRunDto[];\n setRuns(rows);\n } catch {\n // best-effort\n }\n }, [includeRecent, limit]);\n\n useEffect(() => {\n refresh();\n }, [refresh]);\n\n usePausingInterval(refresh, pollMs);\n\n const dismissRun = useCallback(\n async (runId: string) => {\n setRuns((current) => current.filter((run) => run.id !== runId));\n try {\n const res = await fetch(\n agentNativePath(`/_agent-native/runs/${runId}`),\n {\n method: \"DELETE\",\n headers: { \"X-Agent-Native-CSRF\": \"1\" },\n },\n );\n if (!res.ok) throw new Error(`Dismiss failed (${res.status})`);\n } catch {\n refresh();\n }\n },\n [refresh],\n );\n\n const stopRun = useCallback(\n async (runId: string) => {\n // Optimistic: mark as cancelled immediately so the UI is responsive.\n setRuns((current) =>\n current.map((run) =>\n run.id === runId\n ? { ...run, status: \"cancelled\" as ProgressStatus }\n : run,\n ),\n );\n try {\n const res = await fetch(\n agentNativePath(`/_agent-native/agent-chat/runs/${runId}/stop`),\n {\n method: \"POST\",\n headers: { \"X-Agent-Native-CSRF\": \"1\" },\n },\n );\n if (!res.ok) throw new Error(`Stop failed (${res.status})`);\n } catch {\n // Reconcile from server on failure\n refresh();\n }\n },\n [refresh],\n );\n\n const hasRuns = runs.length > 0;\n const activeCount = useMemo(\n () => runs.filter((run) => run.status === \"running\").length,\n [runs],\n );\n const failedCount = useMemo(\n () => runs.filter((run) => run.status === \"failed\").length,\n [runs],\n );\n const triggerLabel =\n activeCount > 0\n ? `${activeCount} active run${activeCount > 1 ? \"s\" : \"\"}`\n : failedCount > 0\n ? `${failedCount} failed run${failedCount > 1 ? \"s\" : \"\"}`\n : hasRuns\n ? \"Recent runs\"\n : \"No recent runs\";\n const TriggerIcon =\n activeCount > 0\n ? IconLoader2\n : failedCount > 0\n ? IconAlertCircle\n : IconClock;\n const triggerTone =\n activeCount > 0\n ? \"text-primary\"\n : failedCount > 0\n ? \"text-destructive\"\n : \"text-muted-foreground\";\n const terminalCount = runs.length - activeCount;\n\n return {\n runs,\n hasRuns,\n activeCount,\n failedCount,\n terminalCount,\n triggerLabel,\n TriggerIcon,\n triggerTone,\n dismissRun,\n stopRun,\n };\n}\n\n/**\n * Header-bar progress indicator. Shows a spinner icon or labeled Runs pill\n * with a count badge when runs are active; opens a popover with live progress.\n * Same inline-header pattern as <NotificationsBell /> — drop it into the\n * header, no floating overlay over the main content.\n */\nexport function RunsTray({\n pollMs = 3000,\n limit = 5,\n hideWhenIdle = true,\n showRecent,\n triggerVariant = \"icon\",\n onOpenThread,\n align = \"end\",\n className,\n}: RunsTrayProps) {\n const [open, setOpen] = useState(false);\n const {\n runs,\n hasRuns,\n activeCount,\n failedCount,\n terminalCount,\n triggerLabel,\n TriggerIcon,\n triggerTone,\n dismissRun,\n stopRun,\n } = useRunsTrayState({\n pollMs,\n limit,\n hideWhenIdle,\n showRecent,\n });\n\n if (!hasRuns && hideWhenIdle) return null;\n\n return (\n <Popover open={open} onOpenChange={setOpen}>\n <TooltipProvider delayDuration={200}>\n <Tooltip>\n <TooltipTrigger asChild>\n <PopoverTrigger asChild>\n <button\n type=\"button\"\n aria-label={triggerLabel}\n aria-expanded={open}\n className={cn(\n \"an-runs-tray__trigger relative inline-flex shrink-0 items-center justify-center rounded-md text-muted-foreground hover:bg-accent/40 hover:text-foreground\",\n triggerVariant === \"pill\"\n ? \"h-7 min-w-[68px] gap-1.5 border border-border/70 bg-background px-2 text-[11px] font-medium\"\n : \"h-8 w-8\",\n open && \"bg-accent/50 text-foreground\",\n className,\n )}\n >\n <TriggerIcon\n size={triggerVariant === \"pill\" ? 14 : 18}\n className={cn(triggerTone, activeCount > 0 && \"animate-spin\")}\n aria-hidden\n />\n {triggerVariant === \"pill\" ? (\n <span className=\"leading-none\">Runs</span>\n ) : null}\n {activeCount > 0 ? (\n <span\n aria-hidden\n className={cn(\n \"an-runs-tray__badge rounded-full bg-primary text-[10px] font-medium leading-[14px] text-primary-foreground\",\n triggerVariant === \"pill\"\n ? \"min-w-4 px-1\"\n : \"absolute -right-0.5 -top-0.5 px-1\",\n )}\n >\n {activeCount > 9 ? \"9+\" : activeCount}\n </span>\n ) : failedCount > 0 ? (\n <span\n aria-hidden\n className={cn(\n \"rounded-full bg-destructive text-[10px] font-medium leading-[14px] text-destructive-foreground\",\n triggerVariant === \"pill\"\n ? \"min-w-4 px-1\"\n : \"absolute -right-0.5 -top-0.5 px-1\",\n )}\n >\n {failedCount > 9 ? \"9+\" : failedCount}\n </span>\n ) : null}\n </button>\n </PopoverTrigger>\n </TooltipTrigger>\n <TooltipContent>{triggerLabel}</TooltipContent>\n </Tooltip>\n </TooltipProvider>\n <PopoverContent\n align={align}\n sideOffset={8}\n className=\"an-runs-tray__menu w-80 max-w-[calc(100vw-24px)] p-0\"\n >\n <RunsTrayContent\n runs={runs}\n hasRuns={hasRuns}\n activeCount={activeCount}\n terminalCount={terminalCount}\n onDismiss={dismissRun}\n onStop={stopRun}\n onOpenThread={onOpenThread}\n />\n </PopoverContent>\n </Popover>\n );\n}\n\nexport function RunsTrayMenuItem({\n pollMs = 3000,\n limit = 5,\n hideWhenIdle = false,\n showRecent = true,\n onOpenThread,\n}: Pick<\n RunsTrayProps,\n \"pollMs\" | \"limit\" | \"hideWhenIdle\" | \"showRecent\" | \"onOpenThread\"\n>) {\n const {\n runs,\n hasRuns,\n activeCount,\n failedCount,\n terminalCount,\n triggerLabel,\n TriggerIcon,\n triggerTone,\n dismissRun,\n stopRun,\n } = useRunsTrayState({\n pollMs,\n limit,\n hideWhenIdle,\n showRecent,\n });\n\n if (!hasRuns && hideWhenIdle) return null;\n\n return (\n <DropdownMenuSub>\n <DropdownMenuSubTrigger className=\"gap-2\">\n <TriggerIcon\n size={14}\n className={cn(triggerTone, activeCount > 0 && \"animate-spin\")}\n aria-hidden\n />\n <span className=\"min-w-0 flex-1 truncate\">Agent runs</span>\n {activeCount > 0 ? (\n <span\n aria-label={triggerLabel}\n className=\"rounded-full bg-primary px-1.5 text-[10px] font-medium leading-4 text-primary-foreground\"\n >\n {activeCount > 9 ? \"9+\" : activeCount}\n </span>\n ) : failedCount > 0 ? (\n <span\n aria-label={triggerLabel}\n className=\"rounded-full bg-destructive px-1.5 text-[10px] font-medium leading-4 text-destructive-foreground\"\n >\n {failedCount > 9 ? \"9+\" : failedCount}\n </span>\n ) : null}\n </DropdownMenuSubTrigger>\n <DropdownMenuSubContent\n sideOffset={6}\n className=\"an-runs-tray__menu w-80 max-w-[calc(100vw-24px)] p-0\"\n >\n <RunsTrayContent\n runs={runs}\n hasRuns={hasRuns}\n activeCount={activeCount}\n terminalCount={terminalCount}\n onDismiss={dismissRun}\n onStop={stopRun}\n onOpenThread={onOpenThread}\n />\n </DropdownMenuSubContent>\n </DropdownMenuSub>\n );\n}\n\nfunction RunsTrayContent({\n runs,\n hasRuns,\n activeCount,\n terminalCount,\n onDismiss,\n onStop,\n onOpenThread,\n}: {\n runs: AgentRunDto[];\n hasRuns: boolean;\n activeCount: number;\n terminalCount: number;\n onDismiss: (runId: string) => void;\n onStop: (runId: string) => void;\n onOpenThread?: (threadId: string, run: AgentRunDto) => void;\n}) {\n return (\n <>\n <div className=\"border-b border-border px-3 py-2\">\n <div className=\"text-sm font-medium text-foreground\">Agent runs</div>\n <div className=\"mt-0.5 text-[11px] text-muted-foreground\">\n {activeCount > 0\n ? `${activeCount} running${terminalCount > 0 ? ` · ${terminalCount} recent` : \"\"}`\n : hasRuns\n ? `${runs.length} recent run${runs.length > 1 ? \"s\" : \"\"}`\n : \"No tracked work yet\"}\n </div>\n </div>\n {hasRuns ? (\n <div className=\"max-h-96 divide-y divide-border overflow-y-auto\">\n {runs.map((run) => (\n <RunRow\n key={run.id}\n run={run}\n onDismiss={onDismiss}\n onStop={onStop}\n onOpenThread={onOpenThread}\n />\n ))}\n </div>\n ) : (\n <div className=\"px-3 py-6 text-center\">\n <IconClock\n size={22}\n className=\"mx-auto text-muted-foreground/50\"\n aria-hidden\n />\n <div className=\"mt-2 text-sm font-medium text-foreground\">\n No recent runs\n </div>\n <p className=\"mt-1 text-xs leading-relaxed text-muted-foreground\">\n Background agent work will appear here while it runs and after it\n finishes.\n </p>\n </div>\n )}\n </>\n );\n}\n\nfunction getRunThreadId(run: AgentRunDto): string | undefined {\n const metadata = run.metadata ?? {};\n const direct =\n typeof metadata.threadId === \"string\"\n ? metadata.threadId\n : typeof metadata.thread_id === \"string\"\n ? metadata.thread_id\n : undefined;\n if (direct?.trim()) return direct.trim();\n\n const surfaceUrl =\n typeof metadata.surfaceUrl === \"string\" ? metadata.surfaceUrl : undefined;\n const match = surfaceUrl?.match(/^agent-native:\\/\\/threads\\/(.+)$/);\n return match?.[1] ? decodeURIComponent(match[1]) : undefined;\n}\n\nfunction formatRunTime(run: AgentRunDto): string {\n const when = run.completedAt ?? run.updatedAt ?? run.startedAt;\n const timestamp = Date.parse(when);\n if (!Number.isFinite(timestamp)) return \"\";\n const diffMs = Date.now() - timestamp;\n const prefix = run.status === \"running\" ? \"Updated\" : \"Finished\";\n if (diffMs < 30_000) return `${prefix} just now`;\n const minutes = Math.floor(diffMs / 60_000);\n if (minutes < 60) return `${prefix} ${minutes}m ago`;\n const hours = Math.floor(minutes / 60);\n if (hours < 24) return `${prefix} ${hours}h ago`;\n return `${prefix} ${new Date(timestamp).toLocaleDateString([], {\n month: \"short\",\n day: \"numeric\",\n })}`;\n}\n\nfunction isAgentTeamRun(run: AgentRunDto): boolean {\n return (\n typeof run.metadata === \"object\" &&\n run.metadata !== null &&\n (run.metadata as Record<string, unknown>).kind === \"agent-team\"\n );\n}\n\nfunction RunRow({\n run,\n onDismiss,\n onStop,\n onOpenThread,\n}: {\n run: AgentRunDto;\n onDismiss: (runId: string) => void;\n onStop: (runId: string) => void;\n onOpenThread?: (threadId: string, run: AgentRunDto) => void;\n}) {\n const threadId = getRunThreadId(run);\n const isRunning = run.status === \"running\";\n const canStop = isRunning && isAgentTeamRun(run);\n\n return (\n <div className=\"flex flex-col gap-1.5 px-3 py-2.5 text-sm\">\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"min-w-0 flex-1\">\n <div className=\"truncate font-medium text-foreground\">\n {run.title}\n </div>\n {run.step ? (\n <div className=\"mt-0.5 truncate text-xs text-muted-foreground\">\n {run.step}\n </div>\n ) : null}\n </div>\n <StatusPill status={run.status} />\n </div>\n {run.percent != null || isRunning ? (\n <div className=\"h-1 w-full overflow-hidden rounded bg-muted\">\n {run.percent != null ? (\n <div\n className={cn(\n \"h-full transition-all\",\n run.status === \"failed\"\n ? \"bg-destructive\"\n : run.status === \"cancelled\"\n ? \"bg-muted-foreground/50\"\n : \"bg-primary\",\n )}\n style={{ width: `${run.percent}%` }}\n />\n ) : (\n <div className=\"h-full w-1/3 animate-pulse bg-primary/60\" />\n )}\n </div>\n ) : null}\n <div className=\"flex items-center justify-between gap-2\">\n <span className=\"min-w-0 truncate text-[10px] text-muted-foreground/70\">\n {formatRunTime(run)}\n </span>\n <div className=\"flex shrink-0 items-center gap-1\">\n {threadId && onOpenThread ? (\n <button\n type=\"button\"\n className=\"inline-flex h-6 items-center gap-1 rounded px-1.5 text-[11px] font-medium text-muted-foreground hover:bg-accent/60 hover:text-foreground\"\n onClick={() => onOpenThread(threadId, run)}\n >\n Open\n <IconExternalLink size={12} aria-hidden />\n </button>\n ) : null}\n {canStop ? (\n <button\n type=\"button\"\n aria-label={`Stop ${run.title}`}\n className=\"inline-flex h-6 w-6 cursor-pointer items-center justify-center rounded text-muted-foreground hover:bg-accent/60 hover:text-destructive\"\n onClick={() => onStop(run.id)}\n >\n <IconPlayerStop size={13} aria-hidden />\n </button>\n ) : null}\n {!isRunning ? (\n <button\n type=\"button\"\n aria-label={`Hide ${run.title}`}\n className=\"inline-flex h-6 w-6 cursor-pointer items-center justify-center rounded text-muted-foreground hover:bg-accent/60 hover:text-foreground\"\n onClick={() => onDismiss(run.id)}\n >\n <IconX size={13} aria-hidden />\n </button>\n ) : null}\n </div>\n </div>\n </div>\n );\n}\n\nconst STATUS_COPY: Record<ProgressStatus, string> = {\n running: \"Running\",\n succeeded: \"Done\",\n failed: \"Failed\",\n cancelled: \"Stopped\",\n};\n\nconst STATUS_PILL_STYLES: Record<ProgressStatus, string> = {\n running: \"bg-primary/10 text-primary\",\n succeeded: \"bg-emerald-500/10 text-emerald-600 dark:text-emerald-400\",\n failed: \"bg-destructive/10 text-destructive\",\n cancelled: \"bg-muted text-muted-foreground\",\n};\n\nfunction StatusPill({ status }: { status: ProgressStatus }) {\n const { Icon, className } = STATUS_GLYPHS[status];\n const spinClass = status === \"running\" ? \" animate-spin\" : \"\";\n return (\n <span\n className={cn(\n \"inline-flex h-5 shrink-0 items-center gap-1 rounded-md px-1.5 text-[10px] font-medium\",\n STATUS_PILL_STYLES[status],\n )}\n >\n <Icon size={12} className={`${className}${spinClass}`} aria-hidden />\n {STATUS_COPY[status]}\n </span>\n );\n}\n\n// dark: variants only where there's no semantic token for the colour\n// (e.g. success green isn't in shadcn's default palette).\nconst STATUS_GLYPHS: Record<\n ProgressStatus,\n { Icon: typeof IconLoader2; className: string }\n> = {\n running: { Icon: IconLoader2, className: \"text-primary\" },\n succeeded: {\n Icon: IconCheck,\n className: \"text-emerald-600 dark:text-emerald-400\",\n },\n failed: { Icon: IconAlertCircle, className: \"text-destructive\" },\n cancelled: { Icon: IconClock, className: \"text-muted-foreground\" },\n};\n"]}
1
+ {"version":3,"file":"RunsTray.js","sourceRoot":"","sources":["../../../src/client/progress/RunsTray.tsx"],"names":[],"mappings":"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","sourcesContent":["import { agentNativePath } from \"../api-path.js\";\nimport { useCallback, useEffect, useMemo, useState } from \"react\";\nimport {\n IconAlertCircle,\n IconCheck,\n IconClock,\n IconExternalLink,\n IconLoader2,\n IconPlayerStop,\n IconSubtask,\n IconX,\n} from \"@tabler/icons-react\";\nimport { usePausingInterval } from \"../use-pausing-interval.js\";\nimport type { AgentRun, ProgressStatus } from \"../../progress/types.js\";\nimport { cn } from \"../utils.js\";\nimport {\n Popover,\n PopoverContent,\n PopoverTrigger,\n} from \"../components/ui/popover.js\";\nimport {\n DropdownMenuSub,\n DropdownMenuSubContent,\n DropdownMenuSubTrigger,\n} from \"../components/ui/dropdown-menu.js\";\nimport {\n Tooltip,\n TooltipContent,\n TooltipProvider,\n TooltipTrigger,\n} from \"../components/ui/tooltip.js\";\n\ntype AgentRunDto = AgentRun;\ntype RunsTrayTriggerVariant = \"icon\" | \"pill\";\n\ninterface RunsTrayProps {\n /** Poll interval in ms. 0 disables. Default 3000. */\n pollMs?: number;\n /** Max runs to show in the dropdown. Default 5. */\n limit?: number;\n /** Hide the trigger entirely when no active runs. Default true. */\n hideWhenIdle?: boolean;\n /** Include recent terminal runs instead of active runs only. Defaults to !hideWhenIdle. */\n showRecent?: boolean;\n /** Compact icon for app headers, or a labeled pill for the agent panel. */\n triggerVariant?: RunsTrayTriggerVariant;\n /** Called when a run can open a related agent chat thread. */\n onOpenThread?: (threadId: string, run: AgentRunDto) => void;\n align?: \"start\" | \"center\" | \"end\";\n className?: string;\n}\n\ninterface RunsTrayState {\n runs: AgentRunDto[];\n hasRuns: boolean;\n activeCount: number;\n failedCount: number;\n terminalCount: number;\n triggerLabel: string;\n TriggerIcon: typeof IconLoader2;\n triggerTone: string;\n dismissRun: (runId: string) => void;\n stopRun: (runId: string) => void;\n}\n\nfunction useRunsTrayState({\n pollMs = 3000,\n limit = 5,\n hideWhenIdle = true,\n showRecent,\n}: Pick<\n RunsTrayProps,\n \"pollMs\" | \"limit\" | \"hideWhenIdle\" | \"showRecent\"\n>): RunsTrayState {\n const [runs, setRuns] = useState<AgentRunDto[]>([]);\n const includeRecent = showRecent ?? !hideWhenIdle;\n\n const refresh = useCallback(async () => {\n try {\n const query = new URLSearchParams({ limit: String(limit) });\n if (!includeRecent) query.set(\"active\", \"true\");\n const res = await fetch(\n agentNativePath(`/_agent-native/runs?${query.toString()}`),\n );\n if (!res.ok) return;\n const rows = (await res.json()) as AgentRunDto[];\n setRuns(rows);\n } catch {\n // best-effort\n }\n }, [includeRecent, limit]);\n\n useEffect(() => {\n refresh();\n }, [refresh]);\n\n usePausingInterval(refresh, pollMs);\n\n const dismissRun = useCallback(\n async (runId: string) => {\n setRuns((current) => current.filter((run) => run.id !== runId));\n try {\n const res = await fetch(\n agentNativePath(`/_agent-native/runs/${runId}`),\n {\n method: \"DELETE\",\n headers: { \"X-Agent-Native-CSRF\": \"1\" },\n },\n );\n if (!res.ok) throw new Error(`Dismiss failed (${res.status})`);\n } catch {\n refresh();\n }\n },\n [refresh],\n );\n\n const stopRun = useCallback(\n async (runId: string) => {\n // Optimistic: mark as cancelled immediately so the UI is responsive.\n setRuns((current) =>\n current.map((run) =>\n run.id === runId\n ? { ...run, status: \"cancelled\" as ProgressStatus }\n : run,\n ),\n );\n try {\n const res = await fetch(\n agentNativePath(`/_agent-native/agent-chat/runs/${runId}/stop`),\n {\n method: \"POST\",\n headers: { \"X-Agent-Native-CSRF\": \"1\" },\n },\n );\n if (!res.ok) throw new Error(`Stop failed (${res.status})`);\n } catch {\n // Reconcile from server on failure\n refresh();\n }\n },\n [refresh],\n );\n\n const hasRuns = runs.length > 0;\n const activeCount = useMemo(\n () => runs.filter((run) => run.status === \"running\").length,\n [runs],\n );\n const failedCount = useMemo(\n () => runs.filter((run) => run.status === \"failed\").length,\n [runs],\n );\n const triggerLabel =\n activeCount > 0\n ? `${activeCount} active run${activeCount > 1 ? \"s\" : \"\"}`\n : failedCount > 0\n ? `${failedCount} failed run${failedCount > 1 ? \"s\" : \"\"}`\n : hasRuns\n ? \"Recent runs\"\n : \"No recent runs\";\n const TriggerIcon =\n activeCount > 0\n ? IconLoader2\n : failedCount > 0\n ? IconAlertCircle\n : IconSubtask;\n const triggerTone =\n activeCount > 0\n ? \"text-primary\"\n : failedCount > 0\n ? \"text-destructive\"\n : \"text-muted-foreground\";\n const terminalCount = runs.length - activeCount;\n\n return {\n runs,\n hasRuns,\n activeCount,\n failedCount,\n terminalCount,\n triggerLabel,\n TriggerIcon,\n triggerTone,\n dismissRun,\n stopRun,\n };\n}\n\n/**\n * Header-bar progress indicator. Shows a spinner icon or labeled Runs pill\n * with a count badge when runs are active; opens a popover with live progress.\n * Same inline-header pattern as <NotificationsBell /> — drop it into the\n * header, no floating overlay over the main content.\n */\nexport function RunsTray({\n pollMs = 3000,\n limit = 5,\n hideWhenIdle = true,\n showRecent,\n triggerVariant = \"icon\",\n onOpenThread,\n align = \"end\",\n className,\n}: RunsTrayProps) {\n const [open, setOpen] = useState(false);\n const {\n runs,\n hasRuns,\n activeCount,\n failedCount,\n terminalCount,\n triggerLabel,\n TriggerIcon,\n triggerTone,\n dismissRun,\n stopRun,\n } = useRunsTrayState({\n pollMs,\n limit,\n hideWhenIdle,\n showRecent,\n });\n\n if (!hasRuns && hideWhenIdle) return null;\n\n return (\n <Popover open={open} onOpenChange={setOpen}>\n <TooltipProvider delayDuration={200}>\n <Tooltip>\n <TooltipTrigger asChild>\n <PopoverTrigger asChild>\n <button\n type=\"button\"\n aria-label={triggerLabel}\n aria-expanded={open}\n className={cn(\n \"an-runs-tray__trigger relative inline-flex shrink-0 items-center justify-center rounded-md text-muted-foreground hover:bg-accent/40 hover:text-foreground\",\n triggerVariant === \"pill\"\n ? \"h-7 min-w-[68px] gap-1.5 border border-border/70 bg-background px-2 text-[11px] font-medium\"\n : \"h-8 w-8\",\n open && \"bg-accent/50 text-foreground\",\n className,\n )}\n >\n <TriggerIcon\n size={triggerVariant === \"pill\" ? 14 : 18}\n className={cn(triggerTone, activeCount > 0 && \"animate-spin\")}\n aria-hidden\n />\n {triggerVariant === \"pill\" ? (\n <span className=\"leading-none\">Runs</span>\n ) : null}\n {activeCount > 0 ? (\n <span\n aria-hidden\n className={cn(\n \"an-runs-tray__badge rounded-full bg-primary text-[10px] font-medium leading-[14px] text-primary-foreground\",\n triggerVariant === \"pill\"\n ? \"min-w-4 px-1\"\n : \"absolute -right-0.5 -top-0.5 px-1\",\n )}\n >\n {activeCount > 9 ? \"9+\" : activeCount}\n </span>\n ) : failedCount > 0 ? (\n <span\n aria-hidden\n className={cn(\n \"rounded-full bg-destructive text-[10px] font-medium leading-[14px] text-destructive-foreground\",\n triggerVariant === \"pill\"\n ? \"min-w-4 px-1\"\n : \"absolute -right-0.5 -top-0.5 px-1\",\n )}\n >\n {failedCount > 9 ? \"9+\" : failedCount}\n </span>\n ) : null}\n </button>\n </PopoverTrigger>\n </TooltipTrigger>\n <TooltipContent>{triggerLabel}</TooltipContent>\n </Tooltip>\n </TooltipProvider>\n <PopoverContent\n align={align}\n sideOffset={8}\n className=\"an-runs-tray__menu w-80 max-w-[calc(100vw-24px)] p-0\"\n >\n <RunsTrayContent\n runs={runs}\n hasRuns={hasRuns}\n activeCount={activeCount}\n terminalCount={terminalCount}\n onDismiss={dismissRun}\n onStop={stopRun}\n onOpenThread={onOpenThread}\n />\n </PopoverContent>\n </Popover>\n );\n}\n\nexport function RunsTrayMenuItem({\n pollMs = 3000,\n limit = 5,\n hideWhenIdle = false,\n showRecent = true,\n onOpenThread,\n}: Pick<\n RunsTrayProps,\n \"pollMs\" | \"limit\" | \"hideWhenIdle\" | \"showRecent\" | \"onOpenThread\"\n>) {\n const [submenuOpen, setSubmenuOpen] = useState(false);\n const {\n runs,\n hasRuns,\n activeCount,\n failedCount,\n terminalCount,\n triggerLabel,\n TriggerIcon,\n triggerTone,\n dismissRun,\n stopRun,\n } = useRunsTrayState({\n pollMs,\n limit,\n hideWhenIdle,\n showRecent,\n });\n\n if (!hasRuns && hideWhenIdle) return null;\n\n return (\n <DropdownMenuSub open={submenuOpen} onOpenChange={setSubmenuOpen}>\n <DropdownMenuSubTrigger\n aria-label={`Agent runs, ${triggerLabel}`}\n className=\"cursor-pointer gap-2\"\n onClick={(event) => {\n event.preventDefault();\n setSubmenuOpen(true);\n }}\n onKeyDown={(event) => {\n if (event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n setSubmenuOpen(true);\n }\n }}\n >\n <TriggerIcon\n size={14}\n className={cn(triggerTone, activeCount > 0 && \"animate-spin\")}\n aria-hidden\n />\n <span className=\"min-w-0 flex-1 truncate\">Agent runs</span>\n {activeCount > 0 ? (\n <span\n aria-label={triggerLabel}\n className=\"rounded-full bg-primary px-1.5 text-[10px] font-medium leading-4 text-primary-foreground\"\n >\n {activeCount > 9 ? \"9+\" : activeCount}\n </span>\n ) : failedCount > 0 ? (\n <span\n aria-label={triggerLabel}\n className=\"rounded-full bg-destructive px-1.5 text-[10px] font-medium leading-4 text-destructive-foreground\"\n >\n {failedCount > 9 ? \"9+\" : failedCount}\n </span>\n ) : null}\n </DropdownMenuSubTrigger>\n <DropdownMenuSubContent\n sideOffset={6}\n className=\"an-runs-tray__menu w-80 max-w-[calc(100vw-24px)] p-0\"\n >\n <RunsTrayContent\n runs={runs}\n hasRuns={hasRuns}\n activeCount={activeCount}\n terminalCount={terminalCount}\n onDismiss={dismissRun}\n onStop={stopRun}\n onOpenThread={onOpenThread}\n />\n </DropdownMenuSubContent>\n </DropdownMenuSub>\n );\n}\n\nfunction RunsTrayContent({\n runs,\n hasRuns,\n activeCount,\n terminalCount,\n onDismiss,\n onStop,\n onOpenThread,\n}: {\n runs: AgentRunDto[];\n hasRuns: boolean;\n activeCount: number;\n terminalCount: number;\n onDismiss: (runId: string) => void;\n onStop: (runId: string) => void;\n onOpenThread?: (threadId: string, run: AgentRunDto) => void;\n}) {\n return (\n <>\n <div className=\"border-b border-border px-3 py-2\">\n <div className=\"text-sm font-medium text-foreground\">Agent runs</div>\n <div className=\"mt-0.5 text-[11px] text-muted-foreground\">\n {activeCount > 0\n ? `${activeCount} running${terminalCount > 0 ? ` · ${terminalCount} recent` : \"\"}`\n : hasRuns\n ? `${runs.length} recent run${runs.length > 1 ? \"s\" : \"\"}`\n : \"No tracked work yet\"}\n </div>\n </div>\n {hasRuns ? (\n <div className=\"max-h-96 divide-y divide-border overflow-y-auto\">\n {runs.map((run) => (\n <RunRow\n key={run.id}\n run={run}\n onDismiss={onDismiss}\n onStop={onStop}\n onOpenThread={onOpenThread}\n />\n ))}\n </div>\n ) : (\n <div className=\"px-3 py-6 text-center\">\n <IconClock\n size={22}\n className=\"mx-auto text-muted-foreground/50\"\n aria-hidden\n />\n <div className=\"mt-2 text-sm font-medium text-foreground\">\n No recent runs\n </div>\n <p className=\"mt-1 text-xs leading-relaxed text-muted-foreground\">\n Background agent work will appear here while it runs and after it\n finishes.\n </p>\n </div>\n )}\n </>\n );\n}\n\nfunction getRunThreadId(run: AgentRunDto): string | undefined {\n const metadata = run.metadata ?? {};\n const direct =\n typeof metadata.threadId === \"string\"\n ? metadata.threadId\n : typeof metadata.thread_id === \"string\"\n ? metadata.thread_id\n : undefined;\n if (direct?.trim()) return direct.trim();\n\n const surfaceUrl =\n typeof metadata.surfaceUrl === \"string\" ? metadata.surfaceUrl : undefined;\n const match = surfaceUrl?.match(/^agent-native:\\/\\/threads\\/(.+)$/);\n return match?.[1] ? decodeURIComponent(match[1]) : undefined;\n}\n\nfunction formatRunTime(run: AgentRunDto): string {\n const when = run.completedAt ?? run.updatedAt ?? run.startedAt;\n const timestamp = Date.parse(when);\n if (!Number.isFinite(timestamp)) return \"\";\n const diffMs = Date.now() - timestamp;\n const prefix = run.status === \"running\" ? \"Updated\" : \"Finished\";\n if (diffMs < 30_000) return `${prefix} just now`;\n const minutes = Math.floor(diffMs / 60_000);\n if (minutes < 60) return `${prefix} ${minutes}m ago`;\n const hours = Math.floor(minutes / 60);\n if (hours < 24) return `${prefix} ${hours}h ago`;\n return `${prefix} ${new Date(timestamp).toLocaleDateString([], {\n month: \"short\",\n day: \"numeric\",\n })}`;\n}\n\nfunction isAgentTeamRun(run: AgentRunDto): boolean {\n return (\n typeof run.metadata === \"object\" &&\n run.metadata !== null &&\n (run.metadata as Record<string, unknown>).kind === \"agent-team\"\n );\n}\n\nfunction RunRow({\n run,\n onDismiss,\n onStop,\n onOpenThread,\n}: {\n run: AgentRunDto;\n onDismiss: (runId: string) => void;\n onStop: (runId: string) => void;\n onOpenThread?: (threadId: string, run: AgentRunDto) => void;\n}) {\n const threadId = getRunThreadId(run);\n const isRunning = run.status === \"running\";\n const canStop = isRunning && isAgentTeamRun(run);\n\n return (\n <div className=\"flex flex-col gap-1.5 px-3 py-2.5 text-sm\">\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"min-w-0 flex-1\">\n <div className=\"truncate font-medium text-foreground\">\n {run.title}\n </div>\n {run.step ? (\n <div className=\"mt-0.5 truncate text-xs text-muted-foreground\">\n {run.step}\n </div>\n ) : null}\n </div>\n <StatusPill status={run.status} />\n </div>\n {run.percent != null || isRunning ? (\n <div className=\"h-1 w-full overflow-hidden rounded bg-muted\">\n {run.percent != null ? (\n <div\n className={cn(\n \"h-full transition-all\",\n run.status === \"failed\"\n ? \"bg-destructive\"\n : run.status === \"cancelled\"\n ? \"bg-muted-foreground/50\"\n : \"bg-primary\",\n )}\n style={{ width: `${run.percent}%` }}\n />\n ) : (\n <div className=\"h-full w-1/3 animate-pulse bg-primary/60\" />\n )}\n </div>\n ) : null}\n <div className=\"flex items-center justify-between gap-2\">\n <span className=\"min-w-0 truncate text-[10px] text-muted-foreground/70\">\n {formatRunTime(run)}\n </span>\n <div className=\"flex shrink-0 items-center gap-1\">\n {threadId && onOpenThread ? (\n <button\n type=\"button\"\n className=\"inline-flex h-6 items-center gap-1 rounded px-1.5 text-[11px] font-medium text-muted-foreground hover:bg-accent/60 hover:text-foreground\"\n onClick={() => onOpenThread(threadId, run)}\n >\n Open\n <IconExternalLink size={12} aria-hidden />\n </button>\n ) : null}\n {canStop ? (\n <button\n type=\"button\"\n aria-label={`Stop ${run.title}`}\n className=\"inline-flex h-6 w-6 cursor-pointer items-center justify-center rounded text-muted-foreground hover:bg-accent/60 hover:text-destructive\"\n onClick={() => onStop(run.id)}\n >\n <IconPlayerStop size={13} aria-hidden />\n </button>\n ) : null}\n {!isRunning ? (\n <button\n type=\"button\"\n aria-label={`Hide ${run.title}`}\n className=\"inline-flex h-6 w-6 cursor-pointer items-center justify-center rounded text-muted-foreground hover:bg-accent/60 hover:text-foreground\"\n onClick={() => onDismiss(run.id)}\n >\n <IconX size={13} aria-hidden />\n </button>\n ) : null}\n </div>\n </div>\n </div>\n );\n}\n\nconst STATUS_COPY: Record<ProgressStatus, string> = {\n running: \"Running\",\n succeeded: \"Done\",\n failed: \"Failed\",\n cancelled: \"Stopped\",\n};\n\nconst STATUS_PILL_STYLES: Record<ProgressStatus, string> = {\n running: \"bg-primary/10 text-primary\",\n succeeded: \"bg-emerald-500/10 text-emerald-600 dark:text-emerald-400\",\n failed: \"bg-destructive/10 text-destructive\",\n cancelled: \"bg-muted text-muted-foreground\",\n};\n\nfunction StatusPill({ status }: { status: ProgressStatus }) {\n const { Icon, className } = STATUS_GLYPHS[status];\n const spinClass = status === \"running\" ? \" animate-spin\" : \"\";\n return (\n <span\n className={cn(\n \"inline-flex h-5 shrink-0 items-center gap-1 rounded-md px-1.5 text-[10px] font-medium\",\n STATUS_PILL_STYLES[status],\n )}\n >\n <Icon size={12} className={`${className}${spinClass}`} aria-hidden />\n {STATUS_COPY[status]}\n </span>\n );\n}\n\n// dark: variants only where there's no semantic token for the colour\n// (e.g. success green isn't in shadcn's default palette).\nconst STATUS_GLYPHS: Record<\n ProgressStatus,\n { Icon: typeof IconLoader2; className: string }\n> = {\n running: { Icon: IconLoader2, className: \"text-primary\" },\n succeeded: {\n Icon: IconCheck,\n className: \"text-emerald-600 dark:text-emerald-400\",\n },\n failed: { Icon: IconAlertCircle, className: \"text-destructive\" },\n cancelled: { Icon: IconClock, className: \"text-muted-foreground\" },\n};\n"]}
@@ -5,12 +5,10 @@ export interface ResourceEditorProps {
5
5
  /** Controlled view mode — if provided, the editor won't manage its own view state */
6
6
  view?: "visual" | "code";
7
7
  onViewChange?: (v: "visual" | "code") => void;
8
- /** Called whenever save status changes */
9
- onSaveStatusChange?: (status: "idle" | "saving" | "saved") => void;
10
8
  /** When true, the editor's internal toolbar row is hidden */
11
9
  hideToolbar?: boolean;
12
10
  /** When true, content can be viewed and selected but not modified */
13
11
  readOnly?: boolean;
14
12
  }
15
- export declare function ResourceEditor({ resource, onSave, view: controlledView, onViewChange, onSaveStatusChange, hideToolbar, readOnly, }: ResourceEditorProps): import("react/jsx-runtime").JSX.Element;
13
+ export declare function ResourceEditor({ resource, onSave, view: controlledView, onViewChange, hideToolbar, readOnly, }: ResourceEditorProps): import("react/jsx-runtime").JSX.Element;
16
14
  //# sourceMappingURL=ResourceEditor.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ResourceEditor.d.ts","sourceRoot":"","sources":["../../../src/client/resources/ResourceEditor.tsx"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAiBnD,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,QAAQ,CAAC;IACnB,MAAM,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,qFAAqF;IACrF,IAAI,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,CAAC,CAAC,EAAE,QAAQ,GAAG,MAAM,KAAK,IAAI,CAAC;IAC9C,0CAA0C;IAC1C,kBAAkB,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ,GAAG,OAAO,KAAK,IAAI,CAAC;IACnE,6DAA6D;IAC7D,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,qEAAqE;IACrE,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAwkCD,wBAAgB,cAAc,CAAC,EAC7B,QAAQ,EACR,MAAM,EACN,IAAI,EAAE,cAAc,EACpB,YAAY,EACZ,kBAAkB,EAClB,WAAW,EACX,QAAQ,GACT,EAAE,mBAAmB,2CAgMrB"}
1
+ {"version":3,"file":"ResourceEditor.d.ts","sourceRoot":"","sources":["../../../src/client/resources/ResourceEditor.tsx"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAiBnD,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,QAAQ,CAAC;IACnB,MAAM,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,qFAAqF;IACrF,IAAI,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,CAAC,CAAC,EAAE,QAAQ,GAAG,MAAM,KAAK,IAAI,CAAC;IAC9C,6DAA6D;IAC7D,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,qEAAqE;IACrE,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAwkCD,wBAAgB,cAAc,CAAC,EAC7B,QAAQ,EACR,MAAM,EACN,IAAI,EAAE,cAAc,EACpB,YAAY,EACZ,WAAW,EACX,QAAQ,GACT,EAAE,mBAAmB,2CA4KrB"}
@@ -704,40 +704,29 @@ function RemoteAgentFormEditor({ resource, onChange, readOnly, }) {
704
704
  const labelClass = "block text-[11px] font-medium text-muted-foreground mb-1";
705
705
  return (_jsx("div", { className: "flex flex-1 min-h-0 flex-col overflow-y-auto p-4", children: _jsxs("div", { className: "max-w-md space-y-3", children: [_jsx("p", { className: "text-[11px] text-muted-foreground/70 leading-snug", children: "Connected remote agent over the A2A protocol. @-mention it in chat to delegate tasks." }), _jsxs("div", { children: [_jsx("label", { className: labelClass, children: "Name" }), _jsx("input", { className: inputClass, value: value.name, readOnly: readOnly, onChange: (e) => update({ name: e.target.value }), placeholder: "Analytics" })] }), _jsxs("div", { children: [_jsx("label", { className: labelClass, children: "URL" }), _jsx("input", { className: inputClass, value: value.url, readOnly: readOnly, onChange: (e) => update({ url: e.target.value }), placeholder: "https://analytics.example.com" }), _jsxs("p", { className: "mt-1 text-[10px] text-muted-foreground/50", children: ["A2A endpoint. The agent card is served at", " ", _jsx("code", { children: "/.well-known/agent-card.json" }), "."] })] }), _jsxs("div", { children: [_jsx("label", { className: labelClass, children: "Description" }), _jsx("textarea", { className: cn(inputClass, "resize-y"), rows: 3, value: value.description, readOnly: readOnly, onChange: (e) => update({ description: e.target.value }), placeholder: "What this agent is good at \u2014 helps the main agent decide when to delegate." })] }), _jsxs("div", { children: [_jsx("label", { className: labelClass, children: "Color" }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("input", { type: "color", value: value.color, disabled: readOnly, onChange: (e) => update({ color: e.target.value }), className: "h-8 w-10 cursor-pointer rounded border border-border bg-transparent" }), _jsx("input", { className: cn(inputClass, "flex-1"), value: value.color, readOnly: readOnly, onChange: (e) => update({ color: e.target.value }), placeholder: "#6B7280" })] })] })] }) }));
706
706
  }
707
- export function ResourceEditor({ resource, onSave, view: controlledView, onViewChange, onSaveStatusChange, hideToolbar, readOnly, }) {
707
+ export function ResourceEditor({ resource, onSave, view: controlledView, onViewChange, hideToolbar, readOnly, }) {
708
708
  const [content, setContent] = useState(resource.content);
709
709
  const [internalView, setInternalView] = useState(getViewPref);
710
710
  const view = controlledView ?? internalView;
711
- const [saveStatus, setSaveStatus] = useState("idle");
712
711
  const debounceRef = useRef(null);
713
712
  const prevIdRef = useRef(resource.id);
714
713
  // Reset content when resource changes
715
714
  useEffect(() => {
716
715
  if (prevIdRef.current !== resource.id) {
717
716
  setContent(resource.content);
718
- setSaveStatus("idle");
719
- onSaveStatusChange?.("idle");
720
717
  prevIdRef.current = resource.id;
721
718
  }
722
- }, [resource.id, resource.content, onSaveStatusChange]);
719
+ }, [resource.id, resource.content]);
723
720
  const handleChange = useCallback((newContent) => {
724
721
  if (readOnly)
725
722
  return;
726
723
  setContent(newContent);
727
- setSaveStatus("idle");
728
- onSaveStatusChange?.("idle");
729
724
  if (debounceRef.current)
730
725
  clearTimeout(debounceRef.current);
731
726
  debounceRef.current = setTimeout(() => {
732
- setSaveStatus("saving");
733
- onSaveStatusChange?.("saving");
734
727
  onSave(newContent);
735
- setTimeout(() => {
736
- setSaveStatus("saved");
737
- onSaveStatusChange?.("saved");
738
- }, 300);
739
728
  }, 1000);
740
- }, [onSave, onSaveStatusChange, readOnly]);
729
+ }, [onSave, readOnly]);
741
730
  const switchView = useCallback((v) => {
742
731
  setInternalView(v);
743
732
  setViewPref(v);
@@ -763,15 +752,11 @@ export function ResourceEditor({ resource, onSave, view: controlledView, onViewC
763
752
  }
764
753
  // Markdown files get visual/code toggle
765
754
  if (isMarkdown) {
766
- return (_jsxs("div", { className: "flex h-full flex-col", children: [_jsx("style", { children: editorStyles }), !hideToolbar && (_jsxs("div", { className: "flex items-center justify-between border-b border-border px-3 py-2", children: [_jsxs("div", { className: "flex items-center gap-1", children: [_jsx("button", { onClick: () => switchView("visual"), className: cn("rounded-md px-2 py-1.5 text-[12px] leading-none", view === "visual"
767
- ? "bg-accent text-foreground"
768
- : "text-muted-foreground hover:bg-accent/50 hover:text-foreground"), style: CONTROL_STYLE, children: "Visual" }), _jsx("button", { onClick: () => switchView("code"), className: cn("rounded-md px-2 py-1.5 text-[12px] leading-none", view === "code"
769
- ? "bg-accent text-foreground"
770
- : "text-muted-foreground hover:bg-accent/50 hover:text-foreground"), style: CONTROL_STYLE, children: "Code" })] }), _jsx("span", { className: "text-[11px] text-muted-foreground/60", children: saveStatus === "saving"
771
- ? "Saving..."
772
- : saveStatus === "saved"
773
- ? "Saved"
774
- : "" })] })), view === "visual" ? (_jsx("div", { className: "flex-1 min-h-0 overflow-y-auto p-3", children: _jsx(VisualMarkdownEditor, { content: content, onChange: handleChange, resourcePath: resource.path, readOnly: readOnly }) }, resource.id + "-visual")) : (_jsx("textarea", { value: content, onChange: (e) => handleChange(e.target.value), readOnly: readOnly, className: "flex-1 min-h-0 resize-none bg-transparent p-3 text-[13px] text-foreground outline-none placeholder:text-muted-foreground/50", style: {
755
+ return (_jsxs("div", { className: "flex h-full flex-col", children: [_jsx("style", { children: editorStyles }), !hideToolbar && (_jsx("div", { className: "flex items-center justify-between border-b border-border px-3 py-2", children: _jsxs("div", { className: "flex items-center gap-1", children: [_jsx("button", { onClick: () => switchView("visual"), className: cn("rounded-md px-2 py-1.5 text-[12px] leading-none", view === "visual"
756
+ ? "bg-accent text-foreground"
757
+ : "text-muted-foreground hover:bg-accent/50 hover:text-foreground"), style: CONTROL_STYLE, children: "Visual" }), _jsx("button", { onClick: () => switchView("code"), className: cn("rounded-md px-2 py-1.5 text-[12px] leading-none", view === "code"
758
+ ? "bg-accent text-foreground"
759
+ : "text-muted-foreground hover:bg-accent/50 hover:text-foreground"), style: CONTROL_STYLE, children: "Code" })] }) })), view === "visual" ? (_jsx("div", { className: "flex-1 min-h-0 overflow-y-auto p-3", children: _jsx(VisualMarkdownEditor, { content: content, onChange: handleChange, resourcePath: resource.path, readOnly: readOnly }) }, resource.id + "-visual")) : (_jsx("textarea", { value: content, onChange: (e) => handleChange(e.target.value), readOnly: readOnly, className: "flex-1 min-h-0 resize-none bg-transparent p-3 text-[13px] text-foreground outline-none placeholder:text-muted-foreground/50", style: {
775
760
  fontFamily: 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace',
776
761
  lineHeight: 1.6,
777
762
  }, spellCheck: false }))] }));