agent-mockingbird 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (227) hide show
  1. package/.agents/skills/btca-cli/SKILL.md +64 -0
  2. package/.agents/skills/btca-cli/agents/openai.yaml +3 -0
  3. package/.agents/skills/frontend-design/SKILL.md +42 -0
  4. package/.agents/skills/frontend-design/agents/openai.yaml +3 -0
  5. package/.env.example +36 -0
  6. package/.githooks/pre-commit +33 -0
  7. package/.github/workflows/ci.yml +309 -0
  8. package/.opencode/bun.lock +18 -0
  9. package/.opencode/package.json +5 -0
  10. package/.opencode/tools/agent_type_manager.ts +100 -0
  11. package/.opencode/tools/config_manager.ts +87 -0
  12. package/.opencode/tools/cron_manager.ts +145 -0
  13. package/.opencode/tools/memory_get.ts +43 -0
  14. package/.opencode/tools/memory_remember.ts +53 -0
  15. package/.opencode/tools/memory_search.ts +48 -0
  16. package/AGENTS.md +126 -0
  17. package/MEMORY.md +2 -0
  18. package/README.md +451 -0
  19. package/THIRD_PARTY_NOTICES.md +11 -0
  20. package/agent-mockingbird.config.example.json +135 -0
  21. package/apps/server/package.json +32 -0
  22. package/apps/server/src/backend/agents/bootstrapContext.ts +362 -0
  23. package/apps/server/src/backend/agents/openclawImport.test.ts +133 -0
  24. package/apps/server/src/backend/agents/openclawImport.ts +797 -0
  25. package/apps/server/src/backend/agents/opencodeConfig.ts +428 -0
  26. package/apps/server/src/backend/agents/service.ts +10 -0
  27. package/apps/server/src/backend/config/example-config.test.ts +20 -0
  28. package/apps/server/src/backend/config/orchestration.ts +243 -0
  29. package/apps/server/src/backend/config/policy.ts +158 -0
  30. package/apps/server/src/backend/config/schema.test.ts +15 -0
  31. package/apps/server/src/backend/config/schema.ts +391 -0
  32. package/apps/server/src/backend/config/semantic.test.ts +34 -0
  33. package/apps/server/src/backend/config/semantic.ts +149 -0
  34. package/apps/server/src/backend/config/service.test.ts +75 -0
  35. package/apps/server/src/backend/config/service.ts +207 -0
  36. package/apps/server/src/backend/config/smoke.ts +77 -0
  37. package/apps/server/src/backend/config/store.test.ts +123 -0
  38. package/apps/server/src/backend/config/store.ts +581 -0
  39. package/apps/server/src/backend/config/testFixtures.ts +5 -0
  40. package/apps/server/src/backend/config/types.ts +56 -0
  41. package/apps/server/src/backend/contracts/events.ts +320 -0
  42. package/apps/server/src/backend/contracts/runtime.ts +111 -0
  43. package/apps/server/src/backend/cron/executor.ts +435 -0
  44. package/apps/server/src/backend/cron/repository.ts +170 -0
  45. package/apps/server/src/backend/cron/service.ts +660 -0
  46. package/apps/server/src/backend/cron/storage.ts +92 -0
  47. package/apps/server/src/backend/cron/types.ts +138 -0
  48. package/apps/server/src/backend/cron/utils.ts +351 -0
  49. package/apps/server/src/backend/db/client.ts +20 -0
  50. package/apps/server/src/backend/db/migrate.ts +40 -0
  51. package/apps/server/src/backend/db/repository.ts +1762 -0
  52. package/apps/server/src/backend/db/schema.ts +113 -0
  53. package/apps/server/src/backend/db/usageDashboard.test.ts +102 -0
  54. package/apps/server/src/backend/db/wipe.ts +13 -0
  55. package/apps/server/src/backend/defaults.ts +32 -0
  56. package/apps/server/src/backend/env.ts +48 -0
  57. package/apps/server/src/backend/heartbeat/activeHours.ts +45 -0
  58. package/apps/server/src/backend/heartbeat/defaultJob.ts +88 -0
  59. package/apps/server/src/backend/heartbeat/heartbeat.test.ts +110 -0
  60. package/apps/server/src/backend/heartbeat/runtimeService.ts +190 -0
  61. package/apps/server/src/backend/heartbeat/service.ts +176 -0
  62. package/apps/server/src/backend/heartbeat/state.test.ts +63 -0
  63. package/apps/server/src/backend/heartbeat/state.ts +167 -0
  64. package/apps/server/src/backend/heartbeat/types.ts +54 -0
  65. package/apps/server/src/backend/http/boundedQueue.test.ts +49 -0
  66. package/apps/server/src/backend/http/boundedQueue.ts +92 -0
  67. package/apps/server/src/backend/http/parsers.ts +40 -0
  68. package/apps/server/src/backend/http/router.ts +61 -0
  69. package/apps/server/src/backend/http/routes/agentRoutes.ts +67 -0
  70. package/apps/server/src/backend/http/routes/backgroundRoutes.ts +203 -0
  71. package/apps/server/src/backend/http/routes/chatRoutes.ts +107 -0
  72. package/apps/server/src/backend/http/routes/configRoutes.ts +602 -0
  73. package/apps/server/src/backend/http/routes/cronRoutes.ts +221 -0
  74. package/apps/server/src/backend/http/routes/dashboardRoutes.ts +308 -0
  75. package/apps/server/src/backend/http/routes/eventRoutes.ts +7 -0
  76. package/apps/server/src/backend/http/routes/heartbeatRoutes.test.ts +41 -0
  77. package/apps/server/src/backend/http/routes/heartbeatRoutes.ts +28 -0
  78. package/apps/server/src/backend/http/routes/index.ts +101 -0
  79. package/apps/server/src/backend/http/routes/mcpRoutes.ts +213 -0
  80. package/apps/server/src/backend/http/routes/memoryRoutes.ts +154 -0
  81. package/apps/server/src/backend/http/routes/runRoutes.ts +310 -0
  82. package/apps/server/src/backend/http/routes/runtimeRoutes.ts +197 -0
  83. package/apps/server/src/backend/http/routes/skillRoutes.ts +112 -0
  84. package/apps/server/src/backend/http/routes/uiRoutes.test.ts +161 -0
  85. package/apps/server/src/backend/http/routes/uiRoutes.ts +177 -0
  86. package/apps/server/src/backend/http/routes/usageRoutes.test.ts +104 -0
  87. package/apps/server/src/backend/http/routes/usageRoutes.ts +767 -0
  88. package/apps/server/src/backend/http/schemas.ts +64 -0
  89. package/apps/server/src/backend/http/sse.ts +144 -0
  90. package/apps/server/src/backend/integration/backend-core.test.ts +2316 -0
  91. package/apps/server/src/backend/logging/logger.ts +64 -0
  92. package/apps/server/src/backend/mcp/service.ts +326 -0
  93. package/apps/server/src/backend/memory/cli.ts +170 -0
  94. package/apps/server/src/backend/memory/conceptExpansion.test.ts +28 -0
  95. package/apps/server/src/backend/memory/conceptExpansion.ts +80 -0
  96. package/apps/server/src/backend/memory/qmdPort.test.ts +54 -0
  97. package/apps/server/src/backend/memory/qmdPort.ts +61 -0
  98. package/apps/server/src/backend/memory/records.test.ts +66 -0
  99. package/apps/server/src/backend/memory/records.ts +229 -0
  100. package/apps/server/src/backend/memory/service.ts +2012 -0
  101. package/apps/server/src/backend/memory/sqliteVec.ts +58 -0
  102. package/apps/server/src/backend/memory/types.ts +104 -0
  103. package/apps/server/src/backend/opencode/agentMockingbirdPlugin.test.ts +396 -0
  104. package/apps/server/src/backend/opencode/client.ts +98 -0
  105. package/apps/server/src/backend/opencode/models.ts +41 -0
  106. package/apps/server/src/backend/opencode/systemPrompt.test.ts +146 -0
  107. package/apps/server/src/backend/opencode/systemPrompt.ts +284 -0
  108. package/apps/server/src/backend/paths.ts +57 -0
  109. package/apps/server/src/backend/prompts/service.ts +100 -0
  110. package/apps/server/src/backend/queue/queue.test.ts +189 -0
  111. package/apps/server/src/backend/queue/service.ts +177 -0
  112. package/apps/server/src/backend/queue/types.ts +39 -0
  113. package/apps/server/src/backend/run/service.ts +576 -0
  114. package/apps/server/src/backend/run/storage.ts +47 -0
  115. package/apps/server/src/backend/run/types.ts +44 -0
  116. package/apps/server/src/backend/runtime/errors.ts +61 -0
  117. package/apps/server/src/backend/runtime/index.ts +72 -0
  118. package/apps/server/src/backend/runtime/memoryPromptDedup.test.ts +153 -0
  119. package/apps/server/src/backend/runtime/memoryPromptDedup.ts +76 -0
  120. package/apps/server/src/backend/runtime/opencodeRuntime/backgroundMethods.ts +765 -0
  121. package/apps/server/src/backend/runtime/opencodeRuntime/coreMethods.ts +705 -0
  122. package/apps/server/src/backend/runtime/opencodeRuntime/eventMethods.ts +503 -0
  123. package/apps/server/src/backend/runtime/opencodeRuntime/memoryMethods.ts +462 -0
  124. package/apps/server/src/backend/runtime/opencodeRuntime/promptMethods.ts +1167 -0
  125. package/apps/server/src/backend/runtime/opencodeRuntime/shared.ts +254 -0
  126. package/apps/server/src/backend/runtime/opencodeRuntime.test.ts +2899 -0
  127. package/apps/server/src/backend/runtime/opencodeRuntime.ts +135 -0
  128. package/apps/server/src/backend/runtime/sessionScope.ts +45 -0
  129. package/apps/server/src/backend/skills/service.ts +442 -0
  130. package/apps/server/src/backend/workspace/resolve.ts +27 -0
  131. package/apps/server/src/cli/agent-mockingbird.mjs +2522 -0
  132. package/apps/server/src/cli/agent-mockingbird.test.ts +68 -0
  133. package/apps/server/src/cli/runtime-assets.mjs +269 -0
  134. package/apps/server/src/cli/runtime-assets.test.ts +52 -0
  135. package/apps/server/src/cli/runtime-layout.mjs +75 -0
  136. package/apps/server/src/cli/standaloneBuild.test.ts +19 -0
  137. package/apps/server/src/cli/standaloneBuild.ts +19 -0
  138. package/apps/server/src/cli/standaloneCronBinary.test.ts +187 -0
  139. package/apps/server/src/index.ts +178 -0
  140. package/apps/server/tsconfig.json +12 -0
  141. package/backlog.md +5 -0
  142. package/bin/agent-mockingbird +2522 -0
  143. package/bin/runtime-layout.mjs +75 -0
  144. package/build-bin.ts +34 -0
  145. package/build-cli.mjs +37 -0
  146. package/build.ts +40 -0
  147. package/bun-env.d.ts +11 -0
  148. package/bun.lock +888 -0
  149. package/bunfig.toml +2 -0
  150. package/components.json +21 -0
  151. package/config.json +130 -0
  152. package/deploy/RELEASE_INSTALL.md +112 -0
  153. package/deploy/docker-compose.yml +42 -0
  154. package/deploy/systemd/README.md +46 -0
  155. package/deploy/systemd/agent-mockingbird.service +28 -0
  156. package/deploy/systemd/opencode.service +25 -0
  157. package/docs/legacy-config-ui-reference.md +51 -0
  158. package/docs/memory-e2e-trace-2026-03-04.md +63 -0
  159. package/docs/memory-ops.md +96 -0
  160. package/docs/memory-runtime-contract.md +42 -0
  161. package/docs/memory-tuning-remote-2026-03-04.md +59 -0
  162. package/docs/opencode-rebase-workflow-plan.md +614 -0
  163. package/docs/opencode-startup-sync-plan.md +94 -0
  164. package/docs/vendor-opencode.md +41 -0
  165. package/drizzle/0000_famous_turbo.sql +49 -0
  166. package/drizzle/0001_cron_memory_aux.sql +160 -0
  167. package/drizzle/0002_runtime_session_bindings.sql +28 -0
  168. package/drizzle/0003_background_runs.sql +27 -0
  169. package/drizzle/0004_memory_open_write.sql +63 -0
  170. package/drizzle/0005_signal_channel.sql +47 -0
  171. package/drizzle/0006_usage_event_dimensions.sql +7 -0
  172. package/drizzle/meta/0000_snapshot.json +341 -0
  173. package/drizzle/meta/_journal.json +55 -0
  174. package/drizzle.config.ts +14 -0
  175. package/eslint.config.mjs +77 -0
  176. package/knip.json +18 -0
  177. package/memory/2026-03-04.md +4 -0
  178. package/opencode.lock.json +16 -0
  179. package/package.json +67 -0
  180. package/packages/agent-mockingbird-installer/README.md +31 -0
  181. package/packages/agent-mockingbird-installer/bin/agent-mockingbird-installer.mjs +44 -0
  182. package/packages/agent-mockingbird-installer/opencode.lock.json +16 -0
  183. package/packages/agent-mockingbird-installer/package.json +23 -0
  184. package/packages/contracts/package.json +19 -0
  185. package/packages/contracts/src/agentTypes.ts +122 -0
  186. package/packages/contracts/src/cron.ts +146 -0
  187. package/packages/contracts/src/dashboard.ts +378 -0
  188. package/packages/contracts/src/index.ts +3 -0
  189. package/packages/contracts/tsconfig.json +4 -0
  190. package/patches/opencode/0001-Wafflebot-OpenCode-baseline.patch +2341 -0
  191. package/patches/opencode/0002-Fix-OpenCode-web-entry-and-settings-icons.patch +104 -0
  192. package/patches/opencode/0003-fix-app-remove-duplicate-sidebar-mount.patch +32 -0
  193. package/patches/opencode/0004-Add-heartbeat-settings-and-usage-nav.patch +506 -0
  194. package/patches/opencode/0005-Use-chart-icon-for-usage-nav.patch +38 -0
  195. package/patches/opencode/0006-Modernize-cron-settings.patch +399 -0
  196. package/patches/opencode/0007-Rename-waffle-namespaces-to-mockingbird.patch +1110 -0
  197. package/patches/opencode/0008-Remove-cron-contract-section.patch +178 -0
  198. package/patches/opencode/0009-Rework-cron-tab-as-operations-console.patch +414 -0
  199. package/patches/opencode/0010-Refine-heartbeat-settings-controls.patch +208 -0
  200. package/runtime-assets/opencode-config/opencode.jsonc +25 -0
  201. package/runtime-assets/opencode-config/package.json +5 -0
  202. package/runtime-assets/opencode-config/plugins/agent-mockingbird.ts +715 -0
  203. package/runtime-assets/workspace/.agents/skills/config-auditor/SKILL.md +25 -0
  204. package/runtime-assets/workspace/.agents/skills/config-editor/SKILL.md +24 -0
  205. package/runtime-assets/workspace/.agents/skills/cron-manager/SKILL.md +57 -0
  206. package/runtime-assets/workspace/.agents/skills/memory-ops/SKILL.md +120 -0
  207. package/runtime-assets/workspace/.agents/skills/runtime-diagnose/SKILL.md +25 -0
  208. package/runtime-assets/workspace/AGENTS.md +56 -0
  209. package/runtime-assets/workspace/MEMORY.md +4 -0
  210. package/scripts/build-release-bundle.sh +66 -0
  211. package/scripts/check-ship.ts +383 -0
  212. package/scripts/dev-opencode.sh +17 -0
  213. package/scripts/dev-stack-opencode.sh +15 -0
  214. package/scripts/dev-stack.sh +61 -0
  215. package/scripts/install-systemd.sh +87 -0
  216. package/scripts/memory-e2e.sh +76 -0
  217. package/scripts/memory-trace-e2e.sh +141 -0
  218. package/scripts/migrate-opencode-env.ts +108 -0
  219. package/scripts/onboard/bootstrap.sh +32 -0
  220. package/scripts/opencode-swap.ts +78 -0
  221. package/scripts/opencode-sync.ts +715 -0
  222. package/scripts/runtime-assets-sync.mjs +83 -0
  223. package/scripts/setup-git-hooks.ts +39 -0
  224. package/tsconfig.json +45 -0
  225. package/tui.json +98 -0
  226. package/turbo.json +36 -0
  227. package/vendor/OPENCODE_VENDOR.md +13 -0
@@ -0,0 +1,104 @@
1
+ From e6f3084be1883e1d02911b034ff4b1903de01ced Mon Sep 17 00:00:00 2001
2
+ From: Matt Campbell <matt@battleshopper.com>
3
+ Date: Mon, 16 Mar 2026 14:17:21 -0500
4
+ Subject: [PATCH 02/10] Fix OpenCode web entry and settings icons
5
+
6
+ ---
7
+ .../app/src/components/dialog-settings.tsx | 10 +++---
8
+ packages/app/src/entry.tsx | 33 +++++++++++++++----
9
+ 2 files changed, 32 insertions(+), 11 deletions(-)
10
+
11
+ diff --git a/packages/app/src/components/dialog-settings.tsx b/packages/app/src/components/dialog-settings.tsx
12
+ index 005920ccb..460555ed1 100644
13
+ --- a/packages/app/src/components/dialog-settings.tsx
14
+ +++ b/packages/app/src/components/dialog-settings.tsx
15
+ @@ -57,23 +57,23 @@ export const DialogSettings: Component = () => {
16
+ <Tabs.SectionTitle>Agent Mockingbird</Tabs.SectionTitle>
17
+ <div class="flex flex-col gap-1.5 w-full">
18
+ <Tabs.Trigger value="agents">
19
+ - <Icon name="sparkles" />
20
+ + <Icon name="brain" />
21
+ {language.t("settings.agents.title")}
22
+ </Tabs.Trigger>
23
+ <Tabs.Trigger value="mcp">
24
+ - <Icon name="plug-2" />
25
+ + <Icon name="providers" />
26
+ {language.t("settings.mcp.title")}
27
+ </Tabs.Trigger>
28
+ <Tabs.Trigger value="skills">
29
+ - <Icon name="book-open" />
30
+ + <Icon name="help" />
31
+ {language.t("settings.skills.title")}
32
+ </Tabs.Trigger>
33
+ <Tabs.Trigger value="runtime">
34
+ - <Icon name="terminal-square" />
35
+ + <Icon name="terminal" />
36
+ {language.t("settings.runtime.title")}
37
+ </Tabs.Trigger>
38
+ <Tabs.Trigger value="cron">
39
+ - <Icon name="clock-3" />
40
+ + <Icon name="warning" />
41
+ {language.t("settings.cron.title")}
42
+ </Tabs.Trigger>
43
+ </div>
44
+ diff --git a/packages/app/src/entry.tsx b/packages/app/src/entry.tsx
45
+ index 2198a7bc1..52b6e7669 100644
46
+ --- a/packages/app/src/entry.tsx
47
+ +++ b/packages/app/src/entry.tsx
48
+ @@ -9,6 +9,8 @@ import { handleNotificationClick } from "@/utils/notification-click"
49
+ import pkg from "../package.json"
50
+ import { ServerConnection } from "./context/server"
51
+
52
+ +const DEFAULT_SERVER_URL_KEY = "opencode.settings.dat:defaultServerUrl"
53
+ +
54
+ const getLocale = () => {
55
+ if (typeof navigator !== "object") return "en" as const
56
+ const languages = navigator.languages?.length ? navigator.languages : [navigator.language]
57
+ @@ -25,6 +27,31 @@ const getRootNotFoundError = () => {
58
+ return locale === "zh" ? (zh[key] ?? en[key]) : en[key]
59
+ }
60
+
61
+ +const getStorage = (key: string) => {
62
+ + if (typeof localStorage === "undefined") return null
63
+ + try {
64
+ + return localStorage.getItem(key)
65
+ + } catch {
66
+ + return null
67
+ + }
68
+ +}
69
+ +
70
+ +const setStorage = (key: string, value: string | null) => {
71
+ + if (typeof localStorage === "undefined") return
72
+ + try {
73
+ + if (value !== null) {
74
+ + localStorage.setItem(key, value)
75
+ + return
76
+ + }
77
+ + localStorage.removeItem(key)
78
+ + } catch {
79
+ + return
80
+ + }
81
+ +}
82
+ +
83
+ +const readDefaultServerUrl = () => getStorage(DEFAULT_SERVER_URL_KEY)
84
+ +const writeDefaultServerUrl = (url: string | null) => setStorage(DEFAULT_SERVER_URL_KEY, url)
85
+ +
86
+ const notify: Platform["notify"] = async (title, description, href) => {
87
+ if (!("Notification" in window)) return
88
+
89
+ @@ -77,12 +104,6 @@ const getCurrentUrl = () => {
90
+ return location.origin
91
+ }
92
+
93
+ -const getDefaultUrl = () => {
94
+ - const lsDefault = readDefaultServerUrl()
95
+ - if (lsDefault) return lsDefault
96
+ - return getCurrentUrl()
97
+ -}
98
+ -
99
+ const platform: Platform = {
100
+ platform: "web",
101
+ version: pkg.version,
102
+ --
103
+ 2.53.0
104
+
@@ -0,0 +1,32 @@
1
+ From 862ffa45d820c9ba32cdce55dbc10a64e9c744b1 Mon Sep 17 00:00:00 2001
2
+ From: Matt Campbell <matt@battleshopper.com>
3
+ Date: Mon, 16 Mar 2026 21:06:59 -0500
4
+ Subject: [PATCH 03/10] fix(app): remove duplicate sidebar mount
5
+
6
+ ---
7
+ packages/app/src/pages/layout.tsx | 2 --
8
+ 1 file changed, 2 deletions(-)
9
+
10
+ diff --git a/packages/app/src/pages/layout.tsx b/packages/app/src/pages/layout.tsx
11
+ index 5626c040b..9df2a3681 100644
12
+ --- a/packages/app/src/pages/layout.tsx
13
+ +++ b/packages/app/src/pages/layout.tsx
14
+ @@ -2272,7 +2272,6 @@ export default function Layout(props: ParentProps) {
15
+ arm()
16
+ }}
17
+ >
18
+ - <div class="@container w-full h-full contain-strict">{sidebarContent()}</div>
19
+ <div class="@container w-full h-full contain-strict">{sidebarContent()}</div>
20
+ <Show when={layout.sidebar.opened()}>
21
+ <div onPointerDown={() => setState("sizing", true)}>
22
+ @@ -2321,7 +2320,6 @@ export default function Layout(props: ParentProps) {
23
+ onClick={(e) => e.stopPropagation()}
24
+ >
25
+ {sidebarContent(true)}
26
+ - {sidebarContent(true)}
27
+ </nav>
28
+ </div>
29
+
30
+ --
31
+ 2.53.0
32
+
@@ -0,0 +1,506 @@
1
+ From d0ccb6f44aaff458f67afa3bde01cf7fd0ddf3ab Mon Sep 17 00:00:00 2001
2
+ From: Matt Campbell <matt@battleshopper.com>
3
+ Date: Wed, 18 Mar 2026 15:36:33 -0500
4
+ Subject: [PATCH 04/10] Add heartbeat settings and usage nav
5
+
6
+ ---
7
+ .../app/src/components/dialog-settings.tsx | 8 +
8
+ .../app/src/components/settings-heartbeat.tsx | 409 ++++++++++++++++++
9
+ packages/app/src/pages/layout.tsx | 2 +
10
+ .../app/src/pages/layout/sidebar-shell.tsx | 11 +
11
+ 4 files changed, 430 insertions(+)
12
+ create mode 100644 packages/app/src/components/settings-heartbeat.tsx
13
+
14
+ diff --git a/packages/app/src/components/dialog-settings.tsx b/packages/app/src/components/dialog-settings.tsx
15
+ index 460555ed1..8b51a29fd 100644
16
+ --- a/packages/app/src/components/dialog-settings.tsx
17
+ +++ b/packages/app/src/components/dialog-settings.tsx
18
+ @@ -13,6 +13,7 @@ import { SettingsMcp } from "./settings-mcp"
19
+ import { SettingsSkills } from "./settings-skills"
20
+ import { SettingsRuntime } from "./settings-runtime"
21
+ import { SettingsCron } from "./settings-cron"
22
+ +import { SettingsHeartbeat } from "./settings-heartbeat"
23
+
24
+ export const DialogSettings: Component = () => {
25
+ const language = useLanguage()
26
+ @@ -72,6 +73,10 @@ export const DialogSettings: Component = () => {
27
+ <Icon name="terminal" />
28
+ {language.t("settings.runtime.title")}
29
+ </Tabs.Trigger>
30
+ + <Tabs.Trigger value="heartbeat">
31
+ + <Icon name="status" />
32
+ + Heartbeat
33
+ + </Tabs.Trigger>
34
+ <Tabs.Trigger value="cron">
35
+ <Icon name="warning" />
36
+ {language.t("settings.cron.title")}
37
+ @@ -110,6 +115,9 @@ export const DialogSettings: Component = () => {
38
+ <Tabs.Content value="runtime" class="no-scrollbar">
39
+ <SettingsRuntime />
40
+ </Tabs.Content>
41
+ + <Tabs.Content value="heartbeat" class="no-scrollbar">
42
+ + <SettingsHeartbeat />
43
+ + </Tabs.Content>
44
+ <Tabs.Content value="cron" class="no-scrollbar">
45
+ <SettingsCron />
46
+ </Tabs.Content>
47
+ diff --git a/packages/app/src/components/settings-heartbeat.tsx b/packages/app/src/components/settings-heartbeat.tsx
48
+ new file mode 100644
49
+ index 000000000..95a1770cc
50
+ --- /dev/null
51
+ +++ b/packages/app/src/components/settings-heartbeat.tsx
52
+ @@ -0,0 +1,409 @@
53
+ +import { Button } from "@opencode-ai/ui/button"
54
+ +import { Switch } from "@opencode-ai/ui/switch"
55
+ +import { showToast } from "@opencode-ai/ui/toast"
56
+ +import { onMount, Show, type Component } from "solid-js"
57
+ +import { createStore } from "solid-js/store"
58
+ +import { waffleJson } from "@/utils/waffle"
59
+ +import {
60
+ + WaffleCard,
61
+ + WaffleInput,
62
+ + WaffleMetaRow,
63
+ + WaffleNotice,
64
+ + WaffleSettingsPage,
65
+ + WaffleSettingsSection,
66
+ + WaffleTextArea,
67
+ + WaffleToolbar,
68
+ +} from "./settings-waffle-shared"
69
+ +
70
+ +type HeartbeatActiveHours = {
71
+ + start: string
72
+ + end: string
73
+ + timezone: string
74
+ +} | null
75
+ +
76
+ +type HeartbeatRuntimeConfig = {
77
+ + enabled: boolean
78
+ + interval: string
79
+ + agentId: string
80
+ + model: string
81
+ + prompt: string
82
+ + ackMaxChars: number
83
+ + activeHours?: HeartbeatActiveHours
84
+ +}
85
+ +
86
+ +type HeartbeatStatus = {
87
+ + config: HeartbeatRuntimeConfig
88
+ + state: {
89
+ + sessionId: string | null
90
+ + running: boolean
91
+ + lastRunAt: string | null
92
+ + lastResult: "idle" | "acknowledged" | "attention" | "skipped" | "error"
93
+ + lastResponse: string | null
94
+ + lastError: string | null
95
+ + updatedAt: string
96
+ + }
97
+ + nextDueAt: string | null
98
+ +}
99
+ +
100
+ +type HeartbeatStatusPayload = {
101
+ + heartbeat: HeartbeatStatus
102
+ +}
103
+ +
104
+ +type RuntimePayload = {
105
+ + hash: string
106
+ + path: string
107
+ +}
108
+ +
109
+ +type HeartbeatFormState = {
110
+ + enabled: boolean
111
+ + interval: string
112
+ + model: string
113
+ + agentId: string
114
+ + ackMaxChars: string
115
+ + prompt: string
116
+ + activeHoursEnabled: boolean
117
+ + activeStart: string
118
+ + activeEnd: string
119
+ + activeTimezone: string
120
+ +}
121
+ +
122
+ +function toForm(config: HeartbeatRuntimeConfig): HeartbeatFormState {
123
+ + return {
124
+ + enabled: config.enabled,
125
+ + interval: config.interval,
126
+ + model: config.model,
127
+ + agentId: config.agentId,
128
+ + ackMaxChars: String(config.ackMaxChars),
129
+ + prompt: config.prompt,
130
+ + activeHoursEnabled: !!config.activeHours,
131
+ + activeStart: config.activeHours?.start ?? "09:00",
132
+ + activeEnd: config.activeHours?.end ?? "17:00",
133
+ + activeTimezone: config.activeHours?.timezone ?? Intl.DateTimeFormat().resolvedOptions().timeZone ?? "UTC",
134
+ + }
135
+ +}
136
+ +
137
+ +function formatTimestamp(value: string | null) {
138
+ + if (!value) return "Never"
139
+ + const date = new Date(value)
140
+ + if (Number.isNaN(date.getTime())) return value
141
+ + return date.toLocaleString()
142
+ +}
143
+ +
144
+ +function formatResult(value: HeartbeatStatus["state"]["lastResult"]) {
145
+ + switch (value) {
146
+ + case "acknowledged":
147
+ + return "Acknowledged"
148
+ + case "attention":
149
+ + return "Needs attention"
150
+ + case "skipped":
151
+ + return "Skipped"
152
+ + case "error":
153
+ + return "Error"
154
+ + default:
155
+ + return "Idle"
156
+ + }
157
+ +}
158
+ +
159
+ +function buildHeartbeatPatch(form: HeartbeatFormState): HeartbeatRuntimeConfig {
160
+ + const interval = form.interval.trim()
161
+ + const model = form.model.trim()
162
+ + const agentId = form.agentId.trim()
163
+ + const prompt = form.prompt.trim()
164
+ + const ackMaxChars = Number(form.ackMaxChars)
165
+ +
166
+ + if (!interval) throw new Error("Interval is required")
167
+ + if (!model) throw new Error("Model is required")
168
+ + if (!prompt) throw new Error("Prompt is required")
169
+ + if (!Number.isFinite(ackMaxChars) || ackMaxChars < 1) throw new Error("Ack max chars must be a positive number")
170
+ +
171
+ + const activeHours =
172
+ + form.activeHoursEnabled
173
+ + ? {
174
+ + start: form.activeStart.trim(),
175
+ + end: form.activeEnd.trim(),
176
+ + timezone: form.activeTimezone.trim(),
177
+ + }
178
+ + : null
179
+ +
180
+ + if (activeHours && (!activeHours.start || !activeHours.end || !activeHours.timezone)) {
181
+ + throw new Error("Active hours require a start, end, and timezone")
182
+ + }
183
+ +
184
+ + return {
185
+ + enabled: form.enabled,
186
+ + interval,
187
+ + model,
188
+ + agentId,
189
+ + prompt,
190
+ + ackMaxChars,
191
+ + activeHours,
192
+ + }
193
+ +}
194
+ +
195
+ +export const SettingsHeartbeat: Component = () => {
196
+ + const [state, setState] = createStore({
197
+ + loading: true,
198
+ + saving: false,
199
+ + running: false,
200
+ + error: "",
201
+ + runtimeHash: "",
202
+ + runtimePath: "",
203
+ + status: null as HeartbeatStatus | null,
204
+ + form: {
205
+ + enabled: true,
206
+ + interval: "15m",
207
+ + model: "",
208
+ + agentId: "build",
209
+ + ackMaxChars: "280",
210
+ + prompt: "",
211
+ + activeHoursEnabled: false,
212
+ + activeStart: "09:00",
213
+ + activeEnd: "17:00",
214
+ + activeTimezone: "UTC",
215
+ + } as HeartbeatFormState,
216
+ + })
217
+ +
218
+ + async function load() {
219
+ + setState("loading", true)
220
+ + setState("error", "")
221
+ + try {
222
+ + const [heartbeat, runtime] = await Promise.all([
223
+ + waffleJson<HeartbeatStatusPayload>("/api/waffle/heartbeat"),
224
+ + waffleJson<RuntimePayload>("/api/waffle/runtime/config"),
225
+ + ])
226
+ + setState("status", heartbeat.heartbeat)
227
+ + setState("runtimeHash", runtime.hash)
228
+ + setState("runtimePath", runtime.path)
229
+ + setState("form", toForm(heartbeat.heartbeat.config))
230
+ + } catch (error) {
231
+ + setState("error", error instanceof Error ? error.message : "Failed to load heartbeat settings")
232
+ + } finally {
233
+ + setState("loading", false)
234
+ + }
235
+ + }
236
+ +
237
+ + async function save() {
238
+ + let heartbeatPatch: HeartbeatRuntimeConfig
239
+ + try {
240
+ + heartbeatPatch = buildHeartbeatPatch(state.form)
241
+ + } catch (error) {
242
+ + setState("error", error instanceof Error ? error.message : "Invalid heartbeat settings")
243
+ + return
244
+ + }
245
+ +
246
+ + setState("saving", true)
247
+ + setState("error", "")
248
+ + try {
249
+ + const runtime = await waffleJson<RuntimePayload>("/api/waffle/runtime/config", {
250
+ + method: "PATCH",
251
+ + body: JSON.stringify({
252
+ + patch: {
253
+ + runtime: {
254
+ + heartbeat: heartbeatPatch,
255
+ + },
256
+ + },
257
+ + expectedHash: state.runtimeHash || undefined,
258
+ + }),
259
+ + })
260
+ + setState("runtimeHash", runtime.hash)
261
+ + setState("runtimePath", runtime.path)
262
+ + showToast({
263
+ + variant: "success",
264
+ + icon: "circle-check",
265
+ + title: "Heartbeat settings saved",
266
+ + })
267
+ + await load()
268
+ + } catch (error) {
269
+ + setState("error", error instanceof Error ? error.message : "Failed to save heartbeat settings")
270
+ + } finally {
271
+ + setState("saving", false)
272
+ + }
273
+ + }
274
+ +
275
+ + async function runNow() {
276
+ + setState("running", true)
277
+ + setState("error", "")
278
+ + try {
279
+ + const payload = await waffleJson<HeartbeatStatusPayload>("/api/waffle/heartbeat/run", {
280
+ + method: "POST",
281
+ + })
282
+ + setState("status", payload.heartbeat)
283
+ + setState("form", toForm(payload.heartbeat.config))
284
+ + showToast({
285
+ + variant: "success",
286
+ + icon: "circle-check",
287
+ + title: "Heartbeat run completed",
288
+ + })
289
+ + } catch (error) {
290
+ + setState("error", error instanceof Error ? error.message : "Failed to run heartbeat")
291
+ + } finally {
292
+ + setState("running", false)
293
+ + }
294
+ + }
295
+ +
296
+ + onMount(() => {
297
+ + void load()
298
+ + })
299
+ +
300
+ + return (
301
+ + <WaffleSettingsPage
302
+ + title="Heartbeat"
303
+ + description="Monitor the dedicated heartbeat session, control its schedule, and choose its model explicitly."
304
+ + actions={
305
+ + <WaffleToolbar>
306
+ + <Button variant="ghost" size="large" onClick={() => void load()} disabled={state.loading || state.saving || state.running}>
307
+ + Refresh
308
+ + </Button>
309
+ + <Button variant="ghost" size="large" onClick={() => void runNow()} disabled={state.loading || state.saving || state.running}>
310
+ + Run now
311
+ + </Button>
312
+ + <Button size="large" onClick={() => void save()} disabled={state.loading || state.saving || state.running}>
313
+ + Save
314
+ + </Button>
315
+ + </WaffleToolbar>
316
+ + }
317
+ + >
318
+ + <Show when={state.error}>
319
+ + <WaffleNotice tone="error">{state.error}</WaffleNotice>
320
+ + </Show>
321
+ +
322
+ + <Show when={state.status}>
323
+ + {(status) => (
324
+ + <>
325
+ + <WaffleSettingsSection title="Status" description="Current scheduler state for the dedicated Heartbeat session.">
326
+ + <WaffleMetaRow label="Config file" value={state.runtimePath || "Unavailable"} />
327
+ + <WaffleMetaRow label="Enabled" value={status().config.enabled ? "Yes" : "No"} />
328
+ + <WaffleMetaRow label="Session id" value={status().state.sessionId ?? "Not created yet"} />
329
+ + <WaffleMetaRow label="Currently running" value={status().state.running ? "Yes" : "No"} />
330
+ + <WaffleMetaRow label="Last run" value={formatTimestamp(status().state.lastRunAt)} />
331
+ + <WaffleMetaRow label="Next due" value={formatTimestamp(status().nextDueAt)} />
332
+ + <WaffleMetaRow label="Last result" value={formatResult(status().state.lastResult)} />
333
+ + <WaffleMetaRow label="Last updated" value={formatTimestamp(status().state.updatedAt)} />
334
+ + </WaffleSettingsSection>
335
+ +
336
+ + <Show when={status().state.lastError}>
337
+ + <WaffleNotice tone="error">{status().state.lastError ?? ""}</WaffleNotice>
338
+ + </Show>
339
+ +
340
+ + <Show when={status().state.lastResponse}>
341
+ + <WaffleSettingsSection title="Last response" description="Most recent heartbeat response captured from the dedicated session.">
342
+ + <WaffleCard>
343
+ + <div class="text-13-regular text-text-strong whitespace-pre-wrap break-words">
344
+ + {status().state.lastResponse}
345
+ + </div>
346
+ + </WaffleCard>
347
+ + </WaffleSettingsSection>
348
+ + </Show>
349
+ + </>
350
+ + )}
351
+ + </Show>
352
+ +
353
+ + <WaffleSettingsSection title="Controls" description="Choose whether heartbeat runs, how often it runs, and which model it uses.">
354
+ + <WaffleCard>
355
+ + <div class="flex flex-wrap items-center justify-between gap-3 rounded-lg border border-border-weak-base bg-surface-base px-4 py-3">
356
+ + <div class="flex flex-col gap-1">
357
+ + <div class="text-13-medium text-text-strong">Heartbeat enabled</div>
358
+ + <div class="text-12-regular text-text-weak">Disable this to stop the scheduler without removing the Heartbeat session.</div>
359
+ + </div>
360
+ + <Switch checked={state.form.enabled} onChange={(enabled) => setState("form", "enabled", enabled)} hideLabel>
361
+ + Heartbeat enabled
362
+ + </Switch>
363
+ + </div>
364
+ +
365
+ + <div class="grid gap-4 sm:grid-cols-2">
366
+ + <div class="flex flex-col gap-2">
367
+ + <span class="text-12-medium text-text-weak">Model</span>
368
+ + <WaffleInput
369
+ + value={state.form.model}
370
+ + placeholder="openai/gpt-5.4"
371
+ + onInput={(event) => setState("form", "model", event.currentTarget.value)}
372
+ + />
373
+ + </div>
374
+ + <div class="flex flex-col gap-2">
375
+ + <span class="text-12-medium text-text-weak">Interval</span>
376
+ + <WaffleInput
377
+ + value={state.form.interval}
378
+ + placeholder="15m"
379
+ + onInput={(event) => setState("form", "interval", event.currentTarget.value)}
380
+ + />
381
+ + </div>
382
+ + <div class="flex flex-col gap-2">
383
+ + <span class="text-12-medium text-text-weak">Agent id</span>
384
+ + <WaffleInput
385
+ + value={state.form.agentId}
386
+ + placeholder="build"
387
+ + onInput={(event) => setState("form", "agentId", event.currentTarget.value)}
388
+ + />
389
+ + </div>
390
+ + <div class="flex flex-col gap-2">
391
+ + <span class="text-12-medium text-text-weak">Ack max chars</span>
392
+ + <WaffleInput
393
+ + type="number"
394
+ + min="1"
395
+ + value={state.form.ackMaxChars}
396
+ + onInput={(event) => setState("form", "ackMaxChars", event.currentTarget.value)}
397
+ + />
398
+ + </div>
399
+ + </div>
400
+ + </WaffleCard>
401
+ + </WaffleSettingsSection>
402
+ +
403
+ + <WaffleSettingsSection title="Active hours" description="Optionally restrict heartbeat runs to a daily time window.">
404
+ + <WaffleCard>
405
+ + <div class="flex flex-wrap items-center justify-between gap-3 rounded-lg border border-border-weak-base bg-surface-base px-4 py-3">
406
+ + <div class="flex flex-col gap-1">
407
+ + <div class="text-13-medium text-text-strong">Restrict to active hours</div>
408
+ + <div class="text-12-regular text-text-weak">When enabled, heartbeat only runs inside the configured local time window.</div>
409
+ + </div>
410
+ + <Switch
411
+ + checked={state.form.activeHoursEnabled}
412
+ + onChange={(enabled) => setState("form", "activeHoursEnabled", enabled)}
413
+ + hideLabel
414
+ + >
415
+ + Restrict to active hours
416
+ + </Switch>
417
+ + </div>
418
+ +
419
+ + <Show when={state.form.activeHoursEnabled}>
420
+ + <div class="grid gap-4 sm:grid-cols-3">
421
+ + <div class="flex flex-col gap-2">
422
+ + <span class="text-12-medium text-text-weak">Start</span>
423
+ + <WaffleInput
424
+ + value={state.form.activeStart}
425
+ + placeholder="09:00"
426
+ + onInput={(event) => setState("form", "activeStart", event.currentTarget.value)}
427
+ + />
428
+ + </div>
429
+ + <div class="flex flex-col gap-2">
430
+ + <span class="text-12-medium text-text-weak">End</span>
431
+ + <WaffleInput
432
+ + value={state.form.activeEnd}
433
+ + placeholder="17:00"
434
+ + onInput={(event) => setState("form", "activeEnd", event.currentTarget.value)}
435
+ + />
436
+ + </div>
437
+ + <div class="flex flex-col gap-2">
438
+ + <span class="text-12-medium text-text-weak">Timezone</span>
439
+ + <WaffleInput
440
+ + value={state.form.activeTimezone}
441
+ + placeholder="America/Chicago"
442
+ + onInput={(event) => setState("form", "activeTimezone", event.currentTarget.value)}
443
+ + />
444
+ + </div>
445
+ + </div>
446
+ + </Show>
447
+ + </WaffleCard>
448
+ + </WaffleSettingsSection>
449
+ +
450
+ + <WaffleSettingsSection title="Prompt" description="This prompt runs in the visible Heartbeat session whenever the scheduler fires.">
451
+ + <WaffleCard>
452
+ + <WaffleTextArea
453
+ + rows={12}
454
+ + value={state.form.prompt}
455
+ + onInput={(event) => setState("form", "prompt", event.currentTarget.value)}
456
+ + />
457
+ + </WaffleCard>
458
+ + </WaffleSettingsSection>
459
+ + </WaffleSettingsPage>
460
+ + )
461
+ +}
462
+ diff --git a/packages/app/src/pages/layout.tsx b/packages/app/src/pages/layout.tsx
463
+ index 9df2a3681..86cfb0b9b 100644
464
+ --- a/packages/app/src/pages/layout.tsx
465
+ +++ b/packages/app/src/pages/layout.tsx
466
+ @@ -2227,6 +2227,8 @@ export default function Layout(props: ParentProps) {
467
+ openProjectKeybind={() => command.keybind("project.open")}
468
+ onOpenProject={chooseProject}
469
+ renderProjectOverlay={projectOverlay}
470
+ + usageLabel={() => "Usage"}
471
+ + onOpenUsage={() => window.location.assign("/usage")}
472
+ settingsLabel={() => language.t("sidebar.settings")}
473
+ settingsKeybind={() => command.keybind("settings.open")}
474
+ onOpenSettings={openSettings}
475
+ diff --git a/packages/app/src/pages/layout/sidebar-shell.tsx b/packages/app/src/pages/layout/sidebar-shell.tsx
476
+ index 924828c2f..7ed209697 100644
477
+ --- a/packages/app/src/pages/layout/sidebar-shell.tsx
478
+ +++ b/packages/app/src/pages/layout/sidebar-shell.tsx
479
+ @@ -25,6 +25,8 @@ export const SidebarContent = (props: {
480
+ openProjectKeybind?: Accessor<string | undefined>
481
+ onOpenProject?: () => void
482
+ renderProjectOverlay: () => JSX.Element
483
+ + usageLabel: Accessor<string>
484
+ + onOpenUsage: () => void
485
+ settingsLabel: Accessor<string>
486
+ settingsKeybind: Accessor<string | undefined>
487
+ onOpenSettings: () => void
488
+ @@ -71,6 +73,15 @@ export const SidebarContent = (props: {
489
+ </DragDropProvider>
490
+ </div>
491
+ <div class="shrink-0 w-full pt-3 pb-6 flex flex-col items-center gap-2">
492
+ + <Tooltip placement={placement()} value={props.usageLabel()}>
493
+ + <IconButton
494
+ + icon="status"
495
+ + variant="ghost"
496
+ + size="large"
497
+ + onClick={props.onOpenUsage}
498
+ + aria-label={props.usageLabel()}
499
+ + />
500
+ + </Tooltip>
501
+ <TooltipKeybind placement={placement()} title={props.settingsLabel()} keybind={props.settingsKeybind() ?? ""}>
502
+ <IconButton
503
+ icon="settings-gear"
504
+ --
505
+ 2.53.0
506
+
@@ -0,0 +1,38 @@
1
+ From c96dd61ea8cfefe92ab911a881be4385713b3a31 Mon Sep 17 00:00:00 2001
2
+ From: Matt Campbell <matt@battleshopper.com>
3
+ Date: Wed, 18 Mar 2026 16:12:22 -0500
4
+ Subject: [PATCH 05/10] Use chart icon for usage nav
5
+
6
+ ---
7
+ packages/app/src/pages/layout/sidebar-shell.tsx | 2 +-
8
+ packages/ui/src/components/icon.tsx | 1 +
9
+ 2 files changed, 2 insertions(+), 1 deletion(-)
10
+
11
+ diff --git a/packages/app/src/pages/layout/sidebar-shell.tsx b/packages/app/src/pages/layout/sidebar-shell.tsx
12
+ index 7ed209697..47df7f298 100644
13
+ --- a/packages/app/src/pages/layout/sidebar-shell.tsx
14
+ +++ b/packages/app/src/pages/layout/sidebar-shell.tsx
15
+ @@ -75,7 +75,7 @@ export const SidebarContent = (props: {
16
+ <div class="shrink-0 w-full pt-3 pb-6 flex flex-col items-center gap-2">
17
+ <Tooltip placement={placement()} value={props.usageLabel()}>
18
+ <IconButton
19
+ - icon="status"
20
+ + icon="chart-bars"
21
+ variant="ghost"
22
+ size="large"
23
+ onClick={props.onOpenUsage}
24
+ diff --git a/packages/ui/src/components/icon.tsx b/packages/ui/src/components/icon.tsx
25
+ index e2eaf107a..f40783117 100644
26
+ --- a/packages/ui/src/components/icon.tsx
27
+ +++ b/packages/ui/src/components/icon.tsx
28
+ @@ -102,6 +102,7 @@ const icons = {
29
+ link: `<path d="M2.08334 12.0833L1.72979 11.7298L1.37624 12.0833L1.72979 12.4369L2.08334 12.0833ZM7.91668 17.9167L7.56312 18.2702L7.91668 18.6238L8.27023 18.2702L7.91668 17.9167ZM17.9167 7.91666L18.2702 8.27022L18.6238 7.91666L18.2702 7.56311L17.9167 7.91666ZM12.0833 2.08333L12.4369 1.72977L12.0833 1.37622L11.7298 1.72977L12.0833 2.08333ZM8.39646 5.06311L8.0429 5.41666L8.75001 6.12377L9.10356 5.77021L8.75001 5.41666L8.39646 5.06311ZM5.77023 9.10355L6.12378 8.74999L5.41668 8.04289L5.06312 8.39644L5.41668 8.74999L5.77023 9.10355ZM14.2298 10.8964L13.8762 11.25L14.5833 11.9571L14.9369 11.6035L14.5833 11.25L14.2298 10.8964ZM11.6036 14.9369L11.9571 14.5833L11.25 13.8762L10.8965 14.2298L11.25 14.5833L11.6036 14.9369ZM7.14646 12.1464L6.7929 12.5L7.50001 13.2071L7.85356 12.8535L7.50001 12.5L7.14646 12.1464ZM12.8536 7.85355L13.2071 7.49999L12.5 6.79289L12.1465 7.14644L12.5 7.49999L12.8536 7.85355ZM2.08334 12.0833L1.72979 12.4369L7.56312 18.2702L7.91668 17.9167L8.27023 17.5631L2.4369 11.7298L2.08334 12.0833ZM17.9167 7.91666L18.2702 7.56311L12.4369 1.72977L12.0833 2.08333L11.7298 2.43688L17.5631 8.27022L17.9167 7.91666ZM12.0833 2.08333L11.7298 1.72977L8.39646 5.06311L8.75001 5.41666L9.10356 5.77021L12.4369 2.43688L12.0833 2.08333ZM5.41668 8.74999L5.06312 8.39644L1.72979 11.7298L2.08334 12.0833L2.4369 12.4369L5.77023 9.10355L5.41668 8.74999ZM14.5833 11.25L14.9369 11.6035L18.2702 8.27022L17.9167 7.91666L17.5631 7.56311L14.2298 10.8964L14.5833 11.25ZM7.91668 17.9167L8.27023 18.2702L11.6036 14.9369L11.25 14.5833L10.8965 14.2298L7.56312 17.5631L7.91668 17.9167ZM7.50001 12.5L7.85356 12.8535L12.8536 7.85355L12.5 7.49999L12.1465 7.14644L7.14646 12.1464L7.50001 12.5Z" fill="currentColor"/>`,
30
+ providers: `<path d="M10.0001 4.37562V2.875M13 4.37793V2.87793M7.00014 4.37793V2.875M10 17.1279V15.6279M13 17.1279V15.6279M7 17.1279V15.6279M15.625 13.0029H17.125M15.625 7.00293H17.125M15.625 10.0029H17.125M2.875 10.0029H4.375M2.875 13.0029H4.375M2.875 7.00293H4.375M4.375 4.37793H15.625V15.6279H4.375V4.37793ZM12.6241 10.0022C12.6241 11.4519 11.4488 12.6272 9.99908 12.6272C8.54934 12.6272 7.37408 11.4519 7.37408 10.0022C7.37408 8.55245 8.54934 7.3772 9.99908 7.3772C11.4488 7.3772 12.6241 8.55245 12.6241 10.0022Z" stroke="currentColor" stroke-linecap="square"/>`,
31
+ models: `<path fill-rule="evenodd" clip-rule="evenodd" d="M17.5 10C12.2917 10 10 12.2917 10 17.5C10 12.2917 7.70833 10 2.5 10C7.70833 10 10 7.70833 10 2.5C10 7.70833 12.2917 10 17.5 10Z" stroke="currentColor"/>`,
32
+ + "chart-bars": `<path d="M3.75 16.25H16.25" stroke="currentColor" stroke-linecap="square"/><path d="M6.25 16.25V10.8333" stroke="currentColor" stroke-linecap="square"/><path d="M10 16.25V7.5" stroke="currentColor" stroke-linecap="square"/><path d="M13.75 16.25V4.16667" stroke="currentColor" stroke-linecap="square"/>`,
33
+ }
34
+
35
+ export interface IconProps extends ComponentProps<"svg"> {
36
+ --
37
+ 2.53.0
38
+