From c01d5d91f6006f2b5b214fbef8997e76f0b269ed Mon Sep 17 00:00:00 2001 From: Duy Nguyen Date: Mon, 19 Oct 2026 20:41:04 +0700 Subject: [PATCH 1/4] feat(content): derive product facts from one source and refresh to v3.40 Channel and provider counts, the stable release, test and Go-line figures now come from src/content/facts.ts. The test claim says the suite runs before every release, matching the runtime's preflighted release flow. Adds stable releases v3.34 to v3.40, Atlas Cloud, coordination-led use-case copy, a softer on-premises data claim and docs/marketing-facts.md with provenance and the release copy refresh routine. Co-Authored-By: Claude Opus 5.5 --- docs/marketing-facts.md | 64 +++++++ src/content/facts.ts | 22 +++ src/content/features.ts | 179 ++++++++++++++++-- src/content/integrations.ts | 3 +- src/content/pages/about.ts | 19 +- src/content/pages/architecture.ts | 4 +- src/content/pages/features.ts | 56 +++--- src/content/pages/integrations.ts | 29 +-- src/content/pages/pricing.ts | 4 +- src/content/pages/roadmap.ts | 84 +++++--- .../pages/secondary-pages-copy.test.ts | 161 ++++++++++++++++ src/content/pages/security.ts | 6 +- src/content/plans.ts | 6 +- .../use-cases/ads-performance-watch.ts | 4 +- .../use-cases/ai-coworker-in-group-chats.ts | 4 +- .../use-cases/creative-ad-production.ts | 4 +- .../use-cases/customer-care-with-handoff.ts | 4 +- src/content/use-cases/hr-recruiting.ts | 4 +- src/content/use-cases/index.ts | 2 +- .../use-cases/meeting-notes-and-decisions.ts | 4 +- .../use-cases/real-estate-market-signals.ts | 4 +- src/content/use-cases/retail-pod-design.ts | 4 +- .../use-cases/sales-knowledge-assistant.ts | 4 +- src/content/use-cases/seo-around-the-clock.ts | 4 +- src/content/use-cases/social-media-care.ts | 4 +- src/content/use-cases/software-delivery.ts | 4 +- .../use-cases/stock-market-sentiment.ts | 4 +- src/content/use-cases/task-chasing.ts | 4 +- src/lib/blocks/marketing-block-schemas.ts | 5 +- src/lib/server/seo/llms-intro.ts | 20 ++ src/lib/server/seo/llms-responses.ts | 11 +- src/pages/[...lang]/integrations.astro | 2 +- 32 files changed, 589 insertions(+), 144 deletions(-) create mode 100644 docs/marketing-facts.md create mode 100644 src/content/facts.ts create mode 100644 src/content/pages/secondary-pages-copy.test.ts create mode 100644 src/lib/server/seo/llms-intro.ts diff --git a/docs/marketing-facts.md b/docs/marketing-facts.md new file mode 100644 index 0000000..3080a36 --- /dev/null +++ b/docs/marketing-facts.md @@ -0,0 +1,64 @@ +# Marketing facts and the release copy refresh + +Every number the site quotes about dewee has one owner and one way to re-check it. This file +records both, so a stale figure is a mistake someone can find in minutes rather than a belief +nobody remembers verifying. The shared values live in `src/content/facts.ts`; pages import +`FACTS` instead of typing the number. + +"dewee repo" below means `nextlevelbuilder/dewee`, checked out next to this repository. `` is +the latest stable tag (`gh release list --repo nextlevelbuilder/dewee --exclude-pre-releases`). + +## Figures + +| Figure | Value | Shown on | Source and command | Last verified | +|---|---|---|---|---| +| Latest stable release | v3.40.0 (2026-10-15) | Homepage proof, About report card, llms.txt, JSON-LD `softwareVersion` | `FACTS.stableRelease`; `gh release list --repo nextlevelbuilder/dewee --exclude-pre-releases --limit 3` | 2026-10-19 | +| Chat channels | 11 | Homepage, Integrations, Features catalogue, llms.txt | `FACTS.channels` = `CHANNELS.length` in `src/content/integrations.ts`; compare with the adapters in dewee `internal/channels/` (Zalo has three variants) | 2026-10-19 | +| Model providers | 22 | Homepage, Integrations, Features catalogue, llms.txt | `FACTS.providers` = `PROVIDERS.length`; compare with dewee `internal/providers` and `docs/02-providers.md` (Atlas Cloud added in v3.39.0) | 2026-10-19 | +| Go test functions | 13,351, shown as "13,000+" | Homepage proof, About report card | `FACTS.goTests`; in the dewee repo: `git grep -h -E '^func Test[A-Z_]' v -- '*_test.go' \| wc -l` | 2026-10-19 (v3.40.0) | +| Go lines under `internal/` | 921,264, shown as "900,000+" | Homepage proof | `FACTS.goLines`; in the dewee repo: `git grep -c '' v -- 'internal/*.go' \| awk -F: '{s+=$NF} END{print s}'` (tests included) | 2026-10-19 (v3.40.0) | +| Test cadence | Full suite runs on one preflighted commit before every release | Homepage proof, About report card | dewee commit `e511ada` (#1351, 2026-10-14): pull requests and `dev` pushes no longer run tests. Never write "on every change" or "on every pull request" | 2026-10-19 | +| Stable release history | v3.28 to v3.40, v3.38.1 has no notes | Features releases, Roadmap "Recently shipped" | `RELEASES` in `src/content/features.ts`; `gh release view v --repo nextlevelbuilder/dewee` for each stable tag | 2026-10-19 | +| Roadmap columns | Now, Next, Later | Roadmap | `src/content/pages/roadmap.ts` header lists the dewee plan or beta release behind every item; no dates are promised | 2026-10-19 | +| Public MCP path | `/mcp/server` (legacy `/mcp` deprecated) | Integrations, Features | dewee `docs/18-http-api.md` at `v` | 2026-10-19 | +| Five layers of defence, AES-256-GCM, seven denied shell categories | as written | Security | dewee `docs/09-security.md`, `internal/crypto/aes.go` | 2026-09-25 | +| 8-stage agent loop, three memory tiers | as written | Architecture | dewee `docs/01-agent-loop.md`, `docs/07-bootstrap-skills-memory.md` | 2026-09-25 | +| Prices ($500 a year licence, On-Premises from $5K) | as written | Pricing, Install | `src/content/plans.ts`, `src/content/early-access.ts` (the owners' decision, not derived) | 2026-09-25 | +| NextLevelBuilder proof (130K+ stars, 115 contributors, 10,000+ businesses, 65K+ members) | as written | About | `plans/reports/scout-260924-2346-ecosystem-story.md`, GitHub counts read 2026-09-24 | 2026-09-24, not re-read in the last refresh | + +Rows dated before 2026-10-19 were not re-checked in the last refresh. Treat them as due. + +## Wording rules the figures depend on + +- Say "before every release", not "on every change", for tests. +- Round tests and lines down ("13,000+", "900,000+"). Vietnamese uses a dot: "13.000+". +- Say "stable" for releases. Betas (`v3.41.0-beta.x`) never appear in feature lists; the Roadmap may + name a beta as "in beta" with its tag. +- Multiplayer means coordinated work by humans and agents: delegation, a shared task board, + approvals, traces. Do not write "first" or "only", live co-editing, shared cursors, real-time + sync, absolute safety, or invented customers and results. + +## Release copy refresh + +Run this when a new stable release ships, and again before any campaign that quotes the numbers. + +1. Read the notes: `gh release view v --repo nextlevelbuilder/dewee`. Skip betas. +2. Update `src/content/facts.ts`: `stableRelease`, then re-count with the commands in the table + (tests and lines at the new tag) and update `goTests` and `goLines`. If the channel or provider + lists changed, update `CHANNELS` or `PROVIDERS` in `src/content/integrations.ts` so the counts follow. +3. Add a `RELEASES` entry at the top of `src/content/features.ts`: benefits first, only what the notes + support, EN and VI. Set `since` on a catalogue item only for a release listed in `RELEASES`, and + update the header comment with the verified date. +4. Update the Roadmap: move shipped items out of Now, add a "Recently shipped" line, and refresh the + "checked" note. Check the in-progress items against the dewee plans and the latest betas. +5. Check the calendar labels on Features (`monthLabel`, `calendarLabel` in `src/content/pages/features.ts`) + when the newest release falls in a new month. +6. Search for stale figures. This should print nothing outside tests and code comments about history: + + ```bash + grep -rnE "8,900|8\.900|on every change|cho mỗi thay đổi|\b10 (chat )?channels|10 kênh" src + grep -rnE "v3\.[0-9]+(\.[0-9]+)?" src/content --include=*.ts | grep -v "features.ts\|roadmap.ts\|facts.ts" + ``` + +7. Update the "Last verified" column above for every figure you re-checked, then run + `pnpm check && pnpm test && pnpm build`. diff --git a/src/content/facts.ts b/src/content/facts.ts new file mode 100644 index 0000000..2cda998 --- /dev/null +++ b/src/content/facts.ts @@ -0,0 +1,22 @@ +/** + * Product facts quoted across the site. Every page that states one of these numbers reads it + * from here, so a release refresh changes one file. Provenance and the refresh routine live in + * docs/marketing-facts.md; update both together. + */ +import { CHANNELS, PROVIDERS } from "~/content/integrations"; + +export const FACTS = { + /** Latest stable runtime release (GitHub releases, nextlevelbuilder/dewee, 2026-10-15). */ + stableRelease: { version: "3.40.0", date: "2026-10-15" }, + /** Chat channels in the catalogue; it mirrors the adapters in the runtime's internal/channels. */ + channels: CHANNELS.length, + /** Model providers in the catalogue (internal/providers). */ + providers: PROVIDERS.length, + /** + * Go test functions (`func Test…`) in the runtime at the stable tag, rounded down for copy. + * The full suite runs on one preflighted commit before every release, not on every pull request. + */ + goTests: { measured: 13_351, rounded: "13,000+", roundedVi: "13.000+" }, + /** Lines of Go under internal/ at the stable tag, tests included, rounded down for copy. */ + goLines: { measured: 921_264, rounded: "900,000+", roundedVi: "900.000+" }, +} as const; diff --git a/src/content/features.ts b/src/content/features.ts index 0aee449..14f6ad6 100644 --- a/src/content/features.ts +++ b/src/content/features.ts @@ -1,17 +1,19 @@ /** * The dewee capability catalogue and the latest stable releases, EN + VI. * - * Sources (verified 2026-09-25): GitHub release notes v3.28.0 → v3.33.0 (all stable), and the - * dewee repo docs: 01-agent-loop, 03-tools-system, 05-channels-messaging, 07-bootstrap-skills-memory, + * Sources (verified 2026-10-19): GitHub release notes v3.28.0 → v3.40.0 (stable only; v3.38.1 is a + * single fix with no notes and has no entry), and the dewee repo docs: 01-agent-loop, 03-tools-system, 05-channels-messaging, 07-bootstrap-skills-memory, * 08-scheduling-cron, 09-security, 10-tracing-observability, 11-agent-teams, 23-multi-tenant-architecture, * 24-knowledge-vault, 25-workflows, ccp-usage-tasks-approvals. * `since` is set only for items that shipped in a verified release listed in RELEASES; older - * capabilities carry no version so we never guess one. Beta releases (v3.34) are left out on purpose. + * capabilities carry no version so we never guess one. Beta releases (v3.41.0-beta.x) are left out on + * purpose. Counts quoted in the catalogue come from FACTS (src/content/facts.ts). */ import type { Bi } from "~/i18n/config"; +import { FACTS } from "~/content/facts"; export type Release = { - /** Git tag without the leading "v", e.g. "3.33.0" */ + /** Git tag without the leading "v", e.g. "3.40.0" */ version: string; /** ISO date the release was published */ date: string; @@ -30,8 +32,143 @@ export type CapabilityGroup = { items: Capability[]; }; -/** Stable releases shipped in September 2026, newest first. */ +/** Stable releases since v3.28 (September and October 2026), newest first. */ export const RELEASES: Release[] = [ + { + version: "3.40.0", + date: "2026-10-15", + title: { en: "A browser your agents can borrow", vi: "Trình duyệt của bạn, agent mượn được" }, + items: { + en: [ + "A connected browser: with your grant, an agent works in tabs of your own Chrome through the dewee extension", + "The admin console is rebuilt, and GitHub sign-in is fixed", + "Leased workers stop before their lease runs out", + "Every stable release is built from one preflighted commit that runs the full test suite first", + ], + vi: [ + "Trình duyệt kết nối: khi bạn cấp quyền, agent làm việc trên các tab Chrome của chính bạn qua tiện ích dewee", + "Giao diện admin console được dựng lại, và đăng nhập GitHub đã được sửa", + "Worker thuê theo lease dừng trước khi lease hết hạn", + "Mỗi bản stable được build từ một commit đã preflight, chạy trọn bộ test trước khi phát hành", + ], + }, + }, + { + version: "3.39.0", + date: "2026-10-14", + title: { en: "Say who may use what", vi: "Ai được dùng gì, nói rõ từng người" }, + items: { + en: [ + "Share levels for agents, and access grants for skills, MCP servers, providers and packages, with an access panel in the console and the CLI", + "Agents improve from evidence: candidate changes are replayed on saved examples, tried on live traffic once approved, and rolled back if they regress", + "Signed external events, starting with GitHub, can wake an agent", + "Memory facts keep their provenance and follow a clear lifecycle", + "The public MCP endpoint is served at /mcp/server, with status checks, connection diagnostics and a setup screen in the console", + "Atlas Cloud joins as a native chat provider", + ], + vi: [ + "Cấp độ chia sẻ cho agent, và quyền truy cập cho skill, MCP server, nhà cung cấp và package, kèm bảng quyền trong console và CLI", + "Agent cải thiện dựa trên bằng chứng: thay đổi đề xuất được chạy lại trên ví dụ đã lưu, thử trên lưu lượng thật sau khi được duyệt, và quay lui nếu kết quả tệ đi", + "Sự kiện bên ngoài có chữ ký, bắt đầu từ GitHub, có thể đánh thức một agent", + "Dữ kiện trong bộ nhớ giữ nguồn gốc và theo một vòng đời rõ ràng", + "Endpoint MCP công khai chuyển sang /mcp/server, kèm kiểm tra trạng thái, chẩn đoán kết nối và màn hình thiết lập trong console", + "Atlas Cloud gia nhập như một nhà cung cấp chat gốc", + ], + }, + }, + { + version: "3.38.0", + date: "2026-10-07", + title: { en: "A CLI for agents, onboarding by conversation", vi: "CLI cho agent, làm quen bằng hội thoại" }, + items: { + en: [ + "An agent-first CLI: structured errors, non-interactive create-and-update commands, idempotency, a handshake and a command manifest", + "Set up a workspace by talking to a Super Agent, with the plan and each step kept as durable state", + "Fewer round trips per request in the control plane", + ], + vi: [ + "CLI ưu tiên agent: lỗi có cấu trúc, lệnh tạo và cập nhật không cần tương tác, idempotency, handshake và manifest lệnh", + "Thiết lập workspace bằng cách trò chuyện với Super Agent, kế hoạch và từng bước được lưu bền vững", + "Control plane xử lý mỗi yêu cầu với ít vòng gọi hơn", + ], + }, + }, + { + version: "3.37.0", + date: "2026-10-05", + title: { en: "Channels that know who is talking", vi: "Kênh chat biết ai đang nói" }, + items: { + en: [ + "Trusted channel identity, plus an inbound gate that screens messages through an MCP tool before the agent sees them (Zalo Personal first)", + "Discord observe mode records every in-scope server message in a tenant-scoped inbox, and support threads let the author keep talking without mentioning the bot", + "A guard that checks Discord's remaining connection budget, so a busy support agent does not get its bot token reset", + ], + vi: [ + "Danh tính kênh đáng tin cậy, cùng một cổng kiểm tra lọc tin nhắn qua một tool MCP trước khi agent nhìn thấy (bắt đầu với Zalo cá nhân)", + "Chế độ observe của Discord ghi mọi tin nhắn trong phạm vi vào hộp thư theo tenant, và thread hỗ trợ cho phép người hỏi tiếp tục trò chuyện mà không cần nhắc tên bot", + "Cơ chế bảo vệ kiểm tra hạn mức kết nối còn lại của Discord, để agent hỗ trợ bận rộn không bị reset token bot", + ], + }, + }, + { + version: "3.36.0", + date: "2026-10-01", + title: { en: "Model catalogs that follow your scope", vi: "Danh mục model theo đúng phạm vi của bạn" }, + items: { + en: [ + "Scoped model catalogs, and GPT-6.1 Sol", + "Reflex decisions show up in trace details", + "Stricter validation of import archives", + ], + vi: [ + "Danh mục model theo phạm vi, và GPT-6.1 Sol", + "Quyết định của Reflex hiện trong chi tiết trace", + "Kiểm tra chặt hơn đối với gói import", + ], + }, + }, + { + version: "3.35.0", + date: "2026-09-29", + title: { en: "The control plane catches up with the runtime", vi: "Control plane bắt kịp runtime" }, + items: { + en: [ + "Agent teams in the console: task detail, task actions and team settings", + "Chat keys confined to a single agent", + "A complete workflow editor, with published versions and run feedback", + "Deep span inspection in traces, with stricter secret redaction", + "A workspace activity audit with readable labels and actor names", + "Providers, models, memory, knowledge vault and schedules reach parity with the runtime dashboard", + "HTTP MCP servers can use static or OAuth authentication", + ], + vi: [ + "Đội agent trong console: chi tiết task, thao tác task và cài đặt đội", + "Chat key bị giới hạn trong một agent duy nhất", + "Trình soạn workflow hoàn chỉnh, có phiên bản đã publish và phản hồi khi chạy", + "Soi sâu từng span trong trace, kèm che secret chặt hơn", + "Nhật ký hoạt động của workspace với nhãn dễ đọc và tên người thực hiện", + "Nhà cung cấp, model, bộ nhớ, kho tri thức và lịch chạy ngang bằng dashboard của runtime", + "MCP server qua HTTP có thể dùng xác thực tĩnh hoặc OAuth", + ], + }, + }, + { + version: "3.34.0", + date: "2026-09-25", + title: { en: "A calmer control plane", vi: "Control plane gọn gàng hơn" }, + items: { + en: [ + "The Calm Precision redesign across every page of the customer control plane", + "Export archives download from the Import/Export tab and the CLI", + "Self-install: first-run owner setup, password sign-in, the dewee selfhost CLI and a per-workspace channels licence", + ], + vi: [ + "Giao diện Calm Precision được áp dụng cho mọi trang của customer control plane", + "Tải gói export ngay từ tab Import/Export và CLI", + "Self-install: thiết lập chủ sở hữu ở lần chạy đầu, đăng nhập bằng mật khẩu, CLI dewee selfhost và license kênh theo từng workspace", + ], + }, + }, { version: "3.33.0", date: "2026-09-24", @@ -57,13 +194,13 @@ export const RELEASES: Release[] = [ title: { en: "dewee becomes an MCP server", vi: "dewee thành MCP server" }, items: { en: [ - "A tenant-aware public MCP endpoint at /v1/mcp, off until you turn it on", + "A tenant-aware public MCP endpoint, off until you turn it on (served at /mcp/server since v3.39)", "Works with Claude Code, Cursor, VS Code/Copilot, Gemini CLI and OpenAI remote MCP", "Write tools need their own scope, mcp.tools.write", "Deploy readiness checks retry instead of failing early", ], vi: [ - "Endpoint MCP công khai theo tenant tại /v1/mcp, chỉ mở khi bạn bật", + "Endpoint MCP công khai theo tenant, chỉ mở khi bạn bật (từ v3.39 phục vụ tại /mcp/server)", "Dùng được với Claude Code, Cursor, VS Code/Copilot, Gemini CLI và OpenAI remote MCP", "Tool ghi dữ liệu cần scope riêng là mcp.tools.write", "Bước kiểm tra sẵn sàng khi deploy sẽ thử lại thay vì dừng sớm", @@ -152,10 +289,10 @@ export const CAPABILITY_GROUPS: CapabilityGroup[] = [ id: "channels", icon: "messages-square", title: { en: "Channels", vi: "Kênh chat" }, - lede: { en: "Where your team and your customers already talk.", vi: "Nơi đội ngũ và khách hàng của bạn vốn đang trò chuyện." }, + lede: { en: "Where your people and your agents already work together.", vi: "Nơi con người và agent của bạn vốn đã làm việc cùng nhau." }, items: [ { - title: { en: "Eleven chat channels", vi: "11 kênh chat" }, + title: { en: `${FACTS.channels} chat channels`, vi: `${FACTS.channels} kênh chat` }, body: { en: "Telegram, Zalo OA, Zalo Bot, Zalo Personal, Slack, Lark, Discord, WhatsApp, Facebook, Pancake and Bitrix24.", vi: "Telegram, Zalo OA, Zalo Bot, Zalo cá nhân, Slack, Lark, Discord, WhatsApp, Facebook, Pancake và Bitrix24.", @@ -191,7 +328,7 @@ export const CAPABILITY_GROUPS: CapabilityGroup[] = [ id: "agents", icon: "users", title: { en: "Agents & teams", vi: "Agent & đội agent" }, - lede: { en: "Specialists that work together, and know when to call a human.", vi: "Những chuyên viên biết phối hợp, và biết lúc nào cần gọi người." }, + lede: { en: "Specialist agents that hand work to each other, and know when to call a person.", vi: "Những agent chuyên trách biết giao việc cho nhau, và biết lúc nào cần gọi người." }, items: [ { title: { en: "One agent, one job", vi: "Mỗi agent một việc" }, @@ -213,6 +350,14 @@ export const CAPABILITY_GROUPS: CapabilityGroup[] = [ title: { en: "Human handoff", vi: "Chuyển cho người thật" }, body: { en: "The agent sends a case summary to the right person, group or topic.", vi: "Agent gửi bản tóm tắt vụ việc tới đúng người, nhóm hoặc topic." }, }, + { + title: { en: "Say who may use which agent", vi: "Nói rõ ai được dùng agent nào" }, + body: { + en: "Share an agent at a set level, and grant access to skills, MCP servers, providers and packages person by person.", + vi: "Chia sẻ agent theo cấp độ, và cấp quyền dùng skill, MCP server, nhà cung cấp, package cho từng người.", + }, + since: "3.39", + }, { title: { en: "Reasoning, per agent", vi: "Mức suy luận theo agent" }, body: { en: "Decide how hard each agent thinks. Settings are checked before they reach the model.", vi: "Chọn mức suy nghĩ cho từng agent. Cấu hình được kiểm tra trước khi tới model." }, @@ -234,10 +379,10 @@ export const CAPABILITY_GROUPS: CapabilityGroup[] = [ id: "models", icon: "cpu", title: { en: "Models & providers", vi: "Model & nhà cung cấp" }, - lede: { en: "21 providers, one setting to switch, and a plan B when one fails.", vi: "21 nhà cung cấp, đổi bằng một thiết lập, và luôn có phương án B khi một hãng trục trặc." }, + lede: { en: `${FACTS.providers} providers, one setting to switch, and a plan B when one fails.`, vi: `${FACTS.providers} nhà cung cấp, đổi bằng một thiết lập, và luôn có phương án B khi một hãng trục trặc.` }, items: [ { - title: { en: "21 model providers", vi: "21 nhà cung cấp model" }, + title: { en: `${FACTS.providers} model providers`, vi: `${FACTS.providers} nhà cung cấp model` }, body: { en: "Model labs, cloud gateways, subscriptions you already pay for, and local models on Ollama.", vi: "Các hãng model, cổng cloud, gói subscription bạn đang trả tiền, và model chạy local qua Ollama.", @@ -337,6 +482,14 @@ export const CAPABILITY_GROUPS: CapabilityGroup[] = [ }, since: "3.32", }, + { + title: { en: "Your own browser, by grant", vi: "Trình duyệt của bạn, khi bạn cấp quyền" }, + body: { + en: "With the dewee Chrome extension, an agent can work in tabs of a person's own Chrome instead of a server browser.", + vi: "Với tiện ích Chrome của dewee, agent có thể làm việc trên các tab Chrome của chính bạn thay vì trình duyệt trên máy chủ.", + }, + since: "3.40", + }, { title: { en: "Skills", vi: "Skill" }, body: { en: "Know-how packed into SKILL.md files, searched when there are many, called with /skill-name.", vi: "Kinh nghiệm đóng gói trong file SKILL.md, tự tìm khi có nhiều, gọi nhanh bằng /tên-skill." }, @@ -488,7 +641,7 @@ export const CAPABILITY_GROUPS: CapabilityGroup[] = [ id: "tenancy", icon: "building-2", title: { en: "Multi-tenant admin & RBAC", vi: "Đa tenant & phân quyền" }, - lede: { en: "One install, many companies or departments, no leaks between them.", vi: "Một bản cài, nhiều công ty hay phòng ban, không rò rỉ giữa các bên." }, + lede: { en: "One install, many companies or departments, each isolated from the others.", vi: "Một bản cài, nhiều công ty hay phòng ban, mỗi bên được cô lập với các bên còn lại." }, items: [ { title: { en: "Tenant isolation", vi: "Cô lập tenant" }, diff --git a/src/content/integrations.ts b/src/content/integrations.ts index ce9ae97..e0a7066 100644 --- a/src/content/integrations.ts +++ b/src/content/integrations.ts @@ -32,6 +32,7 @@ export const PROVIDERS: Integration[] = [ { id: "mistral", brand: "mistral", name: "Mistral" }, { id: "xai", brand: "xai", name: "xAI" }, { id: "openrouter", brand: "openrouter", name: "OpenRouter" }, + { id: "atlascloud", brand: "atlascloud", name: "Atlas Cloud" }, { id: "groq", brand: "groq", name: "Groq" }, { id: "minimax", brand: "minimax", name: "MiniMax" }, { id: "cohere", brand: "cohere", name: "Cohere" }, @@ -51,7 +52,7 @@ export type ProviderGroupId = "api" | "subscription" | "local"; export const PROVIDER_GROUPS: { id: ProviderGroupId; ids: string[] }[] = [ { id: "api", - ids: ["anthropic", "openai", "gemini", "vertex", "deepseek", "qwen", "mistral", "xai", "openrouter", "groq", "minimax", "cohere", "perplexity", "byteplus", "workersai", "vercel"], + ids: ["anthropic", "openai", "gemini", "vertex", "deepseek", "qwen", "mistral", "xai", "openrouter", "atlascloud", "groq", "minimax", "cohere", "perplexity", "byteplus", "workersai", "vercel"], }, { id: "subscription", ids: ["codex", "claude-cli", "opencode", "cline"] }, { id: "local", ids: ["ollama"] }, diff --git a/src/content/pages/about.ts b/src/content/pages/about.ts index 38b28f2..84b9838 100644 --- a/src/content/pages/about.ts +++ b/src/content/pages/about.ts @@ -1,11 +1,12 @@ /** * /about: NextLevelBuilder.io, the founders, how we work, the ecosystem. * Sources: plans/reports/scout-260924-2346-ecosystem-story.md §B (mission and values from - * nlb-web, founder facts), §A.4 and §C (GitHub counts read 2026-09-24), src/content/pages/home.ts - * proof (8,900+ automated tests), src/content/plans.ts (On-Premises includes one year of - * maintenance), dewee GitHub releases (six stable releases in September 2026). + * nlb-web, founder facts), §A.4 and §C (GitHub counts read 2026-09-24), src/content/facts.ts (test + * count and latest stable release, verified 2026-10-19), src/content/plans.ts (On-Premises includes + * one year of maintenance), dewee GitHub releases (stable v3.28 to v3.40 since early September 2026). */ import type { Bi } from "~/i18n/config"; +import { FACTS } from "~/content/facts"; type ReportRow = { subject: string; evidence: string; grade: string; remark: string }; @@ -45,7 +46,7 @@ export const ABOUT: Bi = { hero: { eyebrow: "About us", title: "Builders who *do the homework*.", - lede: "dewee is made by NextLevelBuilder.io, a team of builders from Vietnam. We make AI products for companies that want the results without the risk, and we stay with you after the sale.", + lede: "dewee is made by NextLevelBuilder.io, a team of builders from Vietnam. dewee is our multiplayer agent platform, where humans and AI agents work as one team. We make AI products for companies that want results and keep control, and we stay with you after the sale.", }, proofLabel: "NextLevelBuilder in numbers", proof: [ @@ -80,10 +81,10 @@ export const ABOUT: Bi = { ], columns: { subject: "Subject", evidence: "Evidence", grade: "Grade", remark: "Teacher's remark" }, rows: [ - { subject: "Homework ethic", evidence: "We test before we ship: 8,900+ automated tests guard the dewee codebase.", grade: "10", remark: "Always hands in complete homework." }, + { subject: "Homework ethic", evidence: `We test before we ship: the full suite of ${FACTS.goTests.rounded} Go tests runs on one preflighted commit before every release.`, grade: "10", remark: "Always hands in complete homework." }, { subject: "Shut at the door", evidence: "Tokens, pairing codes and network blocks guard the way in, behind five independent layers of defence.", grade: "10", remark: "Careful and thorough." }, { subject: "Standing by your success", evidence: "Custom workflows built with you, and a year of maintenance with every On-Premises licence.", grade: "10", remark: "Always helps classmates." }, - { subject: "Building in public", evidence: "Six stable dewee releases in September 2026, each one on the public changelog.", grade: "10", remark: "Speaks up in class." }, + { subject: "Building in public", evidence: `Stable dewee releases since v3.28 in early September 2026, the latest v${FACTS.stableRelease.version}, each one on the public changelog.`, grade: "10", remark: "Speaks up in class." }, ], conduct: { label: "Conduct", value: "Good" }, sign: "Self-assessed. You be the judge.", @@ -112,7 +113,7 @@ export const ABOUT: Bi = { hero: { eyebrow: "Về chúng tôi", title: "Những người *làm bài tập đầy đủ*.", - lede: "dewee do NextLevelBuilder.io làm ra, một đội ngũ builder đến từ Việt Nam. Chúng tôi làm sản phẩm AI cho doanh nghiệp muốn có kết quả mà không phải gánh rủi ro, và vẫn ở bên bạn sau khi bán.", + lede: "dewee do NextLevelBuilder.io làm ra, một đội ngũ builder đến từ Việt Nam. dewee là nền tảng cộng tác giữa con người và AI agent của chúng tôi, nơi hai bên làm việc như một đội. Chúng tôi làm sản phẩm AI cho doanh nghiệp muốn có kết quả mà vẫn giữ quyền kiểm soát, và vẫn ở bên bạn sau khi bán.", }, proofLabel: "NextLevelBuilder qua những con số", proof: [ @@ -147,10 +148,10 @@ export const ABOUT: Bi = { ], columns: { subject: "Môn", evidence: "Minh chứng", grade: "Điểm", remark: "Lời phê" }, rows: [ - { subject: "Làm bài tập về nhà", evidence: "Kiểm thử trước khi phát hành: hơn 8.900 bài test tự động canh giữ mã nguồn dewee.", grade: "10", remark: "Làm bài đầy đủ." }, + { subject: "Làm bài tập về nhà", evidence: `Kiểm thử trước khi phát hành: trọn bộ ${FACTS.goTests.roundedVi} test Go được chạy trên một commit đã preflight trước mỗi bản phát hành.`, grade: "10", remark: "Làm bài đầy đủ." }, { subject: "Đóng từ cửa", evidence: "Token, mã ghép nối và chặn mạng nội bộ canh lối vào, sau năm lớp phòng thủ độc lập.", grade: "10", remark: "Cẩn thận, chu đáo." }, { subject: "Đồng hành đến khi bạn thành công", evidence: "Cùng bạn xây workflow riêng, kèm một năm bảo trì cho mỗi license On-Premises.", grade: "10", remark: "Hay giúp đỡ bạn bè." }, - { subject: "Làm việc công khai", evidence: "Sáu bản stable của dewee trong tháng 9/2026, bản nào cũng lên changelog công khai.", grade: "10", remark: "Hăng hái phát biểu xây dựng bài." }, + { subject: "Làm việc công khai", evidence: `Các bản stable của dewee từ v3.28 hồi đầu tháng 9/2026, mới nhất là v${FACTS.stableRelease.version}, bản nào cũng lên changelog công khai.`, grade: "10", remark: "Hăng hái phát biểu xây dựng bài." }, ], conduct: { label: "Hạnh kiểm", value: "Tốt" }, sign: "Tự chấm thôi, bạn chấm lại giúp nhé.", diff --git a/src/content/pages/architecture.ts b/src/content/pages/architecture.ts index 525fb74..1c01a59 100644 --- a/src/content/pages/architecture.ts +++ b/src/content/pages/architecture.ts @@ -76,7 +76,7 @@ export const ARCHITECTURE_PAGE: Bi<{ meta: { title: "Architecture: one Go binary, an 8-stage agent loop", description: - "How dewee works inside: one Go gateway, an 8-stage agent loop, three-tier memory, agent teams, tenant isolation, traces, provider fallback and deployment.", + "How dewee, the multiplayer agent platform, works inside: one Go gateway, an 8-stage agent loop, memory, agent teams, tenant isolation, traces and deployment.", crumb: "Architecture", }, hero: { @@ -227,7 +227,7 @@ export const ARCHITECTURE_PAGE: Bi<{ meta: { title: "Kiến trúc: một file Go, vòng lặp agent 8 bước", description: - "Bên trong dewee: một gateway Go, vòng lặp agent 8 bước, bộ nhớ ba tầng, đội agent, cô lập tenant, trace, dự phòng nhà cung cấp và cách triển khai on-premises.", + "Bên trong dewee, nền tảng cộng tác giữa người và AI agent: gateway Go, vòng lặp agent 8 bước, bộ nhớ, đội agent, cô lập tenant, trace và cách triển khai.", crumb: "Kiến trúc", }, hero: { diff --git a/src/content/pages/features.ts b/src/content/pages/features.ts index c41bc8c..1c21133 100644 --- a/src/content/pages/features.ts +++ b/src/content/pages/features.ts @@ -26,26 +26,26 @@ export const FEATURES_PAGE: Bi<{ }> = { en: { meta: { - title: "Features: everything dewee can do, newest first", + title: "Features: how people and agents work as one team", description: - "Six stable releases in September 2026, then the full catalogue: channels, agent teams, memory, MCP, workflows, traces, security, tenancy, voice and deployment.", + "Agents that work in your chats, hand work to each other, connect your tools and wait for a person where it matters. Then the full catalogue and releases.", crumb: "Features", }, hero: { eyebrow: "Features", - title: "Everything dewee can do, *newest* first.", - lede: "Six stable releases shipped in September alone. Start with what is new this month, then browse the whole catalogue, one area at a time.", - note: "six releases in one month. we were busy.", - primary: "What's new this month", + title: "People and agents, *working as one team*.", + lede: "Agents join the chats your team already uses, hand work to each other, plug into your tools and stop for a person's approval when it matters. Start with what is new, then browse the whole catalogue.", + note: "benefits first, the full list after", + primary: "What's new", secondary: "Browse the catalogue", }, releases: { - eyebrow: "New this month", - title: "September: *six* stable releases.", + eyebrow: "Recently shipped", + title: "Recent releases, *stable* only.", lede: "Every one of them stable, none of them beta. The days circled in red are the days we shipped.", - monthLabel: "September 2026", + monthLabel: "October 2026", weekdays: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"], - calendarLabel: "September 2026 calendar with the six release days circled", + calendarLabel: "October 2026 calendar with the release days circled", releasedOn: "Released", changelog: "Read the full changelog", note: "beta features wait until they earn a place here", @@ -53,7 +53,7 @@ export const FEATURES_PAGE: Bi<{ catalogue: { eyebrow: "The catalogue", title: "Everything, *area* by area.", - lede: "What dewee does today, grouped the way you would evaluate it. A version tag marks what arrived this month.", + lede: "What dewee does today, area by area: where people and agents meet, how agents coordinate, which tools connect and how you stay in control. A version tag shows the release an item arrived in.", indexLabel: "Jump to an area", since: "since", count: (n) => `${n} items`, @@ -61,7 +61,7 @@ export const FEATURES_PAGE: Bi<{ console: { eyebrow: "In the console", title: "See it on *screen*.", - lede: "Real screens from the dewee control plane, running the demo workspace. Agents on the left, the traces they leave on the right.", + lede: "Real screens from the dewee control plane, running the demo workspace. Agents on the left, and on the right the traces that show a person what they did.", }, more: { eyebrow: "Keep reading", @@ -69,12 +69,12 @@ export const FEATURES_PAGE: Bi<{ items: [ { title: "Changelog", meta: "Every release, every fix", path: "/changelog", icon: "history" }, { title: "Documentation", meta: "Set-up, configuration and API", path: "/docs", icon: "book-open" }, - { title: "Use cases", meta: "How teams put agents to work", path: "/use-cases", icon: "briefcase" }, + { title: "Use cases", meta: "How people and agents split the work", path: "/use-cases", icon: "briefcase" }, ], }, cta: { title: "Which of these would your team use *first*?", - body: "Tell us what your team does all day. We will show you which features earn their keep in the first week.", + body: "Tell us what your people do all day and where an agent could take part. We will show you which features earn their keep in the first week.", primary: "Talk to us", secondary: "Ask dewee", note: "bring your hardest workflow", @@ -82,26 +82,26 @@ export const FEATURES_PAGE: Bi<{ }, vi: { meta: { - title: "Tính năng: mọi thứ dewee làm được, mới nhất trước", + title: "Tính năng: con người và agent làm việc như một đội", description: - "Sáu bản phát hành ổn định trong tháng 9/2026, rồi cả danh mục: kênh chat, đội agent, bộ nhớ, MCP, workflow, trace, bảo mật, đa tenant, giọng nói, triển khai.", + "Agent làm việc ngay trong các cuộc chat, giao việc cho nhau, kết nối công cụ của bạn và chờ người duyệt khi cần. Rồi đến danh mục và các bản phát hành.", crumb: "Tính năng", }, hero: { eyebrow: "Tính năng", - title: "Mọi thứ dewee làm được, *mới nhất* trước.", - lede: "Riêng tháng 9 đã có sáu bản phát hành ổn định. Hãy xem những gì mới trong tháng, rồi lật qua cả danh mục, từng mảng một.", - note: "một tháng, sáu bản. chúng tôi bận lắm.", - primary: "Có gì mới tháng này", + title: "Con người và agent, *làm việc như một đội*.", + lede: "Agent vào ngay các cuộc chat đội bạn đang dùng, giao việc cho nhau, kết nối công cụ của bạn và dừng lại chờ người duyệt khi việc quan trọng. Xem điều mới trước, rồi lật qua cả danh mục.", + note: "lợi ích trước, danh sách đầy đủ sau", + primary: "Có gì mới", secondary: "Xem danh mục", }, releases: { - eyebrow: "Mới trong tháng", - title: "Tháng 9: *sáu* bản ổn định.", + eyebrow: "Vừa phát hành", + title: "Các bản *ổn định* gần đây.", lede: "Bản nào cũng ổn định, không bản nào là beta. Những ngày khoanh bút đỏ là ngày chúng tôi phát hành.", - monthLabel: "Tháng 9 năm 2026", + monthLabel: "Tháng 10 năm 2026", weekdays: ["T2", "T3", "T4", "T5", "T6", "T7", "CN"], - calendarLabel: "Lịch tháng 9 năm 2026, khoanh tròn sáu ngày phát hành", + calendarLabel: "Lịch tháng 10 năm 2026, khoanh tròn các ngày phát hành", releasedOn: "Phát hành", changelog: "Xem toàn bộ changelog", note: "tính năng beta phải đợi tới lượt", @@ -109,7 +109,7 @@ export const FEATURES_PAGE: Bi<{ catalogue: { eyebrow: "Danh mục", title: "Tất cả, theo *từng* mảng.", - lede: "Những gì dewee làm được hôm nay, xếp theo cách bạn sẽ đánh giá nó. Nhãn phiên bản đánh dấu những gì vừa có trong tháng.", + lede: "Những gì dewee làm được hôm nay, theo từng mảng: nơi con người và agent gặp nhau, cách các agent phối hợp, công cụ nào kết nối được và bạn giữ quyền kiểm soát ra sao. Nhãn phiên bản cho biết mục đó có từ bản nào.", indexLabel: "Đi tới mảng", since: "từ", count: (n) => `${n} mục`, @@ -117,7 +117,7 @@ export const FEATURES_PAGE: Bi<{ console: { eyebrow: "Trong console", title: "Xem tận *mắt*.", - lede: "Màn hình thật từ control plane của dewee, chạy trên workspace demo. Bên trái là các agent, bên phải là trace mà chúng để lại.", + lede: "Màn hình thật từ control plane của dewee, chạy trên workspace demo. Bên trái là các agent, bên phải là trace cho người xem agent đã làm gì.", }, more: { eyebrow: "Đọc tiếp", @@ -125,12 +125,12 @@ export const FEATURES_PAGE: Bi<{ items: [ { title: "Changelog", meta: "Mọi bản phát hành, mọi bản sửa", path: "/changelog", icon: "history" }, { title: "Tài liệu", meta: "Cài đặt, cấu hình và API", path: "/docs", icon: "book-open" }, - { title: "Tình huống sử dụng", meta: "Cách các đội đưa agent vào việc", path: "/use-cases", icon: "briefcase" }, + { title: "Tình huống sử dụng", meta: "Con người và agent chia việc ra sao", path: "/use-cases", icon: "briefcase" }, ], }, cta: { title: "Đội của bạn sẽ dùng tính năng nào *trước*?", - body: "Kể cho chúng tôi nghe đội bạn làm gì mỗi ngày. Chúng tôi sẽ chỉ ra tính năng nào đáng tiền ngay trong tuần đầu.", + body: "Kể cho chúng tôi nghe mọi người trong đội làm gì mỗi ngày và chỗ nào agent có thể tham gia. Chúng tôi sẽ chỉ ra tính năng nào đáng tiền ngay trong tuần đầu.", primary: "Trò chuyện với chúng tôi", secondary: "Hỏi dewee", note: "mang bài khó nhất tới đây", diff --git a/src/content/pages/integrations.ts b/src/content/pages/integrations.ts index c0b6560..1e18d90 100644 --- a/src/content/pages/integrations.ts +++ b/src/content/pages/integrations.ts @@ -3,11 +3,14 @@ * per-channel details) live in src/content/integrations.ts; this file holds page chrome and the * short notes for subscription and local providers. Facts verified against the dewee repo * (internal/channels, internal/providers, internal/audio, internal/tools, docs/05) and the - * v3.28–v3.33 release notes. The website section describes dewee-webchat v0.1.0, checked + * v3.28–v3.40 release notes. Channel and provider counts come from FACTS (src/content/facts.ts). The website section describes dewee-webchat v0.1.0, checked * against github.com/nextlevelbuilder/dewee-webchat (README and package README). */ import type { Bi } from "~/i18n/config"; import type { ProviderGroupId } from "~/content/integrations"; +import { FACTS } from "~/content/facts"; + +const { channels: CH, providers: PR } = FACTS; /** dewee-webchat links and the embed snippet; the same in both locales. */ export const WEBCHAT = { @@ -50,22 +53,22 @@ export const INTEGRATIONS_PAGE: Bi<{ }> = { en: { meta: { - title: "Integrations: 11 channels, 21 model providers", + title: `Integrations: ${CH} channels, ${PR} model providers`, description: - "Connect dewee to 11 chat channels (Telegram, Zalo, Slack, Lark, WhatsApp and more), 21 model providers, five voice engines and any MCP server. Missing one? Ask us.", + `Put your agents where your team already works: ${CH} chat channels (Telegram, Zalo, Slack, Lark, WhatsApp and more), ${PR} model providers and any MCP server.`, crumb: "Integrations", }, hero: { eyebrow: "Integrations", title: "Works where your team *already* talks.", - lede: "Eleven chat channels, 21 model providers, five voice engines and any MCP server. Switching models is one setting, and a fallback steps in when a provider fails.", + lede: `${CH} chat channels, ${PR} model providers, five voice engines and any MCP server. Your people and your agents meet in the tools they already use. Switching models is one setting, and a fallback steps in when a provider fails.`, note: "no new app to roll out", primary: "See every channel", secondary: "Missing one? Ask us", }, channels: { eyebrow: "Channels", - title: "Eleven channels, *graded* honestly.", + title: `${CH} channels, *graded* honestly.`, lede: "What each channel does today, from how it connects to whether replies stream. On every one of them, a stranger can be asked for a pairing code before the agent answers.", label: "What each chat channel supports: connection, conversations, streaming, voice notes, files and notes", columns: { @@ -81,7 +84,7 @@ export const INTEGRATIONS_PAGE: Bi<{ }, models: { eyebrow: "Models", - title: "21 providers, *one* setting to switch.", + title: `${PR} providers, *one* setting to switch.`, lede: "Bring API keys, use the AI subscriptions your team already pays for, or keep prompts on your own hardware. If a provider rate-limits, times out or goes down, the agent moves to the next model on its list.", groups: { api: "API providers", subscription: "Subscriptions, via OAuth or CLI", local: "Local" }, notes: { @@ -115,7 +118,7 @@ export const INTEGRATIONS_PAGE: Bi<{ { icon: "server", title: "dewee as an MCP server", - body: "A tenant-aware endpoint at /v1/mcp for Claude Code, Cursor, VS Code/Copilot, Gemini CLI and OpenAI remote MCP. Off until you turn it on.", + body: "A tenant-aware endpoint at /mcp/server for Claude Code, Cursor, VS Code/Copilot, Gemini CLI and OpenAI remote MCP. Off until you turn it on.", }, { icon: "wrench", title: "30+ built-in tools", body: "Files, web, browser, memory, messaging and media tools, ready on day one." }, { icon: "square-terminal", title: "Custom tools, no redeploy", body: "Define a command-line tool over the API, with encrypted env vars and a timeout." }, @@ -150,22 +153,22 @@ export const INTEGRATIONS_PAGE: Bi<{ }, vi: { meta: { - title: "Tích hợp: 11 kênh chat, 21 nhà cung cấp model", + title: `Tích hợp: ${CH} kênh chat, ${PR} nhà cung cấp model`, description: - "Kết nối dewee với 11 kênh chat (Telegram, Zalo, Slack, Lark, WhatsApp và nhiều hơn), 21 nhà cung cấp model, năm engine giọng nói, mọi MCP server. Còn thiếu? Hỏi chúng tôi.", + `Đưa agent vào nơi đội bạn đang làm việc: ${CH} kênh chat (Telegram, Zalo, Slack, Lark, WhatsApp và nhiều hơn), ${PR} nhà cung cấp model và mọi MCP server.`, crumb: "Tích hợp", }, hero: { eyebrow: "Tích hợp", title: "Làm việc ngay nơi đội ngũ *đang* trò chuyện.", - lede: "Mười một kênh chat, 21 nhà cung cấp model, năm engine giọng nói và mọi MCP server. Đổi model chỉ là một dòng cài đặt, và có model dự phòng thế chỗ khi một nhà cung cấp gặp sự cố.", + lede: `${CH} kênh chat, ${PR} nhà cung cấp model, năm engine giọng nói và mọi MCP server. Con người và agent gặp nhau ngay trên những công cụ họ vốn dùng. Đổi model chỉ là một dòng cài đặt, và có model dự phòng thế chỗ khi một nhà cung cấp gặp sự cố.`, note: "không cần cài thêm app mới", primary: "Xem mọi kênh", secondary: "Còn thiếu? Hỏi chúng tôi", }, channels: { eyebrow: "Kênh chat", - title: "Mười một kênh, *chấm điểm* thật lòng.", + title: `${CH} kênh, *chấm điểm* thật lòng.`, lede: "Mỗi kênh làm được gì hôm nay, từ cách kết nối tới việc câu trả lời có stream hay không. Ở kênh nào, người lạ cũng có thể bị hỏi mã ghép nối trước khi agent trả lời.", label: "Khả năng của từng kênh chat: kết nối, hội thoại, stream, tin thoại, gửi file và ghi chú", columns: { @@ -181,7 +184,7 @@ export const INTEGRATIONS_PAGE: Bi<{ }, models: { eyebrow: "Model", - title: "21 nhà cung cấp, đổi chỉ bằng *một* cài đặt.", + title: `${PR} nhà cung cấp, đổi chỉ bằng *một* cài đặt.`, lede: "Dùng API key, dùng luôn gói AI mà đội bạn đang trả tiền, hoặc giữ prompt trên phần cứng của riêng bạn. Nếu một nhà cung cấp bị giới hạn tốc độ, quá thời gian hoặc ngừng hoạt động, agent chuyển sang model kế tiếp trong danh sách.", groups: { api: "Nhà cung cấp qua API", subscription: "Gói thuê bao, qua OAuth hoặc CLI", local: "Chạy local" }, notes: { @@ -215,7 +218,7 @@ export const INTEGRATIONS_PAGE: Bi<{ { icon: "server", title: "dewee làm MCP server", - body: "Endpoint /v1/mcp phân tách theo tenant cho Claude Code, Cursor, VS Code/Copilot, Gemini CLI và OpenAI remote MCP. Tắt cho tới khi bạn bật.", + body: "Endpoint /mcp/server phân tách theo tenant cho Claude Code, Cursor, VS Code/Copilot, Gemini CLI và OpenAI remote MCP. Tắt cho tới khi bạn bật.", }, { icon: "wrench", title: "Hơn 30 tool có sẵn", body: "Tool cho file, web, trình duyệt, bộ nhớ, nhắn tin và media, dùng được ngay từ ngày đầu." }, { icon: "square-terminal", title: "Tool tuỳ chỉnh, không cần deploy lại", body: "Tạo tool chạy lệnh qua API, có biến môi trường mã hoá và giới hạn thời gian." }, diff --git a/src/content/pages/pricing.ts b/src/content/pages/pricing.ts index 698757e..78a6b1b 100644 --- a/src/content/pages/pricing.ts +++ b/src/content/pages/pricing.ts @@ -42,7 +42,7 @@ export const PRICING: Bi = { hero: { eyebrow: "Pricing & deployment", title: "Pick where dewee *lives*.", - lede: "Your own machine, our shared cloud, your own runtime on TOSE.sh, or your hardware set up by us. Same product, same care. Every price is on this page, in US dollars.", + lede: "Your own machine, our shared cloud, your own runtime on TOSE.sh, or your hardware set up by us. Same multiplayer agent platform, same care. Every price is on this page, in US dollars.", note: "prices in ink, not pencil", primary: "Compare side by side", secondary: "Talk to us", @@ -136,7 +136,7 @@ export const PRICING: Bi = { hero: { eyebrow: "Bảng giá & triển khai", title: "Dữ liệu ở lại *công ty bạn*.", - lede: "Tại Việt Nam, dewee luôn chạy trên máy của chính bạn: tự cài đặt miễn phí, hoặc để chúng tôi triển khai On-Premises trên VPS hay Mac mini của công ty. Dữ liệu không rời khỏi công ty.", + lede: "Tại Việt Nam, dewee luôn chạy trên máy của chính bạn: tự cài đặt miễn phí, hoặc để chúng tôi triển khai On-Premises trên VPS hay Mac mini của công ty. Dữ liệu dewee lưu nằm ngay trên hạ tầng của công ty.", note: "giá viết bằng mực, không phải bút chì", primary: "So sánh hai cách", secondary: "Nhận báo giá On-Premises", diff --git a/src/content/pages/roadmap.ts b/src/content/pages/roadmap.ts index 725809e..158254f 100644 --- a/src/content/pages/roadmap.ts +++ b/src/content/pages/roadmap.ts @@ -1,12 +1,16 @@ /** - * /roadmap: Now / Next / Later, checked against the dewee repository on 25 Sep 2026. - * Sources (read-only, /Volumes/GOON/www/nlb/dewee): - * - Now: v3.34.0-beta.2 (control plane redesign, beta); plans/260826 docs + API reference epic (in progress). + * /roadmap: Now / Next / Later, checked against the dewee repository on 19 Oct 2026. + * Sources (read-only, the dewee repo and its GitHub releases): + * - Now: decision policies (v3.41.0-beta.1 and beta.2 release notes; the shadow-only evaluator shipped + * in v3.39.0); self-improvement engine (plans/261007-1206, status in-progress, first parts in + * v3.39.0); plans/260826 docs + API reference epic (in progress). The control plane redesign left + * this column when v3.34.0 shipped it as stable. * - Next: plans/260819 owner-level control plane (waves shipped in v3.22 and v3.31); - * plans/260805 hosted fleet hardening (runtime PostgreSQL on Kubernetes, canary rollout). + * plans/260805 hosted fleet hardening (runtime PostgreSQL on Kubernetes, canary rollout); + * plans/261013-2200-773-public-mcp-oauth (pending). * - Later: plans/260917-1815-jev-reflex-memory-gate/stage-2-plus-roadmap.md (deferred register, * explicitly "authorizes nothing"). - * - Shipped: GitHub releases v3.28 to v3.33 (September 2026). + * - Shipped: GitHub releases v3.34 to v3.40 (25 Sep to 15 Oct 2026, stable only). * No dates are promised for anything that has not shipped. */ import type { Bi } from "~/i18n/config"; @@ -34,7 +38,7 @@ export const ROADMAP: Bi = { eyebrow: "Roadmap", title: "What we're *building next*.", lede: "Straight from our own notes: what is in progress, what is planned, and the ideas we have written down but parked. Anything that ships lands on the changelog first.", - note: "checked 25 Sep 2026", + note: "checked 19 Oct 2026", primary: "See the changelog", secondary: "Suggest something", }, @@ -50,9 +54,14 @@ export const ROADMAP: Bi = { caption: "In progress or in beta", items: [ { - title: "A calmer control plane", - body: "A redesign of the control plane from the ground up: new visual tokens, a new shell and components, and every page rebuilt on them.", - meta: "Beta since v3.34.0-beta.2", + title: "Decision policies you can test first", + body: "Write down when an agent should carry on, ask for clarification or stay quiet in a group chat. The policy is measured on labelled messages first and switched on only after it passes.", + meta: "In beta, v3.41.0-beta.2", + }, + { + title: "Agents that improve from evidence", + body: "Changes proposed for an agent or a skill are replayed on saved examples, approved before they go live, tried on live traffic and rolled back if they regress.", + meta: "First parts shipped in v3.39", }, { title: "Official docs and API reference", @@ -76,6 +85,11 @@ export const ROADMAP: Bi = { body: "Moving the runtime database of hosted instances onto Kubernetes, and rolling out each update to a canary first, then to the whole fleet.", meta: "In progress", }, + { + title: "OAuth sign-in for the public MCP server", + body: "Let MCP clients sign in through the control plane instead of sharing one static key. The static key keeps working.", + meta: "Planned", + }, ], }, { @@ -110,16 +124,17 @@ export const ROADMAP: Bi = { }, shipped: { eyebrow: "Recently shipped", - title: "Six stable releases *in September*.", + title: "Seven stable releases *in three weeks*.", lede: "The roadmap is a promise about direction. The changelog is the record of what actually shipped.", - label: "dewee stable releases in September 2026", + label: "dewee stable releases from 25 September to 15 October 2026", items: [ - { date: "3 Sep · v3.28", text: "Speech settings from the CLI, and per-agent reasoning controls" }, - { date: "4 Sep · v3.29", text: "Reasoning delivery visible in traces" }, - { date: "19 Sep · v3.30", text: "Visual workflows, cron triggers and Reflex memory admission" }, - { date: "21 Sep · v3.31", text: "GPT-6 Astra, ClinePass, OpenCode Go and Vercel AI Gateway" }, - { date: "22 Sep · v3.32", text: "A public, tenant-aware MCP HTTP server" }, - { date: "24 Sep · v3.33", text: "English and Vietnamese control plane, time zones, Gemini TTS" }, + { date: "15 Oct · v3.40", text: "A connected browser for agents, and a rebuilt admin console" }, + { date: "14 Oct · v3.39", text: "Access grants for agents and tools, and the first self-improvement loop" }, + { date: "7 Oct · v3.38", text: "An agent-first CLI, and workspace setup by conversation" }, + { date: "5 Oct · v3.37", text: "Trusted channel identity and a Discord support inbox" }, + { date: "1 Oct · v3.36", text: "Scoped model catalogs and GPT-6.1 Sol" }, + { date: "29 Sep · v3.35", text: "Team tasks, workflow editor and MCP OAuth in the control plane" }, + { date: "25 Sep · v3.34", text: "A redesigned control plane, and first-run setup for self-install" }, ], cta: "Read the full changelog", }, @@ -142,7 +157,7 @@ export const ROADMAP: Bi = { eyebrow: "Lộ trình", title: "Chúng tôi *đang làm gì tiếp*.", lede: "Lấy thẳng từ sổ tay của đội: việc đang làm, việc đã lên kế hoạch, và những ý tưởng đã ghi lại nhưng tạm gác. Thứ gì phát hành sẽ lên changelog trước tiên.", - note: "cập nhật 25/9/2026", + note: "cập nhật 19/10/2026", primary: "Xem changelog", secondary: "Góp ý cho chúng tôi", }, @@ -158,9 +173,14 @@ export const ROADMAP: Bi = { caption: "Đang phát triển hoặc đang beta", items: [ { - title: "Control plane gọn gàng hơn", - body: "Thiết kế lại control plane từ gốc: bộ token giao diện mới, khung và component mới, và mọi trang được dựng lại trên nền đó.", - meta: "Beta từ v3.34.0-beta.2", + title: "Chính sách quyết định, đo trước khi bật", + body: "Ghi rõ khi nào agent nên làm tiếp, hỏi lại cho rõ hay im lặng trong nhóm chat. Chính sách được đo trên các tin nhắn đã gắn nhãn trước, và chỉ bật sau khi đạt.", + meta: "Đang beta, v3.41.0-beta.2", + }, + { + title: "Agent cải thiện dựa trên bằng chứng", + body: "Thay đổi đề xuất cho agent hay skill được chạy lại trên ví dụ đã lưu, được duyệt trước khi áp dụng, thử trên lưu lượng thật và quay lui nếu kết quả tệ đi.", + meta: "Phần đầu đã có trong v3.39", }, { title: "Tài liệu chính thức và API reference", @@ -184,6 +204,11 @@ export const ROADMAP: Bi = { body: "Chuyển cơ sở dữ liệu runtime của các instance do đội dewee vận hành sang Kubernetes, và phát hành bản cập nhật cho nhóm canary trước rồi mới đến toàn bộ.", meta: "Đang làm", }, + { + title: "Đăng nhập OAuth cho MCP server công khai", + body: "Để các MCP client đăng nhập qua control plane thay vì dùng chung một key tĩnh. Key tĩnh vẫn dùng được.", + meta: "Đã lên kế hoạch", + }, ], }, { @@ -218,16 +243,17 @@ export const ROADMAP: Bi = { }, shipped: { eyebrow: "Vừa phát hành", - title: "Sáu bản stable *trong tháng 9*.", + title: "Bảy bản stable *trong ba tuần*.", lede: "Lộ trình là lời hứa về hướng đi. Changelog mới là ghi chép về những gì đã thật sự phát hành.", - label: "Các bản stable của dewee trong tháng 9/2026", + label: "Các bản stable của dewee từ 25/9 đến 15/10/2026", items: [ - { date: "3/9 · v3.28", text: "Cấu hình giọng nói từ CLI, và tuỳ chỉnh reasoning cho từng agent" }, - { date: "4/9 · v3.29", text: "Theo dõi quá trình reasoning trong trace" }, - { date: "19/9 · v3.30", text: "Workflow trực quan, trigger theo lịch cron và Reflex memory admission" }, - { date: "21/9 · v3.31", text: "GPT-6 Astra, ClinePass, OpenCode Go và Vercel AI Gateway" }, - { date: "22/9 · v3.32", text: "MCP HTTP server công khai, phân tách theo tenant" }, - { date: "24/9 · v3.33", text: "Control plane tiếng Anh và tiếng Việt, múi giờ, Gemini TTS" }, + { date: "15/10 · v3.40", text: "Trình duyệt kết nối cho agent, và admin console được dựng lại" }, + { date: "14/10 · v3.39", text: "Cấp quyền truy cập cho agent và công cụ, cùng vòng tự cải thiện đầu tiên" }, + { date: "7/10 · v3.38", text: "CLI ưu tiên agent, và thiết lập workspace bằng hội thoại" }, + { date: "5/10 · v3.37", text: "Danh tính kênh đáng tin cậy và hộp thư hỗ trợ trên Discord" }, + { date: "1/10 · v3.36", text: "Danh mục model theo phạm vi và GPT-6.1 Sol" }, + { date: "29/9 · v3.35", text: "Task của đội, trình soạn workflow và MCP OAuth trong control plane" }, + { date: "25/9 · v3.34", text: "Control plane thiết kế lại, và thiết lập lần đầu cho self-install" }, ], cta: "Đọc toàn bộ changelog", }, diff --git a/src/content/pages/secondary-pages-copy.test.ts b/src/content/pages/secondary-pages-copy.test.ts new file mode 100644 index 0000000..c208cb3 --- /dev/null +++ b/src/content/pages/secondary-pages-copy.test.ts @@ -0,0 +1,161 @@ +/** + * Guards for the secondary pages and the release list: figures come from FACTS, no stale + * claims or unsupported promises, EN and VI stay in step, SEO lengths hold (REVIEW.md §3), and + * the Vietnamese deployment rule survives. + */ +import { describe, expect, it } from "vitest"; +import { LOCALES } from "~/i18n/config"; +import { FACTS } from "~/content/facts"; +import { plansFor } from "~/content/plans"; +import { CAPABILITY_GROUPS, RELEASES } from "~/content/features"; +import { USE_CASES } from "~/content/use-cases"; +import { LLMS_INTRO } from "~/lib/server/seo/llms-intro"; +import { FEATURES_PAGE } from "./features"; +import { ABOUT } from "./about"; +import { ROADMAP } from "./roadmap"; +import { INTEGRATIONS_PAGE } from "./integrations"; +import { ARCHITECTURE_PAGE } from "./architecture"; +import { SECURITY_PAGE } from "./security"; +import { PRICING } from "./pricing"; +import { INSTALL } from "./install"; + +const PAGES = { + features: FEATURES_PAGE, + about: ABOUT, + roadmap: ROADMAP, + integrations: INTEGRATIONS_PAGE, + architecture: ARCHITECTURE_PAGE, + security: SECURITY_PAGE, + pricing: PRICING, + install: INSTALL, +}; + +/** Text that must never reach a visitor: stale figures and promises the product does not make. */ +const FORBIDDEN: RegExp[] = [ + /8[,.]900/, + /on every (change|pull request|commit)/i, + /cho mỗi thay đổi/i, + /\b10 (chat )?channels\b/i, + /\b10 kênh/i, + /v3\.33\b/, + /world.?s first/i, + /\bthe first (multiplayer|ai|agent)/i, + /live co-?edit/i, + /shared cursors?/i, + /real-?time (sync|co-?edit|collaboration)/i, +]; + +const corpus = (value: unknown): string => JSON.stringify(value); + +describe("release list", () => { + it("starts at the latest stable release in FACTS", () => { + expect(RELEASES[0].version).toBe(FACTS.stableRelease.version); + expect(RELEASES[0].date).toBe(FACTS.stableRelease.date); + }); + + it("is newest first with unique versions and stable-only tags", () => { + const dates = RELEASES.map((r) => r.date); + expect(dates).toEqual([...dates].sort().reverse()); + expect(new Set(RELEASES.map((r) => r.version)).size).toBe(RELEASES.length); + for (const r of RELEASES) expect(r.version, r.version).toMatch(/^\d+\.\d+\.\d+$/); + }); + + it("covers every stable release from 3.28 to 3.40 that has notes", () => { + const minors = new Set(RELEASES.map((r) => Number(r.version.split(".")[1]))); + for (let minor = 28; minor <= 40; minor++) expect(minors.has(minor), `3.${minor}`).toBe(true); + }); + + it("has the same number of items in English and Vietnamese", () => { + for (const r of RELEASES) { + expect(r.items.en.length, r.version).toBeGreaterThan(0); + expect(r.items.vi.length, r.version).toBe(r.items.en.length); + expect(r.title.en.trim() && r.title.vi.trim(), r.version).toBeTruthy(); + } + }); + + it("only tags a capability `since` a minor version that has a release entry", () => { + const minors = new Set(RELEASES.map((r) => r.version.split(".").slice(0, 2).join("."))); + for (const group of CAPABILITY_GROUPS) { + for (const item of group.items) if (item.since) expect(minors.has(item.since), `${group.id}: ${item.title.en}`).toBe(true); + } + }); +}); + +describe("figures", () => { + it("builds the channel and provider counts from FACTS", () => { + for (const locale of LOCALES) { + const { title, description } = INTEGRATIONS_PAGE[locale].meta; + expect(title).toContain(String(FACTS.channels)); + expect(title).toContain(String(FACTS.providers)); + expect(description).toContain(String(FACTS.channels)); + } + const channelsItem = CAPABILITY_GROUPS.find((g) => g.id === "channels")?.items[0]; + expect(channelsItem?.title.en).toContain(String(FACTS.channels)); + }); + + it("states the test cadence as before every release", () => { + const row = ABOUT.en.work.card.rows[0].evidence; + expect(row).toContain(FACTS.goTests.rounded); + expect(row).toMatch(/before every release/); + expect(ABOUT.vi.work.card.rows[0].evidence).toContain(FACTS.goTests.roundedVi); + }); +}); + +describe("page copy", () => { + for (const [name, page] of Object.entries(PAGES)) { + it(`${name} has no stale figures or unsupported promises`, () => { + for (const locale of LOCALES) { + const text = corpus(page[locale]); + for (const pattern of FORBIDDEN) expect(text, `${name} (${locale}) ${pattern}`).not.toMatch(pattern); + } + }); + } + + it("keeps the use cases free of stale figures and unsupported promises", () => { + const text = corpus(USE_CASES); + for (const pattern of FORBIDDEN) expect(text, String(pattern)).not.toMatch(pattern); + }); + + for (const name of ["features", "integrations", "architecture", "security"] as const) { + for (const locale of LOCALES) { + it(`${name} (${locale}) has a title ≤ 60 and a description of 140–160 characters`, () => { + const { title, description } = PAGES[name][locale].meta; + expect(title.length).toBeLessThanOrEqual(60); + expect(description.length, description).toBeGreaterThanOrEqual(140); + expect(description.length, description).toBeLessThanOrEqual(160); + }); + } + } + + it("lists the same roadmap columns and shipped releases in both languages", () => { + expect(ROADMAP.vi.board.columns.map((c) => [c.id, c.items.length])).toEqual(ROADMAP.en.board.columns.map((c) => [c.id, c.items.length])); + expect(ROADMAP.vi.shipped.items.length).toBe(ROADMAP.en.shipped.items.length); + }); + + it("does not list the redesigned control plane as in progress any more", () => { + const now = ROADMAP.en.board.columns.find((c) => c.id === "now"); + expect(now?.items.some((i) => /Beta since v3\.34/.test(i.meta ?? ""))).toBe(false); + }); + + it("offers Self-install and On-Premises only in Vietnamese", () => { + expect(plansFor("vi").map((p) => p.id)).toEqual(["self-hosted", "on-premises"]); + expect(plansFor("en").map((p) => p.id)).toEqual(["self-hosted", "saas", "dedicated", "on-premises"]); + const vi = corpus(PRICING.vi); + expect(vi).not.toMatch(/AaaS|TOSE/); + }); +}); + +describe("llms.txt intro", () => { + it("carries the category, the current facts and stays within the summary limit", () => { + expect(LLMS_INTRO.summary.en).toContain("multiplayer agent platform"); + expect(LLMS_INTRO.summary.en).toContain(`${FACTS.channels} chat channels`); + expect(LLMS_INTRO.summary.en).toContain(`v${FACTS.stableRelease.version}`); + expect(LLMS_INTRO.summary.vi).toContain("con người và AI agent"); + expect(LLMS_INTRO.summary.vi).toContain(`${FACTS.channels} kênh chat`); + for (const locale of LOCALES) expect(LLMS_INTRO.summary[locale].length).toBeLessThanOrEqual(400); + }); + + it("does not mention AaaS or TOSE in the Vietnamese summary", () => { + expect(LLMS_INTRO.summary.vi).not.toMatch(/AaaS|TOSE/); + }); +}); diff --git a/src/content/pages/security.ts b/src/content/pages/security.ts index e0a370b..73f6021 100644 --- a/src/content/pages/security.ts +++ b/src/content/pages/security.ts @@ -3,7 +3,7 @@ * docs/09-security.md, docs/03-tools-system.md (policy engine, shell deny groups, credentialed CLI), * internal/crypto/aes.go (AES-256-GCM, "aes-gcm:" + base64(nonce + ciphertext + tag)), * docs/23-multi-tenant-architecture.md, docs/05-channels-messaging.md (pairing), the licence - * runbook (DEWEE_LICENSE_REQUIRED, 5-minute heartbeat) and release notes v3.29 / v3.30 / v3.33. + * runbook (DEWEE_LICENSE_REQUIRED, 5-minute heartbeat) and the 3.29, 3.30 and 3.33 release notes. * Honest caveats (CORS and injection defaults) are stated on the page, not hidden. */ import type { Bi } from "~/i18n/config"; @@ -47,7 +47,7 @@ export const SECURITY_PAGE: Bi<{ meta: { title: "Security: shut at the door, opened on purpose", description: - "Five layers of defence in dewee: rate limits, prompt-injection checks, a tool policy engine, secret scrubbing, sandboxing, and AES-256-GCM for stored credentials.", + "Five layers of defence keep people in control of AI agents: rate limits, injection checks, a tool policy engine, approvals, secret scrubbing and AES-256-GCM.", crumb: "Security", }, hero: { @@ -271,7 +271,7 @@ export const SECURITY_PAGE: Bi<{ meta: { title: "Bảo mật: đóng từ cửa, mở có chủ đích", description: - "Năm lớp phòng thủ của dewee: giới hạn tốc độ, kiểm tra prompt injection, policy engine cho tool, che secret, sandbox, và mã hoá AES-256-GCM cho credential lưu trữ.", + "Năm lớp phòng thủ giúp con người kiểm soát AI agent: giới hạn tốc độ, kiểm tra injection, policy engine cho tool, phê duyệt, che secret và mã hoá AES-256-GCM.", crumb: "Bảo mật", }, hero: { diff --git a/src/content/plans.ts b/src/content/plans.ts index 56dbd9e..f876ba6 100644 --- a/src/content/plans.ts +++ b/src/content/plans.ts @@ -94,8 +94,8 @@ export const PLANS: Plan[] = [ period: { en: "custom quote · includes the $500/year licence", vi: "báo giá theo nhu cầu · đã gồm license $500/năm" }, tagline: { en: "Everything on your hardware. We do the setup.", vi: "Mọi thứ trên hạ tầng của bạn. Chúng tôi lo cài đặt." }, includes: { - en: ["Runtime on your VPS or Mac mini", "5 custom workflows built with your team", "1 year of maintenance and updates", "Licence-key guard, billing switched off", "Data never leaves your network"], - vi: ["Runtime trên VPS hoặc Mac mini của bạn", "5 quy trình tuỳ chỉnh dựng cùng đội ngũ bạn", "1 năm bảo trì và cập nhật", "Bảo vệ bằng license key, không tính phí theo lượt", "Dữ liệu không rời khỏi hạ tầng của bạn"], + en: ["Runtime on your VPS or Mac mini", "5 custom workflows built with your team", "1 year of maintenance and updates", "Licence-key guard, billing switched off", "Runs inside your network"], + vi: ["Runtime trên VPS hoặc Mac mini của bạn", "5 quy trình tuỳ chỉnh dựng cùng đội ngũ bạn", "1 năm bảo trì và cập nhật", "Bảo vệ bằng license key, không tính phí theo lượt", "Vận hành trong hạ tầng của bạn"], }, tradeoffs: { en: ["Longer setup (1–3 weeks)", "You own the hardware"], @@ -181,7 +181,7 @@ export const PLAN_COMPARISON: PlanComparisonRow[] = [ "self-hosted": { en: "On your machine", vi: "Trên máy của bạn" }, saas: { en: "On our shared servers", vi: "Trên máy chủ dùng chung của chúng tôi" }, dedicated: { en: "In your runtime on TOSE.sh", vi: "Trong runtime riêng trên TOSE.sh" }, - "on-premises": { en: "Inside your company. It never leaves.", vi: "Trong công ty bạn, không đi đâu cả" }, + "on-premises": { en: "Inside your company, on your hardware.", vi: "Trong công ty bạn, trên hạ tầng của bạn" }, }, }, { diff --git a/src/content/use-cases/ads-performance-watch.ts b/src/content/use-cases/ads-performance-watch.ts index bf3416b..e8b90a2 100644 --- a/src/content/use-cases/ads-performance-watch.ts +++ b/src/content/use-cases/ads-performance-watch.ts @@ -13,8 +13,8 @@ export const adsPerformanceWatch: UseCase = { team: { en: "Performance marketing", vi: "Quảng cáo" }, title: { en: "Watch Google & Facebook ads like a hawk", vi: "Theo dõi & tối ưu quảng cáo Google, Facebook" }, summary: { - en: "Monitor spend, CPA and ROAS, catch anomalies within the hour and suggest budget or creative changes for approval.", - vi: "Theo dõi chi tiêu, CPA, ROAS; bắt bất thường trong vòng một giờ và đề xuất điều chỉnh ngân sách hay creative để duyệt.", + en: "A monitor agent watches spend, CPA and ROAS, and an analyst agent suggests a cause and a fix. A person decides what to change.", + vi: "Agent giám sát theo dõi chi tiêu, CPA, ROAS, còn agent phân tích đề xuất nguyên nhân và cách xử lý. Người thật quyết định thay đổi gì.", }, channels: ["telegram", "slack", "zalo"], detail: { diff --git a/src/content/use-cases/ai-coworker-in-group-chats.ts b/src/content/use-cases/ai-coworker-in-group-chats.ts index f03c42b..91e71aa 100644 --- a/src/content/use-cases/ai-coworker-in-group-chats.ts +++ b/src/content/use-cases/ai-coworker-in-group-chats.ts @@ -13,8 +13,8 @@ export const aiCoworkerInGroupChats: UseCase = { team: { en: "Every team", vi: "Mọi phòng ban" }, title: { en: "A co-worker in your group chats", vi: "Đồng nghiệp AI trong nhóm chat" }, summary: { - en: "An agent that listens, remembers the thread, chips in when @mentioned and keeps decisions from getting lost.", - vi: "Agent lắng nghe, nhớ mạch trao đổi, góp ý khi được @nhắc tên và giữ cho các quyết định không bị trôi.", + en: "An agent that sits in your group chat like a colleague, speaks when @mentioned and turns decisions into tasks with a named owner.", + vi: "Một agent ngồi trong nhóm chat như đồng nghiệp, chỉ lên tiếng khi được nhắc tên và biến quyết định thành đầu việc có người phụ trách.", }, channels: ["telegram", "zalo", "lark", "slack"], detail: { diff --git a/src/content/use-cases/creative-ad-production.ts b/src/content/use-cases/creative-ad-production.ts index b738a9b..0b8288e 100644 --- a/src/content/use-cases/creative-ad-production.ts +++ b/src/content/use-cases/creative-ad-production.ts @@ -12,8 +12,8 @@ export const creativeAdProduction: UseCase = { team: { en: "Creative", vi: "Sáng tạo" }, title: { en: "From brief to storyboard to ad cut", vi: "Thiết kế & dựng phim quảng cáo" }, summary: { - en: "Turn a brief into concepts, scripts, storyboards, images and draft videos, with review rounds right in the chat.", - vi: "Biến brief thành ý tưởng, kịch bản, storyboard, hình ảnh và video nháp, duyệt vòng góp ý ngay trong nhóm chat.", + en: "A strategist, a copywriter and a storyboard agent work from one brief in the team chat, and the creative director decides what moves forward.", + vi: "Agent chiến lược, agent copywriter và agent storyboard cùng làm từ một brief trong nhóm chat, còn giám đốc sáng tạo quyết định hướng đi.", }, channels: ["slack", "discord", "telegram"], detail: { diff --git a/src/content/use-cases/customer-care-with-handoff.ts b/src/content/use-cases/customer-care-with-handoff.ts index 1c02392..f75a6c3 100644 --- a/src/content/use-cases/customer-care-with-handoff.ts +++ b/src/content/use-cases/customer-care-with-handoff.ts @@ -13,8 +13,8 @@ export const customerCareWithHandoff: UseCase = { team: { en: "Customer care", vi: "Chăm sóc khách hàng" }, title: { en: "Customer care that knows when to hand off", vi: "Chăm sóc khách hàng, chuyển người thật khi cần" }, summary: { - en: "Answer customers around the clock from your knowledge base and pass the conversation to a human, with context, when it matters.", - vi: "Trả lời khách 24/7 từ kho tri thức và chuyển cuộc trò chuyện cho nhân viên, kèm đủ ngữ cảnh, khi cần thiết.", + en: "A care agent answers from your knowledge base in the chats customers already use, and hands over to a person, with a case summary, when it matters.", + vi: "Agent chăm sóc trả lời từ tri thức của bạn ngay trong các kênh khách đang dùng, và chuyển cho người thật, kèm bản tóm tắt vụ việc, khi cần.", }, channels: ["zalo", "facebook", "whatsapp", "pancake"], detail: { diff --git a/src/content/use-cases/hr-recruiting.ts b/src/content/use-cases/hr-recruiting.ts index 29afdea..4483677 100644 --- a/src/content/use-cases/hr-recruiting.ts +++ b/src/content/use-cases/hr-recruiting.ts @@ -13,8 +13,8 @@ export const hrRecruiting: UseCase = { team: { en: "People & HR", vi: "Nhân sự" }, title: { en: "Screen candidates without the backlog", vi: "Hỗ trợ tuyển dụng & review hồ sơ" }, summary: { - en: "Read CVs against the job brief, shortlist with reasons, schedule interviews and keep candidates in the loop.", - vi: "Đọc CV theo mô tả công việc, lọc danh sách kèm lý do, xếp lịch phỏng vấn và cập nhật cho ứng viên.", + en: "A screening agent proposes a shortlist with reasons and a scheduling agent finds interview slots. Recruiters make every yes and every no.", + vi: "Agent sàng lọc đề xuất danh sách rút gọn kèm lý do, agent xếp lịch tìm khung phỏng vấn. Mọi quyết định đạt hay không đều do nhà tuyển dụng.", }, channels: ["lark", "slack", "zalo"], detail: { diff --git a/src/content/use-cases/index.ts b/src/content/use-cases/index.ts index ff2ee6a..5836c7a 100644 --- a/src/content/use-cases/index.ts +++ b/src/content/use-cases/index.ts @@ -25,7 +25,7 @@ import { adsPerformanceWatch } from "./ads-performance-watch"; export type { ChannelId, TranscriptLine, UseCase, UseCaseArea, UseCaseDetail } from "./types"; /** Date the use-case copy was last reviewed against the dewee docs (Article JSON-LD dates). */ -export const USE_CASES_REVIEWED = "2026-09-25"; +export const USE_CASES_REVIEWED = "2026-10-19"; export const USE_CASES: UseCase[] = [ softwareDelivery, diff --git a/src/content/use-cases/meeting-notes-and-decisions.ts b/src/content/use-cases/meeting-notes-and-decisions.ts index e504d2e..0b511f5 100644 --- a/src/content/use-cases/meeting-notes-and-decisions.ts +++ b/src/content/use-cases/meeting-notes-and-decisions.ts @@ -12,8 +12,8 @@ export const meetingNotesAndDecisions: UseCase = { team: { en: "Leadership", vi: "Ban điều hành" }, title: { en: "Meetings that end with decisions", vi: "Tóm tắt cuộc họp & đề xuất quyết định" }, summary: { - en: "Summarise the meeting, pull out decisions and owners, and propose next steps for each team to confirm.", - vi: "Tóm tắt cuộc họp, rút ra quyết định và người phụ trách, đề xuất bước tiếp theo cho từng phòng ban xác nhận.", + en: "A minutes agent summarises the meeting and sends each decision to its owner to confirm. A follow-up agent then checks on the tasks.", + vi: "Agent biên bản tóm tắt cuộc họp và gửi từng quyết định cho người phụ trách xác nhận. Sau đó, agent theo dõi kiểm tra các đầu việc.", }, channels: ["lark", "slack", "telegram"], detail: { diff --git a/src/content/use-cases/real-estate-market-signals.ts b/src/content/use-cases/real-estate-market-signals.ts index acfdad0..315478e 100644 --- a/src/content/use-cases/real-estate-market-signals.ts +++ b/src/content/use-cases/real-estate-market-signals.ts @@ -12,8 +12,8 @@ export const realEstateMarketSignals: UseCase = { team: { en: "Real estate", vi: "Bất động sản" }, title: { en: "Read the real-estate market every morning", vi: "Thu thập tín hiệu thị trường bất động sản" }, summary: { - en: "Scan listings, news and zoning updates, spot price moves by district and deliver a morning brief to the sales floor.", - vi: "Quét tin rao, tin tức, quy hoạch; phát hiện biến động giá theo khu vực và gửi bản tin sáng cho đội kinh doanh.", + en: "A research agent reads listings, news and zoning updates before sunrise and posts a sourced brief to the sales group, where people take it from there.", + vi: "Agent nghiên cứu đọc tin đăng, tin tức và quy hoạch từ trước bình minh, rồi gửi bản tin có nguồn vào nhóm sales để mọi người tiếp tục từ đó.", }, channels: ["zalo", "telegram"], detail: { diff --git a/src/content/use-cases/retail-pod-design.ts b/src/content/use-cases/retail-pod-design.ts index 083a063..78eb801 100644 --- a/src/content/use-cases/retail-pod-design.ts +++ b/src/content/use-cases/retail-pod-design.ts @@ -12,8 +12,8 @@ export const retailPodDesign: UseCase = { team: { en: "Retail & e-commerce", vi: "Bán lẻ & TMĐT" }, title: { en: "Research, design and ship print-on-demand", vi: "Nghiên cứu, thiết kế & sản xuất POD" }, summary: { - en: "Find trending niches, generate on-brand designs, prepare mockups and listings, and hand them to production.", - vi: "Tìm ngách đang lên, tạo thiết kế đúng thương hiệu, dựng mockup và mô tả sản phẩm, rồi chuyển cho sản xuất.", + en: "A trend agent, a design agent and a listing agent hand work along from niche to mockup. A person approves every idea and listing before it goes live.", + vi: "Agent xu hướng, agent thiết kế và agent mô tả sản phẩm chuyền tay nhau từ ngách đến mockup. Người thật duyệt từng ý tưởng và từng sản phẩm trước khi lên kệ.", }, channels: ["slack", "telegram"], detail: { diff --git a/src/content/use-cases/sales-knowledge-assistant.ts b/src/content/use-cases/sales-knowledge-assistant.ts index b848562..86b4a23 100644 --- a/src/content/use-cases/sales-knowledge-assistant.ts +++ b/src/content/use-cases/sales-knowledge-assistant.ts @@ -12,8 +12,8 @@ export const salesKnowledgeAssistant: UseCase = { team: { en: "Sales", vi: "Kinh doanh" }, title: { en: "Every salesperson, fully briefed", vi: "Trợ lý sales với tri thức nội bộ" }, summary: { - en: "Instant answers on pricing, specs and policies from your internal knowledge, plus quotes and follow-ups drafted for approval.", - vi: "Trả lời tức thì về giá, thông số, chính sách từ tri thức nội bộ; soạn sẵn báo giá và lịch chăm sóc để duyệt.", + en: "A sales assistant answers from your knowledge base, drafts quotes and follow-ups, and sends discounts outside policy to the sales manager to approve.", + vi: "Trợ lý bán hàng trả lời từ tri thức nội bộ, soạn báo giá và lịch chăm sóc, và chuyển mức giảm giá ngoài chính sách cho quản lý duyệt.", }, channels: ["zalo", "lark", "telegram"], detail: { diff --git a/src/content/use-cases/seo-around-the-clock.ts b/src/content/use-cases/seo-around-the-clock.ts index e462fdc..95849d7 100644 --- a/src/content/use-cases/seo-around-the-clock.ts +++ b/src/content/use-cases/seo-around-the-clock.ts @@ -13,8 +13,8 @@ export const seoAroundTheClock: UseCase = { team: { en: "Growth", vi: "Tăng trưởng" }, title: { en: "SEO that never clocks out", vi: "Phân tích & cải thiện SEO 24/7" }, summary: { - en: "Audit pages, watch rankings, find content gaps and draft fixes, with a weekly report your team can act on.", - vi: "Rà soát trang, theo dõi thứ hạng, tìm khoảng trống nội dung và đề xuất bản sửa, kèm báo cáo tuần dễ hành động.", + en: "An audit agent and a content strategist agent find the issues and the gaps, then draft fixes for your team to review and ship, with a weekly report.", + vi: "Agent rà soát và agent chiến lược nội dung tìm lỗi và khoảng trống, rồi soạn bản sửa để đội bạn duyệt và đưa lên, kèm báo cáo hằng tuần.", }, channels: ["slack", "lark"], detail: { diff --git a/src/content/use-cases/social-media-care.ts b/src/content/use-cases/social-media-care.ts index fbe0d68..b0c1420 100644 --- a/src/content/use-cases/social-media-care.ts +++ b/src/content/use-cases/social-media-care.ts @@ -12,8 +12,8 @@ export const socialMediaCare: UseCase = { team: { en: "Marketing", vi: "Marketing" }, title: { en: "Look after every social account", vi: "Chăm sóc tài khoản mạng xã hội" }, summary: { - en: "Plan the calendar, draft posts in your voice, answer comments and DMs, and flag anything that needs a human.", - vi: "Lên lịch nội dung, viết bài đúng giọng thương hiệu, trả lời bình luận và tin nhắn, báo lại khi cần người xử lý.", + en: "A content agent drafts the plan for a marketer to approve, while a community agent answers comments and DMs and hands sensitive cases to a person.", + vi: "Agent nội dung soạn kế hoạch để marketer duyệt, còn agent cộng đồng trả lời bình luận và tin nhắn, rồi chuyển các vụ nhạy cảm cho người thật.", }, channels: ["facebook", "pancake", "zalo"], detail: { diff --git a/src/content/use-cases/software-delivery.ts b/src/content/use-cases/software-delivery.ts index 0aa3f1d..e5efa71 100644 --- a/src/content/use-cases/software-delivery.ts +++ b/src/content/use-cases/software-delivery.ts @@ -12,8 +12,8 @@ export const softwareDelivery: UseCase = { team: { en: "Engineering", vi: "Kỹ thuật" }, title: { en: "Ship software while the team sleeps", vi: "Tự động hoá quy trình phát triển phần mềm" }, summary: { - en: "Agents triage issues, write and review code, run tests and open pull requests, then report in the team chat.", - vi: "Agent phân loại issue, viết và review code, chạy test, mở pull request rồi báo cáo ngay trong nhóm chat.", + en: "A triage agent, a developer agent and a reviewer agent pass each issue along and open the pull request. Your engineers decide what merges.", + vi: "Agent phân loại, agent lập trình và agent review chuyển tiếp từng issue cho nhau rồi mở pull request. Kỹ sư của bạn quyết định cái nào được merge.", }, channels: ["slack", "discord", "telegram"], detail: { diff --git a/src/content/use-cases/stock-market-sentiment.ts b/src/content/use-cases/stock-market-sentiment.ts index 7388891..3a08b23 100644 --- a/src/content/use-cases/stock-market-sentiment.ts +++ b/src/content/use-cases/stock-market-sentiment.ts @@ -13,8 +13,8 @@ export const stockMarketSentiment: UseCase = { team: { en: "Investment research", vi: "Phân tích đầu tư" }, title: { en: "Measure market sentiment, not just price", vi: "Đo lường sentiment thị trường chứng khoán" }, summary: { - en: "Track news, forums and filings for the tickers you follow, score the mood and alert the desk on sharp shifts.", - vi: "Theo dõi tin tức, diễn đàn và báo cáo của mã bạn quan tâm, chấm điểm tâm lý thị trường và cảnh báo khi đổi chiều.", + en: "A sentiment agent tracks news, forums and filings for your tickers, scores the mood and alerts the desk on sharp shifts. Analysts make the call.", + vi: "Agent phân tích tâm lý theo dõi tin tức, diễn đàn và báo cáo của các mã bạn quan tâm, chấm điểm và cảnh báo khi đổi chiều. Quyết định thuộc về chuyên viên phân tích.", }, channels: ["telegram", "discord"], detail: { diff --git a/src/content/use-cases/task-chasing.ts b/src/content/use-cases/task-chasing.ts index 24f2767..2ec0ae9 100644 --- a/src/content/use-cases/task-chasing.ts +++ b/src/content/use-cases/task-chasing.ts @@ -13,8 +13,8 @@ export const taskChasing: UseCase = { team: { en: "Operations", vi: "Vận hành" }, title: { en: "The polite colleague who chases every task", vi: "Quản lý & nhắc việc nội bộ mỗi ngày" }, summary: { - en: "Collect daily updates, nudge owners before deadlines, spot blockers early and give managers a clean status board.", - vi: "Thu thập cập nhật mỗi ngày, nhắc người phụ trách trước hạn, phát hiện điểm nghẽn sớm và gửi quản lý bảng tiến độ gọn gàng.", + en: "A coordinator agent collects daily updates in the chats people already use, nudges owners before deadlines and tells the manager the day a blocker appears.", + vi: "Agent điều phối thu thập cập nhật mỗi ngày trong các nhóm chat quen thuộc, nhắc người phụ trách trước hạn và báo quản lý ngay khi có điểm nghẽn.", }, channels: ["lark", "zalo", "telegram", "bitrix24"], detail: { diff --git a/src/lib/blocks/marketing-block-schemas.ts b/src/lib/blocks/marketing-block-schemas.ts index f4f3917..d3a86dc 100644 --- a/src/lib/blocks/marketing-block-schemas.ts +++ b/src/lib/blocks/marketing-block-schemas.ts @@ -188,7 +188,7 @@ export const MARKETING_BLOCKS: BlockDef[] = [ render: "section", description: "A ruler timeline (horizontal on wide screens); the last entry pulses as 'now'.", schema: timeline, - example: { items: [{ date: "Feb 2026", text: "GoClaw's first commit" }, { date: "Sep 2026", text: "dewee v3.33" }], label: "Milestones" }, + example: { items: [{ date: "Feb 2026", text: "GoClaw's first commit" }, { date: "Oct 2026", text: "dewee v3.40" }], label: "Milestones" }, }, { type: "ProofStrip", @@ -196,7 +196,8 @@ export const MARKETING_BLOCKS: BlockDef[] = [ render: "section", description: "A ruler of verified numbers. Never estimate: every value needs a source.", schema: proofStrip, - example: { items: [{ value: "637,225", label: "lines of Go" }, { value: "8,900+", label: "automated tests" }], label: "dewee in numbers" }, + // Example values only; the site itself reads its numbers from src/content/facts.ts. + example: { items: [{ value: "900,000+", label: "lines of Go" }, { value: "13,000+", label: "Go tests" }], label: "dewee in numbers" }, }, { type: "TocIndex", diff --git a/src/lib/server/seo/llms-intro.ts b/src/lib/server/seo/llms-intro.ts new file mode 100644 index 0000000..65eb87f --- /dev/null +++ b/src/lib/server/seo/llms-intro.ts @@ -0,0 +1,20 @@ +/** + * The blockquote summary and reading note at the top of /llms.txt and /vi/llms.txt. Kept apart from + * the worker-only response code so a plain unit test can check it. Numbers come from FACTS, so a + * release refresh updates this text with the rest of the site. + */ +import type { Bi } from "~/i18n/config"; +import { FACTS } from "~/content/facts"; + +const { channels, providers, stableRelease } = FACTS; + +export const LLMS_INTRO: { summary: Bi; note: Bi } = { + summary: { + en: `dewee is the multiplayer agent platform: humans and AI agents work as one team in the chats your team already uses. ${channels} chat channels, ${providers} model providers, agent teams that delegate to each other, human approval for risky actions, traces for every run, and self-install, hosted or on-premises deployment. Latest stable release: v${stableRelease.version}.`, + vi: `dewee là nền tảng cộng tác giữa con người và AI agent: hai bên làm việc như một đội ngay trong các kênh chat đội bạn đang dùng. ${channels} kênh chat, ${providers} nhà cung cấp model, đội agent biết giao việc cho nhau, con người phê duyệt hành động rủi ro, trace cho mọi lần chạy, triển khai tự cài đặt hoặc On-Premises. Bản stable mới nhất: v${stableRelease.version}.`, + }, + note: { + en: "Every page on {site} has a Markdown twin: add `.md` to its URL ({site}/index.md for the home page). The English pages in one file: {site}/llms-full.txt. Vietnamese pages: {site}/vi/llms.txt. Questions: {email}.", + vi: "Mọi trang trên {site} đều có bản Markdown: thêm `.md` vào cuối URL (trang chủ tiếng Việt là {site}/vi.md). Danh sách trang tiếng Anh: {site}/llms.txt, toàn văn tiếng Anh: {site}/llms-full.txt. Liên hệ: {email}.", + }, +}; diff --git a/src/lib/server/seo/llms-responses.ts b/src/lib/server/seo/llms-responses.ts index 7ddbebe..b40ccab 100644 --- a/src/lib/server/seo/llms-responses.ts +++ b/src/lib/server/seo/llms-responses.ts @@ -11,19 +11,12 @@ import { loadDiscovery } from "./discovery-sources"; import { discoveryResponse, siteOrigin, TEXT } from "./discovery-response"; import { listPostMarkdown } from "./dynamic-pages"; import { fullTextSection, joinWithinBudget, LLMS_FULL_MAX_BYTES } from "./llms-full-text"; +import { LLMS_INTRO } from "./llms-intro"; import { llmsTxt, type LlmsLink } from "./llms-txt"; import { LLMS_GROUPS } from "./page-sections"; import { LLMS_FULL_ASSET } from "./seo-manifest"; import { OPENAPI_PATH } from "./seo-paths"; -const INTRO = { - summary: SITE.description, - note: { - en: "Every page on {site} has a Markdown twin: add `.md` to its URL ({site}/index.md for the home page). The English pages in one file: {site}/llms-full.txt. Vietnamese pages: {site}/vi/llms.txt. Questions: {email}.", - vi: "Mọi trang trên {site} đều có bản Markdown: thêm `.md` vào cuối URL (trang chủ tiếng Việt là {site}/vi.md). Danh sách trang tiếng Anh: {site}/llms.txt, toàn văn tiếng Anh: {site}/llms-full.txt. Liên hệ: {email}.", - }, -}; - /** The API description is open to anyone (see /developers), so agents get it next to the pages. */ const resources = (locale: Locale): LlmsLink[] => [ { title: DEVELOPERS[locale].hero.openapi, href: OPENAPI_PATH, description: DEVELOPERS[locale].hero.note, section: "developers" }, @@ -39,7 +32,7 @@ export async function llmsTxtResponse(env: Env, url: URL, locale: Locale): Promi .map((p): LlmsLink => ({ title: p.title, href: p.twin, description: p.description, section: p.section })) .concat(resources(locale)) .sort((a, b) => rank(a) - rank(b) || a.href.split("/").length - b.href.split("/").length || a.href.localeCompare(b.href)); - const body = llmsTxt({ locale, siteUrl: siteOrigin(env, url), name: "dewee", intro: INTRO, email: SITE.email, links }); + const body = llmsTxt({ locale, siteUrl: siteOrigin(env, url), name: "dewee", intro: LLMS_INTRO, email: SITE.email, links }); return discoveryResponse(body, TEXT); } diff --git a/src/pages/[...lang]/integrations.astro b/src/pages/[...lang]/integrations.astro index 0425ddd..9920840 100644 --- a/src/pages/[...lang]/integrations.astro +++ b/src/pages/[...lang]/integrations.astro @@ -1,6 +1,6 @@ --- /** - * /integrations: the eleven channels as a report card, the 21 model providers grouped by how you + * /integrations: the eleven channels as a report card, the model providers grouped by how you * connect them, voice engines, MCP and tools, the dewee-webchat widget for your own website, and a * "missing one? ask us" next step. * Lists live in src/content/integrations.ts; page copy in src/content/pages/integrations.ts. From aa5a77c7c7b211915dc5cdb396cc8a5632748df5 Mon Sep 17 00:00:00 2001 From: Duy Nguyen Date: Mon, 19 Oct 2026 20:41:04 +0700 Subject: [PATCH 2/4] feat(home): reposition the homepage as the multiplayer agent platform New EN/VI hero, a MultiplayerWorkflow block that follows one request from a salesperson through lead and specialist agents to a manager's approval, four collaboration pillars and an outcome-first section order. Hero CTAs open app.dewee.sh and the work story, and their clicks reach PostHog and GTM under the existing consent gate. Co-Authored-By: Claude Opus 5.5 --- src/components/blocks/HomeHero.astro | 26 +- .../blocks/MultiplayerWorkflow.astro | 167 +++++++ src/components/layout/GoogleTagManager.astro | 21 +- src/components/layout/PosthogAnalytics.astro | 9 +- src/content/pages/home-en.ts | 133 ++++++ src/content/pages/home-types.ts | 54 +++ src/content/pages/home-vi.ts | 133 ++++++ src/content/pages/home-workflow.ts | 67 +++ src/content/pages/home.test.ts | 144 ++++++ src/content/pages/home.ts | 417 +----------------- src/content/site.ts | 8 +- src/lib/server/seo/page-sections.ts | 2 +- src/pages/[...lang]/index.astro | 181 ++++---- 13 files changed, 856 insertions(+), 506 deletions(-) create mode 100644 src/components/blocks/MultiplayerWorkflow.astro create mode 100644 src/content/pages/home-en.ts create mode 100644 src/content/pages/home-types.ts create mode 100644 src/content/pages/home-vi.ts create mode 100644 src/content/pages/home-workflow.ts create mode 100644 src/content/pages/home.test.ts diff --git a/src/components/blocks/HomeHero.astro b/src/components/blocks/HomeHero.astro index bbdd0d7..821f724 100644 --- a/src/components/blocks/HomeHero.astro +++ b/src/components/blocks/HomeHero.astro @@ -1,8 +1,10 @@ --- /** - * Homepage hero: the promise on the left, a group chat on the right where dewee is already - * at work. The mascot drops in and peeks over the chat card; chat lines arrive one by one - * (CSS only) and are fully visible without JS or with reduced motion. + * Homepage hero: the promise on the left, a group chat on the right where people and agents are + * already working together. The mascot drops in and peeks over the chat card; chat lines arrive + * one by one (CSS only) and are fully visible without JS or with reduced motion. + * Both calls to action carry `data-hero-cta` so the analytics layer can name hero clicks + * (see PosthogAnalytics.astro and GoogleTagManager.astro); no tracking code lives here. */ import { escapeHtml, inlineMarkup } from "~/lib/inline-markup"; import DeweeMascot from "~/components/mascot/DeweeMascot.astro"; @@ -15,10 +17,12 @@ interface Props { lede: string; primary: { label: string; href: string }; secondary?: { label: string; href: string }; + /** One short line under the buttons, read by everyone (unlike the handwritten `note`). */ + microcopy?: string; note?: string; chat: { channel: string; label: string; lines: Line[] }; } -const { eyebrow, title, lede, primary, secondary, note, chat } = Astro.props; +const { eyebrow, title, lede, primary, secondary, microcopy, note, chat } = Astro.props; // Each sentence of the promise gets its own line so the break never lands mid-thought, and // its last two words are glued together so no sentence ends on a lonely word ("is / yours."). const titleLines = title.split(/(?<=[.!?])\s+/).map((l) => l.replace(/\s+(\S+)$/, "\u00a0$1")); @@ -39,9 +43,10 @@ const richText = (text: string) =>

{titleLines.map((l) => )}

{lede}

- {primary.label} - {secondary && {secondary.label}} + {primary.label} + {secondary && {secondary.label}}
+ {microcopy &&

{microcopy}

}
@@ -82,10 +87,13 @@ const richText = (text: string) => .hero__bg { position: absolute; inset: 0; z-index: -1; --drum-x: 76%; --drum-y: 46%; } .hero__grid { display: grid; grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); gap: clamp(2.5rem, 1rem + 5vw, 6rem); align-items: center; } .hero__copy { display: grid; gap: var(--space-5); justify-items: start; } - .hero__title { max-inline-size: 13ch; } - .hero__line { display: block; text-wrap: pretty; } + /* Balanced lines keep a one-sentence promise from stranding a short word ("got") mid-title. */ + .hero__title { max-inline-size: 18ch; } + .hero__line { display: block; text-wrap: balance; } .hero__lede { max-inline-size: 36rem; } .hero__actions { display: flex; flex-wrap: wrap; gap: var(--space-3); margin-block-start: var(--space-2); } + .hero__micro { display: inline-flex; align-items: center; gap: var(--space-2); font-size: var(--step--1); color: var(--ink-3); } + .hero__micro :global(svg) { flex: none; color: var(--accent-ink); } .hero__demo { position: relative; padding-block-start: clamp(3.5rem, 2rem + 4vw, 5.5rem); } .hero__mascot { position: absolute; inset-block-start: 0; inset-inline-end: 12%; z-index: 0; } @@ -144,7 +152,7 @@ const richText = (text: string) => @media (max-width: 960px) { .hero__grid { grid-template-columns: minmax(0, 1fr); } .hero__bg { --drum-x: 50%; --drum-y: 72%; } - .hero__title { max-inline-size: 16ch; } + .hero__title { max-inline-size: 18ch; } .hero__demo { max-inline-size: 34rem; inline-size: 100%; justify-self: center; } .hero__note { inset-inline-start: auto; inset-inline-end: 0; inset-block-end: -3rem; } } diff --git a/src/components/blocks/MultiplayerWorkflow.astro b/src/components/blocks/MultiplayerWorkflow.astro new file mode 100644 index 0000000..8d5615f --- /dev/null +++ b/src/components/blocks/MultiplayerWorkflow.astro @@ -0,0 +1,167 @@ +--- +/** + * One complete piece of work told as a swimlane: people on one side, agents on the other, the + * shared task board across both, joined by a spine of numbered handoffs. It is an ordered list, + * so it reads in sequence with a screen reader, without JS and with reduced motion; the only + * motion is the shared reveal stagger. `label` must say the story is illustrative. + * Props are plain, already-localised strings so the page builder can render it from JSON. + */ +import SectionHead from "./SectionHead.astro"; +import Icon from "~/components/ui/Icon.astro"; + +type Tone = "neutral" | "progress" | "review" | "done"; +type Status = { label: string; tone: Tone }; +type Step = { + kind: "human" | "agent" | "board"; + actor: string; + role: string; + text: string; + status?: Status; + tasks?: { title: string; owner: string; status: Status }[]; +}; +interface Props { + id?: string; + eyebrow?: string; + title: string; + lede?: string; + label: string; + kinds: { human: string; agent: string; board: string }; + boardLabel: string; + steps: Step[]; + outcome: { title: string; body: string }; + band?: boolean; +} +const { id, eyebrow, title, lede, label, kinds, boardLabel, steps, outcome, band = true } = Astro.props; +const titleId = id ? `${id}-title` : undefined; +const TONE_CLASS: Record = { neutral: "", progress: "chip--accent", review: "chip--warning", done: "chip--success" }; +const ICON = { human: "user", agent: "bot", board: "list-checks" } as const; +const initial = (name: string) => (name.trim().split(/\s+/).slice(-1)[0] ?? "").slice(0, 1).toUpperCase(); +--- + +
+
+
+ +

{label}

+
+ +
    + {steps.map((s, i) => ( +
  1. + +
    +

    + + {s.actor}{s.role} + {kinds[s.kind]} +

    +

    {s.text}

    + {s.tasks && s.tasks.length > 0 && ( +
      + {s.tasks.map((t) => ( +
    • + {t.title}{" "}{t.owner} + {t.status.label} +
    • + ))} +
    + )} + {s.status && ( +

    {s.status.label}

    + )} +
    +
  2. + ))} +
+ +

+ + {outcome.title}. {outcome.body} +

+
+
+ + diff --git a/src/components/layout/GoogleTagManager.astro b/src/components/layout/GoogleTagManager.astro index 1101b4b..2b27a49 100644 --- a/src/components/layout/GoogleTagManager.astro +++ b/src/components/layout/GoogleTagManager.astro @@ -6,16 +6,20 @@ * staging traffic never reaches the container. * part="head": consent defaults + the GTM loader, as high in as possible * part="body": the