@agent-native/core 0.98.1 → 0.98.2

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 (122) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +8 -0
  3. package/corpus/core/docs/content/locales/ar-SA/pr-visual-recap.mdx +25 -1
  4. package/corpus/core/docs/content/locales/ar-SA/template-plan.mdx +1 -1
  5. package/corpus/core/docs/content/locales/de-DE/pr-visual-recap.mdx +25 -1
  6. package/corpus/core/docs/content/locales/de-DE/template-plan.mdx +1 -1
  7. package/corpus/core/docs/content/locales/es-ES/pr-visual-recap.mdx +25 -1
  8. package/corpus/core/docs/content/locales/es-ES/template-plan.mdx +1 -1
  9. package/corpus/core/docs/content/locales/fr-FR/pr-visual-recap.mdx +25 -1
  10. package/corpus/core/docs/content/locales/fr-FR/template-plan.mdx +1 -1
  11. package/corpus/core/docs/content/locales/hi-IN/pr-visual-recap.mdx +25 -1
  12. package/corpus/core/docs/content/locales/hi-IN/template-plan.mdx +1 -1
  13. package/corpus/core/docs/content/locales/ja-JP/pr-visual-recap.mdx +25 -1
  14. package/corpus/core/docs/content/locales/ja-JP/template-plan.mdx +1 -1
  15. package/corpus/core/docs/content/locales/ko-KR/pr-visual-recap.mdx +25 -1
  16. package/corpus/core/docs/content/locales/ko-KR/template-plan.mdx +1 -1
  17. package/corpus/core/docs/content/locales/pt-BR/pr-visual-recap.mdx +25 -1
  18. package/corpus/core/docs/content/locales/pt-BR/template-plan.mdx +1 -1
  19. package/corpus/core/docs/content/locales/zh-CN/pr-visual-recap.mdx +25 -1
  20. package/corpus/core/docs/content/locales/zh-CN/template-plan.mdx +1 -1
  21. package/corpus/core/docs/content/locales/zh-TW/pr-visual-recap.mdx +25 -1
  22. package/corpus/core/docs/content/locales/zh-TW/template-plan.mdx +1 -1
  23. package/corpus/core/docs/content/pr-visual-recap.mdx +50 -1
  24. package/corpus/core/docs/content/template-plan.mdx +1 -1
  25. package/corpus/core/package.json +1 -1
  26. package/corpus/core/src/cli/plan-local.ts +43 -11
  27. package/corpus/core/src/cli/pr-visual-recap-workflow.ts +2 -9
  28. package/corpus/core/src/cli/recap.ts +356 -4
  29. package/corpus/core/src/cli/skills-content/local-files.ts +17 -12
  30. package/corpus/core/src/client/analytics.ts +96 -14
  31. package/corpus/core/src/client/index.ts +5 -0
  32. package/corpus/core/src/client/session-replay.ts +10 -0
  33. package/corpus/core/src/client/settings/DemoModeSection.tsx +4 -28
  34. package/corpus/core/src/client/url-scrub.ts +13 -0
  35. package/corpus/core/src/client/use-demo-mode-status.ts +44 -0
  36. package/corpus/templates/analytics/AGENTS.md +8 -0
  37. package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +18 -4
  38. package/corpus/templates/analytics/app/lib/demo-chart-trend.ts +156 -0
  39. package/corpus/templates/analytics/changelog/2026-07-12-daily-dashboard-emails-reliably-include-the-complete-dashboa.md +6 -0
  40. package/corpus/templates/analytics/changelog/2026-07-12-demo-mode-charts-trend-upward.md +6 -0
  41. package/corpus/templates/analytics/server/jobs/dashboard-report.ts +2 -0
  42. package/corpus/templates/analytics/server/lib/dashboard-report.ts +17 -33
  43. package/corpus/templates/clips/app/components/player/video-player.tsx +5 -1
  44. package/corpus/templates/clips/changelog/2026-07-11-clips-no-longer-stay-stuck-on-preparing-clip-when-they-are-r.md +6 -0
  45. package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +2 -2
  46. package/corpus/templates/content/changelog/2026-07-12-the-left-sidebar-stays-fixed-horizontally-while-scrolling-on.md +6 -0
  47. package/corpus/templates/plan/.agents/skills/visual-plan/references/local-files.md +17 -12
  48. package/corpus/templates/plan/.agents/skills/visual-recap/references/local-files.md +17 -12
  49. package/corpus/templates/plan/actions/validate-local-plan-source.ts +4 -4
  50. package/corpus/templates/plan/app/components/layout/Sidebar.tsx +2 -1
  51. package/corpus/templates/plan/app/lib/plan-local-bridge.ts +41 -0
  52. package/corpus/templates/plan/app/lib/plan-tracking.ts +3 -0
  53. package/corpus/templates/plan/app/pages/PlansPage.tsx +9 -9
  54. package/corpus/templates/plan/app/root.tsx +13 -3
  55. package/corpus/templates/plan/changelog/2026-07-12-local-plan-source-now-stays-on-device.md +6 -0
  56. package/dist/cli/plan-local.d.ts +6 -6
  57. package/dist/cli/plan-local.d.ts.map +1 -1
  58. package/dist/cli/plan-local.js +36 -7
  59. package/dist/cli/plan-local.js.map +1 -1
  60. package/dist/cli/pr-visual-recap-workflow.d.ts +2 -8
  61. package/dist/cli/pr-visual-recap-workflow.d.ts.map +1 -1
  62. package/dist/cli/pr-visual-recap-workflow.js +2 -8
  63. package/dist/cli/pr-visual-recap-workflow.js.map +1 -1
  64. package/dist/cli/recap.d.ts +22 -1
  65. package/dist/cli/recap.d.ts.map +1 -1
  66. package/dist/cli/recap.js +286 -2
  67. package/dist/cli/recap.js.map +1 -1
  68. package/dist/cli/skills-content/local-files.d.ts +1 -1
  69. package/dist/cli/skills-content/local-files.d.ts.map +1 -1
  70. package/dist/cli/skills-content/local-files.js +17 -12
  71. package/dist/cli/skills-content/local-files.js.map +1 -1
  72. package/dist/client/analytics.d.ts +8 -0
  73. package/dist/client/analytics.d.ts.map +1 -1
  74. package/dist/client/analytics.js +77 -13
  75. package/dist/client/analytics.js.map +1 -1
  76. package/dist/client/index.d.ts +2 -1
  77. package/dist/client/index.d.ts.map +1 -1
  78. package/dist/client/index.js +2 -1
  79. package/dist/client/index.js.map +1 -1
  80. package/dist/client/session-replay.d.ts +2 -0
  81. package/dist/client/session-replay.d.ts.map +1 -1
  82. package/dist/client/session-replay.js +7 -0
  83. package/dist/client/session-replay.js.map +1 -1
  84. package/dist/client/settings/DemoModeSection.d.ts.map +1 -1
  85. package/dist/client/settings/DemoModeSection.js +4 -23
  86. package/dist/client/settings/DemoModeSection.js.map +1 -1
  87. package/dist/client/url-scrub.d.ts.map +1 -1
  88. package/dist/client/url-scrub.js +14 -0
  89. package/dist/client/url-scrub.js.map +1 -1
  90. package/dist/client/use-demo-mode-status.d.ts +12 -0
  91. package/dist/client/use-demo-mode-status.d.ts.map +1 -0
  92. package/dist/client/use-demo-mode-status.js +30 -0
  93. package/dist/client/use-demo-mode-status.js.map +1 -0
  94. package/dist/collab/struct-routes.d.ts +1 -1
  95. package/dist/notifications/routes.d.ts +3 -3
  96. package/dist/observability/routes.d.ts +5 -5
  97. package/dist/progress/routes.d.ts +1 -1
  98. package/dist/resources/handlers.d.ts +3 -3
  99. package/dist/secrets/routes.d.ts +9 -9
  100. package/docs/content/locales/ar-SA/pr-visual-recap.mdx +25 -1
  101. package/docs/content/locales/ar-SA/template-plan.mdx +1 -1
  102. package/docs/content/locales/de-DE/pr-visual-recap.mdx +25 -1
  103. package/docs/content/locales/de-DE/template-plan.mdx +1 -1
  104. package/docs/content/locales/es-ES/pr-visual-recap.mdx +25 -1
  105. package/docs/content/locales/es-ES/template-plan.mdx +1 -1
  106. package/docs/content/locales/fr-FR/pr-visual-recap.mdx +25 -1
  107. package/docs/content/locales/fr-FR/template-plan.mdx +1 -1
  108. package/docs/content/locales/hi-IN/pr-visual-recap.mdx +25 -1
  109. package/docs/content/locales/hi-IN/template-plan.mdx +1 -1
  110. package/docs/content/locales/ja-JP/pr-visual-recap.mdx +25 -1
  111. package/docs/content/locales/ja-JP/template-plan.mdx +1 -1
  112. package/docs/content/locales/ko-KR/pr-visual-recap.mdx +25 -1
  113. package/docs/content/locales/ko-KR/template-plan.mdx +1 -1
  114. package/docs/content/locales/pt-BR/pr-visual-recap.mdx +25 -1
  115. package/docs/content/locales/pt-BR/template-plan.mdx +1 -1
  116. package/docs/content/locales/zh-CN/pr-visual-recap.mdx +25 -1
  117. package/docs/content/locales/zh-CN/template-plan.mdx +1 -1
  118. package/docs/content/locales/zh-TW/pr-visual-recap.mdx +25 -1
  119. package/docs/content/locales/zh-TW/template-plan.mdx +1 -1
  120. package/docs/content/pr-visual-recap.mdx +50 -1
  121. package/docs/content/template-plan.mdx +1 -1
  122. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"DemoModeSection.d.ts","sourceRoot":"","sources":["../../../src/client/settings/DemoModeSection.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAmBH,wBAAgB,eAAe,gCA4F9B;AAED,wBAAgB,YAAY,gCAE3B"}
1
+ {"version":3,"file":"DemoModeSection.d.ts","sourceRoot":"","sources":["../../../src/client/settings/DemoModeSection.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAYH,wBAAgB,eAAe,gCA2E9B;AAED,wBAAgB,YAAY,gCAE3B"}
@@ -13,29 +13,13 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
13
13
  * accidentally turned off on stage.
14
14
  */
15
15
  import { IconEyeOff } from "@tabler/icons-react";
16
- import { useQuery } from "@tanstack/react-query";
17
16
  import { useEffect, useState } from "react";
18
17
  import { agentNativePath } from "../api-path.js";
19
- import { useChangeVersion } from "../use-change-version.js";
20
- const DEMO_STATUS_URL = agentNativePath("/_agent-native/demo/status");
18
+ import { useDemoModeStatus } from "../use-demo-mode-status.js";
21
19
  const DEMO_MODE_WRITE_URL = agentNativePath("/_agent-native/application-state/demo-mode");
22
20
  export function DemoModeSection() {
23
21
  const [enabled, setEnabled] = useState(null);
24
- const demoModeVersion = useChangeVersion("app-state:demo-mode");
25
- const { data } = useQuery({
26
- queryKey: ["agent-native", "demo-mode", demoModeVersion],
27
- queryFn: async () => {
28
- const res = await fetch(DEMO_STATUS_URL, {
29
- credentials: "same-origin",
30
- });
31
- if (!res.ok)
32
- return null;
33
- return (await res.json());
34
- },
35
- staleTime: Infinity,
36
- });
37
- const serverEnabled = data?.enabled;
38
- const forced = data?.forced === true;
22
+ const { enabled: serverEnabled, forced, isLoading } = useDemoModeStatus();
39
23
  // Surface the server value once it arrives (and on subsequent polls), but
40
24
  // never clobber an in-flight optimistic toggle with a stale read. When
41
25
  // forced by env, always reflect the locked-on state.
@@ -43,13 +27,10 @@ export function DemoModeSection() {
43
27
  if (forced) {
44
28
  setEnabled(true);
45
29
  }
46
- else if (typeof serverEnabled === "boolean") {
30
+ else if (!isLoading) {
47
31
  setEnabled((prev) => (prev === null ? serverEnabled : prev));
48
32
  }
49
- else if (serverEnabled === undefined && data !== undefined) {
50
- setEnabled((prev) => (prev === null ? false : prev));
51
- }
52
- }, [serverEnabled, forced, data]);
33
+ }, [serverEnabled, forced, isLoading]);
53
34
  const toggle = async (next) => {
54
35
  if (forced)
55
36
  return;
@@ -1 +1 @@
1
- {"version":3,"file":"DemoModeSection.js","sourceRoot":"","sources":["../../../src/client/settings/DemoModeSection.tsx"],"names":[],"mappings":";AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACjD,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAE5C,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAO5D,MAAM,eAAe,GAAG,eAAe,CAAC,4BAA4B,CAAC,CAAC;AACtE,MAAM,mBAAmB,GAAG,eAAe,CACzC,4CAA4C,CAC7C,CAAC;AAEF,MAAM,UAAU,eAAe;IAC7B,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAiB,IAAI,CAAC,CAAC;IAC7D,MAAM,eAAe,GAAG,gBAAgB,CAAC,qBAAqB,CAAC,CAAC;IAEhE,MAAM,EAAE,IAAI,EAAE,GAAG,QAAQ,CAAC;QACxB,QAAQ,EAAE,CAAC,cAAc,EAAE,WAAW,EAAE,eAAe,CAAC;QACxD,OAAO,EAAE,KAAK,IAAI,EAAE;YAClB,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,eAAe,EAAE;gBACvC,WAAW,EAAE,aAAa;aAC3B,CAAC,CAAC;YACH,IAAI,CAAC,GAAG,CAAC,EAAE;gBAAE,OAAO,IAAI,CAAC;YACzB,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAsB,CAAC;QACjD,CAAC;QACD,SAAS,EAAE,QAAQ;KACpB,CAAC,CAAC;IAEH,MAAM,aAAa,GAAG,IAAI,EAAE,OAAO,CAAC;IACpC,MAAM,MAAM,GAAG,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAErC,0EAA0E;IAC1E,uEAAuE;IACvE,qDAAqD;IACrD,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,MAAM,EAAE,CAAC;YACX,UAAU,CAAC,IAAI,CAAC,CAAC;QACnB,CAAC;aAAM,IAAI,OAAO,aAAa,KAAK,SAAS,EAAE,CAAC;YAC9C,UAAU,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QAC/D,CAAC;aAAM,IAAI,aAAa,KAAK,SAAS,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YAC7D,UAAU,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QACvD,CAAC;IACH,CAAC,EAAE,CAAC,aAAa,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC;IAElC,MAAM,MAAM,GAAG,KAAK,EAAE,IAAa,EAAE,EAAE;QACrC,IAAI,MAAM;YAAE,OAAO;QACnB,MAAM,QAAQ,GAAG,OAAO,CAAC;QACzB,yDAAyD;QACzD,UAAU,CAAC,IAAI,CAAC,CAAC;QACjB,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,mBAAmB,EAAE;gBAC3C,MAAM,EAAE,KAAK;gBACb,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;gBAC/C,WAAW,EAAE,SAAS;gBACtB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;aACxC,CAAC,CAAC;YACH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;gBACZ,MAAM,IAAI,KAAK,CAAC,QAAQ,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;YACxC,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,UAAU,CAAC,QAAQ,CAAC,CAAC;QACvB,CAAC;IACH,CAAC,CAAC;IAEF,OAAO,CACL,eAAK,SAAS,EAAC,iGAAiG,aAC9G,eAAK,SAAS,EAAC,SAAS,aACtB,cAAK,SAAS,EAAC,yCAAyC,iCAElD,EACN,YAAG,SAAS,EAAC,0CAA0C,sNAInD,EACH,MAAM,IAAI,CACT,aAAG,SAAS,EAAC,0CAA0C,iCACrC,uCAAsB,8DAEpC,CACL,IACG,EACN,iBACE,IAAI,EAAC,QAAQ,EACb,IAAI,EAAC,QAAQ,kBACC,CAAC,CAAC,OAAO,gBACZ,kBAAkB,EAC7B,QAAQ,EAAE,OAAO,KAAK,IAAI,IAAI,MAAM,EACpC,OAAO,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC;gBAC/B,6CAA6C;gBAC7C,SAAS,EAAE,oGACT,OAAO;oBACL,CAAC,CAAC,YAAY;oBACd,CAAC,CAAC,qDACN,IAAI,OAAO,KAAK,IAAI,IAAI,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,EAAE,YAEpD,eACE,SAAS,EAAE,kFACT,OAAO,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,iBAChC,EAAE,GACF,GACK,IACL,CACP,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,YAAY;IAC1B,OAAO,KAAC,UAAU,IAAC,IAAI,EAAE,EAAE,GAAI,CAAC;AAClC,CAAC","sourcesContent":["/**\n * <DemoModeSection /> — toggle that replaces contact/free-text names, emails,\n * and numbers with realistic fake data everywhere (UI + what the agent sees)\n * while preserving labels, IDs, and structure so the app keeps working.\n *\n * The toggle is stored in application_state under `demo-mode`\n * (`{ enabled: boolean }`) and written via `PUT\n * /_agent-native/application-state/demo-mode` (optimistic — flip first,\n * write in the background). It READS from `/_agent-native/demo/status`\n * instead, which ORs the per-user toggle with the `DEMO_MODE` env: a hosted\n * demo deployment is forced on and the switch is locked so it can't be\n * accidentally turned off on stage.\n */\n\nimport { IconEyeOff } from \"@tabler/icons-react\";\nimport { useQuery } from \"@tanstack/react-query\";\nimport { useEffect, useState } from \"react\";\n\nimport { agentNativePath } from \"../api-path.js\";\nimport { useChangeVersion } from \"../use-change-version.js\";\n\ninterface DemoStatus {\n enabled?: boolean;\n forced?: boolean;\n}\n\nconst DEMO_STATUS_URL = agentNativePath(\"/_agent-native/demo/status\");\nconst DEMO_MODE_WRITE_URL = agentNativePath(\n \"/_agent-native/application-state/demo-mode\",\n);\n\nexport function DemoModeSection() {\n const [enabled, setEnabled] = useState<boolean | null>(null);\n const demoModeVersion = useChangeVersion(\"app-state:demo-mode\");\n\n const { data } = useQuery({\n queryKey: [\"agent-native\", \"demo-mode\", demoModeVersion],\n queryFn: async () => {\n const res = await fetch(DEMO_STATUS_URL, {\n credentials: \"same-origin\",\n });\n if (!res.ok) return null;\n return (await res.json()) as DemoStatus | null;\n },\n staleTime: Infinity,\n });\n\n const serverEnabled = data?.enabled;\n const forced = data?.forced === true;\n\n // Surface the server value once it arrives (and on subsequent polls), but\n // never clobber an in-flight optimistic toggle with a stale read. When\n // forced by env, always reflect the locked-on state.\n useEffect(() => {\n if (forced) {\n setEnabled(true);\n } else if (typeof serverEnabled === \"boolean\") {\n setEnabled((prev) => (prev === null ? serverEnabled : prev));\n } else if (serverEnabled === undefined && data !== undefined) {\n setEnabled((prev) => (prev === null ? false : prev));\n }\n }, [serverEnabled, forced, data]);\n\n const toggle = async (next: boolean) => {\n if (forced) return;\n const previous = enabled;\n // Optimistic: flip immediately, write in the background.\n setEnabled(next);\n try {\n const res = await fetch(DEMO_MODE_WRITE_URL, {\n method: \"PUT\",\n headers: { \"Content-Type\": \"application/json\" },\n credentials: \"include\",\n body: JSON.stringify({ enabled: next }),\n });\n if (!res.ok) {\n throw new Error(`HTTP ${res.status}`);\n }\n } catch {\n setEnabled(previous);\n }\n };\n\n return (\n <div className=\"flex items-start justify-between gap-3 rounded-md border border-border bg-accent/30 px-2.5 py-2\">\n <div className=\"min-w-0\">\n <div className=\"text-[11px] font-medium text-foreground\">\n Enable demo mode\n </div>\n <p className=\"text-[10px] text-muted-foreground mt-0.5\">\n Replace contact/free-text names, emails, and numbers with realistic\n fake data everywhere — in the UI and what the agent sees. Labels, IDs,\n and structure are preserved so the app keeps working.\n </p>\n {forced && (\n <p className=\"text-[10px] text-muted-foreground mt-0.5\">\n Enforced by the <code>DEMO_MODE</code> environment variable — can't\n be turned off here.\n </p>\n )}\n </div>\n <button\n type=\"button\"\n role=\"switch\"\n aria-checked={!!enabled}\n aria-label=\"Enable demo mode\"\n disabled={enabled === null || forced}\n onClick={() => toggle(!enabled)}\n // Theme tokens; streaming agent owns layout.\n className={`relative inline-flex h-4 w-7 shrink-0 cursor-pointer items-center rounded-full transition-colors ${\n enabled\n ? \"bg-primary\"\n : \"bg-muted-foreground/30 hover:bg-muted-foreground/50\"\n } ${enabled === null || forced ? \"opacity-60\" : \"\"}`}\n >\n <span\n className={`inline-block h-3 w-3 transform rounded-full bg-background transition-transform ${\n enabled ? \"translate-x-3.5\" : \"translate-x-0.5\"\n }`}\n />\n </button>\n </div>\n );\n}\n\nexport function DemoModeIcon() {\n return <IconEyeOff size={14} />;\n}\n"]}
1
+ {"version":3,"file":"DemoModeSection.js","sourceRoot":"","sources":["../../../src/client/settings/DemoModeSection.tsx"],"names":[],"mappings":";AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAE5C,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAE/D,MAAM,mBAAmB,GAAG,eAAe,CACzC,4CAA4C,CAC7C,CAAC;AAEF,MAAM,UAAU,eAAe;IAC7B,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAiB,IAAI,CAAC,CAAC;IAC7D,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,iBAAiB,EAAE,CAAC;IAE1E,0EAA0E;IAC1E,uEAAuE;IACvE,qDAAqD;IACrD,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,MAAM,EAAE,CAAC;YACX,UAAU,CAAC,IAAI,CAAC,CAAC;QACnB,CAAC;aAAM,IAAI,CAAC,SAAS,EAAE,CAAC;YACtB,UAAU,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QAC/D,CAAC;IACH,CAAC,EAAE,CAAC,aAAa,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC;IAEvC,MAAM,MAAM,GAAG,KAAK,EAAE,IAAa,EAAE,EAAE;QACrC,IAAI,MAAM;YAAE,OAAO;QACnB,MAAM,QAAQ,GAAG,OAAO,CAAC;QACzB,yDAAyD;QACzD,UAAU,CAAC,IAAI,CAAC,CAAC;QACjB,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,mBAAmB,EAAE;gBAC3C,MAAM,EAAE,KAAK;gBACb,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;gBAC/C,WAAW,EAAE,SAAS;gBACtB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;aACxC,CAAC,CAAC;YACH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;gBACZ,MAAM,IAAI,KAAK,CAAC,QAAQ,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;YACxC,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,UAAU,CAAC,QAAQ,CAAC,CAAC;QACvB,CAAC;IACH,CAAC,CAAC;IAEF,OAAO,CACL,eAAK,SAAS,EAAC,iGAAiG,aAC9G,eAAK,SAAS,EAAC,SAAS,aACtB,cAAK,SAAS,EAAC,yCAAyC,iCAElD,EACN,YAAG,SAAS,EAAC,0CAA0C,sNAInD,EACH,MAAM,IAAI,CACT,aAAG,SAAS,EAAC,0CAA0C,iCACrC,uCAAsB,8DAEpC,CACL,IACG,EACN,iBACE,IAAI,EAAC,QAAQ,EACb,IAAI,EAAC,QAAQ,kBACC,CAAC,CAAC,OAAO,gBACZ,kBAAkB,EAC7B,QAAQ,EAAE,OAAO,KAAK,IAAI,IAAI,MAAM,EACpC,OAAO,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC;gBAC/B,6CAA6C;gBAC7C,SAAS,EAAE,oGACT,OAAO;oBACL,CAAC,CAAC,YAAY;oBACd,CAAC,CAAC,qDACN,IAAI,OAAO,KAAK,IAAI,IAAI,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,EAAE,YAEpD,eACE,SAAS,EAAE,kFACT,OAAO,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,iBAChC,EAAE,GACF,GACK,IACL,CACP,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,YAAY;IAC1B,OAAO,KAAC,UAAU,IAAC,IAAI,EAAE,EAAE,GAAI,CAAC;AAClC,CAAC","sourcesContent":["/**\n * <DemoModeSection /> — toggle that replaces contact/free-text names, emails,\n * and numbers with realistic fake data everywhere (UI + what the agent sees)\n * while preserving labels, IDs, and structure so the app keeps working.\n *\n * The toggle is stored in application_state under `demo-mode`\n * (`{ enabled: boolean }`) and written via `PUT\n * /_agent-native/application-state/demo-mode` (optimistic — flip first,\n * write in the background). It READS from `/_agent-native/demo/status`\n * instead, which ORs the per-user toggle with the `DEMO_MODE` env: a hosted\n * demo deployment is forced on and the switch is locked so it can't be\n * accidentally turned off on stage.\n */\n\nimport { IconEyeOff } from \"@tabler/icons-react\";\nimport { useEffect, useState } from \"react\";\n\nimport { agentNativePath } from \"../api-path.js\";\nimport { useDemoModeStatus } from \"../use-demo-mode-status.js\";\n\nconst DEMO_MODE_WRITE_URL = agentNativePath(\n \"/_agent-native/application-state/demo-mode\",\n);\n\nexport function DemoModeSection() {\n const [enabled, setEnabled] = useState<boolean | null>(null);\n const { enabled: serverEnabled, forced, isLoading } = useDemoModeStatus();\n\n // Surface the server value once it arrives (and on subsequent polls), but\n // never clobber an in-flight optimistic toggle with a stale read. When\n // forced by env, always reflect the locked-on state.\n useEffect(() => {\n if (forced) {\n setEnabled(true);\n } else if (!isLoading) {\n setEnabled((prev) => (prev === null ? serverEnabled : prev));\n }\n }, [serverEnabled, forced, isLoading]);\n\n const toggle = async (next: boolean) => {\n if (forced) return;\n const previous = enabled;\n // Optimistic: flip immediately, write in the background.\n setEnabled(next);\n try {\n const res = await fetch(DEMO_MODE_WRITE_URL, {\n method: \"PUT\",\n headers: { \"Content-Type\": \"application/json\" },\n credentials: \"include\",\n body: JSON.stringify({ enabled: next }),\n });\n if (!res.ok) {\n throw new Error(`HTTP ${res.status}`);\n }\n } catch {\n setEnabled(previous);\n }\n };\n\n return (\n <div className=\"flex items-start justify-between gap-3 rounded-md border border-border bg-accent/30 px-2.5 py-2\">\n <div className=\"min-w-0\">\n <div className=\"text-[11px] font-medium text-foreground\">\n Enable demo mode\n </div>\n <p className=\"text-[10px] text-muted-foreground mt-0.5\">\n Replace contact/free-text names, emails, and numbers with realistic\n fake data everywhere — in the UI and what the agent sees. Labels, IDs,\n and structure are preserved so the app keeps working.\n </p>\n {forced && (\n <p className=\"text-[10px] text-muted-foreground mt-0.5\">\n Enforced by the <code>DEMO_MODE</code> environment variable — can't\n be turned off here.\n </p>\n )}\n </div>\n <button\n type=\"button\"\n role=\"switch\"\n aria-checked={!!enabled}\n aria-label=\"Enable demo mode\"\n disabled={enabled === null || forced}\n onClick={() => toggle(!enabled)}\n // Theme tokens; streaming agent owns layout.\n className={`relative inline-flex h-4 w-7 shrink-0 cursor-pointer items-center rounded-full transition-colors ${\n enabled\n ? \"bg-primary\"\n : \"bg-muted-foreground/30 hover:bg-muted-foreground/50\"\n } ${enabled === null || forced ? \"opacity-60\" : \"\"}`}\n >\n <span\n className={`inline-block h-3 w-3 transform rounded-full bg-background transition-transform ${\n enabled ? \"translate-x-3.5\" : \"translate-x-0.5\"\n }`}\n />\n </button>\n </div>\n );\n}\n\nexport function DemoModeIcon() {\n return <IconEyeOff size={14} />;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"url-scrub.d.ts","sourceRoot":"","sources":["../../src/client/url-scrub.ts"],"names":[],"mappings":"AAgBA,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAqBpE"}
1
+ {"version":3,"file":"url-scrub.d.ts","sourceRoot":"","sources":["../../src/client/url-scrub.ts"],"names":[],"mappings":"AAiBA,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAiCpE"}
@@ -12,6 +12,7 @@ const SENSITIVE_QUERY_PARAMS = new Set([
12
12
  "code",
13
13
  "share",
14
14
  "share_token",
15
+ "bridge",
15
16
  ]);
16
17
  export function scrubUrl(url) {
17
18
  if (!url || typeof url !== "string")
@@ -26,6 +27,19 @@ export function scrubUrl(url) {
26
27
  mutated = true;
27
28
  }
28
29
  }
30
+ if (u.hash.includes("=")) {
31
+ const hashParams = new URLSearchParams(u.hash.slice(1));
32
+ let hashMutated = false;
33
+ for (const key of Array.from(hashParams.keys())) {
34
+ if (SENSITIVE_QUERY_PARAMS.has(key.toLowerCase())) {
35
+ hashParams.set(key, "<redacted>");
36
+ mutated = true;
37
+ hashMutated = true;
38
+ }
39
+ }
40
+ if (hashMutated)
41
+ u.hash = hashParams.toString();
42
+ }
29
43
  if (!mutated)
30
44
  return url;
31
45
  // If the original URL was relative, return only the path/query/fragment.
@@ -1 +1 @@
1
- {"version":3,"file":"url-scrub.js","sourceRoot":"","sources":["../../src/client/url-scrub.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,sBAAsB,GAAG,IAAI,GAAG,CAAC;IACrC,UAAU;IACV,GAAG;IACH,OAAO;IACP,OAAO;IACP,MAAM;IACN,OAAO;IACP,aAAa;CACd,CAAC,CAAC;AAEH,MAAM,UAAU,QAAQ,CAAC,GAAuB;IAC9C,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,GAAG,CAAC;IAChD,IAAI,CAAC;QACH,yDAAyD;QACzD,MAAM,CAAC,GAAG,IAAI,GAAG,CAAC,GAAG,EAAE,0BAA0B,CAAC,CAAC;QACnD,IAAI,OAAO,GAAG,KAAK,CAAC;QACpB,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC;YACpD,IAAI,sBAAsB,CAAC,GAAG,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,EAAE,CAAC;gBAClD,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;gBACtC,OAAO,GAAG,IAAI,CAAC;YACjB,CAAC;QACH,CAAC;QACD,IAAI,CAAC,OAAO;YAAE,OAAO,GAAG,CAAC;QACzB,yEAAyE;QACzE,IAAI,CAAC,CAAC,MAAM,KAAK,0BAA0B,EAAE,CAAC;YAC5C,OAAO,GAAG,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;QAC7C,CAAC;QACD,OAAO,CAAC,CAAC,QAAQ,EAAE,CAAC;IACtB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,GAAG,CAAC;IACb,CAAC;AACH,CAAC","sourcesContent":["/**\n * Query parameters that may carry sensitive values in the URL bar. Browser\n * telemetry and feedback integrations must not copy OAuth codes, share tokens,\n * password params, email-confirm tokens, or similar secrets into downstream\n * systems.\n */\nconst SENSITIVE_QUERY_PARAMS = new Set([\n \"password\",\n \"p\",\n \"token\",\n \"state\",\n \"code\",\n \"share\",\n \"share_token\",\n]);\n\nexport function scrubUrl(url: string | undefined): string | undefined {\n if (!url || typeof url !== \"string\") return url;\n try {\n // Parse using a base origin so relative URLs still work.\n const u = new URL(url, \"http://placeholder.local\");\n let mutated = false;\n for (const key of Array.from(u.searchParams.keys())) {\n if (SENSITIVE_QUERY_PARAMS.has(key.toLowerCase())) {\n u.searchParams.set(key, \"<redacted>\");\n mutated = true;\n }\n }\n if (!mutated) return url;\n // If the original URL was relative, return only the path/query/fragment.\n if (u.origin === \"http://placeholder.local\") {\n return `${u.pathname}${u.search}${u.hash}`;\n }\n return u.toString();\n } catch {\n return url;\n }\n}\n"]}
1
+ {"version":3,"file":"url-scrub.js","sourceRoot":"","sources":["../../src/client/url-scrub.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,sBAAsB,GAAG,IAAI,GAAG,CAAC;IACrC,UAAU;IACV,GAAG;IACH,OAAO;IACP,OAAO;IACP,MAAM;IACN,OAAO;IACP,aAAa;IACb,QAAQ;CACT,CAAC,CAAC;AAEH,MAAM,UAAU,QAAQ,CAAC,GAAuB;IAC9C,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,GAAG,CAAC;IAChD,IAAI,CAAC;QACH,yDAAyD;QACzD,MAAM,CAAC,GAAG,IAAI,GAAG,CAAC,GAAG,EAAE,0BAA0B,CAAC,CAAC;QACnD,IAAI,OAAO,GAAG,KAAK,CAAC;QACpB,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC;YACpD,IAAI,sBAAsB,CAAC,GAAG,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,EAAE,CAAC;gBAClD,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;gBACtC,OAAO,GAAG,IAAI,CAAC;YACjB,CAAC;QACH,CAAC;QACD,IAAI,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YACzB,MAAM,UAAU,GAAG,IAAI,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;YACxD,IAAI,WAAW,GAAG,KAAK,CAAC;YACxB,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC;gBAChD,IAAI,sBAAsB,CAAC,GAAG,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,EAAE,CAAC;oBAClD,UAAU,CAAC,GAAG,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;oBAClC,OAAO,GAAG,IAAI,CAAC;oBACf,WAAW,GAAG,IAAI,CAAC;gBACrB,CAAC;YACH,CAAC;YACD,IAAI,WAAW;gBAAE,CAAC,CAAC,IAAI,GAAG,UAAU,CAAC,QAAQ,EAAE,CAAC;QAClD,CAAC;QACD,IAAI,CAAC,OAAO;YAAE,OAAO,GAAG,CAAC;QACzB,yEAAyE;QACzE,IAAI,CAAC,CAAC,MAAM,KAAK,0BAA0B,EAAE,CAAC;YAC5C,OAAO,GAAG,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;QAC7C,CAAC;QACD,OAAO,CAAC,CAAC,QAAQ,EAAE,CAAC;IACtB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,GAAG,CAAC;IACb,CAAC;AACH,CAAC","sourcesContent":["/**\n * Query parameters that may carry sensitive values in the URL bar. Browser\n * telemetry and feedback integrations must not copy OAuth codes, share tokens,\n * password params, email-confirm tokens, or similar secrets into downstream\n * systems.\n */\nconst SENSITIVE_QUERY_PARAMS = new Set([\n \"password\",\n \"p\",\n \"token\",\n \"state\",\n \"code\",\n \"share\",\n \"share_token\",\n \"bridge\",\n]);\n\nexport function scrubUrl(url: string | undefined): string | undefined {\n if (!url || typeof url !== \"string\") return url;\n try {\n // Parse using a base origin so relative URLs still work.\n const u = new URL(url, \"http://placeholder.local\");\n let mutated = false;\n for (const key of Array.from(u.searchParams.keys())) {\n if (SENSITIVE_QUERY_PARAMS.has(key.toLowerCase())) {\n u.searchParams.set(key, \"<redacted>\");\n mutated = true;\n }\n }\n if (u.hash.includes(\"=\")) {\n const hashParams = new URLSearchParams(u.hash.slice(1));\n let hashMutated = false;\n for (const key of Array.from(hashParams.keys())) {\n if (SENSITIVE_QUERY_PARAMS.has(key.toLowerCase())) {\n hashParams.set(key, \"<redacted>\");\n mutated = true;\n hashMutated = true;\n }\n }\n if (hashMutated) u.hash = hashParams.toString();\n }\n if (!mutated) return url;\n // If the original URL was relative, return only the path/query/fragment.\n if (u.origin === \"http://placeholder.local\") {\n return `${u.pathname}${u.search}${u.hash}`;\n }\n return u.toString();\n } catch {\n return url;\n }\n}\n"]}
@@ -0,0 +1,12 @@
1
+ export interface DemoModeStatus {
2
+ enabled: boolean;
3
+ forced: boolean;
4
+ isLoading: boolean;
5
+ }
6
+ /**
7
+ * Reads the effective Demo mode status for the current user. The shared
8
+ * change-version stream advances after Demo mode application state changes,
9
+ * so mounted consumers refresh without polling or duplicating the route call.
10
+ */
11
+ export declare function useDemoModeStatus(): DemoModeStatus;
12
+ //# sourceMappingURL=use-demo-mode-status.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-demo-mode-status.d.ts","sourceRoot":"","sources":["../../src/client/use-demo-mode-status.ts"],"names":[],"mappings":"AAUA,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;CACpB;AAID;;;;GAIG;AACH,wBAAgB,iBAAiB,IAAI,cAAc,CAoBlD"}
@@ -0,0 +1,30 @@
1
+ import { useQuery } from "@tanstack/react-query";
2
+ import { agentNativePath } from "./api-path.js";
3
+ import { useChangeVersion } from "./use-change-version.js";
4
+ const DEMO_STATUS_URL = agentNativePath("/_agent-native/demo/status");
5
+ /**
6
+ * Reads the effective Demo mode status for the current user. The shared
7
+ * change-version stream advances after Demo mode application state changes,
8
+ * so mounted consumers refresh without polling or duplicating the route call.
9
+ */
10
+ export function useDemoModeStatus() {
11
+ const demoModeVersion = useChangeVersion("app-state:demo-mode");
12
+ const { data, isLoading } = useQuery({
13
+ queryKey: ["agent-native", "demo-mode", demoModeVersion],
14
+ queryFn: async () => {
15
+ const res = await fetch(DEMO_STATUS_URL, {
16
+ credentials: "same-origin",
17
+ });
18
+ if (!res.ok)
19
+ return null;
20
+ return (await res.json());
21
+ },
22
+ staleTime: Infinity,
23
+ });
24
+ return {
25
+ enabled: data?.enabled === true,
26
+ forced: data?.forced === true,
27
+ isLoading,
28
+ };
29
+ }
30
+ //# sourceMappingURL=use-demo-mode-status.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-demo-mode-status.js","sourceRoot":"","sources":["../../src/client/use-demo-mode-status.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAEjD,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAa3D,MAAM,eAAe,GAAG,eAAe,CAAC,4BAA4B,CAAC,CAAC;AAEtE;;;;GAIG;AACH,MAAM,UAAU,iBAAiB;IAC/B,MAAM,eAAe,GAAG,gBAAgB,CAAC,qBAAqB,CAAC,CAAC;IAEhE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,QAAQ,CAAC;QACnC,QAAQ,EAAE,CAAC,cAAc,EAAE,WAAW,EAAE,eAAe,CAAC;QACxD,OAAO,EAAE,KAAK,IAAI,EAAE;YAClB,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,eAAe,EAAE;gBACvC,WAAW,EAAE,aAAa;aAC3B,CAAC,CAAC;YACH,IAAI,CAAC,GAAG,CAAC,EAAE;gBAAE,OAAO,IAAI,CAAC;YACzB,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAkC,CAAC;QAC7D,CAAC;QACD,SAAS,EAAE,QAAQ;KACpB,CAAC,CAAC;IAEH,OAAO;QACL,OAAO,EAAE,IAAI,EAAE,OAAO,KAAK,IAAI;QAC/B,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI;QAC7B,SAAS;KACV,CAAC;AACJ,CAAC","sourcesContent":["import { useQuery } from \"@tanstack/react-query\";\n\nimport { agentNativePath } from \"./api-path.js\";\nimport { useChangeVersion } from \"./use-change-version.js\";\n\ninterface DemoModeStatusResponse {\n enabled?: boolean;\n forced?: boolean;\n}\n\nexport interface DemoModeStatus {\n enabled: boolean;\n forced: boolean;\n isLoading: boolean;\n}\n\nconst DEMO_STATUS_URL = agentNativePath(\"/_agent-native/demo/status\");\n\n/**\n * Reads the effective Demo mode status for the current user. The shared\n * change-version stream advances after Demo mode application state changes,\n * so mounted consumers refresh without polling or duplicating the route call.\n */\nexport function useDemoModeStatus(): DemoModeStatus {\n const demoModeVersion = useChangeVersion(\"app-state:demo-mode\");\n\n const { data, isLoading } = useQuery({\n queryKey: [\"agent-native\", \"demo-mode\", demoModeVersion],\n queryFn: async () => {\n const res = await fetch(DEMO_STATUS_URL, {\n credentials: \"same-origin\",\n });\n if (!res.ok) return null;\n return (await res.json()) as DemoModeStatusResponse | null;\n },\n staleTime: Infinity,\n });\n\n return {\n enabled: data?.enabled === true,\n forced: data?.forced === true,\n isLoading,\n };\n}\n"]}
@@ -13,8 +13,8 @@
13
13
  * Body: { json: any, fieldName?: string, type?: "map"|"array", requestSource?: string }
14
14
  */
15
15
  export declare const postCollabJson: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
16
- ok?: undefined;
17
16
  error: string;
17
+ ok?: undefined;
18
18
  } | {
19
19
  error?: undefined;
20
20
  ok: boolean;
@@ -13,13 +13,13 @@
13
13
  export declare function createNotificationsHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<"" | import("./types.js").Notification[] | {
14
14
  count: number;
15
15
  updated?: undefined;
16
- ok?: undefined;
17
16
  error?: undefined;
17
+ ok?: undefined;
18
18
  } | {
19
19
  count?: undefined;
20
20
  updated: number;
21
- ok?: undefined;
22
21
  error?: undefined;
22
+ ok?: undefined;
23
23
  } | {
24
24
  count?: undefined;
25
25
  updated?: undefined;
@@ -28,7 +28,7 @@ export declare function createNotificationsHandler(): import("h3").EventHandlerW
28
28
  } | {
29
29
  count?: undefined;
30
30
  updated?: undefined;
31
- ok: boolean;
32
31
  error?: undefined;
32
+ ok: boolean;
33
33
  }>>;
34
34
  //# sourceMappingURL=routes.d.ts.map
@@ -41,28 +41,28 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
41
41
  thumbsUpRate: number;
42
42
  avgEvalScore: number;
43
43
  } | {
44
- error?: undefined;
45
- ok?: undefined;
46
44
  summary: import("./types.js").TraceSummary;
47
45
  spans: import("./types.js").TraceSpan[];
48
46
  id?: undefined;
49
- } | {
50
47
  error?: undefined;
51
48
  ok?: undefined;
49
+ } | {
52
50
  summary?: undefined;
53
51
  spans?: undefined;
54
52
  id: string;
55
- } | {
53
+ error?: undefined;
56
54
  ok?: undefined;
55
+ } | {
57
56
  summary?: undefined;
58
57
  spans?: undefined;
59
58
  id?: undefined;
60
59
  error: any;
60
+ ok?: undefined;
61
61
  } | {
62
- error?: undefined;
63
62
  summary?: undefined;
64
63
  spans?: undefined;
65
64
  id?: undefined;
65
+ error?: undefined;
66
66
  ok: boolean;
67
67
  }>>;
68
68
  //# sourceMappingURL=routes.d.ts.map
@@ -15,7 +15,7 @@ export declare function createProgressHandler(): import("h3").EventHandlerWithFe
15
15
  error: string;
16
16
  ok?: undefined;
17
17
  } | {
18
- error?: undefined;
19
18
  ok: boolean;
19
+ error?: undefined;
20
20
  }>>;
21
21
  //# sourceMappingURL=routes.d.ts.map
@@ -49,11 +49,11 @@ export declare function handleUpdateResource(event: any): Promise<import("./stor
49
49
  }>;
50
50
  /** DELETE /_agent-native/resources/:id — delete a resource */
51
51
  export declare function handleDeleteResource(event: any): Promise<{
52
- ok?: undefined;
53
52
  error: string;
53
+ ok?: undefined;
54
54
  } | {
55
- error?: undefined;
56
55
  ok: boolean;
56
+ error?: undefined;
57
57
  }>;
58
58
  /** POST /_agent-native/resources/upload — upload a file as a resource */
59
59
  export declare function handleUploadResource(event: any): Promise<import("./store.js").Resource | {
@@ -74,10 +74,10 @@ export declare function handleUploadResource(event: any): Promise<import("./stor
74
74
  runId: string | null;
75
75
  expiresAt: number | null;
76
76
  metadata: string | null;
77
- error?: undefined;
78
77
  url: string;
79
78
  provider: string;
80
79
  storageSetupRequired?: undefined;
80
+ error?: undefined;
81
81
  } | {
82
82
  error: string;
83
83
  storageSetupRequired: boolean;
@@ -34,37 +34,37 @@ export declare function createListSecretsHandler(): import("h3").EventHandlerWit
34
34
  /** POST /_agent-native/secrets/:key — write a secret. */
35
35
  export declare function createWriteSecretHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
36
36
  error: string;
37
- ok?: undefined;
38
37
  status?: undefined;
38
+ ok?: undefined;
39
39
  } | {
40
- error?: undefined;
41
40
  ok: boolean;
42
41
  status: string;
42
+ error?: undefined;
43
43
  } | {
44
- ok?: undefined;
45
44
  error: string;
46
45
  removed?: undefined;
46
+ ok?: undefined;
47
47
  } | {
48
- error?: undefined;
49
48
  ok: boolean;
50
49
  removed: boolean;
50
+ error?: undefined;
51
51
  }>>;
52
52
  /**
53
53
  * POST /_agent-native/secrets/:key/test — re-run the validator against the
54
54
  * current stored value without changing anything. Useful for the "Test" button.
55
55
  */
56
56
  export declare function createTestSecretHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
57
- ok?: undefined;
58
57
  error: string;
59
58
  note?: undefined;
59
+ ok?: undefined;
60
60
  } | {
61
- error?: undefined;
62
61
  ok: boolean;
63
62
  note?: undefined;
64
- } | {
65
63
  error?: undefined;
64
+ } | {
66
65
  ok: boolean;
67
66
  note: string;
67
+ error?: undefined;
68
68
  } | {
69
69
  note?: undefined;
70
70
  ok: boolean;
@@ -95,12 +95,12 @@ export interface AdHocSecretPayload {
95
95
  export declare function createAdHocSecretHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<AdHocSecretPayload[] | {
96
96
  error: string;
97
97
  } | {
98
- error?: undefined;
99
98
  ok: boolean;
100
99
  key: string;
101
- } | {
102
100
  error?: undefined;
101
+ } | {
103
102
  ok: boolean;
104
103
  removed: boolean;
104
+ error?: undefined;
105
105
  }>>;
106
106
  //# sourceMappingURL=routes.d.ts.map
@@ -123,6 +123,28 @@ npx @agent-native/core@latest recap doctor
123
123
  يثبت مجلد `visual-recap` المخصص له، ويضبط متغير المستودع
124
124
  `VISUAL_RECAP_SKILL_SOURCE=repo`.
125
125
 
126
+ ## اختيار المشغّل
127
+
128
+ يعمل سير العمل افتراضيًا على مشغّلات `ubuntu-latest` المستضافة من GitHub. لاستخدام مشغّل مستضاف آخر أو مشغّل ذاتي الاستضافة، اضبط متغير المستودع أو المؤسسة `VISUAL_RECAP_RUNS_ON` على سلسلة JSON صالحة أو مصفوفة JSON من تسميات المشغّل:
129
+
130
+ ```text
131
+ "ubuntu-latest"
132
+ ["self-hosted", "linux", "x64", "visual-recap"]
133
+ ```
134
+
135
+ علامات الاقتباس في JSON مطلوبة. تحدد هذه القيمة مشغّل مهمة `recap` الكاملة. لتكوينه أثناء الإعداد الموجّه، مرّر قيمة JSON نفسها إلى `--runs-on`، ثم تحقق من سير العمل وتوفر المشغّل باستخدام `doctor`:
136
+
137
+ ```bash
138
+ npx @agent-native/core@latest recap setup --runs-on '["self-hosted","linux","x64","visual-recap"]'
139
+ npx @agent-native/core@latest recap doctor
140
+ ```
141
+
142
+ تستخدم بوابة الأمان الخفيفة `ubuntu-latest` بشكل منفصل. يمكن للمستودعات التي تعطل مشغّلات GitHub المستضافة ضبط `VISUAL_RECAP_GATE_RUNS_ON` على تسمية نصية واحدة بلا JSON، أو استخدام `--gate-runs-on visual-recap-gate`. تُستخدم التسمية المخصصة فقط لمؤلفي `OWNER` أو `MEMBER` أو `COLLABORATOR` من المستودع نفسه؛ تُفرض `ubuntu-latest` على الفروع والمؤلفين الآخرين وقد تبقى عملياتهم بلا جدولة عند تعطيل مشغّلات GitHub المستضافة. استخدم مشغّلًا مخصصًا ويفضل أن يكون مؤقتًا؛ لا تسحب البوابة القياسية شجرة PR وتقيّم منطق سير العمل وبيانات PR الوصفية.
143
+
144
+ يرفض `recap doctor` بيانات JSON غير الصالحة أو غير المدعومة، ويبلغ عندما لا يعرض GitHub مشغّلًا متصلًا يطابق جميع التسميات المكوّنة. يظل تسجيل المشغّل وتثبيته وترقياته وسعته وصيانته من مسؤوليتك؛ يحدد Agent Native التسميات ولكنه لا يوفر المشغّل ولا يديره.
145
+
146
+ **الأمان:** يُفضّل استخدام مشغّلات ذاتية الاستضافة مؤقتة. يجب أن تخدم المشغّلات الدائمة ذاتية الاستضافة المستودعات الخاصة التي يقتصر المساهمون فيها على أشخاص موثوق بهم. يُغلق سير العمل بأمان قبل جدولة عمل ذاتي الاستضافة لمؤلفي PR غير الموثوق بهم في المستودعات العامة أو المتفرعة، لكن الجهاز الدائم يظل محتفظًا بحالة مساحة العمل والعمليات بين المهام. لا تضعف هذه البوابة ولا توجه مساهمات غير موثوقة إلى مشغّل دائم.
147
+
126
148
  ## تحديد الخلفية
127
149
 
128
150
  اختر وكيل الترميز الذي يقوم بتشغيل المهارة باستخدام متغير المستودع `VISUAL_RECAP_AGENT`:
@@ -460,10 +482,12 @@ jobs:
460
482
  model: ${{ vars.VISUAL_RECAP_MODEL || '' }}
461
483
  reasoning: ${{ vars.VISUAL_RECAP_REASONING || '' }}
462
484
  skill-source: ${{ vars.VISUAL_RECAP_SKILL_SOURCE || 'auto' }}
485
+ runs-on: ${{ vars.VISUAL_RECAP_RUNS_ON || '"ubuntu-latest"' }}
486
+ gate-runs-on: ${{ vars.VISUAL_RECAP_GATE_RUNS_ON || 'ubuntu-latest' }}
463
487
  # cli-version: "latest" # pin to a specific @agent-native/core version
464
488
  ```
465
489
 
466
- تنطبق نفس الأسرار والمتغيرات الموضحة في [Secrets and variables](#secrets-and-variables) - قم بتعيينها في إعدادات الريبو الخاصة بك بنفس الطريقة كما في متغير النسخ.
490
+ تنطبق الأسرار والمتغيرات نفسها الموضحة في [Secrets and variables](#secrets-and-variables). يمرر المتصل القابل لإعادة الاستخدام `VISUAL_RECAP_RUNS_ON` كـ `runs-on` للمهمة الكاملة، و`VISUAL_RECAP_GATE_RUNS_ON` كتسمية مفردة `gate-runs-on` للبوابة. يستخدم كلاهما `ubuntu-latest` عند الحذف.
467
491
 
468
492
  ### التثبيت عبر CLI
469
493
 
@@ -290,7 +290,7 @@ npx @agent-native/core@latest plan local serve --dir plans/<slug> --kind plan --
290
290
  ```
291
291
 
292
292
  يبدو شكل الجسر URL
293
- `https://plan.agent-native.com/local-plans/<slug>?bridge=http://127.0.0.1:...`.
293
+ `https://plan.agent-native.com/local-plans/local-<opaque-id>#bridge=http://127.0.0.1:...`.
294
294
  الصفحة هي عارض الخطة العادي، لكن المتصفح يجلب `plan.mdx`،
295
295
  `canvas.mdx`، و`prototype.mdx`، و`.plan-state.json`، وأصول الصور المحلية من
296
296
  جسر المضيف المحلي. لا تتم كتابة محتوى الخطة في قاعدة البيانات المستضافة وهي
@@ -123,6 +123,28 @@ Referenzdateien, mit denen der Skill geliefert wird. Wenn Ihr Repo absichtlich a
123
123
  pinnen den festgeschriebenen `visual-recap`-Ordner an und legen die Repository-Variable fest
124
124
  `VISUAL_RECAP_SKILL_SOURCE=repo`.
125
125
 
126
+ ## Runner-Auswahl
127
+
128
+ Standardmäßig wird der Workflow auf von GitHub gehosteten `ubuntu-latest`-Runnern ausgeführt. Um einen anderen gehosteten Runner oder einen eigenen selbst gehosteten Runner zu verwenden, setzen Sie die Repository- oder Organisationsvariable `VISUAL_RECAP_RUNS_ON` auf eine gültige JSON-Zeichenfolge oder ein JSON-Array mit Runner-Labels:
129
+
130
+ ```text
131
+ "ubuntu-latest"
132
+ ["self-hosted", "linux", "x64", "visual-recap"]
133
+ ```
134
+
135
+ Die JSON-Anführungszeichen sind erforderlich. Dieser Wert wählt den Runner für den vollständigen `recap`-Job. Übergeben Sie zur Konfiguration während der geführten Einrichtung denselben JSON-Wert an `--runs-on` und prüfen Sie anschließend den Workflow und die Verfügbarkeit des Runners mit `doctor`:
136
+
137
+ ```bash
138
+ npx @agent-native/core@latest recap setup --runs-on '["self-hosted","linux","x64","visual-recap"]'
139
+ npx @agent-native/core@latest recap doctor
140
+ ```
141
+
142
+ Das einfache Gate verwendet separat `ubuntu-latest`. Repositorys ohne von GitHub gehostete Runner können `VISUAL_RECAP_GATE_RUNS_ON` auf ein einzelnes Klartext-Label ohne JSON setzen oder `--gate-runs-on visual-recap-gate` verwenden. Das benutzerdefinierte Label gilt nur für `OWNER`-, `MEMBER`- oder `COLLABORATOR`-Autoren desselben Repositorys; Forks und andere Autoren werden auf `ubuntu-latest` gezwungen und können ohne GitHub-gehostete Runner ungeplant bleiben. Nutzen Sie einen dedizierten, möglichst kurzlebigen Runner: Das Standard-Gate checkt den PR-Baum nicht aus und wertet Workflow-Logik sowie PR-Metadaten aus.
143
+
144
+ `recap doctor` lehnt fehlerhaftes oder nicht unterstütztes JSON ab und meldet, wenn GitHub keinen Online-Runner bereitstellt, der alle konfigurierten Labels erfüllt. Registrierung, Installation, Upgrades, Kapazität und Wartung des Runners liegen weiterhin in Ihrer Verantwortung; Agent Native wählt die Labels aus, stellt oder betreibt den Runner jedoch nicht.
145
+
146
+ **Sicherheit:** Bevorzugen Sie kurzlebige selbst gehostete Runner. Persistente selbst gehostete Runner sollten nur privaten Repositorys mit vertrauenswürdigen Mitwirkenden dienen. Der Workflow bricht ab, bevor selbst gehostete Arbeit für nicht vertrauenswürdige Autoren öffentlicher oder Fork-PRs eingeplant wird, doch eine persistente Maschine behält zwischen Jobs weiterhin Arbeitsbereichs- und Prozesszustand. Schwächen Sie dieses Gate nicht ab und leiten Sie keine nicht vertrauenswürdigen Beiträge an einen persistenten Runner weiter.
147
+
126
148
  ## Backend-Auswahl
127
149
 
128
150
  Wählen Sie mit der Repository-Variable `VISUAL_RECAP_AGENT` aus, welcher Codierungsagent den Skill ausführt:
@@ -460,10 +482,12 @@ jobs:
460
482
  model: ${{ vars.VISUAL_RECAP_MODEL || '' }}
461
483
  reasoning: ${{ vars.VISUAL_RECAP_REASONING || '' }}
462
484
  skill-source: ${{ vars.VISUAL_RECAP_SKILL_SOURCE || 'auto' }}
485
+ runs-on: ${{ vars.VISUAL_RECAP_RUNS_ON || '"ubuntu-latest"' }}
486
+ gate-runs-on: ${{ vars.VISUAL_RECAP_GATE_RUNS_ON || 'ubuntu-latest' }}
463
487
  # cli-version: "latest" # pin to a specific @agent-native/core version
464
488
  ```
465
489
 
466
- Es gelten die gleichen Geheimnisse und Variablen wie in [Secrets and variables](#secrets-and-variables) beschrieben legen Sie sie in Ihren Repo-Einstellungen auf die gleiche Weise fest wie für die Kopiervariante.
490
+ Es gelten dieselben Geheimnisse und Variablen wie unter [Secrets and variables](#secrets-and-variables). Der wiederverwendbare Aufrufer leitet `VISUAL_RECAP_RUNS_ON` als `runs-on` für den vollständigen Job und `VISUAL_RECAP_GATE_RUNS_ON` als einfaches `gate-runs-on`-Label für das Gate weiter. Ohne Eingabe verwenden beide `ubuntu-latest`.
467
491
 
468
492
  ### Installation über CLI
469
493
 
@@ -292,7 +292,7 @@ npx @agent-native/core@latest plan local serve --dir plans/<slug> --kind plan --
292
292
  ```
293
293
 
294
294
  Die Brücke URL sieht aus wie
295
- `https://plan.agent-native.com/local-plans/<slug>?bridge=http://127.0.0.1:...`.
295
+ `https://plan.agent-native.com/local-plans/local-<opaque-id>#bridge=http://127.0.0.1:...`.
296
296
  Die Seite ist der normale Plan-Viewer, aber der Browser ruft `plan.mdx` ab,
297
297
  `canvas.mdx`, `prototype.mdx`, `.plan-state.json` und lokale Bildressourcen von
298
298
  die Localhost-Brücke. Planinhalte werden nicht in die gehostete Datenbank geschrieben und sind
@@ -123,6 +123,28 @@ archivos de referencia con los que se envía la habilidad. Si su repositorio per
123
123
  fija su carpeta `visual-recap` comprometida, establece la variable del repositorio
124
124
  `VISUAL_RECAP_SKILL_SOURCE=repo`.
125
125
 
126
+ ## Selección del ejecutor
127
+
128
+ De forma predeterminada, el flujo de trabajo se ejecuta en ejecutores `ubuntu-latest` alojados por GitHub. Para usar otro ejecutor alojado o uno autohospedado, establezca la variable de repositorio u organización `VISUAL_RECAP_RUNS_ON` en una cadena JSON válida o en una matriz JSON de etiquetas de ejecutor:
129
+
130
+ ```text
131
+ "ubuntu-latest"
132
+ ["self-hosted", "linux", "x64", "visual-recap"]
133
+ ```
134
+
135
+ Las comillas JSON son obligatorias. Este valor selecciona el ejecutor del trabajo completo `recap`. Para configurarlo durante la instalación guiada, pase el mismo valor JSON a `--runs-on` y después valide el flujo de trabajo y la disponibilidad del ejecutor con `doctor`:
136
+
137
+ ```bash
138
+ npx @agent-native/core@latest recap setup --runs-on '["self-hosted","linux","x64","visual-recap"]'
139
+ npx @agent-native/core@latest recap doctor
140
+ ```
141
+
142
+ La barrera ligera usa `ubuntu-latest` por separado. Los repositorios sin ejecutores alojados por GitHub pueden establecer `VISUAL_RECAP_GATE_RUNS_ON` en una sola etiqueta de texto sin JSON, o usar `--gate-runs-on visual-recap-gate`. La etiqueta personalizada solo se usa para autores `OWNER`, `MEMBER` o `COLLABORATOR` del mismo repositorio; los forks y demás autores se fuerzan a `ubuntu-latest` y pueden quedar sin programar si los ejecutores alojados están desactivados. Use un ejecutor dedicado y, preferiblemente, efímero: la barrera estándar no descarga el árbol del PR y evalúa la lógica del flujo de trabajo y los metadatos del PR.
143
+
144
+ `recap doctor` rechaza JSON con formato incorrecto o no compatible e informa cuando GitHub no expone ningún ejecutor en línea que coincida con todas las etiquetas configuradas. El registro, la instalación, las actualizaciones, la capacidad y el mantenimiento del ejecutor siguen siendo su responsabilidad; Agent Native selecciona las etiquetas, pero no proporciona ni opera el ejecutor.
145
+
146
+ **Seguridad:** Prefiera ejecutores autohospedados efímeros. Los ejecutores autohospedados persistentes solo deben servir a repositorios privados cuyos colaboradores sean de confianza. El flujo de trabajo se cierra de forma segura antes de asignar trabajo autohospedado a autores no confiables de PR públicos o de bifurcaciones, pero una máquina persistente conserva el estado del espacio de trabajo y de los procesos entre trabajos. No debilite esa barrera ni envíe contribuciones no confiables a un ejecutor persistente.
147
+
126
148
  ## Selección de backend
127
149
 
128
150
  Elija qué agente de codificación ejecuta la habilidad con la variable de repositorio `VISUAL_RECAP_AGENT`:
@@ -460,10 +482,12 @@ jobs:
460
482
  model: ${{ vars.VISUAL_RECAP_MODEL || '' }}
461
483
  reasoning: ${{ vars.VISUAL_RECAP_REASONING || '' }}
462
484
  skill-source: ${{ vars.VISUAL_RECAP_SKILL_SOURCE || 'auto' }}
485
+ runs-on: ${{ vars.VISUAL_RECAP_RUNS_ON || '"ubuntu-latest"' }}
486
+ gate-runs-on: ${{ vars.VISUAL_RECAP_GATE_RUNS_ON || 'ubuntu-latest' }}
463
487
  # cli-version: "latest" # pin to a specific @agent-native/core version
464
488
  ```
465
489
 
466
- Se aplican los mismos secretos y variables descritos en [Secrets and variables](#secrets-and-variables); configúrelos en la configuración de su repositorio de la misma manera que para la variante de copia.
490
+ Se aplican los mismos secretos y variables descritos en [Secrets and variables](#secrets-and-variables); configúrelos como en la variante de copia. El llamador reutilizable reenvía `VISUAL_RECAP_RUNS_ON` como `runs-on` para el trabajo completo y `VISUAL_RECAP_GATE_RUNS_ON` como la etiqueta simple `gate-runs-on` para la barrera. Ambos conservan `ubuntu-latest` cuando se omiten.
467
491
 
468
492
  ### Instalación mediante CLI
469
493
 
@@ -290,7 +290,7 @@ npx @agent-native/core@latest plan local serve --dir plans/<slug> --kind plan --
290
290
  ```
291
291
 
292
292
  Parece el puente URL
293
- `https://plan.agent-native.com/local-plans/<slug>?bridge=http://127.0.0.1:...`.
293
+ `https://plan.agent-native.com/local-plans/local-<opaque-id>#bridge=http://127.0.0.1:...`.
294
294
  La página es el visor de planes normal, pero el navegador recupera `plan.mdx`,
295
295
  `canvas.mdx`, `prototype.mdx`, `.plan-state.json` y recursos de imagen locales de
296
296
  el puente del host local. El contenido del plan no se escribe en la base de datos alojada y se
@@ -123,6 +123,28 @@ fichiers de référence avec lesquels la compétence est livrée. Si votre dép
123
123
  épingle son dossier `visual-recap` validé, définit la variable du référentiel
124
124
  `VISUAL_RECAP_SKILL_SOURCE=repo`.
125
125
 
126
+ ## Sélection du runner
127
+
128
+ Par défaut, le workflow s'exécute sur des runners `ubuntu-latest` hébergés par GitHub. Pour utiliser un autre runner hébergé ou votre propre runner auto-hébergé, définissez la variable de dépôt ou d'organisation `VISUAL_RECAP_RUNS_ON` sur une chaîne JSON valide ou un tableau JSON d'étiquettes de runner :
129
+
130
+ ```text
131
+ "ubuntu-latest"
132
+ ["self-hosted", "linux", "x64", "visual-recap"]
133
+ ```
134
+
135
+ Les guillemets JSON sont obligatoires. Cette valeur sélectionne le runner du job complet `recap`. Pour le configurer pendant l'installation guidée, transmettez la même valeur JSON à `--runs-on`, puis validez le workflow et la disponibilité du runner avec `doctor` :
136
+
137
+ ```bash
138
+ npx @agent-native/core@latest recap setup --runs-on '["self-hosted","linux","x64","visual-recap"]'
139
+ npx @agent-native/core@latest recap doctor
140
+ ```
141
+
142
+ Le gate léger utilise séparément `ubuntu-latest`. Les dépôts sans runners hébergés par GitHub peuvent définir `VISUAL_RECAP_GATE_RUNS_ON` sur une seule étiquette en texte brut, sans JSON, ou utiliser `--gate-runs-on visual-recap-gate`. L'étiquette personnalisée ne s'applique qu'aux auteurs `OWNER`, `MEMBER` ou `COLLABORATOR` du même dépôt ; les forks et autres auteurs sont forcés sur `ubuntu-latest` et peuvent rester non planifiés si les runners hébergés sont désactivés. Utilisez un runner dédié et de préférence éphémère : le gate standard n'extrait pas l'arborescence de la PR et évalue la logique du workflow ainsi que les métadonnées de la PR.
143
+
144
+ `recap doctor` rejette les données JSON mal formées ou non prises en charge et signale lorsque GitHub ne présente aucun runner en ligne correspondant à toutes les étiquettes configurées. L'enregistrement, l'installation, les mises à niveau, la capacité et la maintenance du runner restent sous votre responsabilité ; Agent Native sélectionne les étiquettes, mais ne fournit ni n'exploite le runner.
145
+
146
+ **Sécurité :** Privilégiez les runners auto-hébergés éphémères. Les runners auto-hébergés persistants ne doivent servir que des dépôts privés dont les contributeurs sont dignes de confiance. Le workflow échoue de manière sûre avant de planifier du travail auto-hébergé pour des auteurs non fiables de PR publiques ou provenant de forks, mais une machine persistante conserve toujours l'état de l'espace de travail et des processus entre les jobs. N'affaiblissez pas cette barrière et n'acheminez pas de contributions non fiables vers un runner persistant.
147
+
126
148
  ## Sélection du back-end
127
149
 
128
150
  Choisissez quel agent de codage exécute la compétence avec la variable du référentiel `VISUAL_RECAP_AGENT` :
@@ -461,10 +483,12 @@ jobs:
461
483
  model: ${{ vars.VISUAL_RECAP_MODEL || '' }}
462
484
  reasoning: ${{ vars.VISUAL_RECAP_REASONING || '' }}
463
485
  skill-source: ${{ vars.VISUAL_RECAP_SKILL_SOURCE || 'auto' }}
486
+ runs-on: ${{ vars.VISUAL_RECAP_RUNS_ON || '"ubuntu-latest"' }}
487
+ gate-runs-on: ${{ vars.VISUAL_RECAP_GATE_RUNS_ON || 'ubuntu-latest' }}
464
488
  # cli-version: "latest" # pin to a specific @agent-native/core version
465
489
  ```
466
490
 
467
- Les mêmes secrets et variables décrits dans [Secrets and variables](#secrets-and-variables) s'appliquent : définissez-les dans les paramètres de votre dépôt de la même manière que pour la variante de copie.
491
+ Les mêmes secrets et variables décrits dans [Secrets and variables](#secrets-and-variables) s'appliquent. L'appelant réutilisable transmet `VISUAL_RECAP_RUNS_ON` via `runs-on` pour le job complet et `VISUAL_RECAP_GATE_RUNS_ON` via l'étiquette simple `gate-runs-on` pour le gate. Les deux utilisent `ubuntu-latest` par défaut.
468
492
 
469
493
  ### Installation via le CLI
470
494
 
@@ -290,7 +290,7 @@ npx @agent-native/core@latest plan local serve --dir plans/<slug> --kind plan --
290
290
  ```
291
291
 
292
292
  Le pont URL ressemble à
293
- `https://plan.agent-native.com/local-plans/<slug>?bridge=http://127.0.0.1:...`.
293
+ `https://plan.agent-native.com/local-plans/local-<opaque-id>#bridge=http://127.0.0.1:...`.
294
294
  La page est la visionneuse de plan normale, mais le navigateur récupère `plan.mdx`,
295
295
  `canvas.mdx`, `prototype.mdx`, `.plan-state.json` et éléments d'image locaux de
296
296
  le pont localhost. Le contenu du plan n'est pas écrit dans la base de données hébergée et est