@desplega.ai/agent-swarm 2.7.6 → 2.7.7
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.
- package/dist/{acp-adapter-bp91d37f.js → acp-adapter-7b4vk3h5.js} +5 -4
- package/dist/{actions-yj0eeaqh.js → actions-3tt153af.js} +18 -18
- package/dist/{amp-adapter-4pg9h34q.js → amp-adapter-njpts0yr.js} +1 -1
- package/dist/{anthropic-messages-x4q6rfq5.js → anthropic-messages-xey2skwx.js} +7 -7
- package/dist/{app-909e97cr.js → app-nky4378f.js} +3 -3
- package/dist/{assistant-qdyb5fyy.js → assistant-jnb669pa.js} +19 -19
- package/dist/{azure-openai-responses-s1w5n70z.js → azure-openai-responses-26t7j9ra.js} +5 -5
- package/dist/{boot-reembed-fk0s9z18.js → boot-reembed-9mgvaztt.js} +1 -1
- package/dist/{boot-reembed-gs1rzrnh.js → boot-reembed-qs2djdej.js} +1 -1
- package/dist/{boot-scrub-sweep-4qfd4kfx.js → boot-scrub-sweep-yjpdj7s1.js} +3 -3
- package/dist/{boot-seeds-yr00k13d.js → boot-seeds-5cm6ambd.js} +4 -4
- package/dist/{boxd-vdmfssd9.js → boxd-nv507va8.js} +1 -1
- package/dist/{claude-adapter-qta1f1xn.js → claude-adapter-fn287zmm.js} +5 -4
- package/dist/{claude-managed-setup-sc6j4dm0.js → claude-managed-setup-wtdz2mj2.js} +1 -1
- package/dist/{claude-sdk-session-zgrz6yvc.js → claude-sdk-session-x91kqyhm.js} +8288 -8224
- package/dist/{cli-wp9sp1qb.js → cli-08bmz9z9.js} +1 -1
- package/dist/{cli-yy3rhsdw.js → cli-1c6tzt8g.js} +7 -7
- package/dist/{cli-q0k1w5zd.js → cli-35rsp8ye.js} +1 -1
- package/dist/{cli-xnrwazav.js → cli-3fg0bdcv.js} +1 -1
- package/dist/{cli-89knwags.js → cli-5b456bpf.js} +2 -2
- package/dist/{cli-drr5v6av.js → cli-5jnhv72d.js} +1 -1
- package/dist/{cli-dxae2yg7.js → cli-5zx699j9.js} +2 -2
- package/dist/{cli-0at8w34w.js → cli-6g0hb4cp.js} +8 -8
- package/dist/{cli-e3r2neck.js → cli-6pfd54vx.js} +5 -5
- package/dist/{cli-bsn8nhrq.js → cli-6vcp76qk.js} +3 -3
- package/dist/{cli-ekwks2vs.js → cli-72v799xn.js} +1 -1
- package/dist/cli-82j36f2m.js +45 -0
- package/dist/{cli-yx4kfngt.js → cli-8305gbq0.js} +29 -16
- package/dist/{cli-4necwtsv.js → cli-8sckt74b.js} +8 -8
- package/dist/{cli-vvpx3gsx.js → cli-95jzxcp3.js} +1 -1
- package/dist/{cli-hr2e2fft.js → cli-adeegh7d.js} +7 -7
- package/dist/{cli-m48g6p95.js → cli-afkqkrtw.js} +3 -3
- package/dist/{cli-0qatpy8q.js → cli-ayayvfg4.js} +24 -4
- package/dist/{cli-bqt1mptt.js → cli-az48n0nr.js} +4 -4
- package/dist/{cli-zm72anhg.js → cli-bfv7vdg6.js} +9 -9
- package/dist/{cli-hgaazk69.js → cli-caj33vcq.js} +5 -5
- package/dist/{cli-3ypk9vk1.js → cli-cb2a8pna.js} +1 -1
- package/dist/{cli-waqbxg08.js → cli-cppkf404.js} +17 -17
- package/dist/{cli-yn4kxs06.js → cli-ddaanqkd.js} +4 -4
- package/dist/{cli-386tyxxg.js → cli-e0kmphd6.js} +2 -2
- package/dist/{cli-54t00gt8.js → cli-e46hg0xw.js} +1 -1
- package/dist/{cli-jm2vfpfd.js → cli-e5bzrn01.js} +1 -1
- package/dist/{cli-436532ck.js → cli-excnnjg2.js} +165 -153
- package/dist/{cli-71ptav7x.js → cli-ferkkrg9.js} +1 -1
- package/dist/{cli-vpf89f34.js → cli-fmjd0t1y.js} +3 -3
- package/dist/{cli-pd7vagab.js → cli-fyt2g4wd.js} +3 -3
- package/dist/{cli-gdhpcbhf.js → cli-g08zdaec.js} +1 -1
- package/dist/{cli-ehq736cn.js → cli-g7s47cc5.js} +4 -4
- package/dist/{cli-svj5090z.js → cli-gz6q4eve.js} +3 -3
- package/dist/{cli-8snvgs5r.js → cli-je518txm.js} +10 -10
- package/dist/{cli-rmmmrjkd.js → cli-jtjtabxe.js} +1 -1
- package/dist/{cli-611a1294.js → cli-k0wwfqbg.js} +18 -1
- package/dist/{cli-es6va8jk.js → cli-k1nje3v3.js} +38 -43
- package/dist/{cli-p5gp8pcx.js → cli-k8196sh2.js} +6 -6
- package/dist/{cli-nwj0ee2w.js → cli-k899ehvh.js} +2 -2
- package/dist/{cli-decfb6v8.js → cli-m0da8j35.js} +23 -9
- package/dist/{cli-he9wchme.js → cli-mkk9rnzz.js} +4 -4
- package/dist/{cli-nhrvpaj7.js → cli-n6bv12jn.js} +510 -4
- package/dist/{cli-8bc1w0tw.js → cli-nmzhp1b5.js} +14 -12
- package/dist/{cli-0yv5z2pg.js → cli-p82cnnjq.js} +1 -1
- package/dist/{cli-c60vetmm.js → cli-pc6n7fre.js} +1 -1
- package/dist/{cli-xds5f77d.js → cli-pjctyea3.js} +2 -2
- package/dist/{cli-xbbndvjy.js → cli-pq73h5mz.js} +3 -3
- package/dist/{cli-aa6yxc7m.js → cli-pz4rk021.js} +1 -1
- package/dist/{cli-xnqgpshy.js → cli-q3tkegby.js} +1 -1
- package/dist/cli-qghe0h66.js +67 -0
- package/dist/{cli-zbbwez0r.js → cli-qh4d4tvk.js} +4 -4
- package/dist/{cli-w6dt9ged.js → cli-rtt8g4v9.js} +1 -1
- package/dist/{cli-1h3wcpbw.js → cli-snpj5zbj.js} +2 -2
- package/dist/{cli-a7v854rf.js → cli-tgrv9cjx.js} +263 -12
- package/dist/{cli-faxe6thg.js → cli-vz22fyqv.js} +186 -54
- package/dist/{cli-k2gzwsj2.js → cli-wk5yrfwd.js} +160 -26
- package/dist/{cli-shpbx5ee.js → cli-ws19768n.js} +1641 -49
- package/dist/{cli-70r77n04.js → cli-xae4g51r.js} +1 -1
- package/dist/{cli-kqdg1bsb.js → cli-yndmfxb3.js} +2 -2
- package/dist/{cli-bjb3z51z.js → cli-yyj3j5m4.js} +2 -2
- package/dist/cli.js +21 -21
- package/dist/{cloudflare-workers-ai-system-one-bm60fjm0.js → cloudflare-workers-ai-system-one-ef2a980q.js} +2 -2
- package/dist/{codex-adapter-ayd1fb6h.js → codex-adapter-cf2yxe9m.js} +3 -2
- package/dist/{codex-hook-9efm2x1m.js → codex-hook-a0c6g6t6.js} +1 -1
- package/dist/{codex-session-runner-jesa32xc.js → codex-session-runner-6vfzdgbd.js} +3 -2
- package/dist/{commands-hanewgf2.js → commands-t3mfy7vn.js} +3 -3
- package/dist/{controller-dt1n3c1q.js → controller-q7xrbaz0.js} +3 -3
- package/dist/{controllers-connect-xx8x64n5.js → controllers-connect-c6nm70kg.js} +19 -19
- package/dist/{cursor-adapter-9s4dwwdw.js → cursor-adapter-ftd81ga8.js} +1 -1
- package/dist/{db-3vttcbv4.js → db-dk572x25.js} +3 -3
- package/dist/{db-9dv0g1ab.js → db-vemtpmcc.js} +4 -2
- package/dist/{dispatcher-tx9hd1sv.js → dispatcher-kh7ecxh9.js} +17 -17
- package/dist/{dsh-adapter-007y4ejs.js → dsh-adapter-v8628r89.js} +1 -1
- package/dist/{e2b-we6hs6q5.js → e2b-fr12yccx.js} +1 -1
- package/dist/{e2b-a19d28rz.js → e2b-x6ywzak3.js} +1 -1
- package/dist/{easter-egg-3d-rgbrqb8s.js → easter-egg-3d-nenw4x63.js} +1 -1
- package/dist/{embedding-space-ny1e2940.js → embedding-space-0jepb239.js} +6 -6
- package/dist/{embeddings-2t386t13.js → embeddings-xxt49yxg.js} +1 -1
- package/dist/{execute-pksh0ceh.js → execute-1mhw6e4x.js} +1 -1
- package/dist/{external-channel-phgmhmd7.js → external-channel-55v12e60.js} +2 -2
- package/dist/{fx-adapter-fy8rr921.js → fx-adapter-bs8wqrk5.js} +6 -5
- package/dist/{google-generative-ai-8y3m3k4b.js → google-generative-ai-v94hr7h0.js} +5 -5
- package/dist/{google-vertex-w3v98n7b.js → google-vertex-g7vr6cf8.js} +5 -5
- package/dist/{grok-adapter-q0abvpaj.js → grok-adapter-gpjqmkcq.js} +6 -5
- package/dist/handlers-zxmp1ta4.js +32 -0
- package/dist/{heartbeat-kadyek8p.js → heartbeat-k42svnr0.js} +20 -20
- package/dist/{hook-9k551xnj.js → hook-114m7hap.js} +4 -3
- package/dist/{hook-5zs3msf6.js → hook-6nphweh0.js} +24 -9
- package/dist/{http-mhz9vw1f.js → http-r8cc4wz7.js} +1025 -173
- package/dist/index-a8phxqnk.js +37 -0
- package/dist/{index-vdygbq10.js → index-e3s8wmxy.js} +1 -1
- package/dist/{index-q361cg2f.js → index-fpk4bcd9.js} +1 -1
- package/dist/index-k6h00t4c.js +30 -0
- package/dist/{index-rvr00sg7.js → index-nh3wyqdt.js} +1017 -574
- package/dist/index-xrz7te8h.js +33 -0
- package/dist/{keepalive-w56f32cg.js → keepalive-x060nt9p.js} +3 -3
- package/dist/lead-956sd2yc.js +71 -0
- package/dist/{maintenance-7mdgxcea.js → maintenance-2xxfexhf.js} +1 -1
- package/dist/{mistral-conversations-4pmwj1c0.js → mistral-conversations-3gjpdb4b.js} +4 -4
- package/dist/{oauth-refresh-sweep-29txy9vk.js → oauth-refresh-sweep-fckc4ar5.js} +2 -2
- package/dist/{onboard-kpsrkarj.js → onboard-zc48j237.js} +3 -3
- package/dist/{openai-codex-responses-44j573nm.js → openai-codex-responses-8pbxq9h5.js} +5 -5
- package/dist/{openai-completions-a4gee2a8.js → openai-completions-rm00s912.js} +4 -4
- package/dist/{openai-decisions-w4qkrcar.js → openai-decisions-8v957r2r.js} +1 -1
- package/dist/{openai-responses-rbp6h5kr.js → openai-responses-agxpf96k.js} +5 -5
- package/dist/{opencode-adapter-j25vm3rr.js → opencode-adapter-r690h1cr.js} +4 -3
- package/dist/{otel-impl-6kkct7a8.js → otel-impl-r5xbh81q.js} +4 -4
- package/dist/{pi-mono-adapter-exhkzhn2.js → pi-mono-adapter-c7v8sdec.js} +7 -6
- package/dist/{pricing-refresh-n2x7jh6k.js → pricing-refresh-3pmre11v.js} +3 -3
- package/dist/{rbac-roles-81ypx6es.js → rbac-roles-3vbshwsb.js} +1 -1
- package/dist/{rbac-roles-b5htkjby.js → rbac-roles-910mmkgz.js} +2 -2
- package/dist/{render-v2-qn6jmpk4.js → render-v2-pxykkbab.js} +3 -3
- package/dist/{resume-wd30wx23.js → resume-adda0bwy.js} +6 -6
- package/dist/{runtime-4hsg7h60.js → runtime-fnbm7stk.js} +1 -1
- package/dist/{session-access-5qvfevhk.js → session-access-2fvamrdc.js} +4 -4
- package/dist/{session-source-backfill-n1czavbr.js → session-source-backfill-08xvv0sm.js} +2 -2
- package/dist/{setup-dnb56qy5.js → setup-1aqmvmh1.js} +1 -1
- package/dist/{typesafe-system-one-5mavm259.js → typesafe-system-one-0bmqcxfg.js} +2 -2
- package/dist/{virtual-modules-76xxv9fs.js → virtual-modules-d2pjk9j1.js} +5 -5
- package/dist/{work-objects-4n21tfkm.js → work-objects-9s0yrm5j.js} +20 -20
- package/dist/{worker-follow-up-77hvzxww.js → worker-follow-up-3q78fqmr.js} +17 -17
- package/dist/worker-y15hy08h.js +71 -0
- package/openapi.json +197 -4
- package/package.json +5 -5
- package/src/apps/definition.ts +6 -5
- package/src/be/db/page-theme-tokens.ts +12 -0
- package/src/be/db/tasks/read.ts +9 -1
- package/src/be/db/tasks/write.ts +19 -0
- package/src/be/db-client.ts +107 -7
- package/src/be/db.ts +34 -2
- package/src/be/migrations/269_pages_design_system.sql +23 -0
- package/src/be/seed/schedules-seeder.ts +0 -10
- package/src/be/seed-scripts/catalog/catalog-report.inline.ts +2 -0
- package/src/be/seed-scripts/catalog/catalog-report.ts +2 -0
- package/src/be/seed-scripts/catalog/memory-eval.ts +2 -0
- package/src/be/seed-scripts/catalog/swarm-update-check.ts +150 -0
- package/src/be/seed-scripts/index.ts +9 -0
- package/src/be/seed-skills/bundled-files.generated.json +3 -3
- package/src/be/swarm-config-guard.ts +32 -0
- package/src/be/task-auto-title-listener.ts +127 -0
- package/src/commands/onboard/agent-fs-version.ts +1 -1
- package/src/commands/profile-sync.ts +151 -16
- package/src/commands/runner.ts +15 -7
- package/src/hooks/hook.ts +20 -9
- package/src/http/index.ts +3 -0
- package/src/http/pages-public.ts +93 -129
- package/src/http/pages.ts +94 -21
- package/src/pages/browser-sdk.ts +48 -21
- package/src/pages/color-literals.ts +115 -0
- package/src/pages/page-head.ts +766 -0
- package/src/pages/version.ts +3 -0
- package/src/providers/amp-adapter.ts +1 -1
- package/src/providers/codex-adapter.ts +1 -0
- package/src/providers/codex-app-server.ts +27 -1
- package/src/providers/pi-mono-extension.ts +30 -15
- package/src/tasks/follow-up-gate.ts +318 -0
- package/src/tasks/worker-follow-up.ts +21 -1
- package/src/tests/codex-app-server.test.ts +59 -0
- package/src/tests/codex-gateway-route.test.ts +50 -1
- package/src/tests/create-page-tool.test.ts +501 -0
- package/src/tests/db-client-busy-retry.test.ts +81 -0
- package/src/tests/db-task-read-contract.test.ts +1 -0
- package/src/tests/db-task-write-contract.test.ts +2 -0
- package/src/tests/db-tasks-read.test.ts +21 -0
- package/src/tests/fixtures/pages/head-v1.html +56 -0
- package/src/tests/follow-up-gate-shadow.test.ts +513 -0
- package/src/tests/identity-field-budget.test.ts +1 -1
- package/src/tests/internal-ai/credentials.test.ts +62 -0
- package/src/tests/internal-ai/task-title.test.ts +283 -0
- package/src/tests/merge-gate-detect-changes.test.ts +145 -0
- package/src/tests/page-proxy.test.ts +1 -1
- package/src/tests/page-session-dev-cookie.test.ts +188 -0
- package/src/tests/pages-frame-ancestors.test.ts +28 -1
- package/src/tests/pages-head-v2.test.ts +534 -0
- package/src/tests/pages-http.test.ts +330 -0
- package/src/tests/pages-public-html.test.ts +43 -0
- package/src/tests/pages-storage.test.ts +182 -0
- package/src/tests/pages-versioning.test.ts +72 -0
- package/src/tests/profile-sync-stale-writeback.test.ts +334 -0
- package/src/tests/profile-sync.test.ts +11 -15
- package/src/tests/scheduled-task-required-params-migration.test.ts +3 -1
- package/src/tests/script-connections.test.ts +2 -0
- package/src/tests/seed-registry.test.ts +28 -6
- package/src/tests/seed-schedules.test.ts +61 -8
- package/src/tests/seed-scripts.test.ts +142 -2
- package/src/tests/swarm-diff.test.ts +121 -2
- package/src/tests/task-auto-title-config-guard.test.ts +37 -0
- package/src/tests/task-auto-title-listener.test.ts +165 -0
- package/src/tests/theme-import.test.ts +758 -0
- package/src/tests/themes-page-theme.test.ts +155 -0
- package/src/tests/themes-presets.test.ts +172 -0
- package/src/tests/workflow-script-principal.test.ts +2 -1
- package/src/themes/classics.ts +949 -0
- package/src/themes/import.ts +420 -0
- package/src/themes/page-fields.ts +40 -0
- package/src/themes/page-theme.ts +102 -0
- package/src/themes/presets.ts +561 -0
- package/src/themes/tokens.ts +218 -0
- package/src/tools/app-upsert.ts +1 -1
- package/src/tools/create-page.ts +118 -12
- package/src/tools/utils.ts +33 -0
- package/src/types.ts +45 -3
- package/src/utils/constants.ts +19 -0
- package/src/utils/follow-up-gate-schedule-ids.ts +13 -0
- package/src/utils/internal-ai/credentials.ts +146 -93
- package/src/utils/internal-ai/index.ts +10 -0
- package/src/utils/internal-ai/models.ts +18 -0
- package/src/utils/internal-ai/task-title.ts +282 -0
- package/src/utils/page-session.ts +37 -0
- package/templates/schedules/daily-status-report/content.md +88 -2
- package/templates/schedules/daily-swarm-update-check/content.md +16 -40
- package/templates/skills/apps/SKILL.md +1 -1
- package/templates/skills/apps/content.md +1 -1
- package/templates/skills/heartbeat-runbook/content.md +1 -1
- package/templates/skills/pages/config.json +1 -1
- package/templates/skills/pages/content.md +539 -86
- package/templates/skills/pages/files/examples/annotated-pr.html +21 -12
- package/templates/skills/pages/files/examples/multiplayer-board.html +41 -41
- package/templates/skills/pages/files/examples/report-page.html +125 -124
- package/dist/cli-2pnswgh8.js +0 -189
- package/dist/handlers-by3sfxyx.js +0 -32
- package/dist/index-4f2pvxae.js +0 -37
- package/dist/index-98gz7tmp.js +0 -30
- package/dist/index-k08z2cak.js +0 -33
- package/dist/lead-enq35nqp.js +0 -70
- package/dist/worker-gg1576j3.js +0 -70
- package/templates/schedules/daily-workflow-health-audit/config.json +0 -19
- package/templates/schedules/daily-workflow-health-audit/content.md +0 -191
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Page theme token contract: the dashboard token names that HTML pages
|
|
3
|
+
* (design version 2) expose.
|
|
4
|
+
*
|
|
5
|
+
* A page's look comes only from these custom properties. Presets, per-page
|
|
6
|
+
* overrides and imported themes all resolve to this set, so one switch
|
|
7
|
+
* restyles the whole page. Sidebar, action, peer, type-scale and easing
|
|
8
|
+
* tokens are dashboard-only and stay out of the contract.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/** Color tokens: one value per mode. */
|
|
12
|
+
export const PAGE_THEME_COLOR_TOKENS = [
|
|
13
|
+
"--color-background",
|
|
14
|
+
"--color-foreground",
|
|
15
|
+
"--color-card",
|
|
16
|
+
"--color-card-foreground",
|
|
17
|
+
"--color-surface",
|
|
18
|
+
"--color-popover",
|
|
19
|
+
"--color-popover-foreground",
|
|
20
|
+
"--color-primary",
|
|
21
|
+
"--color-primary-foreground",
|
|
22
|
+
"--color-secondary",
|
|
23
|
+
"--color-secondary-foreground",
|
|
24
|
+
"--color-muted",
|
|
25
|
+
"--color-muted-foreground",
|
|
26
|
+
"--color-accent",
|
|
27
|
+
"--color-accent-foreground",
|
|
28
|
+
"--color-destructive",
|
|
29
|
+
"--color-destructive-foreground",
|
|
30
|
+
"--color-border",
|
|
31
|
+
"--color-border-subtle",
|
|
32
|
+
"--color-input",
|
|
33
|
+
"--color-ring",
|
|
34
|
+
"--color-chart-1",
|
|
35
|
+
"--color-chart-2",
|
|
36
|
+
"--color-chart-3",
|
|
37
|
+
"--color-chart-4",
|
|
38
|
+
"--color-chart-5",
|
|
39
|
+
"--color-status-success",
|
|
40
|
+
"--color-status-success-strong",
|
|
41
|
+
"--color-status-success-foreground",
|
|
42
|
+
"--color-status-active",
|
|
43
|
+
"--color-status-active-strong",
|
|
44
|
+
"--color-status-active-foreground",
|
|
45
|
+
"--color-status-error",
|
|
46
|
+
"--color-status-error-strong",
|
|
47
|
+
"--color-status-error-foreground",
|
|
48
|
+
"--color-status-info",
|
|
49
|
+
"--color-status-info-strong",
|
|
50
|
+
"--color-status-info-foreground",
|
|
51
|
+
"--color-status-pending",
|
|
52
|
+
"--color-status-pending-strong",
|
|
53
|
+
"--color-status-pending-foreground",
|
|
54
|
+
"--color-status-warning",
|
|
55
|
+
"--color-status-warning-strong",
|
|
56
|
+
"--color-status-warning-foreground",
|
|
57
|
+
"--color-status-paused",
|
|
58
|
+
"--color-status-paused-strong",
|
|
59
|
+
"--color-status-paused-foreground",
|
|
60
|
+
"--color-status-neutral",
|
|
61
|
+
"--color-status-neutral-strong",
|
|
62
|
+
"--color-status-neutral-foreground",
|
|
63
|
+
"--color-status-success-solid",
|
|
64
|
+
"--color-status-active-solid",
|
|
65
|
+
] as const;
|
|
66
|
+
|
|
67
|
+
/** Font and radius tokens: one value for both modes. */
|
|
68
|
+
export const PAGE_THEME_SHARED_TOKENS = [
|
|
69
|
+
"--font-sans",
|
|
70
|
+
"--font-mono",
|
|
71
|
+
"--radius-sm",
|
|
72
|
+
"--radius-md",
|
|
73
|
+
"--radius-lg",
|
|
74
|
+
"--radius-xl",
|
|
75
|
+
] as const;
|
|
76
|
+
|
|
77
|
+
export const PAGE_THEME_TOKENS = [...PAGE_THEME_COLOR_TOKENS, ...PAGE_THEME_SHARED_TOKENS] as const;
|
|
78
|
+
|
|
79
|
+
export type PageThemeToken = (typeof PAGE_THEME_TOKENS)[number];
|
|
80
|
+
export type PageThemeTokenValues = Partial<Record<PageThemeToken, string>>;
|
|
81
|
+
export type PageThemeTokens = { light: PageThemeTokenValues; dark: PageThemeTokenValues };
|
|
82
|
+
|
|
83
|
+
type PageThemeColorToken = (typeof PAGE_THEME_COLOR_TOKENS)[number];
|
|
84
|
+
type PageThemeSharedToken = (typeof PAGE_THEME_SHARED_TOKENS)[number];
|
|
85
|
+
|
|
86
|
+
/** Hive values from the light `@theme` block of `apps/ui/src/styles/globals.css`. */
|
|
87
|
+
const HIVE_SHARED_TOKENS: Record<PageThemeSharedToken, string> = {
|
|
88
|
+
"--font-sans": '"Space Grotesk", sans-serif',
|
|
89
|
+
"--font-mono": '"Space Mono", monospace',
|
|
90
|
+
"--radius-sm": "0.375rem",
|
|
91
|
+
"--radius-md": "0.5rem",
|
|
92
|
+
"--radius-lg": "0.625rem",
|
|
93
|
+
"--radius-xl": "0.625rem",
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const HIVE_LIGHT_COLORS: Record<PageThemeColorToken, string> = {
|
|
97
|
+
"--color-background": "oklch(1 0 0)",
|
|
98
|
+
"--color-foreground": "oklch(0.141 0.005 285.823)",
|
|
99
|
+
"--color-card": "oklch(1 0 0)",
|
|
100
|
+
"--color-card-foreground": "oklch(0.141 0.005 285.823)",
|
|
101
|
+
"--color-surface": "oklch(0.985 0.0015 286)",
|
|
102
|
+
"--color-popover": "oklch(1 0 0)",
|
|
103
|
+
"--color-popover-foreground": "oklch(0.141 0.005 285.823)",
|
|
104
|
+
"--color-primary": "oklch(0.555 0.163 48.998)",
|
|
105
|
+
"--color-primary-foreground": "oklch(0.985 0 0)",
|
|
106
|
+
"--color-secondary": "oklch(0.967 0.001 286.375)",
|
|
107
|
+
"--color-secondary-foreground": "oklch(0.21 0.006 285.885)",
|
|
108
|
+
"--color-muted": "oklch(0.967 0.001 286.375)",
|
|
109
|
+
"--color-muted-foreground": "oklch(0.552 0.016 285.938)",
|
|
110
|
+
"--color-accent": "oklch(0.943 0.003 286.375)",
|
|
111
|
+
"--color-accent-foreground": "oklch(0.21 0.006 285.885)",
|
|
112
|
+
"--color-destructive": "oklch(0.577 0.245 27.325)",
|
|
113
|
+
"--color-destructive-foreground": "oklch(0.985 0 0)",
|
|
114
|
+
"--color-border": "oklch(0.92 0.004 286.32)",
|
|
115
|
+
"--color-border-subtle": "oklch(0.945 0.003 286)",
|
|
116
|
+
"--color-input": "oklch(0.92 0.004 286.32)",
|
|
117
|
+
"--color-ring": "oklch(0.555 0.163 48.998)",
|
|
118
|
+
"--color-chart-1": "oklch(0.646 0.222 41.116)",
|
|
119
|
+
"--color-chart-2": "oklch(0.6 0.118 184.704)",
|
|
120
|
+
"--color-chart-3": "oklch(0.398 0.07 227.392)",
|
|
121
|
+
"--color-chart-4": "oklch(0.828 0.189 84.429)",
|
|
122
|
+
"--color-chart-5": "oklch(0.769 0.188 70.08)",
|
|
123
|
+
"--color-status-success": "oklch(0.74 0.1 163)",
|
|
124
|
+
"--color-status-success-strong": "oklch(0.5 0.09 163)",
|
|
125
|
+
"--color-status-success-foreground": "oklch(0.21 0.006 285.885)",
|
|
126
|
+
"--color-status-active": "oklch(0.81 0.11 75)",
|
|
127
|
+
"--color-status-active-strong": "oklch(0.55 0.1 68)",
|
|
128
|
+
"--color-status-active-foreground": "oklch(0.21 0.006 285.885)",
|
|
129
|
+
"--color-status-error": "oklch(0.72 0.12 25)",
|
|
130
|
+
"--color-status-error-strong": "oklch(0.51 0.14 25)",
|
|
131
|
+
"--color-status-error-foreground": "oklch(0.21 0.006 285.885)",
|
|
132
|
+
"--color-status-info": "oklch(0.74 0.09 235)",
|
|
133
|
+
"--color-status-info-strong": "oklch(0.5 0.09 240)",
|
|
134
|
+
"--color-status-info-foreground": "oklch(0.21 0.006 285.885)",
|
|
135
|
+
"--color-status-pending": "oklch(0.84 0.1 95)",
|
|
136
|
+
"--color-status-pending-strong": "oklch(0.55 0.09 90)",
|
|
137
|
+
"--color-status-pending-foreground": "oklch(0.21 0.006 285.885)",
|
|
138
|
+
"--color-status-warning": "oklch(0.76 0.11 55)",
|
|
139
|
+
"--color-status-warning-strong": "oklch(0.53 0.11 50)",
|
|
140
|
+
"--color-status-warning-foreground": "oklch(0.21 0.006 285.885)",
|
|
141
|
+
"--color-status-paused": "oklch(0.71 0.1 262)",
|
|
142
|
+
"--color-status-paused-strong": "oklch(0.5 0.1 262)",
|
|
143
|
+
"--color-status-paused-foreground": "oklch(0.21 0.006 285.885)",
|
|
144
|
+
"--color-status-neutral": "oklch(0.62 0.014 286)",
|
|
145
|
+
"--color-status-neutral-strong": "oklch(0.48 0.014 286)",
|
|
146
|
+
"--color-status-neutral-foreground": "oklch(0.21 0.006 285.885)",
|
|
147
|
+
"--color-status-success-solid": "oklch(0.627 0.194 149.2)",
|
|
148
|
+
"--color-status-active-solid": "oklch(0.666 0.179 58.318)",
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
const HIVE_DARK_COLORS: Record<PageThemeColorToken, string> = {
|
|
152
|
+
"--color-background": "oklch(0.141 0.005 285.823)",
|
|
153
|
+
"--color-foreground": "oklch(0.985 0 0)",
|
|
154
|
+
"--color-card": "oklch(0.21 0.006 285.885)",
|
|
155
|
+
"--color-card-foreground": "oklch(0.985 0 0)",
|
|
156
|
+
"--color-surface": "oklch(0.185 0.006 286)",
|
|
157
|
+
"--color-popover": "oklch(0.21 0.006 285.885)",
|
|
158
|
+
"--color-popover-foreground": "oklch(0.985 0 0)",
|
|
159
|
+
"--color-primary": "oklch(0.769 0.188 70.08)",
|
|
160
|
+
"--color-primary-foreground": "oklch(0.21 0.006 285.885)",
|
|
161
|
+
"--color-secondary": "oklch(0.274 0.006 286.033)",
|
|
162
|
+
"--color-secondary-foreground": "oklch(0.985 0 0)",
|
|
163
|
+
"--color-muted": "oklch(0.274 0.006 286.033)",
|
|
164
|
+
"--color-muted-foreground": "oklch(0.705 0.015 286.067)",
|
|
165
|
+
"--color-accent": "oklch(0.274 0.006 286.033)",
|
|
166
|
+
"--color-accent-foreground": "oklch(0.985 0 0)",
|
|
167
|
+
"--color-destructive": "oklch(0.704 0.191 22.216)",
|
|
168
|
+
"--color-destructive-foreground": "oklch(0.985 0 0)",
|
|
169
|
+
"--color-border": "oklch(1 0 0 / 10%)",
|
|
170
|
+
"--color-border-subtle": "oklch(1 0 0 / 8%)",
|
|
171
|
+
"--color-input": "oklch(1 0 0 / 15%)",
|
|
172
|
+
"--color-ring": "oklch(0.769 0.188 70.08)",
|
|
173
|
+
"--color-chart-1": "oklch(0.488 0.243 264.376)",
|
|
174
|
+
"--color-chart-2": "oklch(0.696 0.17 162.48)",
|
|
175
|
+
"--color-chart-3": "oklch(0.769 0.188 70.08)",
|
|
176
|
+
"--color-chart-4": "oklch(0.627 0.265 303.9)",
|
|
177
|
+
"--color-chart-5": "oklch(0.645 0.246 16.439)",
|
|
178
|
+
"--color-status-success": "oklch(0.78 0.1 163)",
|
|
179
|
+
"--color-status-success-strong": "oklch(0.78 0.1 163)",
|
|
180
|
+
"--color-status-success-foreground": "oklch(0.21 0.006 285.885)",
|
|
181
|
+
"--color-status-active": "oklch(0.84 0.1 80)",
|
|
182
|
+
"--color-status-active-strong": "oklch(0.84 0.1 80)",
|
|
183
|
+
"--color-status-active-foreground": "oklch(0.21 0.006 285.885)",
|
|
184
|
+
"--color-status-error": "oklch(0.74 0.11 22)",
|
|
185
|
+
"--color-status-error-strong": "oklch(0.74 0.11 22)",
|
|
186
|
+
"--color-status-error-foreground": "oklch(0.21 0.006 285.885)",
|
|
187
|
+
"--color-status-info": "oklch(0.78 0.09 235)",
|
|
188
|
+
"--color-status-info-strong": "oklch(0.78 0.09 235)",
|
|
189
|
+
"--color-status-info-foreground": "oklch(0.21 0.006 285.885)",
|
|
190
|
+
"--color-status-pending": "oklch(0.86 0.09 95)",
|
|
191
|
+
"--color-status-pending-strong": "oklch(0.86 0.09 95)",
|
|
192
|
+
"--color-status-pending-foreground": "oklch(0.21 0.006 285.885)",
|
|
193
|
+
"--color-status-warning": "oklch(0.79 0.1 55)",
|
|
194
|
+
"--color-status-warning-strong": "oklch(0.79 0.1 55)",
|
|
195
|
+
"--color-status-warning-foreground": "oklch(0.21 0.006 285.885)",
|
|
196
|
+
"--color-status-paused": "oklch(0.74 0.09 260)",
|
|
197
|
+
"--color-status-paused-strong": "oklch(0.74 0.09 260)",
|
|
198
|
+
"--color-status-paused-foreground": "oklch(0.21 0.006 285.885)",
|
|
199
|
+
"--color-status-neutral": "oklch(0.72 0.012 286)",
|
|
200
|
+
"--color-status-neutral-strong": "oklch(0.72 0.012 286)",
|
|
201
|
+
"--color-status-neutral-foreground": "oklch(0.21 0.006 285.885)",
|
|
202
|
+
"--color-status-success-solid": "oklch(0.723 0.219 149.6)",
|
|
203
|
+
"--color-status-active-solid": "oklch(0.769 0.188 70.08)",
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Full Hive values for every contract token, copied from the light `@theme`
|
|
208
|
+
* and `.dark` blocks of `apps/ui/src/styles/globals.css` (keep in sync;
|
|
209
|
+
* `themes-presets.test.ts` fails on drift). The shared font and radius
|
|
210
|
+
* tokens come from the light block and apply to both modes.
|
|
211
|
+
*/
|
|
212
|
+
export const HIVE_PAGE_TOKENS: {
|
|
213
|
+
light: Record<PageThemeToken, string>;
|
|
214
|
+
dark: Record<PageThemeToken, string>;
|
|
215
|
+
} = {
|
|
216
|
+
light: { ...HIVE_LIGHT_COLORS, ...HIVE_SHARED_TOKENS },
|
|
217
|
+
dark: { ...HIVE_DARK_COLORS, ...HIVE_SHARED_TOKENS },
|
|
218
|
+
};
|
package/src/tools/app-upsert.ts
CHANGED
|
@@ -24,7 +24,7 @@ export const registerAppUpsertTool = (server: McpServer) => {
|
|
|
24
24
|
{
|
|
25
25
|
title: "Create or update an app",
|
|
26
26
|
description:
|
|
27
|
-
"Stores a schema-backed app definition with models, queries/actions, pages, reusable elements, optional userConfig fields, and an optional top-level theme (a dashboard preset slug for the app's canvas
|
|
27
|
+
"Stores a schema-backed app definition with models, queries/actions, pages, reusable elements, optional userConfig fields, and an optional top-level theme (a dashboard preset slug for the app's canvas: hive (stock; omit to inherit the viewer's theme), meadow, iris, rose, cobalt, ember, carbon, paper (the warm editorial paper look), plus the classic presets github, vscode, material, solarized, tokyo, monokai, gruvbox, catppuccin; viewers can override it per-user; unknown slugs degrade to the viewer's dashboard theme), then returns its dashboard URL. userConfig is versioned schema only; each user's values are stored separately, survive rollback, and schema changes are always compatible. Pages may bind a declared field read-only at exactly /user/<field>; pure and bound reusable elements must receive that value through a prop. Elements are private by default: pure elements read declared props, allow $item/$index inside repeats, may expose one leaf ElementSlot, and cannot invoke actions; bound elements may use the defining app's queries/actions, while exported bound elements cannot navigate. Prop kinds include enum with a required non-empty enum values array. Pages or elements reuse them with literal ElementRef targets, and cross-app refs require export: true. Zero-model pure-UI apps are valid. Pass appId to update; breaking a referenced export is blocked by the compatibility gate unless forceElementBreak explicitly names it.",
|
|
28
28
|
annotations: { destructiveHint: false },
|
|
29
29
|
inputSchema: z.object({
|
|
30
30
|
name: z.string().min(1).describe("Human-readable app name."),
|
package/src/tools/create-page.ts
CHANGED
|
@@ -12,6 +12,10 @@
|
|
|
12
12
|
* the title slugifies to empty (e.g. all symbols), fall back to the
|
|
13
13
|
* generated page id.
|
|
14
14
|
*
|
|
15
|
+
* Design fields: `theme`, `themeTokens` and `designVersion` set how an HTML
|
|
16
|
+
* page is styled. A new page gets designVersion 2. On an upsert, an omitted
|
|
17
|
+
* field keeps the stored value, and `null` clears `theme` / `themeTokens`.
|
|
18
|
+
*
|
|
15
19
|
* Architecture note: this tool runs on the API server (per
|
|
16
20
|
* `src/server.ts:155`+ pattern) and accesses `src/be/db` directly. The
|
|
17
21
|
* worker-side DB-boundary invariant doesn't apply because `src/tools/*`
|
|
@@ -23,9 +27,18 @@ import { authorizeAssetKeyWrite } from "@/be/asset-key-auth";
|
|
|
23
27
|
import { resolveTaskAuditUserId } from "@/be/audit-user";
|
|
24
28
|
import { createPage, getPage, getPageBySlug, updatePage } from "@/be/db";
|
|
25
29
|
import { getPageEditCounter } from "@/be/db/page-versions";
|
|
30
|
+
import { classAttributeValues, countColorLiterals } from "@/pages/color-literals";
|
|
26
31
|
import { snapshotPage } from "@/pages/version";
|
|
32
|
+
import { readThemeFields } from "@/themes/page-fields";
|
|
33
|
+
import { PAGE_THEME_COLOR_TOKENS } from "@/themes/tokens";
|
|
27
34
|
import { createToolRegistrar, swarmToolOutputSchema, toolErr, toolOk } from "@/tools/utils";
|
|
28
|
-
import {
|
|
35
|
+
import {
|
|
36
|
+
AssetKeySchema,
|
|
37
|
+
type Page,
|
|
38
|
+
PageAuthModeSchema,
|
|
39
|
+
PageContentTypeSchema,
|
|
40
|
+
PageThemeTokensInputSchema,
|
|
41
|
+
} from "@/types";
|
|
29
42
|
import { getAppUrl, getPublicMcpBaseUrl } from "@/utils/constants";
|
|
30
43
|
|
|
31
44
|
/** Same slugifier used by the HTTP createPage handler. */
|
|
@@ -46,6 +59,26 @@ function getAppBaseUrl(): string {
|
|
|
46
59
|
return getAppUrl();
|
|
47
60
|
}
|
|
48
61
|
|
|
62
|
+
/** A `swarm-` primitive class inside one class attribute value. */
|
|
63
|
+
const SWARM_CLASS = /(?:^|\s)swarm-/;
|
|
64
|
+
const COLOR_TOKEN_REFERENCE = /var\(\s*--color-/i;
|
|
65
|
+
const PAGE_COLOR_TOKENS: ReadonlySet<string> = new Set<string>(PAGE_THEME_COLOR_TOKENS);
|
|
66
|
+
|
|
67
|
+
/** The body uses a `swarm-` class or a `var(--color-...)` token, which only design version 2 styles. */
|
|
68
|
+
function bodyUsesDesignSystem(html: string): boolean {
|
|
69
|
+
return (
|
|
70
|
+
COLOR_TOKEN_REFERENCE.test(html) ||
|
|
71
|
+
classAttributeValues(html).some((value) => SWARM_CLASS.test(value))
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** The stored overrides set light colors and no dark colors, so dark mode keeps the preset's colors. */
|
|
76
|
+
function hasLightOnlyColors(tokens: Page["themeTokens"]): boolean {
|
|
77
|
+
if (!tokens) return false;
|
|
78
|
+
const isColor = (name: string) => PAGE_COLOR_TOKENS.has(name);
|
|
79
|
+
return Object.keys(tokens.light).some(isColor) && !Object.keys(tokens.dark).some(isColor);
|
|
80
|
+
}
|
|
81
|
+
|
|
49
82
|
export const registerCreatePageTool = (server: McpServer) => {
|
|
50
83
|
createToolRegistrar(server)(
|
|
51
84
|
"create_page",
|
|
@@ -56,7 +89,15 @@ export const registerCreatePageTool = (server: McpServer) => {
|
|
|
56
89
|
"Calls are upsert-by-(agent, slug): if you previously created a page " +
|
|
57
90
|
"with the same slug, its prior state is snapshotted and the row is " +
|
|
58
91
|
"updated. Use this for static reports, dashboards, or JSON action " +
|
|
59
|
-
"specs that don't need a long-lived process."
|
|
92
|
+
"specs that don't need a long-lived process. " +
|
|
93
|
+
"HTML pages use the swarm design system (designVersion 2): theme " +
|
|
94
|
+
"tokens such as var(--color-primary), the swarm-* primitives, and " +
|
|
95
|
+
"Tailwind v4 utilities such as bg-card and text-muted-foreground. " +
|
|
96
|
+
"Set `theme` to a preset id or pass `themeTokens` (a token map, or a " +
|
|
97
|
+
'CSS variable block with format "css") to restyle the whole page. ' +
|
|
98
|
+
"Do not hardcode colors: inside the dashboard the page follows the " +
|
|
99
|
+
"viewer's theme and dark mode. Pages created before designVersion 2 " +
|
|
100
|
+
"keep the old look; pass designVersion: 2 when you rewrite one.",
|
|
60
101
|
annotations: { destructiveHint: false },
|
|
61
102
|
inputSchema: z.object({
|
|
62
103
|
key: AssetKeySchema.optional().describe(
|
|
@@ -99,6 +140,27 @@ export const registerCreatePageTool = (server: McpServer) => {
|
|
|
99
140
|
.describe(
|
|
100
141
|
"Declared credential needs for JSON pages (renderer ignores for v1 — reserved for follow-up).",
|
|
101
142
|
),
|
|
143
|
+
theme: z
|
|
144
|
+
.string()
|
|
145
|
+
.nullable()
|
|
146
|
+
.optional()
|
|
147
|
+
.describe(
|
|
148
|
+
"Theme preset id that restyles the whole page, for example paper, github or hive. An unknown id returns an error that lists every preset. Omit to follow the viewer's theme (on update, to keep the stored one). Pass null to clear it.",
|
|
149
|
+
),
|
|
150
|
+
themeTokens: PageThemeTokensInputSchema.nullable()
|
|
151
|
+
.optional()
|
|
152
|
+
.describe(
|
|
153
|
+
'Per-page token overrides, applied on top of the preset. Send { light, dark } maps of token name to CSS value (for example { light: { "--color-primary": "oklch(0.55 0.2 260)" } }), or { format: "css", css } with a CSS variable block (:root, .dark, @theme) as shadcn, tweakcn and Tailwind v4 export it. Stored normalized. Names outside the token contract, and values that reference a variable outside it, are ignored and reported in the result. Replaces the stored overrides. Omit to keep them on update. Pass null to clear them.',
|
|
154
|
+
),
|
|
155
|
+
designVersion: z
|
|
156
|
+
.number()
|
|
157
|
+
.int()
|
|
158
|
+
.min(1)
|
|
159
|
+
.max(2)
|
|
160
|
+
.optional()
|
|
161
|
+
.describe(
|
|
162
|
+
"Design system version. 2 is the swarm design system and the default for new pages. 1 is the original look that older pages keep. Omit on update to keep the stored value.",
|
|
163
|
+
),
|
|
102
164
|
}),
|
|
103
165
|
outputSchema: swarmToolOutputSchema({
|
|
104
166
|
yourAgentId: z.string().optional(),
|
|
@@ -107,6 +169,11 @@ export const registerCreatePageTool = (server: McpServer) => {
|
|
|
107
169
|
version: z.number().optional(),
|
|
108
170
|
app_url: z.string().optional(),
|
|
109
171
|
api_url: z.string().optional(),
|
|
172
|
+
designVersion: z.number().optional(),
|
|
173
|
+
ignoredTokens: z.array(z.string()).optional(),
|
|
174
|
+
colorLiteralCount: z.number().optional(),
|
|
175
|
+
usesDesignSystem: z.boolean().optional(),
|
|
176
|
+
lightOnlyColors: z.boolean().optional(),
|
|
110
177
|
}),
|
|
111
178
|
},
|
|
112
179
|
async (input, requestInfo, _meta) => {
|
|
@@ -115,6 +182,9 @@ export const registerCreatePageTool = (server: McpServer) => {
|
|
|
115
182
|
return toolErr(msg);
|
|
116
183
|
}
|
|
117
184
|
|
|
185
|
+
const themeFields = readThemeFields(input);
|
|
186
|
+
if ("error" in themeFields) return toolErr(themeFields.error);
|
|
187
|
+
|
|
118
188
|
const slug = input.slug ?? slugify(input.title);
|
|
119
189
|
const finalSlug = slug || "page"; // Fallback if title slugifies to empty.
|
|
120
190
|
|
|
@@ -167,6 +237,10 @@ export const registerCreatePageTool = (server: McpServer) => {
|
|
|
167
237
|
passwordHash: passwordHash ?? null,
|
|
168
238
|
body: input.body,
|
|
169
239
|
needsCredentials: needsCredentialsNames ?? null,
|
|
240
|
+
// Omitted design fields keep the stored values.
|
|
241
|
+
theme: themeFields.theme,
|
|
242
|
+
themeTokens: themeFields.themeTokens,
|
|
243
|
+
designVersion: input.designVersion,
|
|
170
244
|
});
|
|
171
245
|
if (!updated) {
|
|
172
246
|
const msg = `Failed to update existing page ${existing.id}.`;
|
|
@@ -188,6 +262,9 @@ export const registerCreatePageTool = (server: McpServer) => {
|
|
|
188
262
|
passwordHash,
|
|
189
263
|
body: input.body,
|
|
190
264
|
needsCredentials: needsCredentialsNames,
|
|
265
|
+
theme: themeFields.theme,
|
|
266
|
+
themeTokens: themeFields.themeTokens,
|
|
267
|
+
designVersion: input.designVersion,
|
|
191
268
|
});
|
|
192
269
|
id = created.id;
|
|
193
270
|
} catch (err) {
|
|
@@ -209,17 +286,46 @@ export const registerCreatePageTool = (server: McpServer) => {
|
|
|
209
286
|
const appUrl = `${getAppBaseUrl()}/pages/${id}`;
|
|
210
287
|
const version = await getPageEditCounter(id);
|
|
211
288
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
289
|
+
// Judged on the saved page, so an upsert that keeps an older design
|
|
290
|
+
// version is judged by the version it keeps. The NUDGES entry for this
|
|
291
|
+
// tool reads these flags. Only HTML pages get the design system head.
|
|
292
|
+
const designFlags =
|
|
293
|
+
fresh.contentType === "text/html"
|
|
294
|
+
? {
|
|
295
|
+
usesDesignSystem:
|
|
296
|
+
typeof input.theme === "string" ||
|
|
297
|
+
Boolean(input.themeTokens) ||
|
|
298
|
+
bodyUsesDesignSystem(fresh.body),
|
|
299
|
+
lightOnlyColors: hasLightOnlyColors(fresh.themeTokens),
|
|
300
|
+
...(fresh.designVersion >= 2
|
|
301
|
+
? { colorLiteralCount: countColorLiterals(fresh.body) }
|
|
302
|
+
: {}),
|
|
303
|
+
}
|
|
304
|
+
: {};
|
|
305
|
+
const { ignored } = themeFields;
|
|
306
|
+
|
|
307
|
+
return toolOk(
|
|
308
|
+
`Page "${input.title}" saved (slug=${finalSlug}, version=${version}, designVersion=${fresh.designVersion}).` +
|
|
309
|
+
(ignored.length > 0
|
|
310
|
+
? " Some theme tokens were ignored (outside the page token contract or referencing a variable outside it)."
|
|
311
|
+
: ""),
|
|
312
|
+
{
|
|
313
|
+
details:
|
|
314
|
+
`API: ${apiUrl}\n App: ${appUrl}` +
|
|
315
|
+
(ignored.length > 0 ? `\n Ignored theme tokens: ${ignored.join(", ")}` : ""),
|
|
316
|
+
data: {
|
|
317
|
+
yourAgentId: requestInfo.agentId,
|
|
318
|
+
id,
|
|
319
|
+
key: fresh.key,
|
|
320
|
+
version,
|
|
321
|
+
app_url: appUrl,
|
|
322
|
+
api_url: apiUrl,
|
|
323
|
+
designVersion: fresh.designVersion,
|
|
324
|
+
...(ignored.length > 0 ? { ignoredTokens: ignored } : {}),
|
|
325
|
+
...designFlags,
|
|
326
|
+
},
|
|
221
327
|
},
|
|
222
|
-
|
|
328
|
+
);
|
|
223
329
|
},
|
|
224
330
|
);
|
|
225
331
|
};
|
package/src/tools/utils.ts
CHANGED
|
@@ -22,6 +22,7 @@ import { upsertKv } from "../be/db";
|
|
|
22
22
|
import { MCP_OVERFLOW_NAMESPACE, mcpOverflowNamespace } from "../kv-overflow";
|
|
23
23
|
import { joinKvPath, type KvShapeEntry, splitKvPath, summarizeKvShape } from "../kv-view";
|
|
24
24
|
import { withSpan } from "../otel";
|
|
25
|
+
import { COLOR_LITERAL_THRESHOLD } from "../pages/color-literals";
|
|
25
26
|
import type { PermissionVerb } from "../rbac/permissions";
|
|
26
27
|
import { SCRIPT_LONG_TIMEOUT_HINT_MS } from "../scripts-runtime/executors/types";
|
|
27
28
|
import { scrubObject, scrubSecrets } from "../utils/secret-scrubber";
|
|
@@ -314,6 +315,37 @@ const workflowLongScriptTimeoutNudge = (r: SwarmToolResult): string | undefined
|
|
|
314
315
|
return hint ? WORKFLOW_LONG_SCRIPT_TIMEOUT_NUDGE : undefined;
|
|
315
316
|
};
|
|
316
317
|
|
|
318
|
+
/**
|
|
319
|
+
* One call can fall into more than one create_page trap, and a result carries
|
|
320
|
+
* one nudge, so this entry joins one sentence per trap that applies: a
|
|
321
|
+
* design version 1 page that uses the design system, theme tokens that set
|
|
322
|
+
* light colors only, and hardcoded colors. The tool computes the flags.
|
|
323
|
+
*/
|
|
324
|
+
const createPageNudge = (r: SwarmToolResult): string | undefined => {
|
|
325
|
+
if (!r.ok) return undefined;
|
|
326
|
+
const data = r.data as
|
|
327
|
+
| {
|
|
328
|
+
designVersion?: unknown;
|
|
329
|
+
usesDesignSystem?: unknown;
|
|
330
|
+
lightOnlyColors?: unknown;
|
|
331
|
+
colorLiteralCount?: unknown;
|
|
332
|
+
}
|
|
333
|
+
| undefined;
|
|
334
|
+
const count = data?.colorLiteralCount;
|
|
335
|
+
const sentences = [
|
|
336
|
+
data?.designVersion === 1 && data.usesDesignSystem === true
|
|
337
|
+
? "This page is on designVersion 1 (the original look), so design system classes, tokens and themes have no effect. Pass designVersion: 2."
|
|
338
|
+
: undefined,
|
|
339
|
+
data?.lightOnlyColors === true
|
|
340
|
+
? "Your themeTokens set colors for light mode only. Dark mode keeps the preset's dark colors; add a dark map (or a .dark block) to restyle it too."
|
|
341
|
+
: undefined,
|
|
342
|
+
typeof count === "number" && count >= COLOR_LITERAL_THRESHOLD
|
|
343
|
+
? `This page hardcodes ${count} color values. Use theme tokens (var(--color-primary), bg-primary, text-muted-foreground) so theme and dark-mode switches restyle it. Until then the page stays light in a dark dashboard.`
|
|
344
|
+
: undefined,
|
|
345
|
+
].filter((sentence) => sentence !== undefined);
|
|
346
|
+
return sentences.length > 0 ? sentences.join(" ") : undefined;
|
|
347
|
+
};
|
|
348
|
+
|
|
317
349
|
/**
|
|
318
350
|
* Central conditional nudges, keyed by tool name. Applied by the finalize
|
|
319
351
|
* pipeline when the tool did not set an explicit nudge. Keep entries to a
|
|
@@ -333,6 +365,7 @@ export const NUDGES: Record<string, (result: SwarmToolResult) => string | undefi
|
|
|
333
365
|
"patch-workflow-node": workflowLongScriptTimeoutNudge,
|
|
334
366
|
"slack-download-file": slackDownloadFileNudge,
|
|
335
367
|
"slack-read": slackReadNudge,
|
|
368
|
+
create_page: createPageNudge,
|
|
336
369
|
"script-search": (r) => {
|
|
337
370
|
if (!r.ok) return undefined;
|
|
338
371
|
// proxyScriptsApi wraps the parsed HTTP body as data = { status, data },
|
package/src/types.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import { normalizeAssetKey } from "./assets/key";
|
|
6
6
|
import { isSafeBundlePath } from "./extensions/bundle-path";
|
|
7
|
+
import { THEME_IMPORT_FORMATS } from "./themes/import";
|
|
7
8
|
import { MAX_PROFILE_FILE_LENGTH } from "./utils/constants";
|
|
8
9
|
import { z } from "./utils/zod-openapi";
|
|
9
10
|
|
|
@@ -1695,6 +1696,8 @@ export const AgentLogEventTypeSchema = z.enum([
|
|
|
1695
1696
|
"task_deferred_wait_woke",
|
|
1696
1697
|
// The lead follow-up was skipped because the woken waiter already carries the result
|
|
1697
1698
|
"task_follow_up_suppressed",
|
|
1699
|
+
// Shadow follow-up gate: what a verifier said about a gated run (never skips the follow-up)
|
|
1700
|
+
"task_follow_up_gate_shadow",
|
|
1698
1701
|
// store-progress refused a completion once because its citations were inaccurate
|
|
1699
1702
|
"task_citation_check_refused",
|
|
1700
1703
|
"channel_message",
|
|
@@ -2773,10 +2776,36 @@ export type PageContentType = z.infer<typeof PageContentTypeSchema>;
|
|
|
2773
2776
|
export const PageAuthModeSchema = z.enum(["public", "authed", "password"]);
|
|
2774
2777
|
export type PageAuthMode = z.infer<typeof PageAuthModeSchema>;
|
|
2775
2778
|
|
|
2779
|
+
// Per-page theme overrides as stored: page token names (`--color-primary`,
|
|
2780
|
+
// `--radius-lg`) mapped to CSS values, per mode. Writes go through
|
|
2781
|
+
// `importThemeTokens` (src/themes/import.ts), which maps outside names onto the
|
|
2782
|
+
// token contract and checks every value, so the stored form is always normalized.
|
|
2783
|
+
export const PageThemeTokensSchema = z
|
|
2784
|
+
.object({
|
|
2785
|
+
light: z.record(z.string(), z.string()),
|
|
2786
|
+
dark: z.record(z.string(), z.string()),
|
|
2787
|
+
})
|
|
2788
|
+
.openapi("PageThemeTokens");
|
|
2789
|
+
|
|
2790
|
+
// Write-side `themeTokens` input, mirroring `ThemeImportInput` (src/themes/import.ts)
|
|
2791
|
+
// as one flat object: the default `tokens` format sends `light` / `dark` maps,
|
|
2792
|
+
// the `css` format sends a CSS variable block. `importThemeTokens` checks which
|
|
2793
|
+
// fields go with which format, so a mismatch gets a precise error message. The
|
|
2794
|
+
// `format` enum is the importer's own list, so a new importer needs no change
|
|
2795
|
+
// here unless it takes a new payload field.
|
|
2796
|
+
export const PageThemeTokensInputSchema = z.strictObject({
|
|
2797
|
+
format: z.enum(THEME_IMPORT_FORMATS).optional(),
|
|
2798
|
+
light: z.record(z.string(), z.string()).optional(),
|
|
2799
|
+
dark: z.record(z.string(), z.string()).optional(),
|
|
2800
|
+
css: z.string().optional(),
|
|
2801
|
+
});
|
|
2802
|
+
export type PageThemeTokensInput = z.infer<typeof PageThemeTokensInputSchema>;
|
|
2803
|
+
|
|
2776
2804
|
// PageSnapshot captures the mutable content fields frozen per-version in
|
|
2777
2805
|
// page_versions.snapshot. Omits id / agentId / slug / timestamps (these are
|
|
2778
2806
|
// invariant across versions for a given page id; the slug is a parent-only
|
|
2779
|
-
// identifier).
|
|
2807
|
+
// identifier). The design fields are optional because snapshots taken before
|
|
2808
|
+
// design version 2 do not carry them (they read as design version 1).
|
|
2780
2809
|
export const PageSnapshotSchema = z
|
|
2781
2810
|
.object({
|
|
2782
2811
|
title: z.string(),
|
|
@@ -2786,6 +2815,9 @@ export const PageSnapshotSchema = z
|
|
|
2786
2815
|
passwordHash: z.string().optional(),
|
|
2787
2816
|
body: z.string(),
|
|
2788
2817
|
needsCredentials: z.array(z.string()).optional(),
|
|
2818
|
+
theme: z.string().nullable().optional(),
|
|
2819
|
+
themeTokens: PageThemeTokensSchema.nullable().optional(),
|
|
2820
|
+
designVersion: z.number().int().min(1).optional(),
|
|
2789
2821
|
})
|
|
2790
2822
|
.openapi("PageSnapshot");
|
|
2791
2823
|
export type PageSnapshot = z.infer<typeof PageSnapshotSchema>;
|
|
@@ -2803,6 +2835,9 @@ export const PageSchema = z
|
|
|
2803
2835
|
passwordHash: z.string().optional(),
|
|
2804
2836
|
body: z.string(),
|
|
2805
2837
|
needsCredentials: z.array(z.string()).optional(),
|
|
2838
|
+
theme: z.string().nullable(),
|
|
2839
|
+
themeTokens: PageThemeTokensSchema.nullable(),
|
|
2840
|
+
designVersion: z.number().int().min(1),
|
|
2806
2841
|
viewCount: z.number().int().min(0).default(0),
|
|
2807
2842
|
createdAt: z.string(),
|
|
2808
2843
|
updatedAt: z.string(),
|
|
@@ -2826,8 +2861,15 @@ export type WorkflowSummary = Omit<
|
|
|
2826
2861
|
"definition" | "triggers" | "cooldown" | "input" | "triggerSchema"
|
|
2827
2862
|
> & { nodeCount: number };
|
|
2828
2863
|
|
|
2829
|
-
/**
|
|
2830
|
-
|
|
2864
|
+
/**
|
|
2865
|
+
* `/api/pages` list item: drops the (potentially huge) `body` and `passwordHash`,
|
|
2866
|
+
* plus the design fields (`theme`, `themeTokens`, `designVersion`). List views do
|
|
2867
|
+
* not render a page, so those only come with `?fields=full` or the get-by-id route.
|
|
2868
|
+
*/
|
|
2869
|
+
export type PageSummary = Omit<
|
|
2870
|
+
Page,
|
|
2871
|
+
"body" | "passwordHash" | "theme" | "themeTokens" | "designVersion"
|
|
2872
|
+
>;
|
|
2831
2873
|
|
|
2832
2874
|
/** `/api/schedules` list item — swaps the full `taskTemplate` for a short preview. */
|
|
2833
2875
|
export type ScheduledTaskSummary = Omit<ScheduledTask, "taskTemplate"> & {
|
package/src/utils/constants.ts
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { isEnvFlagEnabled, parseEnvFlag } from "./env-flag";
|
|
6
|
+
import { isDevOrTestNodeEnv } from "./node-env";
|
|
6
7
|
|
|
7
8
|
/** Maximum profile-file length in JavaScript characters (not UTF-8 bytes). */
|
|
8
9
|
export const MAX_PROFILE_FILE_LENGTH = 256 * 1024;
|
|
@@ -45,6 +46,24 @@ export function getAppUrl(fallback = DEFAULT_APP_URL): string {
|
|
|
45
46
|
return (getConfiguredAppUrls()[0] || fallback).replace(/\/+$/, "");
|
|
46
47
|
}
|
|
47
48
|
|
|
49
|
+
/**
|
|
50
|
+
* Local dashboard origins (Vite ports and portless `*.localhost`) that may
|
|
51
|
+
* iframe pages without being listed in `APP_URL`. Returned only when NODE_ENV
|
|
52
|
+
* is explicitly development or test: deployed images leave NODE_ENV unset, and
|
|
53
|
+
* an unset value must not let a localhost origin frame production pages.
|
|
54
|
+
*/
|
|
55
|
+
export function getLocalDevAppOrigins(): string[] {
|
|
56
|
+
if (!isDevOrTestNodeEnv()) return [];
|
|
57
|
+
return [
|
|
58
|
+
"http://localhost:5274",
|
|
59
|
+
"http://localhost:5175",
|
|
60
|
+
"http://127.0.0.1:5274",
|
|
61
|
+
"http://127.0.0.1:5175",
|
|
62
|
+
"https://*.localhost",
|
|
63
|
+
"http://*.localhost",
|
|
64
|
+
];
|
|
65
|
+
}
|
|
66
|
+
|
|
48
67
|
/**
|
|
49
68
|
* Internal API/MCP base URL — how workers/agents and in-process callers reach
|
|
50
69
|
* the API server. May be a private/cluster address (e.g. the Helm ClusterIP
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
2
|
+
|
|
3
|
+
/** Split `FOLLOW_UP_GATE_SCHEDULE_IDS`, a comma-separated id list. Blank entries are dropped. */
|
|
4
|
+
export function parseFollowUpGateScheduleIds(raw: string | undefined): string[] {
|
|
5
|
+
return (raw ?? "")
|
|
6
|
+
.split(",")
|
|
7
|
+
.map((id) => id.trim())
|
|
8
|
+
.filter(Boolean);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function isFollowUpGateScheduleId(id: string): boolean {
|
|
12
|
+
return UUID_RE.test(id);
|
|
13
|
+
}
|