my-durable 0.0.0-stage → 0.0.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 (259) hide show
  1. package/.env.example +7 -0
  2. package/LICENSE +21 -0
  3. package/README.md +328 -2
  4. package/ai_docs/durable_pi/announcement.md +860 -0
  5. package/ai_docs/durable_pi/changelog.md +51 -0
  6. package/ai_docs/durable_pi/chord-delta-findings.md +362 -0
  7. package/ai_docs/durable_pi/coding-agent-integration.md +70 -0
  8. package/ai_docs/durable_pi/pico-v5-chord-usage.md +391 -0
  9. package/ai_docs/durable_pi/pico-v5-handoff.md +921 -0
  10. package/ai_docs/durable_pi/readme.md +617 -0
  11. package/ai_docs/durable_pi/sources.md +22 -0
  12. package/ai_docs/durable_pi/spec.md +4696 -0
  13. package/ai_docs/durable_pi/vacation-planner.md +57 -0
  14. package/ai_docs/pi/chord.md +268 -0
  15. package/ai_docs/pi/cli-integration.md +110 -0
  16. package/ai_docs/pi/cli.md +325 -0
  17. package/ai_docs/pi/codemode.md +211 -0
  18. package/ai_docs/pi/compaction.md +467 -0
  19. package/ai_docs/pi/configuration.md +51 -0
  20. package/ai_docs/pi/containerization.md +187 -0
  21. package/ai_docs/pi/custom-provider.md +177 -0
  22. package/ai_docs/pi/environment-variables.md +102 -0
  23. package/ai_docs/pi/extensions.md +278 -0
  24. package/ai_docs/pi/how-pi-works.md +53 -0
  25. package/ai_docs/pi/index.md +43 -0
  26. package/ai_docs/pi/json.md +230 -0
  27. package/ai_docs/pi/keybindings.md +198 -0
  28. package/ai_docs/pi/llama-cpp.md +118 -0
  29. package/ai_docs/pi/mcp.md +274 -0
  30. package/ai_docs/pi/message-types.md +265 -0
  31. package/ai_docs/pi/models.md +209 -0
  32. package/ai_docs/pi/overview.md +166 -0
  33. package/ai_docs/pi/packages.md +133 -0
  34. package/ai_docs/pi/pi-agent-core.md +568 -0
  35. package/ai_docs/pi/pi-ai.md +1942 -0
  36. package/ai_docs/pi/prompt-templates.md +63 -0
  37. package/ai_docs/pi/providers.md +204 -0
  38. package/ai_docs/pi/quickstart.md +140 -0
  39. package/ai_docs/pi/readme.md +99 -0
  40. package/ai_docs/pi/rpc-commands.md +864 -0
  41. package/ai_docs/pi/rpc-extension-ui.md +204 -0
  42. package/ai_docs/pi/rpc.md +197 -0
  43. package/ai_docs/pi/sdk.md +151 -0
  44. package/ai_docs/pi/security.md +103 -0
  45. package/ai_docs/pi/session-format.md +297 -0
  46. package/ai_docs/pi/sessions.md +70 -0
  47. package/ai_docs/pi/settings.md +173 -0
  48. package/ai_docs/pi/shell-aliases.md +97 -0
  49. package/ai_docs/pi/skills.md +97 -0
  50. package/ai_docs/pi/slash-commands.md +64 -0
  51. package/ai_docs/pi/sources.md +57 -0
  52. package/ai_docs/pi/terminal-setup.md +223 -0
  53. package/ai_docs/pi/termux.md +121 -0
  54. package/ai_docs/pi/themes.md +140 -0
  55. package/ai_docs/pi/tmux.md +59 -0
  56. package/ai_docs/pi/toolkit.md +168 -0
  57. package/ai_docs/pi/tui.md +129 -0
  58. package/ai_docs/pi/usage.md +98 -0
  59. package/ai_docs/pi/virtual-models.md +118 -0
  60. package/ai_docs/pi/windows.md +71 -0
  61. package/apps/dpi-backend/.nvmrc +1 -0
  62. package/apps/dpi-backend/API.md +99 -0
  63. package/apps/dpi-backend/README.md +119 -0
  64. package/apps/dpi-backend/deploy/backend.env.example +9 -0
  65. package/apps/dpi-backend/deploy/dpi-backend.plist +26 -0
  66. package/apps/dpi-backend/deploy/dpi-backend.service +22 -0
  67. package/apps/dpi-backend/package.json +33 -0
  68. package/apps/dpi-backend/prompts/one_sentence.md +3 -0
  69. package/apps/dpi-backend/prompts/smart_ass_opus_fix.md +169 -0
  70. package/apps/dpi-backend/scripts/check-contract.mjs +151 -0
  71. package/apps/dpi-backend/scripts/migrate-playground.ts +66 -0
  72. package/apps/dpi-backend/scripts/record-nested-fixture.mts +154 -0
  73. package/apps/dpi-backend/scripts/token.ts +57 -0
  74. package/apps/dpi-backend/src/addons.ts +139 -0
  75. package/apps/dpi-backend/src/auth.ts +20 -0
  76. package/apps/dpi-backend/src/config.ts +54 -0
  77. package/apps/dpi-backend/src/contract-check.ts +118 -0
  78. package/apps/dpi-backend/src/errors.ts +50 -0
  79. package/apps/dpi-backend/src/extensions.ts +510 -0
  80. package/apps/dpi-backend/src/faux.ts +80 -0
  81. package/apps/dpi-backend/src/history.ts +165 -0
  82. package/apps/dpi-backend/src/idempotency.ts +20 -0
  83. package/apps/dpi-backend/src/jev.ts +43 -0
  84. package/apps/dpi-backend/src/jobs.ts +790 -0
  85. package/apps/dpi-backend/src/main.ts +76 -0
  86. package/apps/dpi-backend/src/model-catalog.ts +180 -0
  87. package/apps/dpi-backend/src/pairing-files.ts +142 -0
  88. package/apps/dpi-backend/src/pairing.ts +459 -0
  89. package/apps/dpi-backend/src/routes.ts +217 -0
  90. package/apps/dpi-backend/src/shared-state.ts +85 -0
  91. package/apps/dpi-backend/src/store.ts +1240 -0
  92. package/apps/dpi-backend/src/streams.ts +256 -0
  93. package/apps/dpi-backend/src/tunnel.ts +955 -0
  94. package/apps/dpi-backend/tsconfig.json +13 -0
  95. package/apps/dpi-playground/README.md +83 -0
  96. package/apps/dpi-playground/apps/dpi-playground-fe/dist/assets/index-CFGc7lHT.js +77 -0
  97. package/apps/dpi-playground/apps/dpi-playground-fe/dist/assets/index-DFdKpwm_.css +1 -0
  98. package/apps/dpi-playground/apps/dpi-playground-fe/dist/icons/anthropic.png +0 -0
  99. package/apps/dpi-playground/apps/dpi-playground-fe/dist/icons/deepseek.png +0 -0
  100. package/apps/dpi-playground/apps/dpi-playground-fe/dist/icons/durable-pi.svg +14 -0
  101. package/apps/dpi-playground/apps/dpi-playground-fe/dist/icons/gemini.png +0 -0
  102. package/apps/dpi-playground/apps/dpi-playground-fe/dist/icons/grok.png +0 -0
  103. package/apps/dpi-playground/apps/dpi-playground-fe/dist/icons/kimi.png +0 -0
  104. package/apps/dpi-playground/apps/dpi-playground-fe/dist/icons/lab.png +0 -0
  105. package/apps/dpi-playground/apps/dpi-playground-fe/dist/icons/meta.png +0 -0
  106. package/apps/dpi-playground/apps/dpi-playground-fe/dist/icons/minimax.png +0 -0
  107. package/apps/dpi-playground/apps/dpi-playground-fe/dist/icons/openai.png +0 -0
  108. package/apps/dpi-playground/apps/dpi-playground-fe/dist/icons/qwen.png +0 -0
  109. package/apps/dpi-playground/apps/dpi-playground-fe/dist/icons/xiaomi.png +0 -0
  110. package/apps/dpi-playground/apps/dpi-playground-fe/dist/icons/zai.png +0 -0
  111. package/apps/dpi-playground/apps/dpi-playground-fe/dist/index.html +16 -0
  112. package/apps/dpi-playground/apps/dpi-playground-fe/index.html +15 -0
  113. package/apps/dpi-playground/apps/dpi-playground-fe/package.json +5 -0
  114. package/apps/dpi-playground/apps/dpi-playground-fe/public/icons/anthropic.png +0 -0
  115. package/apps/dpi-playground/apps/dpi-playground-fe/public/icons/deepseek.png +0 -0
  116. package/apps/dpi-playground/apps/dpi-playground-fe/public/icons/durable-pi.svg +14 -0
  117. package/apps/dpi-playground/apps/dpi-playground-fe/public/icons/gemini.png +0 -0
  118. package/apps/dpi-playground/apps/dpi-playground-fe/public/icons/grok.png +0 -0
  119. package/apps/dpi-playground/apps/dpi-playground-fe/public/icons/kimi.png +0 -0
  120. package/apps/dpi-playground/apps/dpi-playground-fe/public/icons/lab.png +0 -0
  121. package/apps/dpi-playground/apps/dpi-playground-fe/public/icons/meta.png +0 -0
  122. package/apps/dpi-playground/apps/dpi-playground-fe/public/icons/minimax.png +0 -0
  123. package/apps/dpi-playground/apps/dpi-playground-fe/public/icons/openai.png +0 -0
  124. package/apps/dpi-playground/apps/dpi-playground-fe/public/icons/qwen.png +0 -0
  125. package/apps/dpi-playground/apps/dpi-playground-fe/public/icons/xiaomi.png +0 -0
  126. package/apps/dpi-playground/apps/dpi-playground-fe/public/icons/zai.png +0 -0
  127. package/apps/dpi-playground/apps/dpi-playground-fe/src/App.vue +180 -0
  128. package/apps/dpi-playground/apps/dpi-playground-fe/src/client/commands.ts +119 -0
  129. package/apps/dpi-playground/apps/dpi-playground-fe/src/client/composables.ts +129 -0
  130. package/apps/dpi-playground/apps/dpi-playground-fe/src/client/hub.ts +40 -0
  131. package/apps/dpi-playground/apps/dpi-playground-fe/src/client/icon-path.ts +7 -0
  132. package/apps/dpi-playground/apps/dpi-playground-fe/src/client/ids.ts +2 -0
  133. package/apps/dpi-playground/apps/dpi-playground-fe/src/client/jobs.ts +80 -0
  134. package/apps/dpi-playground/apps/dpi-playground-fe/src/client/link-policy.ts +16 -0
  135. package/apps/dpi-playground/apps/dpi-playground-fe/src/client/markdown-policy.ts +115 -0
  136. package/apps/dpi-playground/apps/dpi-playground-fe/src/client/markdown.ts +60 -0
  137. package/apps/dpi-playground/apps/dpi-playground-fe/src/client/pair-close.ts +23 -0
  138. package/apps/dpi-playground/apps/dpi-playground-fe/src/client/pair-handoff.ts +56 -0
  139. package/apps/dpi-playground/apps/dpi-playground-fe/src/client/pair.ts +166 -0
  140. package/apps/dpi-playground/apps/dpi-playground-fe/src/client/qrcode.ts +323 -0
  141. package/apps/dpi-playground/apps/dpi-playground-fe/src/client/signature.ts +146 -0
  142. package/apps/dpi-playground/apps/dpi-playground-fe/src/client/store.ts +373 -0
  143. package/apps/dpi-playground/apps/dpi-playground-fe/src/client/stream.ts +183 -0
  144. package/apps/dpi-playground/apps/dpi-playground-fe/src/components/AgentView.vue +342 -0
  145. package/apps/dpi-playground/apps/dpi-playground-fe/src/components/Composer.vue +198 -0
  146. package/apps/dpi-playground/apps/dpi-playground-fe/src/components/ContextBar.vue +254 -0
  147. package/apps/dpi-playground/apps/dpi-playground-fe/src/components/DeviceCredentialsDialog.vue +40 -0
  148. package/apps/dpi-playground/apps/dpi-playground-fe/src/components/DeviceIcon.vue +56 -0
  149. package/apps/dpi-playground/apps/dpi-playground-fe/src/components/DirBrowser.vue +95 -0
  150. package/apps/dpi-playground/apps/dpi-playground-fe/src/components/ExtensionsPanel.vue +96 -0
  151. package/apps/dpi-playground/apps/dpi-playground-fe/src/components/HelpDialog.vue +69 -0
  152. package/apps/dpi-playground/apps/dpi-playground-fe/src/components/Inspector.vue +425 -0
  153. package/apps/dpi-playground/apps/dpi-playground-fe/src/components/JobCard.vue +221 -0
  154. package/apps/dpi-playground/apps/dpi-playground-fe/src/components/MobileNav.vue +39 -0
  155. package/apps/dpi-playground/apps/dpi-playground-fe/src/components/ModelPicker.vue +71 -0
  156. package/apps/dpi-playground/apps/dpi-playground-fe/src/components/NetworkSidebar.vue +236 -0
  157. package/apps/dpi-playground/apps/dpi-playground-fe/src/components/NewSessionDialog.vue +114 -0
  158. package/apps/dpi-playground/apps/dpi-playground-fe/src/components/OfflinePanel.vue +128 -0
  159. package/apps/dpi-playground/apps/dpi-playground-fe/src/components/PairDeviceWizard.vue +433 -0
  160. package/apps/dpi-playground/apps/dpi-playground-fe/src/components/PromptDialog.vue +89 -0
  161. package/apps/dpi-playground/apps/dpi-playground-fe/src/components/StateEditor.vue +179 -0
  162. package/apps/dpi-playground/apps/dpi-playground-fe/src/components/TasksPanel.vue +280 -0
  163. package/apps/dpi-playground/apps/dpi-playground-fe/src/components/Toast.vue +11 -0
  164. package/apps/dpi-playground/apps/dpi-playground-fe/src/components/TokenManager.vue +175 -0
  165. package/apps/dpi-playground/apps/dpi-playground-fe/src/components/TopBar.vue +114 -0
  166. package/apps/dpi-playground/apps/dpi-playground-fe/src/components/TranscriptItem.vue +152 -0
  167. package/apps/dpi-playground/apps/dpi-playground-fe/src/components/TreeRow.vue +195 -0
  168. package/apps/dpi-playground/apps/dpi-playground-fe/src/components/TunnelStatusBar.vue +107 -0
  169. package/apps/dpi-playground/apps/dpi-playground-fe/src/extensions.css +105 -0
  170. package/apps/dpi-playground/apps/dpi-playground-fe/src/jobs.css +655 -0
  171. package/apps/dpi-playground/apps/dpi-playground-fe/src/main.ts +22 -0
  172. package/apps/dpi-playground/apps/dpi-playground-fe/src/network.css +675 -0
  173. package/apps/dpi-playground/apps/dpi-playground-fe/src/style.css +3203 -0
  174. package/apps/dpi-playground/apps/dpi-playground-fe/tsconfig.json +16 -0
  175. package/apps/dpi-playground/apps/dpi-playground-fe/vite.config.ts +14 -0
  176. package/apps/dpi-playground/apps/dpi-playground-hub/package.json +5 -0
  177. package/apps/dpi-playground/apps/dpi-playground-hub/src/audit.ts +406 -0
  178. package/apps/dpi-playground/apps/dpi-playground-hub/src/client-ip.ts +72 -0
  179. package/apps/dpi-playground/apps/dpi-playground-hub/src/credentials.ts +265 -0
  180. package/apps/dpi-playground/apps/dpi-playground-hub/src/guard.ts +43 -0
  181. package/apps/dpi-playground/apps/dpi-playground-hub/src/http-errors.ts +156 -0
  182. package/apps/dpi-playground/apps/dpi-playground-hub/src/identity.ts +60 -0
  183. package/apps/dpi-playground/apps/dpi-playground-hub/src/limiter.ts +165 -0
  184. package/apps/dpi-playground/apps/dpi-playground-hub/src/main.ts +460 -0
  185. package/apps/dpi-playground/apps/dpi-playground-hub/src/monitor.ts +320 -0
  186. package/apps/dpi-playground/apps/dpi-playground-hub/src/mux.ts +277 -0
  187. package/apps/dpi-playground/apps/dpi-playground-hub/src/pairing.ts +816 -0
  188. package/apps/dpi-playground/apps/dpi-playground-hub/src/proxy.ts +128 -0
  189. package/apps/dpi-playground/apps/dpi-playground-hub/src/rotation.ts +583 -0
  190. package/apps/dpi-playground/apps/dpi-playground-hub/src/seed.ts +11 -0
  191. package/apps/dpi-playground/apps/dpi-playground-hub/src/transport.ts +108 -0
  192. package/apps/dpi-playground/apps/dpi-playground-hub/src/tunnel-token.ts +11 -0
  193. package/apps/dpi-playground/apps/dpi-playground-hub/src/tunnel-transport.ts +560 -0
  194. package/apps/dpi-playground/apps/dpi-playground-hub/src/tunnel.ts +873 -0
  195. package/apps/dpi-playground/apps/dpi-playground-hub/tsconfig.json +13 -0
  196. package/apps/dpi-playground/package.json +37 -0
  197. package/apps/dpi-sdk/README.md +74 -0
  198. package/apps/dpi-sdk/package.json +20 -0
  199. package/apps/dpi-sdk/scripts/net.ts +73 -0
  200. package/apps/dpi-sdk/src/api.ts +631 -0
  201. package/apps/dpi-sdk/src/app-state.ts +111 -0
  202. package/apps/dpi-sdk/src/client.ts +330 -0
  203. package/apps/dpi-sdk/src/errors.ts +16 -0
  204. package/apps/dpi-sdk/src/jobs.ts +99 -0
  205. package/apps/dpi-sdk/src/models.ts +472 -0
  206. package/apps/dpi-sdk/src/node/chunk.ts +488 -0
  207. package/apps/dpi-sdk/src/node/config.ts +175 -0
  208. package/apps/dpi-sdk/src/node/drivers.ts +402 -0
  209. package/apps/dpi-sdk/src/node/ops.ts +114 -0
  210. package/apps/dpi-sdk/src/node/private-dir.ts +48 -0
  211. package/apps/dpi-sdk/src/node/registry-migration.ts +549 -0
  212. package/apps/dpi-sdk/src/node/registry.ts +390 -0
  213. package/apps/dpi-sdk/src/node/sse-writer.ts +151 -0
  214. package/apps/dpi-sdk/src/protocol/backoff.ts +31 -0
  215. package/apps/dpi-sdk/src/protocol/limits.ts +199 -0
  216. package/apps/dpi-sdk/src/protocol/pairing.ts +311 -0
  217. package/apps/dpi-sdk/src/protocol/rest-routes.ts +105 -0
  218. package/apps/dpi-sdk/src/protocol/tunnel.ts +331 -0
  219. package/apps/dpi-sdk/src/sse.ts +117 -0
  220. package/apps/dpi-sdk/src/transcript.ts +291 -0
  221. package/apps/dpi-sdk/tsconfig.json +13 -0
  222. package/apps/dpi-sdk/w +20 -0
  223. package/apps/dpi-tui/README.md +54 -0
  224. package/apps/dpi-tui/bin/dpi-tui.mjs +5 -0
  225. package/apps/dpi-tui/package.json +20 -0
  226. package/apps/dpi-tui/src/app.ts +433 -0
  227. package/apps/dpi-tui/src/commands.ts +151 -0
  228. package/apps/dpi-tui/src/main.ts +122 -0
  229. package/apps/dpi-tui/src/pairing.ts +287 -0
  230. package/apps/dpi-tui/src/pickers.ts +238 -0
  231. package/apps/dpi-tui/src/render.ts +164 -0
  232. package/apps/dpi-tui/src/theme.ts +57 -0
  233. package/apps/dpi-tui/tsconfig.json +14 -0
  234. package/bin/device.mjs +5 -0
  235. package/bin/hub.mjs +5 -0
  236. package/bin/tui.mjs +5 -0
  237. package/deploy/Caddyfile +120 -0
  238. package/docs/verification.md +102 -0
  239. package/dpi-network.example.yaml +62 -0
  240. package/images/01_durable_agent_network.svg +185 -0
  241. package/images/02_pi_made_durable.svg +204 -0
  242. package/images/03_restart_mid_tool.svg +168 -0
  243. package/images/07_one_api_every_client.svg +123 -0
  244. package/images/08_device_session_agent.svg +123 -0
  245. package/images/09_compact_keep_history.svg +143 -0
  246. package/images/10_shared_app_state.svg +177 -0
  247. package/images/11_durable_jobs_wait.svg +141 -0
  248. package/images/12_tools_change_mid_run.svg +231 -0
  249. package/images/13_hook_blocks_tool.svg +122 -0
  250. package/images/14_fork_at_any_entry.svg +119 -0
  251. package/images/15_web_client.png +0 -0
  252. package/images/16_terminal_client.svg +43 -0
  253. package/images/17_devices_are_data.svg +182 -0
  254. package/images/18_should_every_agent_be_durable.svg +118 -0
  255. package/images/19_sqlite_on_every_device.svg +155 -0
  256. package/images/20_admission_and_effects.svg +134 -0
  257. package/justfile +33 -0
  258. package/package.json +41 -4
  259. package/scripts/verify.mjs +210 -0
@@ -0,0 +1,122 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="1200" height="560" viewBox="0 0 1200 560" role="img" aria-labelledby="title desc" font-family="Inter, 'Segoe UI', Helvetica, Arial, sans-serif">
2
+ <title id="title">An extension can block a tool</title>
3
+ <desc id="desc">An agent's bash call to rm -rf build/ reaches the jev-damage-control beforeTool hook, which judges it irreversible, cuts the path to execution and returns a blocked tool result to the agent.</desc>
4
+ <defs>
5
+ <symbol id="g-pi" viewBox="0 0 64 64">
6
+ <rect x="1.5" y="1.5" width="61" height="61" rx="12" fill="#141011" stroke="#FF5252" stroke-width="2.5"/>
7
+ <g fill="#FFFFFF" transform="translate(16 16) scale(8)">
8
+ <path d="M0 0H3V2H2V1H0Z"/>
9
+ <path d="M0 1H1V2H2V3H1V4H0Z"/>
10
+ <path d="M3 2H4V4H3Z"/>
11
+ </g>
12
+ </symbol>
13
+ <marker id="head-red" viewBox="0 0 10 10" refX="7" refY="5" markerWidth="5" markerHeight="5" orient="auto" markerUnits="strokeWidth">
14
+ <path d="M0 0L10 5L0 10Z" fill="#FF5252"/>
15
+ </marker>
16
+ <marker id="head-muted" viewBox="0 0 10 10" refX="7" refY="5" markerWidth="5" markerHeight="5" orient="auto" markerUnits="strokeWidth">
17
+ <path d="M0 0L10 5L0 10Z" fill="#B9AAAE"/>
18
+ </marker>
19
+ <marker id="head-quiet" viewBox="0 0 10 10" refX="7" refY="5" markerWidth="5" markerHeight="5" orient="auto" markerUnits="strokeWidth">
20
+ <path d="M0 0L10 5L0 10Z" fill="#6B5A5E"/>
21
+ </marker>
22
+ </defs>
23
+ <style>
24
+ .mono { font-family: 'JetBrains Mono', 'SF Mono', Menlo, Consolas, monospace; }
25
+ .an { animation-duration: 13s; animation-iteration-count: infinite; animation-timing-function: cubic-bezier(.4, 0, .2, 1); }
26
+ .lin { animation-timing-function: linear; }
27
+ .draw { stroke-dasharray: 1 2; }
28
+ .pop { transform-box: fill-box; transform-origin: center; }
29
+
30
+ /* 1. the agent issues the bash call and it travels to the hook */
31
+ .call { animation-name: call; }
32
+ @keyframes call { 0%, 4% { opacity: 0; transform: translateX(-100px) } 7% { opacity: 1 } 11%, 96% { opacity: 1; transform: translateX(0) } 100% { opacity: 0; transform: translateX(0) } }
33
+ .a1-line { animation-name: a1-line; } .a1-head { animation-name: a1-head; }
34
+ @keyframes a1-line { 0%, 8% { stroke-dashoffset: 1; opacity: 1 } 11%, 96% { stroke-dashoffset: 0; opacity: 1 } 100% { stroke-dashoffset: 0; opacity: 0 } }
35
+ @keyframes a1-head { 0%, 10.5% { opacity: 0 } 11%, 96% { opacity: 1 } 100% { opacity: 0 } }
36
+ .a2-line { animation-name: a2-line; } .a2-head { animation-name: a2-head; }
37
+ @keyframes a2-line { 0%, 12% { stroke-dashoffset: 1; opacity: 1 } 17%, 96% { stroke-dashoffset: 0; opacity: 1 } 100% { stroke-dashoffset: 0; opacity: 0 } }
38
+ @keyframes a2-head { 0%, 16.5% { opacity: 0 } 17%, 96% { opacity: 1 } 100% { opacity: 0 } }
39
+
40
+ /* 2. the hook judges the call */
41
+ .sweep { animation-name: sweep; }
42
+ @keyframes sweep { 0%, 18% { stroke-dashoffset: 1; opacity: 1 } 28%, 96% { stroke-dashoffset: 0; opacity: 1 } 100% { stroke-dashoffset: 0; opacity: 0 } }
43
+ .note { animation-name: note; }
44
+ @keyframes note { 0%, 18% { fill: #B9AAAE } 20%, 28% { fill: #F3EDEE } 31%, 100% { fill: #B9AAAE } }
45
+
46
+ /* 3. the path to execution is cut */
47
+ .cut { animation-name: cut; }
48
+ @keyframes cut { 0%, 29% { opacity: 0; transform: scaleY(0) } 32%, 96% { opacity: 1; transform: scaleY(1) } 100% { opacity: 0; transform: scaleY(1) } }
49
+ .dead { animation-name: dead; } .dead-head { animation-name: dead-head; }
50
+ @keyframes dead { 0%, 29% { stroke: #B9AAAE } 32%, 96% { stroke: #6B5A5E } 100% { stroke: #B9AAAE } }
51
+ @keyframes dead-head { 0%, 29% { opacity: 0 } 32%, 96% { opacity: 1 } 100% { opacity: 0 } }
52
+
53
+ /* 4. the blocked result returns to the agent */
54
+ .r1-line { animation-name: r1-line; }
55
+ @keyframes r1-line { 0%, 34% { stroke-dashoffset: 1; opacity: 1 } 40%, 96% { stroke-dashoffset: 0; opacity: 1 } 100% { stroke-dashoffset: 0; opacity: 0 } }
56
+ .result { animation-name: result; }
57
+ @keyframes result { 0%, 40% { opacity: 0; transform: scale(.94) } 43%, 96% { opacity: 1; transform: scale(1) } 100% { opacity: 0; transform: scale(1) } }
58
+ .why1 { animation-name: why1; } .why2 { animation-name: why2; }
59
+ @keyframes why1 { 0%, 43% { opacity: 0 } 46%, 96% { opacity: 1 } 100% { opacity: 0 } }
60
+ @keyframes why2 { 0%, 45% { opacity: 0 } 48%, 96% { opacity: 1 } 100% { opacity: 0 } }
61
+ .r2-line { animation-name: r2-line; } .r2-head { animation-name: r2-head; }
62
+ @keyframes r2-line { 0%, 48% { stroke-dashoffset: 1; opacity: 1 } 53%, 96% { stroke-dashoffset: 0; opacity: 1 } 100% { stroke-dashoffset: 0; opacity: 0 } }
63
+ @keyframes r2-head { 0%, 52.5% { opacity: 0 } 53%, 96% { opacity: 1 } 100% { opacity: 0 } }
64
+
65
+ @media (prefers-reduced-motion: reduce) { * { animation: none !important; } }
66
+ </style>
67
+
68
+ <rect width="1200" height="560" rx="18" fill="#141011"/>
69
+ <text x="48" y="70" font-size="34" font-weight="600" fill="#F3EDEE">An extension can block a tool</text>
70
+
71
+ <!-- agent -> bash call -> hook -->
72
+ <path class="an draw a1-line" pathLength="1" d="M130 182H164" fill="none" stroke="#B9AAAE" stroke-width="2.5"/>
73
+ <path class="an a1-head" d="M156 182H164" fill="none" stroke="#B9AAAE" stroke-width="2.5" marker-end="url(#head-muted)"/>
74
+ <path class="an draw a2-line" pathLength="1" d="M460 182H502" fill="none" stroke="#B9AAAE" stroke-width="2.5"/>
75
+ <path class="an a2-head" d="M494 182H502" fill="none" stroke="#B9AAAE" stroke-width="2.5" marker-end="url(#head-muted)"/>
76
+
77
+ <g class="an call">
78
+ <rect x="168" y="158" width="290" height="48" rx="8" fill="#21191B" stroke="#443438" stroke-width="2"/>
79
+ <text class="mono" x="184" y="190" font-size="22"><tspan fill="#B9AAAE">bash</tspan><tspan x="263" fill="#F3EDEE">rm -rf build/</tspan></text>
80
+ </g>
81
+ <use href="#g-pi" x="48" y="142" width="80" height="80"/>
82
+
83
+ <!-- beforeTool hook -->
84
+ <rect x="506" y="126" width="342" height="112" rx="12" fill="#21191B" stroke="#443438" stroke-width="2"/>
85
+ <path class="an lin draw sweep" pathLength="1" d="M506 182V138A12 12 0 0 1 518 126H836A12 12 0 0 1 848 138V226A12 12 0 0 1 836 238H518A12 12 0 0 1 506 226Z" fill="none" stroke="#FF5252" stroke-width="2.5"/>
86
+ <text x="526" y="170" font-size="24" font-weight="600" fill="#F3EDEE">beforeTool hook</text>
87
+ <text class="mono" x="526" y="206" font-size="20" fill="#B9AAAE">jev-damage-control</text>
88
+ <rect x="742" y="150" width="44" height="24" rx="12" fill="#FF5252"/>
89
+ <circle cx="774" cy="162" r="8" fill="#F3EDEE"/>
90
+ <text x="796" y="169" font-size="20" font-weight="600" letter-spacing="1" fill="#FF5252">ON</text>
91
+ <text class="an note" x="604" y="280" font-size="22" fill="#B9AAAE">Jev judges each bash call in about 300 ms.</text>
92
+
93
+ <!-- hook -> execution, cut -->
94
+ <path d="M850 182H916" fill="none" stroke="#B9AAAE" stroke-width="2.5"/>
95
+ <path class="an dead" d="M916 182H980" fill="none" stroke="#6B5A5E" stroke-width="2.5"/>
96
+ <path d="M972 182H980" fill="none" stroke="#B9AAAE" stroke-width="2.5" marker-end="url(#head-muted)"/>
97
+ <path class="an dead-head" d="M972 182H980" fill="none" stroke="#6B5A5E" stroke-width="2.5" marker-end="url(#head-quiet)"/>
98
+ <g class="an pop cut">
99
+ <rect x="902" y="170" width="28" height="24" fill="#141011"/>
100
+ <path d="M910 166V198M922 166V198" stroke="#FF5252" stroke-width="4" stroke-linecap="round"/>
101
+ </g>
102
+ <rect x="984" y="158" width="168" height="48" rx="8" fill="#181314" stroke="#3A2E31" stroke-width="2"/>
103
+ <text x="1068" y="189" font-size="20" letter-spacing="1" text-anchor="middle" fill="#6B5A5E">EXECUTION</text>
104
+
105
+ <!-- hook -> blocked result -> agent -->
106
+ <path class="an draw r1-line" pathLength="1" d="M580 239V350H480" fill="none" stroke="#FF5252" stroke-width="2.5"/>
107
+ <path class="an draw r2-line" pathLength="1" d="M160 350H88V226" fill="none" stroke="#FF5252" stroke-width="2.5"/>
108
+ <path class="an r2-head" d="M88 234V226" fill="none" stroke="#FF5252" stroke-width="2.5" marker-end="url(#head-red)"/>
109
+ <g class="an pop result">
110
+ <rect x="160" y="326" width="320" height="48" rx="8" fill="#21191B" stroke="#FF5252" stroke-width="2"/>
111
+ <text x="320" y="357" font-size="20" letter-spacing="1" text-anchor="middle" fill="#F3EDEE">TOOL RESULT<tspan fill="#FF5252"> · BLOCKED</tspan></text>
112
+ </g>
113
+ <g class="mono" font-size="20" fill="#B9AAAE">
114
+ <text class="an why1" x="160" y="410"><tspan fill="#FF5252">irreversible (0.99):</tspan> nothing would</text>
115
+ <text class="an why2" x="160" y="436">restore what this removes or overwrites</text>
116
+ </g>
117
+
118
+ <g font-size="22" fill="#B9AAAE">
119
+ <text x="48" y="500">Extensions add tools, hooks and prompt sections.</text>
120
+ <text x="48" y="530">Toggle one per agent and it applies on the next request.</text>
121
+ </g>
122
+ </svg>
@@ -0,0 +1,119 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 520" width="1200" height="520" role="img" aria-labelledby="title desc" font-family="Inter, 'Segoe UI', Helvetica, Arial, sans-serif">
2
+ <title id="title">Fork at any entry</title>
3
+ <desc id="desc">Main's thread forks at entry #8: the experiment agent starts with a copy of entries 1 to 8, then Main and experiment each add their own entries.</desc>
4
+ <style>
5
+ .mono { font-family: 'JetBrains Mono', 'SF Mono', Menlo, Consolas, monospace; }
6
+ @keyframes e1 { 0%, 3.33% { opacity: 0; transform: scale(0.55) } 4.69% { opacity: 1; transform: scale(1.12) } 5.42%, 96% { opacity: 1; transform: scale(1) } 100% { opacity: 0; transform: scale(0.55) } }
7
+ .e1 { animation: e1 12s ease-in-out infinite; transform-box: fill-box; transform-origin: center; }
8
+ @keyframes e2 { 0%, 5.58% { opacity: 0; transform: scale(0.55) } 6.94% { opacity: 1; transform: scale(1.12) } 7.67%, 96% { opacity: 1; transform: scale(1) } 100% { opacity: 0; transform: scale(0.55) } }
9
+ .e2 { animation: e2 12s ease-in-out infinite; transform-box: fill-box; transform-origin: center; }
10
+ @keyframes e3 { 0%, 7.83% { opacity: 0; transform: scale(0.55) } 9.19% { opacity: 1; transform: scale(1.12) } 9.92%, 96% { opacity: 1; transform: scale(1) } 100% { opacity: 0; transform: scale(0.55) } }
11
+ .e3 { animation: e3 12s ease-in-out infinite; transform-box: fill-box; transform-origin: center; }
12
+ @keyframes e4 { 0%, 10.08% { opacity: 0; transform: scale(0.55) } 11.44% { opacity: 1; transform: scale(1.12) } 12.17%, 96% { opacity: 1; transform: scale(1) } 100% { opacity: 0; transform: scale(0.55) } }
13
+ .e4 { animation: e4 12s ease-in-out infinite; transform-box: fill-box; transform-origin: center; }
14
+ @keyframes e5 { 0%, 12.33% { opacity: 0; transform: scale(0.55) } 13.69% { opacity: 1; transform: scale(1.12) } 14.42%, 96% { opacity: 1; transform: scale(1) } 100% { opacity: 0; transform: scale(0.55) } }
15
+ .e5 { animation: e5 12s ease-in-out infinite; transform-box: fill-box; transform-origin: center; }
16
+ @keyframes e6 { 0%, 14.58% { opacity: 0; transform: scale(0.55) } 15.94% { opacity: 1; transform: scale(1.12) } 16.67%, 96% { opacity: 1; transform: scale(1) } 100% { opacity: 0; transform: scale(0.55) } }
17
+ .e6 { animation: e6 12s ease-in-out infinite; transform-box: fill-box; transform-origin: center; }
18
+ @keyframes e7 { 0%, 16.83% { opacity: 0; transform: scale(0.55) } 18.19% { opacity: 1; transform: scale(1.12) } 18.92%, 96% { opacity: 1; transform: scale(1) } 100% { opacity: 0; transform: scale(0.55) } }
19
+ .e7 { animation: e7 12s ease-in-out infinite; transform-box: fill-box; transform-origin: center; }
20
+ @keyframes e8 { 0%, 19.08% { opacity: 0; transform: scale(0.55) } 20.44% { opacity: 1; transform: scale(1.12) } 21.17%, 96% { opacity: 1; transform: scale(1) } 100% { opacity: 0; transform: scale(0.55) } }
21
+ .e8 { animation: e8 12s ease-in-out infinite; transform-box: fill-box; transform-origin: center; }
22
+ @keyframes red8 { 0%, 22.5% { opacity: 0 } 24.58%, 96% { opacity: 1 } 100% { opacity: 0 } }
23
+ .red8 { animation: red8 12s ease-in-out infinite; }
24
+ @keyframes ring8 { 0%, 22.5% { opacity: 0; transform: scale(0.55) } 24.12% { opacity: 1; transform: scale(1.12) } 25%, 96% { opacity: 1; transform: scale(1) } 100% { opacity: 0; transform: scale(0.55) } }
25
+ .ring8 { animation: ring8 12s ease-in-out infinite; transform-box: fill-box; transform-origin: center; }
26
+ @keyframes lbl8 { 0%, 22.92% { opacity: 0 } 25%, 96% { opacity: 1 } 100% { opacity: 0 } }
27
+ .lbl8 { animation: lbl8 12s ease-in-out infinite; }
28
+ @keyframes cap { 0%, 25.83% { opacity: 0 } 28.75%, 96% { opacity: 1 } 100% { opacity: 0 } }
29
+ .cap { animation: cap 12s ease-in-out infinite; }
30
+ @keyframes branch { 0%, 29.17% { stroke-dashoffset: 341.6 } 36.67%, 96% { stroke-dashoffset: 0 } 100% { stroke-dashoffset: 341.6 } }
31
+ .branch { animation: branch 12s ease-in-out infinite; }
32
+ @keyframes exptile { 0%, 36.25% { opacity: 0; transform: scale(0.7) } 38.15% { opacity: 1; transform: scale(1.12) } 39.17%, 96% { opacity: 1; transform: scale(1) } 100% { opacity: 0; transform: scale(0.7) } }
33
+ .exptile { animation: exptile 12s ease-in-out infinite; transform-box: fill-box; transform-origin: center; }
34
+ @keyframes tree2 { 0%, 36.67% { stroke-dashoffset: 69.0 } 39.17%, 96% { stroke-dashoffset: 0 } 100% { stroke-dashoffset: 69.0 } }
35
+ .tree2 { animation: tree2 12s ease-in-out infinite; }
36
+ @keyframes treeexp { 0%, 38.33% { opacity: 0 } 40.83%, 96% { opacity: 1 } 100% { opacity: 0 } }
37
+ .treeexp { animation: treeexp 12s ease-in-out infinite; }
38
+ @keyframes e9 { 0%, 40.83% { opacity: 0; transform: scale(0.55) } 42.19% { opacity: 1; transform: scale(1.12) } 42.92%, 96% { opacity: 1; transform: scale(1) } 100% { opacity: 0; transform: scale(0.55) } }
39
+ .e9 { animation: e9 12s ease-in-out infinite; transform-box: fill-box; transform-origin: center; }
40
+ @keyframes f1 { 0%, 43.33% { opacity: 0; transform: scale(0.55) } 44.69% { opacity: 1; transform: scale(1.12) } 45.42%, 96% { opacity: 1; transform: scale(1) } 100% { opacity: 0; transform: scale(0.55) } }
41
+ .f1 { animation: f1 12s ease-in-out infinite; transform-box: fill-box; transform-origin: center; }
42
+ @keyframes e10 { 0%, 45.83% { opacity: 0; transform: scale(0.55) } 47.19% { opacity: 1; transform: scale(1.12) } 47.92%, 96% { opacity: 1; transform: scale(1) } 100% { opacity: 0; transform: scale(0.55) } }
43
+ .e10 { animation: e10 12s ease-in-out infinite; transform-box: fill-box; transform-origin: center; }
44
+ @keyframes f2 { 0%, 48.33% { opacity: 0; transform: scale(0.55) } 49.69% { opacity: 1; transform: scale(1.12) } 50.42%, 96% { opacity: 1; transform: scale(1) } 100% { opacity: 0; transform: scale(0.55) } }
45
+ .f2 { animation: f2 12s ease-in-out infinite; transform-box: fill-box; transform-origin: center; }
46
+ @keyframes e11 { 0%, 50.83% { opacity: 0; transform: scale(0.55) } 52.19% { opacity: 1; transform: scale(1.12) } 52.92%, 96% { opacity: 1; transform: scale(1) } 100% { opacity: 0; transform: scale(0.55) } }
47
+ .e11 { animation: e11 12s ease-in-out infinite; transform-box: fill-box; transform-origin: center; }
48
+ @keyframes f3 { 0%, 53.33% { opacity: 0; transform: scale(0.55) } 54.69% { opacity: 1; transform: scale(1.12) } 55.42%, 96% { opacity: 1; transform: scale(1) } 100% { opacity: 0; transform: scale(0.55) } }
49
+ .f3 { animation: f3 12s ease-in-out infinite; transform-box: fill-box; transform-origin: center; }
50
+ @keyframes e12 { 0%, 55.83% { opacity: 0; transform: scale(0.55) } 57.19% { opacity: 1; transform: scale(1.12) } 57.92%, 96% { opacity: 1; transform: scale(1) } 100% { opacity: 0; transform: scale(0.55) } }
51
+ .e12 { animation: e12 12s ease-in-out infinite; transform-box: fill-box; transform-origin: center; }
52
+ @media (prefers-reduced-motion: reduce) { * { animation: none !important; } }
53
+ </style>
54
+ <defs>
55
+ <symbol id="g-pi" viewBox="0 0 64 64">
56
+ <rect x="1.5" y="1.5" width="61" height="61" rx="12" fill="#141011" stroke="#FF5252" stroke-width="2.5"/>
57
+ <g fill="#FFFFFF" transform="translate(16 16) scale(8)">
58
+ <path d="M0 0H3V2H2V1H0Z"/>
59
+ <path d="M0 1H1V2H2V3H1V4H0Z"/>
60
+ <path d="M3 2H4V4H3Z"/>
61
+ </g>
62
+ </symbol>
63
+ <symbol id="g-server" viewBox="0 0 64 44">
64
+ <rect x="3" y="4" width="58" height="16" rx="4" fill="#21191B" stroke="#F3EDEE" stroke-width="2.5"/>
65
+ <rect x="3" y="24" width="58" height="16" rx="4" fill="#21191B" stroke="#F3EDEE" stroke-width="2.5"/>
66
+ <circle cx="13" cy="12" r="2.6" fill="#FF5252"/><circle cx="13" cy="32" r="2.6" fill="#FF5252"/>
67
+ <path d="M36 12 H53 M36 32 H53" stroke="#B9AAAE" stroke-width="2.5" stroke-linecap="round"/>
68
+ </symbol>
69
+ </defs>
70
+
71
+ <rect width="1200" height="520" rx="18" fill="#141011"/>
72
+ <text x="48" y="70" font-size="34" font-weight="600" fill="#F3EDEE">Fork at any entry</text>
73
+
74
+ <!-- Agent tree on the device -->
75
+ <rect x="48" y="148" width="324" height="244" rx="12" fill="#21191B" stroke="#443438" stroke-width="2"/>
76
+ <use href="#g-server" x="72" y="172" width="64" height="44"/>
77
+ <text class="mono" x="152" y="202" font-size="22" fill="#F3EDEE">mac-mini</text>
78
+ <path d="M104 220 V268 H124" stroke="#B9AAAE" stroke-width="2" fill="none"/>
79
+ <use href="#g-pi" x="128" y="248" width="40" height="40"/>
80
+ <text x="182" y="276" font-size="22" fill="#F3EDEE">Main</text>
81
+ <path class="tree2" d="M148 292 V340 H168" stroke="#FF5252" stroke-width="2" fill="none" stroke-dasharray="69"/>
82
+ <g class="treeexp">
83
+ <use href="#g-pi" x="172" y="320" width="40" height="40"/>
84
+ <text x="226" y="348" font-size="22" fill="#F3EDEE">experiment</text>
85
+ </g>
86
+
87
+ <!-- Main thread: the fork copies entries 1..8 -->
88
+ <rect class="cap" x="406" y="166" width="402" height="44" rx="22" fill="#FF5252" fill-opacity=".08" stroke="#FF5252" stroke-opacity=".45" stroke-width="2"/>
89
+ <path d="M392 188 H1032" stroke="#B9AAAE" stroke-width="2.5" fill="none"/>
90
+ <path class="branch" d="M782 205 C782 282 800 328 848 328 H1032" stroke="#FF5252" stroke-width="2.5" fill="none" stroke-dasharray="341.6"/>
91
+ <circle class="e1" cx="432" cy="188" r="11" fill="#141011" stroke="#F3EDEE" stroke-width="2.5"/>
92
+ <circle class="e2" cx="482" cy="188" r="11" fill="#141011" stroke="#F3EDEE" stroke-width="2.5"/>
93
+ <circle class="e3" cx="532" cy="188" r="11" fill="#141011" stroke="#F3EDEE" stroke-width="2.5"/>
94
+ <circle class="e4" cx="582" cy="188" r="11" fill="#141011" stroke="#F3EDEE" stroke-width="2.5"/>
95
+ <circle class="e5" cx="632" cy="188" r="11" fill="#141011" stroke="#F3EDEE" stroke-width="2.5"/>
96
+ <circle class="e6" cx="682" cy="188" r="11" fill="#141011" stroke="#F3EDEE" stroke-width="2.5"/>
97
+ <circle class="e7" cx="732" cy="188" r="11" fill="#141011" stroke="#F3EDEE" stroke-width="2.5"/>
98
+ <circle class="e8" cx="782" cy="188" r="11" fill="#141011" stroke="#F3EDEE" stroke-width="2.5"/>
99
+ <circle class="e9" cx="832" cy="188" r="11" fill="#141011" stroke="#B9AAAE" stroke-width="2.5"/>
100
+ <circle class="e10" cx="882" cy="188" r="11" fill="#141011" stroke="#B9AAAE" stroke-width="2.5"/>
101
+ <circle class="e11" cx="932" cy="188" r="11" fill="#141011" stroke="#B9AAAE" stroke-width="2.5"/>
102
+ <circle class="e12" cx="982" cy="188" r="11" fill="#141011" stroke="#B9AAAE" stroke-width="2.5"/>
103
+ <circle class="red8" cx="782" cy="188" r="11" fill="#FF5252"/>
104
+ <circle class="ring8" cx="782" cy="188" r="17" fill="none" stroke="#FF5252" stroke-width="2.5"/>
105
+ <text class="mono lbl8" x="782" y="152" text-anchor="middle" font-size="22" font-weight="700" fill="#FF5252">#8</text>
106
+ <circle class="f1" cx="882" cy="328" r="11" fill="#FF5252"/>
107
+ <circle class="f2" cx="932" cy="328" r="11" fill="#FF5252"/>
108
+ <circle class="f3" cx="982" cy="328" r="11" fill="#FF5252"/>
109
+
110
+ <use href="#g-pi" x="1040" y="156" width="64" height="64"/>
111
+ <text x="1072" y="252" text-anchor="middle" font-size="22" fill="#F3EDEE">Main</text>
112
+ <g class="exptile">
113
+ <use href="#g-pi" x="1040" y="296" width="64" height="64"/>
114
+ <text x="1072" y="390" text-anchor="middle" font-size="22" fill="#F3EDEE">experiment</text>
115
+ </g>
116
+
117
+ <text x="48" y="458" font-size="22" fill="#B9AAAE">A fork copies the conversation up to that entry.</text>
118
+ <text x="48" y="488" font-size="22" fill="#B9AAAE">Both agents keep their own history from there.</text>
119
+ </svg>
Binary file
@@ -0,0 +1,43 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1106 608" width="1106" height="608" role="img">
2
+ <title>dpi-tui · vm-1:release-notes</title>
3
+ <style>text{font-family:"JetBrains Mono","SF Mono",Menlo,Consolas,"DejaVu Sans Mono",monospace;font-size:15.0px;white-space:pre;font-variant-ligatures:none}</style>
4
+ <rect width="1106" height="608" rx="14" fill="#141011" stroke="#443438" stroke-width="2"/>
5
+ <path d="M1 40 H1105" stroke="#443438" stroke-width="2"/>
6
+ <circle cx="24" cy="20" r="6" fill="#FF5252"/><circle cx="44" cy="20" r="6" fill="#6B5A5E"/><circle cx="64" cy="20" r="6" fill="#6B5A5E"/>
7
+ <text x="553.0" y="25" text-anchor="middle" fill="#B9AAAE" style="font-size:14.0px">dpi-tui · vm-1:release-notes</text>
8
+ <text x="31.0" y="71.2" fill="#ff8997" font-weight="700">dpi</text>
9
+ <text x="58.0" y="71.2" fill="#a08a74"> │ device</text>
10
+ <text x="166.0" y="71.2" fill="#c9a87c">vm-1</text>
11
+ <text x="202.0" y="71.2" fill="#a08a74"> │ workspace</text>
12
+ <text x="337.0" y="71.2" fill="#F3EDEE" font-weight="700">release-notes</text>
13
+ <text x="454.0" y="71.2" fill="#a08a74"> │ agent</text>
14
+ <text x="544.0" y="71.2" fill="#F3EDEE"> Main</text>
15
+ <text x="589.0" y="71.2" fill="#a08a74"> │ </text>
16
+ <text x="634.0" y="71.2" fill="#6fcf97">● live</text>
17
+ <text x="22.0" y="113.8" fill="#8ec5ff">› Make a folder release-notes, write release-notes/changelog.py that prints three short release notes for v1.2.0 as a</text>
18
+ <text x="40.0" y="135.1" fill="#8ec5ff">bulleted list, run it with python3, then tell me in one sentence what you built.</text>
19
+ <text x="22.0" y="177.7" fill="#6fcf97">✓</text>
20
+ <text x="40.0" y="177.7" fill="#F3EDEE" font-weight="700">write</text>
21
+ <text x="94.0" y="177.7" fill="#a08a74">release-notes/changelog.py</text>
22
+ <text x="22.0" y="199.0" fill="#a08a74"> │ </text>
23
+ <text x="58.0" y="199.0" fill="#F3EDEE">Successfully wrote to release-notes/changelog.py</text>
24
+ <text x="22.0" y="241.6" fill="#6fcf97">✓</text>
25
+ <text x="40.0" y="241.6" fill="#F3EDEE" font-weight="700">bash</text>
26
+ <text x="85.0" y="241.6" fill="#a08a74">python3 release-notes/changelog.py</text>
27
+ <text x="22.0" y="262.9" fill="#a08a74"> │ </text>
28
+ <text x="58.0" y="262.9" fill="#F3EDEE">Release notes for v1.2.0</text>
29
+ <text x="22.0" y="284.2" fill="#a08a74"> │ </text>
30
+ <text x="58.0" y="284.2" fill="#F3EDEE">- Added dark mode to the settings panel.</text>
31
+ <text x="22.0" y="305.5" fill="#a08a74"> │ </text>
32
+ <text x="58.0" y="305.5" fill="#F3EDEE">- Improved startup time by 30%.</text>
33
+ <text x="22.0" y="326.8" fill="#a08a74"> │ </text>
34
+ <text x="58.0" y="326.8" fill="#F3EDEE">- Fixed a crash when importing empty files.</text>
35
+ <text x="22.0" y="369.4" fill="#F3EDEE">I created </text>
36
+ <text x="112.0" y="369.4" fill="#c9a87c">release-notes/changelog.py</text>
37
+ <text x="346.0" y="369.4" fill="#F3EDEE">, which prints a &quot;Release notes for v1.2.0&quot; heading and three bulleted notes, and</text>
38
+ <text x="22.0" y="390.7" fill="#F3EDEE">I ran it with python3 to confirm the output.</text>
39
+ <text x="22.0" y="518.5" fill="#a08a74">──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────</text>
40
+ <rect x="31.0" y="524.0" width="9.6" height="21.8" fill="#F3EDEE"/>
41
+ <text x="22.0" y="561.1" fill="#a08a74">──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────</text>
42
+ <text x="31.0" y="582.4" fill="#a08a74">claude-sonnet-5.5: low │ 99% CTX │ 13.7K tok $0.027 │ /help</text>
43
+ </svg>
@@ -0,0 +1,182 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 600" width="1200" height="600" role="img" aria-labelledby="title desc" font-family="Inter, 'Segoe UI', Helvetica, Arial, sans-serif">
2
+ <title id="title">Devices are data</title>
3
+ <desc id="desc">Each device is one row of dpi-network.yaml, an id and a kind, and just net up &lt;id&gt; runs that kind's steps (local: env, register; ssh: provision, deploy, env, service, register; exe-dev adds create and expose) until the device is online.</desc>
4
+ <style>
5
+ .a { animation-duration: 14s; animation-iteration-count: infinite; animation-timing-function: ease-in-out; }
6
+ .ln { animation-timing-function: linear; }
7
+ .x { transform-box: fill-box; transform-origin: center; }
8
+ @keyframes cmd { 0%, 2.5% { opacity: 0; transform: translateY(-8px) } 6.43%, 96% { opacity: 1; transform: translateY(0) } 100% { opacity: 0; transform: translateY(0) } }
9
+ @keyframes leg { 0%, 44.29% { opacity: 0 } 46.43%, 96% { opacity: 1 } 100% { opacity: 0 } }
10
+ @keyframes cap { 0%, 57.14% { opacity: 0 } 61.43%, 96% { opacity: 1 } 100% { opacity: 0 } }
11
+ @keyframes bar0 { 0%, 8.57% { fill: #443438 } 10.71%, 96% { fill: #FF5252 } 100% { fill: #443438 } }
12
+ @keyframes kd0 { 0%, 8.57% { fill: #B9AAAE } 10.71%, 96% { fill: #FF5252 } 100% { fill: #B9AAAE } }
13
+ @keyframes ln0 { 0%, 8.57% { stroke-dashoffset: 1; stroke-opacity: 1; opacity: 1 } 44.29% { stroke-dashoffset: 0; stroke-opacity: 1; opacity: 1 } 48.57%, 96% { stroke-dashoffset: 0; stroke-opacity: .55; opacity: 1 } 100% { stroke-dashoffset: 0; stroke-opacity: .55; opacity: 0 } }
14
+ @keyframes ah0 { 0%, 43.93% { opacity: 0 } 44.29% { opacity: 1 } 48.57%, 96% { opacity: .55 } 100% { opacity: 0 } }
15
+ @keyframes dv0 { 0%, 44.29% { stroke: #443438; fill: #21191B } 46.07%, 96% { stroke: #FF5252; fill: #3A1D20 } 100% { stroke: #443438; fill: #21191B } }
16
+ @keyframes dg0 { 0%, 44.29% { opacity: .35 } 46.07%, 96% { opacity: 1 } 100% { opacity: .35 } }
17
+ @keyframes od0 { 0%, 45% { opacity: 0; transform: scale(.2) } 46.43% { opacity: 1; transform: scale(1.35) } 47.86%, 96% { opacity: 1; transform: scale(1) } 100% { opacity: 0; transform: scale(.2) } }
18
+ @keyframes k00 { 0%, 25.2% { stroke: #6B5A5E; fill: #21191B; transform: scale(1) } 26.28% { stroke: #FF5252; fill: #3A1D20; transform: scale(1.12) } 28.78% { stroke: #FF5252; fill: #3A1D20; transform: scale(1) } 31.63%, 96% { stroke: #FF5252; fill: #21191B; transform: scale(1) } 100% { stroke: #6B5A5E; fill: #21191B; transform: scale(1) } }
19
+ @keyframes k01 { 0%, 40.36% { stroke: #6B5A5E; fill: #21191B; transform: scale(1) } 41.43% { stroke: #FF5252; fill: #3A1D20; transform: scale(1.12) } 43.93% { stroke: #FF5252; fill: #3A1D20; transform: scale(1) } 46.79%, 96% { stroke: #FF5252; fill: #21191B; transform: scale(1) } 100% { stroke: #6B5A5E; fill: #21191B; transform: scale(1) } }
20
+ @keyframes bar1 { 0%, 12.14% { fill: #443438 } 14.29%, 96% { fill: #FF5252 } 100% { fill: #443438 } }
21
+ @keyframes kd1 { 0%, 12.14% { fill: #B9AAAE } 14.29%, 96% { fill: #FF5252 } 100% { fill: #B9AAAE } }
22
+ @keyframes ln1 { 0%, 12.14% { stroke-dashoffset: 1; stroke-opacity: 1; opacity: 1 } 47.86% { stroke-dashoffset: 0; stroke-opacity: 1; opacity: 1 } 52.14%, 96% { stroke-dashoffset: 0; stroke-opacity: .55; opacity: 1 } 100% { stroke-dashoffset: 0; stroke-opacity: .55; opacity: 0 } }
23
+ @keyframes ah1 { 0%, 47.5% { opacity: 0 } 47.86% { opacity: 1 } 52.14%, 96% { opacity: .55 } 100% { opacity: 0 } }
24
+ @keyframes dv1 { 0%, 47.86% { stroke: #443438; fill: #21191B } 49.64%, 96% { stroke: #FF5252; fill: #3A1D20 } 100% { stroke: #443438; fill: #21191B } }
25
+ @keyframes dg1 { 0%, 47.86% { opacity: .35 } 49.64%, 96% { opacity: 1 } 100% { opacity: .35 } }
26
+ @keyframes od1 { 0%, 48.57% { opacity: 0; transform: scale(.2) } 50% { opacity: 1; transform: scale(1.35) } 51.43%, 96% { opacity: 1; transform: scale(1) } 100% { opacity: 0; transform: scale(.2) } }
27
+ @keyframes k10 { 0%, 18.67% { stroke: #6B5A5E; fill: #21191B; transform: scale(1) } 19.74% { stroke: #FF5252; fill: #3A1D20; transform: scale(1.12) } 22.24% { stroke: #FF5252; fill: #3A1D20; transform: scale(1) } 25.1%, 96% { stroke: #FF5252; fill: #21191B; transform: scale(1) } 100% { stroke: #6B5A5E; fill: #21191B; transform: scale(1) } }
28
+ @keyframes k11 { 0%, 23.72% { stroke: #6B5A5E; fill: #21191B; transform: scale(1) } 24.8% { stroke: #FF5252; fill: #3A1D20; transform: scale(1.12) } 27.3% { stroke: #FF5252; fill: #3A1D20; transform: scale(1) } 30.15%, 96% { stroke: #FF5252; fill: #21191B; transform: scale(1) } 100% { stroke: #6B5A5E; fill: #21191B; transform: scale(1) } }
29
+ @keyframes k12 { 0%, 28.78% { stroke: #6B5A5E; fill: #21191B; transform: scale(1) } 29.85% { stroke: #FF5252; fill: #3A1D20; transform: scale(1.12) } 32.35% { stroke: #FF5252; fill: #3A1D20; transform: scale(1) } 35.2%, 96% { stroke: #FF5252; fill: #21191B; transform: scale(1) } 100% { stroke: #6B5A5E; fill: #21191B; transform: scale(1) } }
30
+ @keyframes k13 { 0%, 33.83% { stroke: #6B5A5E; fill: #21191B; transform: scale(1) } 34.9% { stroke: #FF5252; fill: #3A1D20; transform: scale(1.12) } 37.4% { stroke: #FF5252; fill: #3A1D20; transform: scale(1) } 40.26%, 96% { stroke: #FF5252; fill: #21191B; transform: scale(1) } 100% { stroke: #6B5A5E; fill: #21191B; transform: scale(1) } }
31
+ @keyframes k14 { 0%, 43.93% { stroke: #6B5A5E; fill: #21191B; transform: scale(1) } 45% { stroke: #FF5252; fill: #3A1D20; transform: scale(1.12) } 47.5% { stroke: #FF5252; fill: #3A1D20; transform: scale(1) } 50.36%, 96% { stroke: #FF5252; fill: #21191B; transform: scale(1) } 100% { stroke: #6B5A5E; fill: #21191B; transform: scale(1) } }
32
+ @keyframes bar2 { 0%, 15.71% { fill: #443438 } 17.86%, 96% { fill: #FF5252 } 100% { fill: #443438 } }
33
+ @keyframes kd2 { 0%, 15.71% { fill: #B9AAAE } 17.86%, 96% { fill: #FF5252 } 100% { fill: #B9AAAE } }
34
+ @keyframes ln2 { 0%, 15.71% { stroke-dashoffset: 1; stroke-opacity: 1; opacity: 1 } 51.43% { stroke-dashoffset: 0; stroke-opacity: 1; opacity: 1 } 55.71%, 96% { stroke-dashoffset: 0; stroke-opacity: .55; opacity: 1 } 100% { stroke-dashoffset: 0; stroke-opacity: .55; opacity: 0 } }
35
+ @keyframes ah2 { 0%, 51.07% { opacity: 0 } 51.43% { opacity: 1 } 55.71%, 96% { opacity: .55 } 100% { opacity: 0 } }
36
+ @keyframes dv2 { 0%, 51.43% { stroke: #443438; fill: #21191B } 53.21%, 96% { stroke: #FF5252; fill: #3A1D20 } 100% { stroke: #443438; fill: #21191B } }
37
+ @keyframes dg2 { 0%, 51.43% { opacity: .35 } 53.21%, 96% { opacity: 1 } 100% { opacity: .35 } }
38
+ @keyframes od2 { 0%, 52.14% { opacity: 0; transform: scale(.2) } 53.57% { opacity: 1; transform: scale(1.35) } 55%, 96% { opacity: 1; transform: scale(1) } 100% { opacity: 0; transform: scale(.2) } }
39
+ @keyframes k20 { 0%, 22.24% { stroke: #6B5A5E; fill: #21191B; transform: scale(1) } 23.32% { stroke: #FF5252; fill: #3A1D20; transform: scale(1.12) } 25.82% { stroke: #FF5252; fill: #3A1D20; transform: scale(1) } 28.67%, 96% { stroke: #FF5252; fill: #21191B; transform: scale(1) } 100% { stroke: #6B5A5E; fill: #21191B; transform: scale(1) } }
40
+ @keyframes k21 { 0%, 27.3% { stroke: #6B5A5E; fill: #21191B; transform: scale(1) } 28.37% { stroke: #FF5252; fill: #3A1D20; transform: scale(1.12) } 30.87% { stroke: #FF5252; fill: #3A1D20; transform: scale(1) } 33.72%, 96% { stroke: #FF5252; fill: #21191B; transform: scale(1) } 100% { stroke: #6B5A5E; fill: #21191B; transform: scale(1) } }
41
+ @keyframes k22 { 0%, 32.35% { stroke: #6B5A5E; fill: #21191B; transform: scale(1) } 33.42% { stroke: #FF5252; fill: #3A1D20; transform: scale(1.12) } 35.92% { stroke: #FF5252; fill: #3A1D20; transform: scale(1) } 38.78%, 96% { stroke: #FF5252; fill: #21191B; transform: scale(1) } 100% { stroke: #6B5A5E; fill: #21191B; transform: scale(1) } }
42
+ @keyframes k23 { 0%, 37.4% { stroke: #6B5A5E; fill: #21191B; transform: scale(1) } 38.47% { stroke: #FF5252; fill: #3A1D20; transform: scale(1.12) } 40.97% { stroke: #FF5252; fill: #3A1D20; transform: scale(1) } 43.83%, 96% { stroke: #FF5252; fill: #21191B; transform: scale(1) } 100% { stroke: #6B5A5E; fill: #21191B; transform: scale(1) } }
43
+ @keyframes k24 { 0%, 47.5% { stroke: #6B5A5E; fill: #21191B; transform: scale(1) } 48.57% { stroke: #FF5252; fill: #3A1D20; transform: scale(1.12) } 51.07% { stroke: #FF5252; fill: #3A1D20; transform: scale(1) } 53.93%, 96% { stroke: #FF5252; fill: #21191B; transform: scale(1) } 100% { stroke: #6B5A5E; fill: #21191B; transform: scale(1) } }
44
+ @keyframes bar3 { 0%, 19.29% { fill: #443438 } 21.43%, 96% { fill: #FF5252 } 100% { fill: #443438 } }
45
+ @keyframes kd3 { 0%, 19.29% { fill: #B9AAAE } 21.43%, 96% { fill: #FF5252 } 100% { fill: #B9AAAE } }
46
+ @keyframes ln3 { 0%, 19.29% { stroke-dashoffset: 1; stroke-opacity: 1; opacity: 1 } 55% { stroke-dashoffset: 0; stroke-opacity: 1; opacity: 1 } 59.29%, 96% { stroke-dashoffset: 0; stroke-opacity: .55; opacity: 1 } 100% { stroke-dashoffset: 0; stroke-opacity: .55; opacity: 0 } }
47
+ @keyframes ah3 { 0%, 54.64% { opacity: 0 } 55% { opacity: 1 } 59.29%, 96% { opacity: .55 } 100% { opacity: 0 } }
48
+ @keyframes dv3 { 0%, 55% { stroke: #443438; fill: #21191B } 56.79%, 96% { stroke: #FF5252; fill: #3A1D20 } 100% { stroke: #443438; fill: #21191B } }
49
+ @keyframes dg3 { 0%, 55% { opacity: .35 } 56.79%, 96% { opacity: 1 } 100% { opacity: .35 } }
50
+ @keyframes od3 { 0%, 55.71% { opacity: 0; transform: scale(.2) } 57.14% { opacity: 1; transform: scale(1.35) } 58.57%, 96% { opacity: 1; transform: scale(1) } 100% { opacity: 0; transform: scale(.2) } }
51
+ @keyframes k30 { 0%, 20.77% { stroke: #6B5A5E; fill: #21191B; transform: scale(1) } 21.84% { stroke: #FF5252; fill: #3A1D20; transform: scale(1.12) } 24.34% { stroke: #FF5252; fill: #3A1D20; transform: scale(1) } 27.19%, 96% { stroke: #FF5252; fill: #21191B; transform: scale(1) } 100% { stroke: #6B5A5E; fill: #21191B; transform: scale(1) } }
52
+ @keyframes k31 { 0%, 25.82% { stroke: #6B5A5E; fill: #21191B; transform: scale(1) } 26.89% { stroke: #FF5252; fill: #3A1D20; transform: scale(1.12) } 29.39% { stroke: #FF5252; fill: #3A1D20; transform: scale(1) } 32.24%, 96% { stroke: #FF5252; fill: #21191B; transform: scale(1) } 100% { stroke: #6B5A5E; fill: #21191B; transform: scale(1) } }
53
+ @keyframes k32 { 0%, 30.87% { stroke: #6B5A5E; fill: #21191B; transform: scale(1) } 31.94% { stroke: #FF5252; fill: #3A1D20; transform: scale(1.12) } 34.44% { stroke: #FF5252; fill: #3A1D20; transform: scale(1) } 37.3%, 96% { stroke: #FF5252; fill: #21191B; transform: scale(1) } 100% { stroke: #6B5A5E; fill: #21191B; transform: scale(1) } }
54
+ @keyframes k33 { 0%, 35.92% { stroke: #6B5A5E; fill: #21191B; transform: scale(1) } 36.99% { stroke: #FF5252; fill: #3A1D20; transform: scale(1.12) } 39.49% { stroke: #FF5252; fill: #3A1D20; transform: scale(1) } 42.35%, 96% { stroke: #FF5252; fill: #21191B; transform: scale(1) } 100% { stroke: #6B5A5E; fill: #21191B; transform: scale(1) } }
55
+ @keyframes k34 { 0%, 40.97% { stroke: #6B5A5E; fill: #21191B; transform: scale(1) } 42.04% { stroke: #FF5252; fill: #3A1D20; transform: scale(1.12) } 44.54% { stroke: #FF5252; fill: #3A1D20; transform: scale(1) } 47.4%, 96% { stroke: #FF5252; fill: #21191B; transform: scale(1) } 100% { stroke: #6B5A5E; fill: #21191B; transform: scale(1) } }
56
+ @keyframes k35 { 0%, 46.02% { stroke: #6B5A5E; fill: #21191B; transform: scale(1) } 47.09% { stroke: #FF5252; fill: #3A1D20; transform: scale(1.12) } 49.59% { stroke: #FF5252; fill: #3A1D20; transform: scale(1) } 52.45%, 96% { stroke: #FF5252; fill: #21191B; transform: scale(1) } 100% { stroke: #6B5A5E; fill: #21191B; transform: scale(1) } }
57
+ @keyframes k36 { 0%, 51.07% { stroke: #6B5A5E; fill: #21191B; transform: scale(1) } 52.14% { stroke: #FF5252; fill: #3A1D20; transform: scale(1.12) } 54.64% { stroke: #FF5252; fill: #3A1D20; transform: scale(1) } 57.5%, 96% { stroke: #FF5252; fill: #21191B; transform: scale(1) } 100% { stroke: #6B5A5E; fill: #21191B; transform: scale(1) } }
58
+ @media (prefers-reduced-motion: reduce) { * { animation: none !important; } }
59
+ </style>
60
+ <defs>
61
+ <symbol id="g-laptop" viewBox="0 0 64 44">
62
+ <rect x="9" y="3" width="46" height="30" rx="4" fill="#21191B" stroke="#F3EDEE" stroke-width="2.5"/>
63
+ <rect x="15" y="9" width="34" height="18" rx="2" fill="#141011"/>
64
+ <path d="M2 37 H62 L58 42 H6 Z" fill="#F3EDEE"/>
65
+ </symbol>
66
+ <symbol id="g-server" viewBox="0 0 64 44">
67
+ <rect x="3" y="4" width="58" height="16" rx="4" fill="#21191B" stroke="#F3EDEE" stroke-width="2.5"/>
68
+ <rect x="3" y="24" width="58" height="16" rx="4" fill="#21191B" stroke="#F3EDEE" stroke-width="2.5"/>
69
+ <circle cx="13" cy="12" r="2.6" fill="#FF5252"/><circle cx="13" cy="32" r="2.6" fill="#FF5252"/>
70
+ <path d="M36 12 H53 M36 32 H53" stroke="#B9AAAE" stroke-width="2.5" stroke-linecap="round"/>
71
+ </symbol>
72
+ <symbol id="g-cloud" viewBox="0 0 64 44">
73
+ <path d="M18 38 H48 A11 11 0 0 0 48 16 A15 15 0 0 0 20 13 A12.5 12.5 0 0 0 18 38 Z" fill="#21191B" stroke="#F3EDEE" stroke-width="2.5" stroke-linejoin="round"/>
74
+ <rect x="25" y="22" width="16" height="10" rx="2" fill="none" stroke="#FF5252" stroke-width="2.2"/>
75
+ </symbol>
76
+ <!-- Step icons, 24x24 line drawings. They take their stroke colour from the chip. -->
77
+ <symbol id="i-create" viewBox="0 0 24 24"><g fill="none" stroke-width="2.2" stroke-linecap="round"><path d="M12 4.5 V19.5 M 4.5 12 H19.5"/></g></symbol>
78
+ <symbol id="i-provision" viewBox="0 0 24 24"><g fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3 L20 7.5 V16.5 L12 21 L4 16.5 V7.5 Z M 4 7.5 L12 12 L20 7.5 M12 12 V21"/></g></symbol>
79
+ <symbol id="i-deploy" viewBox="0 0 24 24"><g fill="none" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3.5 V14.5 M7.5 10 L12 14.5 L16.5 10 M 4.5 14.5 V19.5 H19.5 V14.5"/></g></symbol>
80
+ <symbol id="i-env" viewBox="0 0 24 24"><g fill="none" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><circle cx="7.5" cy="12" r="4"/><path d="M11.5 12 H21 M17.5 12 V15.5 M21 12 V14.5"/></g></symbol>
81
+ <symbol id="i-service" viewBox="0 0 24 24"><g fill="none" stroke-width="2.2" stroke-linecap="round"><path d="M12 3.5 V11.5 M7.18 7.25 A7.5 7.5 0 1 0 16.82 7.25"/></g></symbol>
82
+ <symbol id="i-expose" viewBox="0 0 24 24"><g fill="none" stroke-width="2" stroke-linecap="round"><circle cx="12" cy="12" r="8.5"/><ellipse cx="12" cy="12" rx="3.6" ry="8.5"/><path d="M3.5 12 H20.5"/></g></symbol>
83
+ <symbol id="i-register" viewBox="0 0 24 24"><g fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><ellipse cx="12" cy="6" rx="7" ry="2.8"/><path d="M5 6 V18 A7 2.8 0 0 0 19 18 V6 M5 12 A7 2.8 0 0 0 19 12"/></g></symbol>
84
+ </defs>
85
+ <rect width="1200" height="600" rx="18" fill="#141011"/>
86
+ <text x="48" y="70" font-size="34" font-weight="600" fill="#F3EDEE">Devices are data</text>
87
+
88
+ <rect x="330" y="160" width="822" height="64" rx="12" fill="#1B1517"/>
89
+ <path d="M351 192 H1051" fill="none" stroke="#443438" stroke-width="2.5"/>
90
+ <path d="M1050 186 L1061 192 L1050 198 Z" fill="#443438"/>
91
+ <path class="a ln" style="animation-name: ln0" d="M351 192 H1051" pathLength="1" stroke-dasharray="1 1" fill="none" stroke="#FF5252" stroke-opacity=".55" stroke-width="2.5"/>
92
+ <path class="a" style="animation-name: ah0" d="M1050 186 L1061 192 L1050 198 Z" fill="#FF5252" opacity=".55"/>
93
+ <g class="a x" style="animation-name: k00" stroke="#FF5252" fill="#21191B"><rect x="677" y="168" width="48" height="48" rx="10" stroke-width="2"/><use href="#i-env" x="686" y="177" width="30" height="30"/></g>
94
+ <g class="a x" style="animation-name: k01" stroke="#FF5252" fill="#21191B"><rect x="974" y="168" width="48" height="48" rx="10" stroke-width="2"/><use href="#i-register" x="983" y="177" width="30" height="30"/></g>
95
+ <rect class="a" style="animation-name: dv0" x="1066" y="162" width="80" height="60" rx="12" fill="#3A1D20" stroke="#FF5252" stroke-width="2.5"/>
96
+ <g class="a" style="animation-name: dg0"><use href="#g-laptop" x="1077" y="172" width="58" height="40"/></g>
97
+ <circle class="a x" style="animation-name: od0" cx="1143" cy="165" r="7" fill="#FF5252" stroke="#141011" stroke-width="3"/>
98
+ <rect x="330" y="236" width="822" height="64" rx="12" fill="#1B1517"/>
99
+ <path d="M351 268 H1051" fill="none" stroke="#443438" stroke-width="2.5"/>
100
+ <path d="M1050 262 L1061 268 L1050 274 Z" fill="#443438"/>
101
+ <path class="a ln" style="animation-name: ln1" d="M351 268 H1051" pathLength="1" stroke-dasharray="1 1" fill="none" stroke="#FF5252" stroke-opacity=".55" stroke-width="2.5"/>
102
+ <path class="a" style="animation-name: ah1" d="M1050 262 L1061 268 L1050 274 Z" fill="#FF5252" opacity=".55"/>
103
+ <g class="a x" style="animation-name: k10" stroke="#FF5252" fill="#21191B"><rect x="479" y="244" width="48" height="48" rx="10" stroke-width="2"/><use href="#i-provision" x="488" y="253" width="30" height="30"/></g>
104
+ <g class="a x" style="animation-name: k11" stroke="#FF5252" fill="#21191B"><rect x="578" y="244" width="48" height="48" rx="10" stroke-width="2"/><use href="#i-deploy" x="587" y="253" width="30" height="30"/></g>
105
+ <g class="a x" style="animation-name: k12" stroke="#FF5252" fill="#21191B"><rect x="677" y="244" width="48" height="48" rx="10" stroke-width="2"/><use href="#i-env" x="686" y="253" width="30" height="30"/></g>
106
+ <g class="a x" style="animation-name: k13" stroke="#FF5252" fill="#21191B"><rect x="776" y="244" width="48" height="48" rx="10" stroke-width="2"/><use href="#i-service" x="785" y="253" width="30" height="30"/></g>
107
+ <g class="a x" style="animation-name: k14" stroke="#FF5252" fill="#21191B"><rect x="974" y="244" width="48" height="48" rx="10" stroke-width="2"/><use href="#i-register" x="983" y="253" width="30" height="30"/></g>
108
+ <rect class="a" style="animation-name: dv1" x="1066" y="238" width="80" height="60" rx="12" fill="#3A1D20" stroke="#FF5252" stroke-width="2.5"/>
109
+ <g class="a" style="animation-name: dg1"><use href="#g-server" x="1077" y="248" width="58" height="40"/></g>
110
+ <circle class="a x" style="animation-name: od1" cx="1143" cy="241" r="7" fill="#FF5252" stroke="#141011" stroke-width="3"/>
111
+ <rect x="330" y="312" width="822" height="64" rx="12" fill="#1B1517"/>
112
+ <path d="M351 344 H1051" fill="none" stroke="#443438" stroke-width="2.5"/>
113
+ <path d="M1050 338 L1061 344 L1050 350 Z" fill="#443438"/>
114
+ <path class="a ln" style="animation-name: ln2" d="M351 344 H1051" pathLength="1" stroke-dasharray="1 1" fill="none" stroke="#FF5252" stroke-opacity=".55" stroke-width="2.5"/>
115
+ <path class="a" style="animation-name: ah2" d="M1050 338 L1061 344 L1050 350 Z" fill="#FF5252" opacity=".55"/>
116
+ <g class="a x" style="animation-name: k20" stroke="#FF5252" fill="#21191B"><rect x="479" y="320" width="48" height="48" rx="10" stroke-width="2"/><use href="#i-provision" x="488" y="329" width="30" height="30"/></g>
117
+ <g class="a x" style="animation-name: k21" stroke="#FF5252" fill="#21191B"><rect x="578" y="320" width="48" height="48" rx="10" stroke-width="2"/><use href="#i-deploy" x="587" y="329" width="30" height="30"/></g>
118
+ <g class="a x" style="animation-name: k22" stroke="#FF5252" fill="#21191B"><rect x="677" y="320" width="48" height="48" rx="10" stroke-width="2"/><use href="#i-env" x="686" y="329" width="30" height="30"/></g>
119
+ <g class="a x" style="animation-name: k23" stroke="#FF5252" fill="#21191B"><rect x="776" y="320" width="48" height="48" rx="10" stroke-width="2"/><use href="#i-service" x="785" y="329" width="30" height="30"/></g>
120
+ <g class="a x" style="animation-name: k24" stroke="#FF5252" fill="#21191B"><rect x="974" y="320" width="48" height="48" rx="10" stroke-width="2"/><use href="#i-register" x="983" y="329" width="30" height="30"/></g>
121
+ <rect class="a" style="animation-name: dv2" x="1066" y="314" width="80" height="60" rx="12" fill="#3A1D20" stroke="#FF5252" stroke-width="2.5"/>
122
+ <g class="a" style="animation-name: dg2"><use href="#g-server" x="1077" y="324" width="58" height="40"/></g>
123
+ <circle class="a x" style="animation-name: od2" cx="1143" cy="317" r="7" fill="#FF5252" stroke="#141011" stroke-width="3"/>
124
+ <rect x="330" y="388" width="822" height="64" rx="12" fill="#1B1517"/>
125
+ <path d="M351 420 H1051" fill="none" stroke="#443438" stroke-width="2.5"/>
126
+ <path d="M1050 414 L1061 420 L1050 426 Z" fill="#443438"/>
127
+ <path class="a ln" style="animation-name: ln3" d="M351 420 H1051" pathLength="1" stroke-dasharray="1 1" fill="none" stroke="#FF5252" stroke-opacity=".55" stroke-width="2.5"/>
128
+ <path class="a" style="animation-name: ah3" d="M1050 414 L1061 420 L1050 426 Z" fill="#FF5252" opacity=".55"/>
129
+ <g class="a x" style="animation-name: k30" stroke="#FF5252" fill="#21191B"><rect x="380" y="396" width="48" height="48" rx="10" stroke-width="2"/><use href="#i-create" x="389" y="405" width="30" height="30"/></g>
130
+ <g class="a x" style="animation-name: k31" stroke="#FF5252" fill="#21191B"><rect x="479" y="396" width="48" height="48" rx="10" stroke-width="2"/><use href="#i-provision" x="488" y="405" width="30" height="30"/></g>
131
+ <g class="a x" style="animation-name: k32" stroke="#FF5252" fill="#21191B"><rect x="578" y="396" width="48" height="48" rx="10" stroke-width="2"/><use href="#i-deploy" x="587" y="405" width="30" height="30"/></g>
132
+ <g class="a x" style="animation-name: k33" stroke="#FF5252" fill="#21191B"><rect x="677" y="396" width="48" height="48" rx="10" stroke-width="2"/><use href="#i-env" x="686" y="405" width="30" height="30"/></g>
133
+ <g class="a x" style="animation-name: k34" stroke="#FF5252" fill="#21191B"><rect x="776" y="396" width="48" height="48" rx="10" stroke-width="2"/><use href="#i-service" x="785" y="405" width="30" height="30"/></g>
134
+ <g class="a x" style="animation-name: k35" stroke="#FF5252" fill="#21191B"><rect x="875" y="396" width="48" height="48" rx="10" stroke-width="2"/><use href="#i-expose" x="884" y="405" width="30" height="30"/></g>
135
+ <g class="a x" style="animation-name: k36" stroke="#FF5252" fill="#21191B"><rect x="974" y="396" width="48" height="48" rx="10" stroke-width="2"/><use href="#i-register" x="983" y="405" width="30" height="30"/></g>
136
+ <rect class="a" style="animation-name: dv3" x="1066" y="390" width="80" height="60" rx="12" fill="#3A1D20" stroke="#FF5252" stroke-width="2.5"/>
137
+ <g class="a" style="animation-name: dg3"><use href="#g-cloud" x="1077" y="400" width="58" height="40"/></g>
138
+ <circle class="a x" style="animation-name: od3" cx="1143" cy="393" r="7" fill="#FF5252" stroke="#141011" stroke-width="3"/>
139
+
140
+ <g>
141
+ <rect x="48" y="100" width="302" height="358" rx="12" fill="#21191B" stroke="#443438" stroke-width="2"/>
142
+ <text x="74" y="135" font-family="'JetBrains Mono', 'SF Mono', Menlo, Consolas, monospace" font-size="22" fill="#B9AAAE">dpi-network.yaml</text>
143
+ <path d="M 49 154 H349" stroke="#443438" stroke-width="2"/>
144
+ <path d="M 64 230 H334 M 64 306 H334 M 64 382 H334" stroke="#443438" stroke-width="1.5"/>
145
+ <rect class="a" style="animation-name: bar0" x="58" y="178" width="4" height="28" rx="2" fill="#FF5252"/>
146
+ <text x="74" y="200.5" font-family="'JetBrains Mono', 'SF Mono', Menlo, Consolas, monospace" font-size="24" fill="#F3EDEE">local:</text>
147
+ <text class="a" style="animation-name: kd0" x="232.4" y="200.5" font-family="'JetBrains Mono', 'SF Mono', Menlo, Consolas, monospace" font-size="24" fill="#FF5252">local</text>
148
+ <rect class="a" style="animation-name: bar1" x="58" y="254" width="4" height="28" rx="2" fill="#FF5252"/>
149
+ <text x="74" y="276.5" font-family="'JetBrains Mono', 'SF Mono', Menlo, Consolas, monospace" font-size="24" fill="#F3EDEE">mac-mini:</text>
150
+ <text class="a" style="animation-name: kd1" x="232.4" y="276.5" font-family="'JetBrains Mono', 'SF Mono', Menlo, Consolas, monospace" font-size="24" fill="#FF5252">ssh</text>
151
+ <rect class="a" style="animation-name: bar2" x="58" y="330" width="4" height="28" rx="2" fill="#FF5252"/>
152
+ <text x="74" y="352.5" font-family="'JetBrains Mono', 'SF Mono', Menlo, Consolas, monospace" font-size="24" fill="#F3EDEE">linux-box:</text>
153
+ <text class="a" style="animation-name: kd2" x="232.4" y="352.5" font-family="'JetBrains Mono', 'SF Mono', Menlo, Consolas, monospace" font-size="24" fill="#FF5252">ssh</text>
154
+ <rect class="a" style="animation-name: bar3" x="58" y="406" width="4" height="28" rx="2" fill="#FF5252"/>
155
+ <text x="74" y="428.5" font-family="'JetBrains Mono', 'SF Mono', Menlo, Consolas, monospace" font-size="24" fill="#F3EDEE">vm-1:</text>
156
+ <text class="a" style="animation-name: kd3" x="232.4" y="428.5" font-family="'JetBrains Mono', 'SF Mono', Menlo, Consolas, monospace" font-size="24" fill="#FF5252">exe-dev</text>
157
+ </g>
158
+
159
+ <g class="a x" style="animation-name: cmd">
160
+ <rect x="566" y="105" width="270" height="44" rx="10" fill="#2A1A1D" stroke="#FF5252" stroke-width="2"/>
161
+ <text x="701" y="135.5" text-anchor="middle" font-family="'JetBrains Mono', 'SF Mono', Menlo, Consolas, monospace" font-size="24" fill="#F3EDEE">just net up &lt;id&gt;</text>
162
+ </g>
163
+ <g class="a" style="animation-name: leg">
164
+ <circle cx="1072" cy="127" r="7" fill="#FF5252"/>
165
+ <text x="1086" y="134.5" font-size="22" fill="#B9AAAE">online</text>
166
+ </g>
167
+
168
+ <g font-size="20" fill="#B9AAAE" text-anchor="middle">
169
+ <text x="404" y="482">create</text>
170
+ <text x="503" y="482">provision</text>
171
+ <text x="602" y="482">deploy</text>
172
+ <text x="701" y="482">env</text>
173
+ <text x="800" y="482">service</text>
174
+ <text x="899" y="482">expose</text>
175
+ <text x="998" y="482">register</text>
176
+ </g>
177
+
178
+ <g class="a" style="animation-name: cap" font-size="22" fill="#B9AAAE" text-anchor="middle">
179
+ <text x="600" y="536">Machine facts live only in <tspan font-family="'JetBrains Mono', 'SF Mono', Menlo, Consolas, monospace">dpi-network.yaml</tspan>.</text>
180
+ <text x="600" y="566">Tokens never do: each device makes its own, and register copies it to the hub.</text>
181
+ </g>
182
+ </svg>