agentic-mermaid 0.1.0 → 0.1.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 (333) hide show
  1. package/CHANGELOG.md +149 -4
  2. package/LICENSES/Apache-2.0.txt +202 -0
  3. package/README.md +25 -32
  4. package/THIRD_PARTY_NOTICES.md +29 -0
  5. package/assets/fonts/FONT-LICENSES.md +9 -5
  6. package/assets/fonts/Inter-Bold.ttf +0 -0
  7. package/assets/fonts/Inter-Medium.ttf +0 -0
  8. package/assets/fonts/Inter-Regular.ttf +0 -0
  9. package/assets/fonts/Inter-SemiBold.ttf +0 -0
  10. package/dist/agent-core.d.ts +463 -0
  11. package/dist/agent-core.js +397 -0
  12. package/dist/agent.d.ts +81 -1822
  13. package/dist/agent.js +336 -36839
  14. package/dist/agentic-mermaid-mcp.js +7 -37908
  15. package/dist/am.js +33638 -36261
  16. package/dist/chunk-4QECACIA.js +4702 -0
  17. package/dist/chunk-CDPKJ2BQ.js +37 -0
  18. package/dist/chunk-QKDLMOJJ.js +3487 -0
  19. package/dist/chunk-WI35L7IR.js +615 -0
  20. package/dist/chunk-WN3YDOBG.js +149 -0
  21. package/dist/chunk-ZYMMKQ3I.js +62637 -0
  22. package/dist/index-D54dqSdC.d.ts +6777 -0
  23. package/dist/index.d.ts +1 -1
  24. package/dist/index.js +310 -32836
  25. package/dist/mcp-cli-SBQSTJFC.js +17 -0
  26. package/docs/README.md +14 -6
  27. package/docs/schemas/style-spec.schema.json +1221 -30
  28. package/examples/styles/catalog.json +54 -0
  29. package/examples/styles/cloudflare-workers-inspired-prototype.mmd +9 -0
  30. package/examples/styles/cloudflare-workers-inspired-prototype.style.json +110 -0
  31. package/examples/styles/cupertino-prototype.mmd +10 -0
  32. package/examples/styles/cupertino-prototype.style.json +63 -0
  33. package/examples/styles/mid-century-report.style.json +2 -2
  34. package/examples/styles/star-chart-atlas.style.json +2 -1
  35. package/examples/styles/transit-route-map.style.json +2 -2
  36. package/examples/styles/vercel-inspired-prototype.mmd +6 -0
  37. package/examples/styles/vercel-inspired-prototype.style.json +87 -0
  38. package/package.json +70 -43
  39. package/server.json +41 -0
  40. package/skills/agentic-mermaid-diagram-workflow/SKILL.md +17 -36
  41. package/skills/agentic-mermaid-diagram-workflow/references/cli.md +4 -5
  42. package/skills/agentic-mermaid-diagram-workflow/references/code-mode.md +15 -31
  43. package/skills/agentic-mermaid-diagram-workflow/references/sequence.md +10 -14
  44. package/skills/agentic-mermaid-live-editor/SKILL.md +100 -206
  45. package/AGENT_NATIVE.md +0 -602
  46. package/Instructions_for_agents.md +0 -83
  47. package/TODO.md +0 -662
  48. package/assets/hero.png +0 -0
  49. package/dist/index-x2Cj1dVv.d.ts +0 -1089
  50. package/docs/agent-api-cookbook.md +0 -308
  51. package/docs/agent-mutation-policy.md +0 -104
  52. package/docs/agent-workflow-examples.md +0 -46
  53. package/docs/api.md +0 -203
  54. package/docs/ascii.md +0 -103
  55. package/docs/assets/improvements/box-start-connector.png +0 -0
  56. package/docs/assets/improvements/comment-dropped-lint.png +0 -0
  57. package/docs/assets/improvements/er-content-loss.png +0 -0
  58. package/docs/assets/improvements/fan-in-trunks.png +0 -0
  59. package/docs/assets/improvements/gantt-family.png +0 -0
  60. package/docs/assets/improvements/gantt-milestone-clamp.png +0 -0
  61. package/docs/assets/improvements/gantt-verify-seam.png +0 -0
  62. package/docs/assets/improvements/subgraph-container-edges.png +0 -0
  63. package/docs/assets/improvements/wrapper-frontmatter.png +0 -0
  64. package/docs/assets/improvements/wrapper-init-directive.png +0 -0
  65. package/docs/assets/improvements/wrapper-leading-comment.png +0 -0
  66. package/docs/assets/style-cookbook/mid-century-report.png +0 -0
  67. package/docs/assets/style-cookbook/star-chart-atlas.png +0 -0
  68. package/docs/assets/style-cookbook/transit-route-map.png +0 -0
  69. package/docs/comparison.md +0 -94
  70. package/docs/config.md +0 -105
  71. package/docs/contributing/adding-diagram-types.md +0 -144
  72. package/docs/contributing/diagram-family-citizenship.matrix.json +0 -2127
  73. package/docs/contributing/diagram-family-citizenship.md +0 -82
  74. package/docs/contributing/harvesting-upstream-tests.md +0 -126
  75. package/docs/contributing/lessons-learned.md +0 -276
  76. package/docs/contributing/releasing.md +0 -71
  77. package/docs/contributing/visual-review-evidence.md +0 -53
  78. package/docs/custom-style-cookbook.md +0 -154
  79. package/docs/design/families/README.md +0 -26
  80. package/docs/design/families/architecture-beta.md +0 -48
  81. package/docs/design/families/flowchart-parser-conformance.md +0 -22
  82. package/docs/design/families/gantt-research.md +0 -137
  83. package/docs/design/families/gantt.md +0 -329
  84. package/docs/design/families/journey-agentic-cards-before.png +0 -0
  85. package/docs/design/families/journey-mermaid-classic-implementation.png +0 -0
  86. package/docs/design/families/journey-mermaid-classic-prototype.png +0 -0
  87. package/docs/design/families/journey-mermaid-classic-prototype.svg +0 -163
  88. package/docs/design/families/journey-mermaid-classic-prototype.svg.png +0 -0
  89. package/docs/design/families/journey-migration-parity.md +0 -433
  90. package/docs/design/families/journey-usage-research.md +0 -423
  91. package/docs/design/families/journey.md +0 -125
  92. package/docs/design/families/xychart.md +0 -85
  93. package/docs/design/system/README.md +0 -45
  94. package/docs/design/system/abstraction-audit.md +0 -224
  95. package/docs/design/system/abstraction-recommendations.md +0 -655
  96. package/docs/design/system/architecture.mmd +0 -29
  97. package/docs/design/system/architecture.svg +0 -137
  98. package/docs/design/system/layout-guarantees-and-robustness.md +0 -351
  99. package/docs/design/system/layout-rubric.md +0 -138
  100. package/docs/design/system/route-contracts.md +0 -848
  101. package/docs/design/system/source-preservation-ladder.md +0 -52
  102. package/docs/design/system/styles-rollout.md +0 -216
  103. package/docs/design/system/ugly-layouts.md +0 -119
  104. package/docs/diagram-families.md +0 -193
  105. package/docs/features.md +0 -147
  106. package/docs/fork-differences.md +0 -130
  107. package/docs/getting-started.md +0 -175
  108. package/docs/issue-derived-test-cases.md +0 -156
  109. package/docs/layout-characterization/README.md +0 -245
  110. package/docs/layout-characterization/contact-sheet-families.md +0 -496
  111. package/docs/layout-characterization/contact-sheet.md +0 -638
  112. package/docs/layout-characterization/properties.md +0 -281
  113. package/docs/layout-characterization/visual-quality.md +0 -160
  114. package/docs/layout-characterization/visual-snapshots/architecture.svg +0 -79
  115. package/docs/layout-characterization/visual-snapshots/class.svg +0 -60
  116. package/docs/layout-characterization/visual-snapshots/er.svg +0 -61
  117. package/docs/layout-characterization/visual-snapshots/flowchart.svg +0 -58
  118. package/docs/layout-characterization/visual-snapshots/gantt.svg +0 -52
  119. package/docs/layout-characterization/visual-snapshots/journey.svg +0 -118
  120. package/docs/layout-characterization/visual-snapshots/pie.svg +0 -36
  121. package/docs/layout-characterization/visual-snapshots/quadrant.svg +0 -47
  122. package/docs/layout-characterization/visual-snapshots/sequence.svg +0 -59
  123. package/docs/layout-characterization/visual-snapshots/state.svg +0 -52
  124. package/docs/layout-characterization/visual-snapshots/timeline.svg +0 -64
  125. package/docs/layout-characterization/visual-snapshots/xychart.svg +0 -97
  126. package/docs/mcp-code-mode-rationale.md +0 -101
  127. package/docs/mcp-http-transport.md +0 -223
  128. package/docs/mermaid-layout-complaints.md +0 -837
  129. package/docs/mutation-testing.md +0 -206
  130. package/docs/project/divergences.md +0 -98
  131. package/docs/project/dogfooding-docs-strategy.md +0 -66
  132. package/docs/project/hosted-mcp-cloudflare-plan.md +0 -338
  133. package/docs/project/lessons-learned.md +0 -1019
  134. package/docs/project/remove-role-styling-plan.md +0 -176
  135. package/docs/quality.md +0 -212
  136. package/docs/react.md +0 -97
  137. package/docs/style-authoring.md +0 -133
  138. package/docs/testing-strategy.md +0 -335
  139. package/docs/theming.md +0 -162
  140. package/examples/agent-improve-auth-flow.ts +0 -206
  141. package/examples/agent-loop.ts +0 -53
  142. package/examples/mcp-vs-cli-complex-diagrams.ts +0 -133
  143. package/llms.txt +0 -104
  144. package/skill-evals/README.md +0 -77
  145. package/skill-evals/fixtures/architecture-cache/input.mmd +0 -5
  146. package/skill-evals/fixtures/class-payment/input.mmd +0 -4
  147. package/skill-evals/fixtures/er-orders/input.mmd +0 -10
  148. package/skill-evals/fixtures/flowchart-cache/input.mmd +0 -3
  149. package/skill-evals/fixtures/gantt-release/input.mmd +0 -7
  150. package/skill-evals/fixtures/journey-checkout/input.mmd +0 -6
  151. package/skill-evals/fixtures/pie-exports/input.mmd +0 -5
  152. package/skill-evals/fixtures/quadrant-priorities/input.mmd +0 -11
  153. package/skill-evals/fixtures/sequence-retry/input.mmd +0 -8
  154. package/skill-evals/fixtures/state-lifecycle/input.mmd +0 -5
  155. package/skill-evals/fixtures/timeline-release/input.mmd +0 -6
  156. package/skill-evals/fixtures/xychart-growth/input.mmd +0 -5
  157. package/skill-evals/shared-benchmark.json +0 -510
  158. package/src/agent/analyze.ts +0 -157
  159. package/src/agent/apply.ts +0 -146
  160. package/src/agent/architecture-body.ts +0 -458
  161. package/src/agent/certificates.ts +0 -110
  162. package/src/agent/class-body.ts +0 -319
  163. package/src/agent/core.ts +0 -207
  164. package/src/agent/create.ts +0 -106
  165. package/src/agent/describe.ts +0 -364
  166. package/src/agent/er-body.ts +0 -224
  167. package/src/agent/facts.ts +0 -337
  168. package/src/agent/families-builtin.ts +0 -934
  169. package/src/agent/families.ts +0 -232
  170. package/src/agent/family-layouts.ts +0 -826
  171. package/src/agent/flowchart-body.ts +0 -372
  172. package/src/agent/flowchart-unsupported.ts +0 -243
  173. package/src/agent/gantt-body.ts +0 -495
  174. package/src/agent/index.ts +0 -10
  175. package/src/agent/journey-body.ts +0 -414
  176. package/src/agent/label-metrics.ts +0 -28
  177. package/src/agent/layout-to-rendered.ts +0 -63
  178. package/src/agent/mutate.ts +0 -97
  179. package/src/agent/mutation-ops.ts +0 -51
  180. package/src/agent/op-schema.ts +0 -439
  181. package/src/agent/parse.ts +0 -223
  182. package/src/agent/pie-body.ts +0 -270
  183. package/src/agent/png.ts +0 -117
  184. package/src/agent/quadrant-body.ts +0 -336
  185. package/src/agent/quality.ts +0 -363
  186. package/src/agent/readability-audit.ts +0 -92
  187. package/src/agent/rendered-route-audit.ts +0 -61
  188. package/src/agent/sequence-body.ts +0 -345
  189. package/src/agent/serialize.ts +0 -214
  190. package/src/agent/state-body.ts +0 -589
  191. package/src/agent/structural-count.ts +0 -150
  192. package/src/agent/timeline-body.ts +0 -280
  193. package/src/agent/trace-log.ts +0 -43
  194. package/src/agent/types.ts +0 -979
  195. package/src/agent/verify.ts +0 -571
  196. package/src/agent/xychart-body.ts +0 -472
  197. package/src/architecture/config.ts +0 -282
  198. package/src/architecture/layout.ts +0 -496
  199. package/src/architecture/parser.ts +0 -375
  200. package/src/architecture/renderer.ts +0 -611
  201. package/src/architecture/types.ts +0 -113
  202. package/src/ascii/ansi.ts +0 -423
  203. package/src/ascii/architecture.ts +0 -210
  204. package/src/ascii/canvas.ts +0 -443
  205. package/src/ascii/class-diagram.ts +0 -697
  206. package/src/ascii/converter.ts +0 -475
  207. package/src/ascii/draw.ts +0 -1693
  208. package/src/ascii/edge-bundling.ts +0 -328
  209. package/src/ascii/edge-routing.ts +0 -330
  210. package/src/ascii/er-diagram.ts +0 -439
  211. package/src/ascii/gantt.ts +0 -276
  212. package/src/ascii/grid.ts +0 -747
  213. package/src/ascii/index.ts +0 -196
  214. package/src/ascii/journey.ts +0 -189
  215. package/src/ascii/meta.ts +0 -244
  216. package/src/ascii/multiline-utils.ts +0 -80
  217. package/src/ascii/pathfinder.ts +0 -301
  218. package/src/ascii/pie.ts +0 -67
  219. package/src/ascii/quadrant.ts +0 -158
  220. package/src/ascii/reverse.ts +0 -216
  221. package/src/ascii/sequence.ts +0 -499
  222. package/src/ascii/shapes/circle.ts +0 -27
  223. package/src/ascii/shapes/corners.ts +0 -143
  224. package/src/ascii/shapes/diamond.ts +0 -27
  225. package/src/ascii/shapes/hexagon.ts +0 -27
  226. package/src/ascii/shapes/index.ts +0 -106
  227. package/src/ascii/shapes/rectangle.ts +0 -168
  228. package/src/ascii/shapes/rounded.ts +0 -27
  229. package/src/ascii/shapes/special.ts +0 -332
  230. package/src/ascii/shapes/stadium.ts +0 -110
  231. package/src/ascii/shapes/state.ts +0 -192
  232. package/src/ascii/shapes/types.ts +0 -73
  233. package/src/ascii/timeline.ts +0 -128
  234. package/src/ascii/types.ts +0 -312
  235. package/src/ascii/validate.ts +0 -120
  236. package/src/ascii/width.ts +0 -81
  237. package/src/ascii/xychart.ts +0 -919
  238. package/src/browser.ts +0 -29
  239. package/src/class/layout.ts +0 -240
  240. package/src/class/parser.ts +0 -332
  241. package/src/class/renderer.ts +0 -638
  242. package/src/class/types.ts +0 -129
  243. package/src/cli/agent-instructions.ts +0 -1
  244. package/src/cli/am-bin.ts +0 -6
  245. package/src/cli/exit-codes.ts +0 -18
  246. package/src/cli/index.ts +0 -1117
  247. package/src/cli/init-agent.ts +0 -147
  248. package/src/color-resolver.ts +0 -152
  249. package/src/elk-instance.ts +0 -114
  250. package/src/er/layout.ts +0 -187
  251. package/src/er/parser.ts +0 -226
  252. package/src/er/renderer.ts +0 -792
  253. package/src/er/types.ts +0 -99
  254. package/src/font-manifest.ts +0 -27
  255. package/src/gantt/layout.ts +0 -365
  256. package/src/gantt/parser.ts +0 -282
  257. package/src/gantt/pipeline.ts +0 -27
  258. package/src/gantt/renderer.ts +0 -414
  259. package/src/gantt/schedule.ts +0 -513
  260. package/src/gantt/types.ts +0 -255
  261. package/src/index.ts +0 -368
  262. package/src/journey/layout.ts +0 -520
  263. package/src/journey/parser.ts +0 -218
  264. package/src/journey/renderer.ts +0 -811
  265. package/src/journey/types.ts +0 -144
  266. package/src/layout/geometry.ts +0 -182
  267. package/src/layout/pass.ts +0 -142
  268. package/src/layout/passes/index.ts +0 -3916
  269. package/src/layout-engine.ts +0 -1565
  270. package/src/layout-rubric.ts +0 -659
  271. package/src/mcp/artifacts.ts +0 -137
  272. package/src/mcp/dynamic-harness.ts +0 -31
  273. package/src/mcp/dynamic-modules.d.ts +0 -7
  274. package/src/mcp/facade.ts +0 -608
  275. package/src/mcp/harness-runtime.ts +0 -203
  276. package/src/mcp/hosted-server.ts +0 -468
  277. package/src/mcp/mcp-bin.ts +0 -3
  278. package/src/mcp/mcp-cli.ts +0 -82
  279. package/src/mcp/protocol.ts +0 -13
  280. package/src/mcp/sandbox.ts +0 -195
  281. package/src/mcp/sdk-decl.ts +0 -387
  282. package/src/mcp/server.ts +0 -362
  283. package/src/mcp/tool-surface.ts +0 -146
  284. package/src/mermaid-source.ts +0 -655
  285. package/src/multiline-utils.ts +0 -228
  286. package/src/parser.ts +0 -1031
  287. package/src/pie/layout.ts +0 -168
  288. package/src/pie/parser.ts +0 -136
  289. package/src/pie/renderer.ts +0 -234
  290. package/src/pie/types.ts +0 -82
  291. package/src/quadrant/layout.ts +0 -191
  292. package/src/quadrant/parser.ts +0 -183
  293. package/src/quadrant/renderer.ts +0 -322
  294. package/src/quadrant/types.ts +0 -113
  295. package/src/render-family-hooks.ts +0 -244
  296. package/src/renderer.ts +0 -1099
  297. package/src/route-contracts.ts +0 -2723
  298. package/src/scene/backend.ts +0 -76
  299. package/src/scene/fidelity.ts +0 -183
  300. package/src/scene/hybrid-backend.ts +0 -174
  301. package/src/scene/ir.ts +0 -181
  302. package/src/scene/marks.ts +0 -127
  303. package/src/scene/rough-backend.ts +0 -378
  304. package/src/scene/seed.ts +0 -46
  305. package/src/scene/style-registry.ts +0 -556
  306. package/src/sequence/layout.ts +0 -456
  307. package/src/sequence/parser.ts +0 -255
  308. package/src/sequence/renderer.ts +0 -604
  309. package/src/sequence/types.ts +0 -154
  310. package/src/shape-clipping.ts +0 -351
  311. package/src/shared/accessibility-directives.ts +0 -22
  312. package/src/shared/batched.ts +0 -61
  313. package/src/shared/color-math.ts +0 -72
  314. package/src/shared/svg-a11y.ts +0 -29
  315. package/src/shared/syntax-error.ts +0 -17
  316. package/src/shared/unicode-ranges.ts +0 -135
  317. package/src/styles.ts +0 -318
  318. package/src/svg-paths.ts +0 -28
  319. package/src/text-metrics.ts +0 -248
  320. package/src/theme.ts +0 -639
  321. package/src/timeline/layout.ts +0 -381
  322. package/src/timeline/parser.ts +0 -215
  323. package/src/timeline/renderer.ts +0 -538
  324. package/src/timeline/types.ts +0 -105
  325. package/src/types.ts +0 -454
  326. package/src/website-worker-modules.d.ts +0 -18
  327. package/src/xychart/axis-utils.ts +0 -109
  328. package/src/xychart/colors.ts +0 -124
  329. package/src/xychart/config.ts +0 -88
  330. package/src/xychart/layout.ts +0 -656
  331. package/src/xychart/parser.ts +0 -504
  332. package/src/xychart/renderer.ts +0 -640
  333. package/src/xychart/types.ts +0 -282
@@ -1,338 +0,0 @@
1
- # Hosted MCP on Cloudflare: plan
2
-
3
- > **Status: shipped (PR #94).** This began as a design plan and is now the
4
- > as-built record of the hosted MCP endpoint live at
5
- > `https://agentic-mermaid.dev/mcp`. The "Where we start" section below is
6
- > preserved as the pre-build starting point; the "Post-review hardening"
7
- > sections (rounds 1–5) track the audit findings applied after the initial
8
- > build.
9
-
10
- Plan for turning the website's `/mcp` 501 placeholder into a real hosted MCP
11
- endpoint at `https://agentic-mermaid.dev/mcp`. The account is on the **Workers
12
- Paid plan with Dynamic Workers** (<https://developers.cloudflare.com/dynamic-workers/>),
13
- which changes the earlier free-tier plan in one fundamental way: hosted Code
14
- Mode (`execute`) is now viable — agent-supplied JavaScript runs in an isolated
15
- dynamic Worker instead of being local-only.
16
-
17
- ## Where we start
18
-
19
- - The website is a Cloudflare Worker with Static Assets
20
- (`website/wrangler.jsonc`, `website/src/worker.js` at the time — now
21
- `worker.ts`); `/mcp` returned an honest 501 before this work.
22
- - The MCP server (`src/mcp/server.ts`) has a transport-agnostic JSON-RPC core,
23
- `handleRequest(req, context)`, plus stdio and node HTTP/SSE transports.
24
- - Tools today: `execute` (Code Mode in a hardened `node:vm` sandbox),
25
- `render_png` (native `@resvg/resvg-js`), `describe`.
26
- - **Viability probe (done):** the pure SDK surface — parse, mutate, verify,
27
- analyze, serialize, describe, `renderMermaidSVG`, `renderMermaidASCII`, the
28
- Code Mode facade — bundles for the workerd target at 2.2 MB raw / 657 KB
29
- gzip with zero Node dependencies, and renders correctly from that bundle.
30
- The only native/VM dependencies in the whole graph are `png.ts` (napi resvg)
31
- and `sandbox.ts` (`node:vm`), both replaced per the table below. elkjs is
32
- the bundled pure-JS build driven synchronously (`src/elk-instance.ts`) and
33
- needs nothing from Node.
34
-
35
- ## Hosted tool surface
36
-
37
- | Tool | Local implementation | Hosted implementation |
38
- |---|---|---|
39
- | `execute` | `node:vm` hardened sandbox | **Dynamic Worker** per code hash: harness module (SDK bundle + the same hardened facade) + agent code as a module, `globalOutbound: null`, `limits: { cpuMs, subRequests: 0 }` |
40
- | `render_png` | native `@resvg/resvg-js` | `@resvg/resvg-wasm` + bundled DejaVu Sans and built-in style faces (convenience surface; wasm output is not covered by the byte-determinism contract) |
41
- | `describe` | pure | same code, unchanged |
42
- | `render_svg` | — (use execute) | pure, hosted-only |
43
- | `render_ascii` | — (use execute) | pure, hosted-only |
44
- | `verify` | — (use execute) | pure, hosted-only |
45
- | `mutate` | — (use execute or CLI `am mutate`) | hosted-only declarative structured edit of an existing `source`; applies an op list, verifies, emits `{ ok, family, source, verify }` only on success |
46
- | `build` | — (use execute or CLI/library `buildMermaid`) | hosted-only declarative authoring from a `family` + op list; same verified envelope as `mutate` |
47
-
48
- The three hosted-only pure render/verify tools exist for cost, not philosophy:
49
- locally, `execute` is free, so Code Mode stays the single entry point
50
- (`docs/mcp-code-mode-rationale.md` still holds). Hosted, every `execute`
51
- spins/bills a dynamic Worker, so the common render/verify paths get direct
52
- tools that cost one ordinary Worker invocation and are edge-cacheable. The
53
- `mutate`/`build` tools are the deliberate structured-edit exception: they give
54
- weak clients a verified op-list path without asking them to write JavaScript,
55
- while still funneling through the same typed mutation core. The implementations
56
- live in the shared hosted core (`src/mcp/hosted-server.ts`), not the website, so
57
- there is exactly one implementation of each.
58
-
59
- ## Hosted `execute` via Dynamic Workers
60
-
61
- Design (API per <https://developers.cloudflare.com/dynamic-workers/api-reference/>):
62
-
63
- - The parent Worker holds a `worker_loaders` binding (`LOADER`). On
64
- `execute`, it calls `env.LOADER.get(id, async () => workerCode)` where:
65
- - `id` = `am-exec-<pkg.version>-<sha256(code)>` — identical code reuses a
66
- warm isolate; the callback only runs on cold load.
67
- - `workerCode.modules` = the prebuilt **harness** (SDK bundle + Code Mode
68
- facade + result plumbing, built by `website/build.ts` and imported into
69
- the parent as a text module) plus the agent code wrapped as ONE module
70
- per isolate. workerd compiles every registry module eagerly at isolate
71
- startup (discovered in the wrangler e2e — a lazy `import()` fallback
72
- inside one isolate is impossible), so the expression-first choice
73
- `sandbox.ts` makes is decided by the loader itself: the parent starts the
74
- expression-form isolate first and falls back to a statement-form isolate
75
- when startup fails with a SyntaxError. Statement-form code costs one
76
- failed isolate attempt; identical repeats never reach the loader thanks
77
- to the response cache.
78
- - `globalOutbound: null` — the isolate cannot `fetch()` or `connect()`
79
- at all; `env` is empty, so there is nothing to reach or leak.
80
- - `getEntrypoint(null, { limits: { cpuMs: min(timeoutMs, 30_000),
81
- subRequests: 0 } })` enforces the compute budget; hitting it throws,
82
- which maps to the same `{ ok: false, error }` shape as a local timeout.
83
- - The harness passes the user function the **same hardened `mermaid` facade**
84
- the vm sandbox uses (`createTracingMermaid`, extracted to a runtime-neutral
85
- `src/mcp/facade.ts`), so read-only results, trusted-diagram checks, and
86
- error messages match local behavior. Sync-only enforcement
87
- (`unsupportedCodeReason`) runs parent-side before any isolate is created —
88
- rejected code costs nothing.
89
- - Result contract is byte-identical in shape to local: `{ ok, value, logs,
90
- error }`, with the same undefined→null promotion and Map/Set JSON
91
- normalization.
92
-
93
- Known, documented divergences from local `execute`:
94
-
95
- 1. `timeoutMs` is enforced as **CPU** milliseconds (`cpuMs`), not wall-clock;
96
- for pure synchronous compute these are close but not identical.
97
- 2. A warm isolate can serve repeated identical code, so module-level global
98
- mutation (`globalThis.x = ...`) may be visible across calls with the same
99
- code — locally every call gets a fresh vm context. Response caching (below)
100
- makes identical requests return the first result anyway; the divergence is
101
- only reachable when the edge cache misses but the isolate is warm.
102
- 3. Isolation authority differs: locally the proxy facade is the security
103
- boundary; hosted, the isolate + `globalOutbound: null` is, and the facade
104
- is kept for behavioral parity.
105
- 4. Hosted output is bounded at the source: console logs cap at 1,000
106
- entries / 256 KB (a truncation marker is appended) and serialized results
107
- at 2 MB, with the parent's capped stream-read of the isolate response as
108
- the backstop. The vm sandbox has no output caps.
109
-
110
- Post-review hardening (external audit, round 1): Worker Loader isolate IDs
111
- carry a `deployTag` — package version **plus a hash of the harness bundle** —
112
- because the Worker Loader contract is that one ID always maps to the same
113
- WorkerCode; without the hash, a harness/SDK change that ships without a
114
- version bump could keep serving stale warm isolates. Request bodies and
115
- isolate responses are stream-read with hard byte caps (no buffering past the
116
- limit), `render_png` clamps `scale` to 0.1–8 and enforces a ~16.7 MP
117
- output-pixel budget, and the e2e probe pins workerd's codegen bans (`eval`,
118
- `Function` constructor) plus the log cap against a live isolate.
119
-
120
- Post-review hardening (external audit, round 2):
121
-
122
- - **Full-deploy cache version.** The harness hash covers Code Mode isolate
123
- IDs, but the /mcp *response* cache also stores `render_svg` / `verify` /
124
- `describe` / `render_png` results, whose implementations live in
125
- `hosted-server.ts`, `mcp-handler.ts`, `png-wasm.ts`, and the wasm/fonts —
126
- none of which are in the harness bundle. `cacheVersion` is now a
127
- `DEPLOY_VERSION` computed at build time (`website/src/deploy-hash.ts`,
128
- emitted to `generated/deploy-version.ts`): a length-prefixed SHA-256 over
129
- the **bundled worker JS closure + harness + wasm + fonts + the main
130
- worker's `compatibility_date`**. Any change to any hosted tool, the
131
- transport, the PNG path, an asset, or the worker's runtime semantics moves
132
- it and invalidates cached results, version bump or not. Isolate IDs keep the
133
- narrower harness hash (they *are* just harness + user module).
134
- - **Cache keys from normalized effective arguments.** The key was a hash of
135
- raw `req.params`; handlers drop unknown args and clamp `scale`/`timeoutMs`,
136
- so a caller could add `{ nonce }` or vary an out-of-range `scale` to force
137
- recompute and defeat the cost control. `cacheKeyFor` (in `hosted-server.ts`,
138
- the single source of truth the handlers also use for clamping) now derives
139
- the key from only the output-affecting arguments, normalized: unknown keys
140
- dropped, `scale` clamped and defaulted to its resolved value (omitted and
141
- explicit `scale: 2` share one entry), `timeoutMs` excluded from `execute`
142
- (it is a budget, not an input). Calls it deems uncacheable (unknown tool,
143
- missing required arg, non-base64 `render_png` output) bypass the cache and
144
- run directly — they error, and errors were never cached.
145
-
146
- **Scope of the cost-control guarantee (external audit, round 3 — adversarial
147
- multi-agent verification found no correctness/poisoning bug; these are the
148
- low-severity refinements it surfaced):**
149
-
150
- - The normalization covers **arguments**, not the `source`/`code` payload,
151
- which is keyed **verbatim by design**. Keying on the raw payload is what
152
- makes a cached response provably correspond to what that exact input
153
- renders; canonicalizing the payload (e.g. stripping Mermaid comments) is
154
- deliberately avoided because two payloads that canonicalize alike are not
155
- guaranteed to render byte-identically — a wrong cached result is far worse
156
- than a missed dedup. A caller can therefore still force recompute by varying
157
- insignificant payload bytes (comments/trailing whitespace). That residual is
158
- bounded by the **WAF rate limit** (the actual abuse backstop; see
159
- `website/README.md`), not by the cache.
160
- - `execute` is cached on `code` alone. Code Mode is intended for deterministic
161
- SDK workflows; a non-deterministic body (`Date`/`Math.random`) has its first
162
- result frozen for the cache TTL. This is pre-existing — `execute` results
163
- were always cached — and inherent to caching arbitrary code; it is not a
164
- cross-caller integrity issue (identical `code` → identical key by definition).
165
- - The response cache is a **cost optimization for legitimate repeat traffic**,
166
- not the abuse control. The WAF rate-limiting rule on `POST /mcp` is the
167
- primary defense against a determined attacker and remains a launch
168
- requirement.
169
-
170
- **Post-review hardening (external audit, round 4 — full-PR multi-agent audit,
171
- 5 lenses):** one P1 (wrapper-breakout containment) and a set of low-severity
172
- test-coverage and transport refinements. The verified fixes:
173
-
174
- - **Wrapper-breakout containment, stated honestly.** Agent code is concatenated
175
- into an ES module, so code that closes the harness function early with `}`
176
- reaches module scope. Two layers close what the isolate boundary already
177
- bounds: (1) the harness wraps the user function in **parentheses** (an
178
- expression position), which makes an injected top-level `import`/`export`/`;`
179
- a **SyntaxError** — so a `import ... from 'cloudflare:sockets'` breakout fails
180
- the isolate start rather than running; and (2) `hardenIsolateGlobals()` runs
181
- at isolate startup, **before** `user.js` is dynamically imported, and strips
182
- capability globals (`fetch`, `caches`, `crypto`, `connect`, …) plus
183
- `Error.prepareStackTrace`. A comma+IIFE tail can still *run* at eval time, but
184
- only against stripped capabilities. These are **defense in depth** — the
185
- guaranteed boundary remains the isolate config (`globalOutbound: null`, empty
186
- env, no bindings, `cpuMs`). `NEUTRALIZED_ISOLATE_GLOBALS` is restricted to
187
- globals provably unused by the harness and the pure SDK at run time (verified
188
- by the wrangler e2e, which still renders after hardening).
189
- - **Test-coverage gaps closed.** The Promise.race wall-clock backstop, the
190
- empty/non-JSON isolate-body malformed guard, `readCapped`'s byte-exact cap
191
- (boundary + cross-chunk + UTF-8 straddle), multi-chunk base64 encoding, and
192
- CORS headers on HTTP-level error responses now have discriminating tests
193
- (each fails when its mechanism is removed).
194
- - **Transport nits.** The Promise.race timer is cleared in a `finally` so a
195
- winning fetch leaves no dangling timer, and an empty/non-JSON isolate body
196
- degrades to `sandbox returned a malformed result` instead of leaking a raw
197
- `JSON.parse` error.
198
-
199
- Residual honesty: `website/e2e-mcp.sh` is a **manual** probe run against a live
200
- `wrangler dev` (it needs the Worker Loader), **not** a CI gate; the CI gate is
201
- `bun test src/__tests__/`. The isolate-level containment (globals stripped on
202
- the real `globalThis`, breakout rejected) is what the e2e pins; the unit suite
203
- pins the pure, runtime-neutral pieces (`neutralizeGlobalsOn` on a throwaway
204
- target, `userModuleSources` rejection, the log cap).
205
-
206
- **Post-review hardening (external audit, round 5 — transport abuse/conformance):**
207
-
208
- - **Batch fan-out cap.** A JSON-RPC array ran every item under `Promise.all`,
209
- so one 128 KB body could pack many `tools/call` items that each spin a
210
- billable isolate/render — a single request amplifying past the per-IP WAF
211
- limit. `MAX_BATCH_ITEMS` (20) now bounds a batch, refused with 400 before any
212
- item runs.
213
- - **`MCP-Protocol-Version` validation + 2025-06-18 batch rule.** The transport
214
- now rejects an explicit unsupported version header with 400, and — because
215
- MCP 2025-06-18 *removed* JSON-RPC batching — refuses an array body when the
216
- header pins that revision. Older negotiated versions (2024-11-05 / 2025-03-26,
217
- or no header) may still batch, so existing clients are unaffected.
218
- - **CORS Origin validation.** Wildcard `Access-Control-Allow-Origin: *` on a
219
- public, credential-less endpoint is not itself an access-control hole — CORS
220
- gates only browser reads and never the agent/server clients that ignore it —
221
- but `*` does let an arbitrary site silently drive a *visitor's* browser
222
- against the endpoint (distributed across visitor IPs, diluting the per-IP WAF
223
- limit). CORS is now **reflective with Origin validation**: a no-Origin client
224
- (agent/server) still gets `*`; a browser Origin is echoed only when it is
225
- same-origin / localhost / allowlisted (`agentic-mermaid.dev`); a disallowed
226
- browser Origin gets no `Access-Control-Allow-Origin` and a 403. This follows
227
- the MCP Streamable HTTP Origin-validation guidance without breaking any
228
- non-browser client (the website itself does not call `/mcp` from the browser).
229
-
230
- ## Transport: stateless Streamable HTTP
231
-
232
- Unchanged from the original plan and still the cheapest correct choice — the
233
- tools are pure functions of their inputs:
234
-
235
- - Single `POST /mcp` accepting JSON-RPC, responding `application/json`.
236
- No sessions, no SSE stream, `GET /mcp` → 405, notifications → 202.
237
- - No Durable Objects, agents SDK, KV, R2, or Queues.
238
- - Protocol version negotiated from a known-good list (the core currently pins
239
- `2024-11-05`; Streamable HTTP clients offer `2025-03-26`+).
240
- - **Response caching:** layout is deterministic, so `tools/call` responses
241
- (except nothing — all eight tools are deterministic) are cached in the Workers
242
- Cache API keyed on SHA-256 of `(tool, canonicalized arguments)`. Repeat
243
- requests skip compute entirely; for `execute` they also skip the dynamic
244
- Worker, which is the biggest cost lever.
245
- - Hygiene: 128 KB body cap, 64 KB Mermaid-source/code caps with structured
246
- errors pointing at the local CLI, `content-type` enforcement, Origin-validated
247
- CORS (no-Origin agent/server clients keep `*`; allowed browser origins are
248
- echoed; disallowed browser origins get 403), one WAF rate-limiting rule
249
- (e.g. 60 req/min/IP on `POST /mcp`, configured in the dashboard and
250
- recorded in `website/README.md`).
251
-
252
- ## Cost model (Workers Paid)
253
-
254
- | Meter | Included | Overage | Our exposure |
255
- |---|---|---|---|
256
- | Worker requests | 10 M/month | $0.30/M | every `/mcp` call + one internal request per uncached `execute` |
257
- | CPU time | 30 M ms/month | $0.02/M ms | renders are ms-scale; `execute` capped at 30 s by `cpuMs` |
258
- | Unique dynamic Workers | 1,000/month | $0.002/Worker/day | one per **unique** `execute` code string per day (hash-keyed + edge-cached; retries and repeats dedupe) |
259
-
260
- Levers that keep this near the plan's $5/month floor: edge caching of
261
- deterministic responses, code-hash isolate reuse, parent-side rejection of
262
- unsupported code, `subRequests: 0`, size caps, and the WAF rate limit as the
263
- abuse backstop. The pure tools keep the high-volume paths (render/verify) off
264
- the dynamic-Worker meter entirely. No other billable primitives are used.
265
-
266
- ## Verification strategy
267
-
268
- Two external toolkits gate this work, per the repo owner's direction:
269
-
270
- - **testing-best-practices** (<https://github.com/adewale/testing-best-practices>):
271
- - Red-green for new behavior: hosted-core tests written to fail without the
272
- implementation; red/green evidence stated in the PR.
273
- - **Differential testing** (`references/differential-testing.md`): the local
274
- `node:vm` sandbox is the reference implementation; a corpus of Code Mode
275
- snippets (happy paths, SDK misuse, mutation attempts, sync violations,
276
- serialization edges) runs through both `executeInSandbox` and the harness
277
- execution semantics, asserting matching `{ ok, value, logs, error }`.
278
- - Error-path tests, not just invalid input: loader failures, cpu-limit
279
- exceptions, oversized bodies, wrong content types, malformed JSON-RPC.
280
- - Real objects over mocks: transport tests drive the actual `fetch` handler
281
- with real `Request`/`Response`; the only faked seam is the Worker Loader
282
- binding (a purpose-built fake implementing `get/getEntrypoint`, since
283
- workerd is not available inside `bun test`), and a `wrangler dev` e2e
284
- covers the real binding.
285
- - **cfdoctor** (<https://github.com/adewale/cfdoctor>): static scan
286
- (`cfdoctor_static_scan.py`) plus the skill's audit playbook run against
287
- `website/` before shipping; findings triaged with evidence and fixed or
288
- explicitly accepted in the audit output.
289
-
290
- Plus the repo's own gates: `bun test src/__tests__/` (existing MCP tests must
291
- stay green through the refactor), `bunx tsc --noEmit`, `bun run
292
- website:check`, and a `wrangler dev` end-to-end pass (`website/e2e-mcp.sh`:
293
- initialize → tools/list → each tool, including a real dynamic-Worker
294
- `execute`). Known local-dev limitation found in that pass: wrangler dev does
295
- not enforce dynamic-worker `cpuMs`, so an unbounded sync loop wedges local
296
- workerd; production enforces `cpuMs` at the runtime and the parent races a
297
- wall-clock backstop so `/mcp` always answers. The e2e also flushed out an
298
- ELK instantiation failure under workerd (fixed in `src/elk-instance.ts` by
299
- flipping elk-worker's environment sniff with a temporary `document` global
300
- instead of deleting the undeletable `self`).
301
-
302
- ## Implementation steps
303
-
304
- 1. **Extract runtime-neutral modules.** `src/mcp/protocol.ts` (JSON-RPC
305
- types + reply/error helpers), `src/mcp/facade.ts` (`createTracingMermaid`,
306
- `unsupportedCodeReason`, wrapping helpers) importing the pure agent
307
- surface, not the barrel (the barrel exports `png.ts`, which drags native
308
- resvg into every graph). `server.ts`/`sandbox.ts` keep their exports and
309
- behavior; existing tests untouched.
310
- 2. **Hosted core** `src/mcp/hosted-server.ts`: `handleHostedRequest(req,
311
- context)` with the eight-tool surface; `execute` and `render_png` accept
312
- injected implementations so the core stays runtime-neutral and testable in
313
- `bun test`.
314
- 3. **Harness** `src/mcp/dynamic-harness.ts` (entry compiled to a single text
315
- asset by `website/build.ts`): fetch handler that builds the facade, imports
316
- the user module (expression-first with `SyntaxError` fallback), executes,
317
- serializes to the `ExecuteResult` contract.
318
- 4. **Website worker** `website/src/worker.ts`: existing static/redirect logic
319
- unchanged; `/mcp` branch implements the streamable-HTTP handler, cache
320
- layer, loader-backed `execute`, wasm `render_png`. `wrangler.jsonc` gains
321
- `worker_loaders`, text/data module rules for the harness and font, and
322
- keeps Static Assets.
323
- 5. **Tests** as described under verification; `src/__tests__/website-build.test.ts`
324
- now asserts the live hosted endpoint (the 501 assertion it carried is gone).
325
- 6. **cfdoctor audit + wrangler dev e2e**, fix findings.
326
- 7. **Docs**: `website/README.md`, `docs/mcp-http-transport.md` cross-link, a
327
- hosted-MCP section documenting the endpoint, tool surface, limits,
328
- divergences, and the dashboard-side rate-limit rule.
329
-
330
- ## Explicit non-goals
331
-
332
- - No REST render API — MCP only.
333
- - No accounts or tokens; abuse control is caps + rate limiting.
334
- - No hosted artifact storage; `render_png` returns base64 only (`output:
335
- "file"/"url"` stay local-server features).
336
- - Durable Object facets, dynamic Workflows, and egress-controlled outbound
337
- fetch (Dynamic Workers features) are all unused — the isolates need no
338
- state and no network.