@vellumai/assistant 0.9.0 → 0.9.1-staging.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (222) hide show
  1. package/ARCHITECTURE.md +18 -34
  2. package/bun.lock +7 -8
  3. package/docs/activation-funnel-telemetry.md +4 -4
  4. package/docs/architecture/security.md +29 -28
  5. package/docs/stt-provider-onboarding.md +3 -5
  6. package/docs/workflows-testing.md +13 -44
  7. package/docs/workflows.md +3 -5
  8. package/node_modules/@vellumai/ces-client/src/__tests__/ces-client.test.ts +47 -0
  9. package/node_modules/@vellumai/ces-client/src/rpc-client.ts +28 -5
  10. package/node_modules/@vellumai/environments/src/seeds.ts +2 -5
  11. package/node_modules/@vellumai/gateway-client/src/index.ts +17 -6
  12. package/node_modules/@vellumai/gateway-client/src/outbound-contract.ts +119 -0
  13. package/node_modules/@vellumai/gateway-client/src/types.ts +15 -84
  14. package/openapi.yaml +135 -59
  15. package/package.json +2 -1
  16. package/scripts/sync-llm-catalog.ts +6 -15
  17. package/scripts/sync-web-search-catalog.ts +3 -11
  18. package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +14 -26
  19. package/src/__tests__/agent-loop-compaction-strip.test.ts +240 -0
  20. package/src/__tests__/agent-loop-output-hooks.test.ts +69 -0
  21. package/src/__tests__/agent-loop-override-profile.test.ts +25 -0
  22. package/src/__tests__/always-loaded-tools-guard.test.ts +2 -3
  23. package/src/__tests__/app-dir-path-guard.test.ts +0 -1
  24. package/src/__tests__/assistant-feature-flag-guard.test.ts +1 -4
  25. package/src/__tests__/assistant-feature-flag-guardrails.test.ts +0 -2
  26. package/src/__tests__/avatar-identity-sync.test.ts +2 -27
  27. package/src/__tests__/btw-routes.test.ts +6 -8
  28. package/src/__tests__/checker.test.ts +0 -3
  29. package/src/__tests__/config-loader-backfill.test.ts +103 -6
  30. package/src/__tests__/config-watcher.test.ts +0 -18
  31. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +22 -0
  32. package/src/__tests__/credential-broker.test.ts +449 -1
  33. package/src/__tests__/credential-execution-tools.test.ts +0 -1
  34. package/src/__tests__/credential-prompt-route.test.ts +3 -4
  35. package/src/__tests__/credential-routes.test.ts +360 -0
  36. package/src/__tests__/credential-security-invariants.test.ts +2 -13
  37. package/src/__tests__/dynamic-page-surface.test.ts +101 -1
  38. package/src/__tests__/fixtures/credential-security-fixtures.ts +2 -33
  39. package/src/__tests__/gateway-only-guard.test.ts +3 -7
  40. package/src/__tests__/identity-routes.test.ts +0 -189
  41. package/src/__tests__/inbound-invite-redemption.test.ts +4 -4
  42. package/src/__tests__/invite-redemption-service.test.ts +4 -4
  43. package/src/__tests__/llm-callsite-catalog.test.ts +5 -6
  44. package/src/__tests__/llm-catalog-parity.test.ts +0 -22
  45. package/src/__tests__/llm-resolver.test.ts +49 -24
  46. package/src/__tests__/oauth-provider-seed-logos.test.ts +4 -6
  47. package/src/__tests__/onboarding-persona-write.test.ts +1 -1
  48. package/src/__tests__/persona-resolver.test.ts +11 -14
  49. package/src/__tests__/plugin-api-model-profiles.test.ts +178 -0
  50. package/src/__tests__/registry.test.ts +2 -7
  51. package/src/__tests__/schedule-routes-workflow-validation.test.ts +1 -10
  52. package/src/__tests__/schedule-routes.test.ts +0 -30
  53. package/src/__tests__/schedule-tools.test.ts +2 -18
  54. package/src/__tests__/skill-execute-input.test.ts +46 -1
  55. package/src/__tests__/skill-runtime-path.test.ts +2 -3
  56. package/src/__tests__/subagent-tools.test.ts +116 -0
  57. package/src/__tests__/surface-completion-nudge-hook.test.ts +367 -0
  58. package/src/__tests__/token-estimator-accuracy.benchmark.test.ts +1 -29
  59. package/src/__tests__/token-manager.test.ts +519 -0
  60. package/src/__tests__/tool-executor.test.ts +0 -79
  61. package/src/__tests__/trusted-contact-multichannel.test.ts +3 -3
  62. package/src/__tests__/trusted-contact-verification.test.ts +6 -6
  63. package/src/__tests__/voice-invite-redemption.test.ts +2 -2
  64. package/src/__tests__/web-search-catalog-parity.test.ts +6 -25
  65. package/src/__tests__/workspace-greetings.test.ts +152 -0
  66. package/src/agent/loop.ts +25 -5
  67. package/src/api/README.md +6 -6
  68. package/src/api/responses/conversation-message.ts +2 -4
  69. package/src/api/responses/home.ts +0 -4
  70. package/src/approvals/guardian-request-resolvers.ts +2 -2
  71. package/src/calls/relay-access-wait.ts +1 -1
  72. package/src/calls/voice-session-bridge.ts +2 -2
  73. package/src/cli/commands/plugins.ts +143 -2
  74. package/src/cli/lib/__tests__/diff-plugin.test.ts +443 -0
  75. package/src/cli/lib/__tests__/merge-plugin-tree.test.ts +313 -0
  76. package/src/cli/lib/__tests__/upgrade-plugin.test.ts +253 -2
  77. package/src/cli/lib/diff-plugin.ts +346 -0
  78. package/src/cli/lib/install-from-github.ts +105 -17
  79. package/src/cli/lib/merge-plugin-tree.ts +228 -0
  80. package/src/cli/lib/plugin-fingerprint.ts +14 -0
  81. package/src/cli/lib/upgrade-plugin.ts +270 -10
  82. package/src/cli/program.ts +0 -2
  83. package/src/config/bundled-skills/subagent/SKILL.md +4 -0
  84. package/src/config/bundled-skills/subagent/TOOLS.json +4 -0
  85. package/src/config/bundled-skills/workflows/SKILL.md +0 -1
  86. package/src/config/bundled-tool-registry.ts +2 -7
  87. package/src/config/call-site-defaults.ts +12 -2
  88. package/src/config/feature-flag-registry.json +1 -17
  89. package/src/config/inference-profile-validation.ts +26 -0
  90. package/src/config/loader.ts +4 -0
  91. package/src/config/profile-order.ts +28 -0
  92. package/src/config/schemas/elevenlabs.ts +0 -1
  93. package/src/config/schemas/platform.ts +0 -8
  94. package/src/config/seed-inference-profiles.ts +25 -20
  95. package/src/contacts/contact-store.ts +87 -96
  96. package/src/contacts/contacts-write.ts +5 -21
  97. package/src/context/compactor.ts +2 -2
  98. package/src/credential-execution/process-manager.ts +55 -14
  99. package/src/credential-execution/prompted-credential.ts +2 -3
  100. package/src/daemon/config-watcher.ts +0 -4
  101. package/src/daemon/conversation-agent-loop.ts +15 -4
  102. package/src/daemon/conversation-slash.ts +2 -23
  103. package/src/daemon/conversation-tool-setup.ts +11 -3
  104. package/src/daemon/conversation.ts +2 -0
  105. package/src/daemon/handlers/config-channels.ts +20 -16
  106. package/src/daemon/handlers/config-slack-channel.ts +2 -3
  107. package/src/daemon/lifecycle.ts +0 -7
  108. package/src/daemon/message-types/conversations.ts +3 -3
  109. package/src/daemon/message-types/sync.ts +0 -1
  110. package/src/daemon/orphan-reaper.test.ts +0 -19
  111. package/src/daemon/orphan-reaper.ts +2 -24
  112. package/src/daemon/server.ts +0 -10
  113. package/src/home/relationship-state.ts +2 -4
  114. package/src/memory/__tests__/memory-retrospective-job.test.ts +195 -401
  115. package/src/memory/bookmark-crud.ts +1 -2
  116. package/src/memory/db-init.ts +7 -17
  117. package/src/memory/embedding-backend.ts +23 -0
  118. package/src/memory/embedding-billing-breaker.ts +96 -0
  119. package/src/memory/jobs-store.ts +25 -13
  120. package/src/memory/jobs-worker.ts +52 -0
  121. package/src/memory/memory-retrospective-constants.ts +4 -4
  122. package/src/memory/memory-retrospective-job.ts +19 -227
  123. package/src/memory/migrations/291-contact-channels-renormalize-addresses.ts +62 -0
  124. package/src/memory/migrations/__tests__/291-contact-channels-renormalize-addresses.test.ts +311 -0
  125. package/src/memory/migrations/__tests__/run-migrations.test.ts +52 -0
  126. package/src/memory/migrations/index.ts +1 -0
  127. package/src/memory/migrations/run-migrations.ts +41 -0
  128. package/src/memory/migrations/validate-migration-state.ts +1 -1
  129. package/src/memory/schema/contacts.ts +0 -4
  130. package/src/messaging/providers/slack/adapter.ts +1 -1
  131. package/src/notifications/adapters/shared.ts +29 -0
  132. package/src/notifications/adapters/slack.ts +5 -32
  133. package/src/notifications/adapters/telegram.ts +2 -20
  134. package/src/notifications/broadcaster.ts +10 -1
  135. package/src/notifications/home-feed-side-effect.ts +4 -3
  136. package/src/notifications/notification-utils.ts +17 -19
  137. package/src/notifications/types.ts +7 -0
  138. package/src/oauth/AGENTS.md +5 -24
  139. package/src/plugin-api/constants.ts +1 -1
  140. package/src/plugin-api/index.ts +6 -1
  141. package/src/plugin-api/model-profiles.ts +33 -0
  142. package/src/plugin-api/types.ts +50 -2
  143. package/src/plugins/defaults/index.ts +25 -0
  144. package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +54 -2
  145. package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +107 -7
  146. package/src/plugins/defaults/surface-completion-nudge/hooks/post-model-call.ts +276 -0
  147. package/src/plugins/defaults/surface-completion-nudge/hooks/stop.ts +22 -0
  148. package/src/plugins/defaults/surface-completion-nudge/nudge-state-store.ts +46 -0
  149. package/src/plugins/defaults/surface-completion-nudge/package.json +14 -0
  150. package/src/plugins/defaults/task-progress-nudge/hooks/post-tool-use.ts +1 -1
  151. package/src/prompts/persona-resolver.ts +2 -2
  152. package/src/runtime/AGENTS.md +0 -1
  153. package/src/runtime/actor-trust-resolver.ts +12 -44
  154. package/src/runtime/btw-sidechain.ts +3 -6
  155. package/src/runtime/channel-approval-types.ts +18 -45
  156. package/src/runtime/channel-invite-transports/telegram.ts +4 -4
  157. package/src/runtime/channel-verification-service.ts +4 -3
  158. package/src/runtime/invite-redemption-service.ts +3 -3
  159. package/src/runtime/routes/__tests__/plugins-routes.test.ts +218 -1
  160. package/src/runtime/routes/app-routes.ts +1 -1
  161. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +2 -2
  162. package/src/runtime/routes/assets/vellum-design-system.css +1959 -0
  163. package/src/runtime/routes/btw-routes.ts +1 -27
  164. package/src/runtime/routes/conversation-compaction-routes.ts +1 -1
  165. package/src/runtime/routes/conversation-routes.ts +2 -2
  166. package/src/runtime/routes/credential-routes.ts +40 -16
  167. package/src/runtime/routes/empty-state-greeting-cache.ts +1 -2
  168. package/src/runtime/routes/identity-routes.ts +1 -296
  169. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +1 -1
  170. package/src/runtime/routes/plugins-routes.ts +171 -5
  171. package/src/runtime/routes/schedule-routes.ts +0 -22
  172. package/src/runtime/routes/workflow-routes.test.ts +4 -43
  173. package/src/runtime/routes/workflow-routes.ts +0 -28
  174. package/src/runtime/routes/workspace-greetings.ts +55 -0
  175. package/src/runtime/sync/resource-sync-events.ts +1 -11
  176. package/src/schedule/inference-profile.ts +2 -14
  177. package/src/subagent/manager.ts +6 -0
  178. package/src/subagent/types.ts +6 -0
  179. package/src/tools/AGENTS.md +3 -3
  180. package/src/tools/browser/browser-execution.ts +1 -1
  181. package/src/tools/network/web-search-error.ts +1 -1
  182. package/src/tools/permission-checker.ts +1 -1
  183. package/src/tools/schedule/create.ts +3 -9
  184. package/src/tools/schedule/update.ts +2 -10
  185. package/src/tools/side-effects.ts +2 -17
  186. package/src/tools/skills/execute.ts +34 -0
  187. package/src/tools/subagent/spawn.ts +34 -9
  188. package/src/tools/tool-approval-handler.ts +1 -3
  189. package/src/tools/tool-manifest.ts +0 -2
  190. package/src/tools/ui-surface/definitions.ts +39 -1
  191. package/src/tools/workflows/run-workflow.test.ts +8 -18
  192. package/src/util/platform.ts +2 -2
  193. package/src/workflows/capabilities.ts +2 -3
  194. package/src/workflows/run-manager.test.ts +0 -25
  195. package/src/workflows/run-manager.ts +2 -24
  196. package/src/__tests__/app-control-no-global-cgevent.test.ts +0 -98
  197. package/src/__tests__/credential-security-e2e.test.ts +0 -362
  198. package/src/__tests__/credential-vault-unit.test.ts +0 -1528
  199. package/src/__tests__/credential-vault.test.ts +0 -1706
  200. package/src/__tests__/identity-intro-cache.test.ts +0 -315
  201. package/src/__tests__/secret-onetime-send.test.ts +0 -182
  202. package/src/cli/commands/__tests__/task.test.ts +0 -914
  203. package/src/cli/commands/task.ts +0 -771
  204. package/src/config/bundled-skills/personal-page/SKILL.md +0 -57
  205. package/src/config/bundled-skills/personal-page/TOOLS.json +0 -27
  206. package/src/config/bundled-skills/personal-page/tools/app-refresh.ts +0 -17
  207. package/src/config/preloaded-apps/personal-page/src/components/About.tsx +0 -22
  208. package/src/config/preloaded-apps/personal-page/src/components/App.tsx +0 -16
  209. package/src/config/preloaded-apps/personal-page/src/components/Features.tsx +0 -77
  210. package/src/config/preloaded-apps/personal-page/src/components/Hero.tsx +0 -57
  211. package/src/config/preloaded-apps/personal-page/src/components/Pending.tsx +0 -28
  212. package/src/config/preloaded-apps/personal-page/src/components/animations.tsx +0 -234
  213. package/src/config/preloaded-apps/personal-page/src/components/icons.tsx +0 -48
  214. package/src/config/preloaded-apps/personal-page/src/components/media.ts +0 -16
  215. package/src/config/preloaded-apps/personal-page/src/index.html +0 -20
  216. package/src/config/preloaded-apps/personal-page/src/main.tsx +0 -7
  217. package/src/config/preloaded-apps/personal-page/src/profile-data.ts +0 -82
  218. package/src/config/preloaded-apps/personal-page/src/styles.css +0 -759
  219. package/src/memory/__tests__/preloaded-apps.test.ts +0 -85
  220. package/src/memory/preloaded-apps.ts +0 -116
  221. package/src/runtime/routes/identity-intro-cache.ts +0 -172
  222. package/src/tools/credentials/vault.ts +0 -712
@@ -1,57 +0,0 @@
1
- ---
2
- name: personal-page
3
- description: Build or update the user's personal page — a cinematic landing page about THE USER themselves (their bio, work, life areas). Use whenever the user asks for a personal page, a page about them, or to update what's on their page. NOT for general landing pages or other apps (that's app-builder). The page already exists in the workspace; this skill is how to fill it.
4
- metadata:
5
- emoji: "✨"
6
- vellum:
7
- display-name: "Personal Page"
8
- category: "content"
9
- activation-hints:
10
- - "User asks for a personal page, profile page, or landing page about themselves"
11
- - "User asks to change, refresh, or add something to their personal page"
12
- - "Onboarding asks you to turn research about the user into a page"
13
- avoid-when:
14
- - "User wants a landing page for a product, company, or someone else — use app-builder"
15
- ---
16
-
17
- You fill in the user's personal page — a finished, cinematic dark landing page
18
- that already lives in the workspace as the app `personal-page`. Your job is
19
- **content, not construction**: the design is done.
20
-
21
- If `/workspace/data/apps/personal-page/` does not exist, this skill does not
22
- apply (the preseeded page is rolled out gradually) — build whatever the user
23
- asked for with app-builder instead.
24
-
25
- **Present it as yours.** To the user this is "the page you filled in for
26
- them" — fine to acknowledge the page shell exists, but never narrate the
27
- mechanics below (file paths, refresh calls, contracts) or your progress in
28
- chat. Do the work, then one short line.
29
-
30
- ## How to populate it
31
-
32
- 1. Read `/workspace/data/apps/personal-page/src/profile-data.ts`. The contract
33
- comment at the top explains every field and shows a worked example.
34
- 2. Overwrite the `profile` export with what you know or learned about the
35
- user. Set `status: "ready"`. Touch ONLY this file — never the layout,
36
- styles, animations, or media. Do NOT use `app_create`; the app exists.
37
- 3. Call `app_refresh` (app_id: `personal-page`). If it reports compile errors,
38
- fix your profile-data.ts edit and refresh again. **`app_refresh` is the
39
- only build step — never compile manually (no esbuild, bundlers, or build
40
- commands in the terminal); the platform's compiler has the correct JSX
41
- setup and a manual build will produce a broken page.**
42
- 4. As soon as the refresh is clean, call `app_open` (app_id: `personal-page`)
43
- so the finished page is up and waiting — don't wait to be asked.
44
-
45
- ## Writing the content
46
-
47
- - Facts beat adjectives: names, numbers, places. One fact per bullet.
48
- - Only what you actually verified; write around gaps rather than inventing.
49
- - Third person, warm but grounded. The page should feel like a profile in a
50
- design magazine, not a résumé.
51
-
52
- ## Later edits
53
-
54
- - Content tweaks ("add my marathon PR", "fix my title") → same flow: edit
55
- `profile-data.ts`, `app_refresh`.
56
- - Only if the user explicitly asks to change the **design or layout** may you
57
- edit the other source files, following app-builder conventions.
@@ -1,27 +0,0 @@
1
- {
2
- "version": 1,
3
- "tools": [
4
- {
5
- "name": "app_refresh",
6
- "description": "Trigger recompilation and surface refresh for an app after making file changes. Call this once after you finish editing app files with generic file tools.",
7
- "category": "apps",
8
- "risk": "low",
9
- "input_schema": {
10
- "type": "object",
11
- "properties": {
12
- "app_id": {
13
- "type": "string",
14
- "description": "The ID of the app to refresh"
15
- },
16
- "change_summary": {
17
- "type": "string",
18
- "description": "Short summary of what changed, using git conventional commit format (e.g. 'feat: add dark mode toggle', 'fix: form validation'). Used as the version history entry."
19
- }
20
- },
21
- "required": ["app_id"]
22
- },
23
- "executor": "tools/app-refresh.ts",
24
- "execution_target": "host"
25
- }
26
- ]
27
- }
@@ -1,17 +0,0 @@
1
- import { setAppCommitMessage } from "../../../../memory/app-git-service.js";
2
- import * as appStore from "../../../../memory/app-store.js";
3
- import { executeAppRefresh } from "../../../../tools/apps/executors.js";
4
- import type {
5
- ToolContext,
6
- ToolExecutionResult,
7
- } from "../../../../tools/types.js";
8
-
9
- export async function run(
10
- input: Record<string, unknown>,
11
- context: ToolContext,
12
- ): Promise<ToolExecutionResult> {
13
- if (typeof input.change_summary === "string" && input.change_summary.trim()) {
14
- setAppCommitMessage(context.conversationId, input.change_summary.trim());
15
- }
16
- return executeAppRefresh({ app_id: input.app_id as string }, appStore);
17
- }
@@ -1,22 +0,0 @@
1
- import { profile } from "../profile-data";
2
- import { AnimatedParagraph, WordsPullUpMultiStyle } from "./animations";
3
-
4
- export function About() {
5
- const { about } = profile;
6
- return (
7
- <section class="about" id="about">
8
- <div class="about-card">
9
- <div class="about-label">{about.label}</div>
10
- <h2 class="about-heading">
11
- <WordsPullUpMultiStyle
12
- segments={about.heading.map((seg) => ({
13
- text: seg.text,
14
- className: seg.italic ? "serif-italic" : undefined,
15
- }))}
16
- />
17
- </h2>
18
- <AnimatedParagraph className="about-body" text={about.body} />
19
- </div>
20
- </section>
21
- );
22
- }
@@ -1,16 +0,0 @@
1
- import { profile } from "../profile-data";
2
- import { About } from "./About";
3
- import { Features } from "./Features";
4
- import { Hero } from "./Hero";
5
- import { Pending } from "./Pending";
6
-
7
- export function App() {
8
- if (profile.status !== "ready") return <Pending />;
9
- return (
10
- <main>
11
- <Hero />
12
- <About />
13
- <Features />
14
- </main>
15
- );
16
- }
@@ -1,77 +0,0 @@
1
- import type { ComponentChildren } from "preact";
2
-
3
- import { profile } from "../profile-data";
4
- import { StaggerCard, WordsPullUpMultiStyle } from "./animations";
5
- import { ArrowRight, Check } from "./icons";
6
- import { CARD_ICONS } from "./media";
7
-
8
- function CheckLine({ children }: { children: ComponentChildren }) {
9
- return (
10
- <li>
11
- <Check className="check" />
12
- <span>{children}</span>
13
- </li>
14
- );
15
- }
16
-
17
- function LearnMore() {
18
- return (
19
- <a class="learn-more">
20
- Learn more
21
- <ArrowRight size={14} />
22
- </a>
23
- );
24
- }
25
-
26
- export function Features() {
27
- const { features } = profile;
28
- return (
29
- <section class="features">
30
- <div class="features-inner">
31
- <div class="features-header">
32
- <div>
33
- <WordsPullUpMultiStyle
34
- segments={[{ text: features.headingLine1, className: "line1" }]}
35
- />
36
- </div>
37
- <div style={{ marginTop: "0.3em" }}>
38
- <WordsPullUpMultiStyle
39
- segments={[{ text: features.headingLine2, className: "line2" }]}
40
- delayBase={0.2}
41
- />
42
- </div>
43
- </div>
44
-
45
- <div class="feature-grid">
46
- <StaggerCard index={0} className="feature-card visual">
47
- <div class="card-backdrop" />
48
- <div class="vlabel">{features.statusLabel}</div>
49
- </StaggerCard>
50
-
51
- {features.cards.slice(0, 3).map((card, i) => (
52
- <StaggerCard
53
- index={i + 1}
54
- className="feature-card"
55
- id={`feature-${i}`}
56
- key={card.title}
57
- >
58
- <img
59
- class="feature-icon"
60
- src={CARD_ICONS[i % CARD_ICONS.length]}
61
- alt=""
62
- />
63
- <div class="feature-num">{`0${i + 1}`}</div>
64
- <div class="feature-title">{card.title}</div>
65
- <ul class="feature-list">
66
- {card.bullets.map((b) => (
67
- <CheckLine key={b}>{b}</CheckLine>
68
- ))}
69
- </ul>
70
- <LearnMore />
71
- </StaggerCard>
72
- ))}
73
- </div>
74
- </div>
75
- </section>
76
- );
77
- }
@@ -1,57 +0,0 @@
1
- import { profile } from "../profile-data";
2
- import { FadeIn, WordsPullUp } from "./animations";
3
-
4
- function scrollToSection(id: string) {
5
- document.getElementById(id)?.scrollIntoView({ behavior: "smooth" });
6
- }
7
-
8
- export function Hero() {
9
- const { hero, features } = profile;
10
- // Nav entries map to the page's real sections: the about card plus one
11
- // entry per feature card. Labels come from the content itself, so every
12
- // tab has a guaranteed scroll target.
13
- const navItems = [
14
- { label: "About", target: "about" },
15
- ...features.cards.slice(0, 3).map((card, i) => ({
16
- label: card.title.replace(/\.+$/, ""),
17
- target: `feature-${i}`,
18
- })),
19
- ];
20
- return (
21
- <section class="hero">
22
- <div class="hero-frame">
23
- <div class="hero-backdrop" />
24
- <div class="noise-overlay" />
25
- <div class="hero-gradient" />
26
-
27
- <nav class="navbar">
28
- {navItems.map((item) => (
29
- <a
30
- href={`#${item.target}`}
31
- key={item.target}
32
- onClick={(e) => {
33
- e.preventDefault();
34
- scrollToSection(item.target);
35
- }}
36
- >
37
- {item.label}
38
- </a>
39
- ))}
40
- </nav>
41
-
42
- <div class="hero-content">
43
- <div class="hero-title-col">
44
- <h1 class="hero-title">
45
- <WordsPullUp text={hero.title} showAsterisk />
46
- </h1>
47
- </div>
48
- <div class="hero-desc-col">
49
- <FadeIn as="p" className="hero-desc" delay={0.5}>
50
- {hero.description}
51
- </FadeIn>
52
- </div>
53
- </div>
54
- </div>
55
- </section>
56
- );
57
- }
@@ -1,28 +0,0 @@
1
- import { FadeIn, WordsPullUpMultiStyle } from "./animations";
2
-
3
- export function Pending() {
4
- return (
5
- <section class="pending">
6
- <div class="noise-overlay" />
7
- <div class="pending-content">
8
- <FadeIn as="div" className="pending-label">
9
- YOUR PAGE
10
- </FadeIn>
11
- <h1 class="pending-heading">
12
- <WordsPullUpMultiStyle
13
- segments={[
14
- { text: "Being" },
15
- { text: "researched", className: "serif-italic" },
16
- { text: "right now." },
17
- ]}
18
- delayBase={0.2}
19
- />
20
- </h1>
21
- <FadeIn as="p" className="pending-sub" delay={0.9}>
22
- Your assistant is out on the open web learning about you. Check back
23
- in a minute.
24
- </FadeIn>
25
- </div>
26
- </section>
27
- );
28
- }
@@ -1,234 +0,0 @@
1
- import type { ComponentChildren, JSX } from "preact";
2
- import { useEffect, useRef, useState } from "preact/hooks";
3
-
4
- type Segment = { text: string; className?: string };
5
-
6
- function splitWords(text: string): string[] {
7
- return text.split(/\s+/).filter(Boolean);
8
- }
9
-
10
- function useInViewOnce<T extends HTMLElement>(
11
- rootMargin = "0px",
12
- ): { ref: (el: T | null) => void; inView: boolean } {
13
- const [inView, setInView] = useState(false);
14
- const elRef = useRef<T | null>(null);
15
- const obsRef = useRef<IntersectionObserver | null>(null);
16
-
17
- function ref(el: T | null) {
18
- if (elRef.current === el) return;
19
- if (obsRef.current) {
20
- obsRef.current.disconnect();
21
- obsRef.current = null;
22
- }
23
- elRef.current = el;
24
- if (el) {
25
- const obs = new IntersectionObserver(
26
- ([entry]) => {
27
- if (entry.isIntersecting) {
28
- setInView(true);
29
- obs.disconnect();
30
- obsRef.current = null;
31
- }
32
- },
33
- { threshold: 0.15, rootMargin },
34
- );
35
- obs.observe(el);
36
- obsRef.current = obs;
37
- }
38
- }
39
-
40
- useEffect(() => () => obsRef.current?.disconnect(), []);
41
-
42
- return { ref, inView };
43
- }
44
-
45
- export function WordsPullUp({
46
- text,
47
- className,
48
- showAsterisk = false,
49
- delayBase = 0,
50
- }: {
51
- text: string;
52
- className?: string;
53
- showAsterisk?: boolean;
54
- delayBase?: number;
55
- }) {
56
- const { ref, inView } = useInViewOnce<HTMLSpanElement>();
57
- const words = splitWords(text);
58
- return (
59
- <span ref={ref} class={`${className ?? ""} ${inView ? "pull-in" : ""}`}>
60
- {words.map((word, i) => {
61
- const isLast = i === words.length - 1;
62
- return (
63
- <span
64
- class="pull-word"
65
- style={{
66
- animationDelay: `${delayBase + i * 0.08}s`,
67
- position: "relative",
68
- }}
69
- >
70
- {word}
71
- {showAsterisk && isLast ? (
72
- <span class="hero-asterisk">*</span>
73
- ) : null}
74
- {i < words.length - 1 ? "\u00A0" : null}
75
- </span>
76
- );
77
- })}
78
- </span>
79
- );
80
- }
81
-
82
- export function WordsPullUpMultiStyle({
83
- segments,
84
- className,
85
- delayBase = 0,
86
- }: {
87
- segments: Segment[];
88
- className?: string;
89
- delayBase?: number;
90
- }) {
91
- const { ref, inView } = useInViewOnce<HTMLSpanElement>();
92
- let globalIdx = 0;
93
- return (
94
- <span
95
- ref={ref}
96
- class={`${className ?? ""} ${inView ? "pull-in" : ""}`}
97
- style={{
98
- display: "inline-flex",
99
- flexWrap: "wrap",
100
- justifyContent: "center",
101
- columnGap: "0.25em",
102
- rowGap: "0.05em",
103
- }}
104
- >
105
- {segments.map((seg) => {
106
- const words = splitWords(seg.text);
107
- return (
108
- <>
109
- {words.map((word) => {
110
- const delay = delayBase + globalIdx * 0.08;
111
- globalIdx += 1;
112
- return (
113
- <span
114
- class={`pull-word ${seg.className ?? ""}`}
115
- style={{ animationDelay: `${delay}s` }}
116
- >
117
- {word}
118
- </span>
119
- );
120
- })}
121
- </>
122
- );
123
- })}
124
- </span>
125
- );
126
- }
127
-
128
- export function AnimatedParagraph({
129
- text,
130
- className,
131
- }: {
132
- text: string;
133
- className?: string;
134
- }) {
135
- const ref = useRef<HTMLParagraphElement | null>(null);
136
-
137
- useEffect(() => {
138
- let raf = 0;
139
- let stopped = false;
140
-
141
- function update() {
142
- if (stopped || !ref.current) return;
143
- const spans =
144
- ref.current.querySelectorAll<HTMLSpanElement>(".about-letter");
145
- const rect = ref.current.getBoundingClientRect();
146
- const vh = window.innerHeight || document.documentElement.clientHeight;
147
- const startY = 0.8 * vh;
148
- const endY = 0.2 * vh;
149
- const total = startY - endY + rect.height;
150
- const elapsed = startY - rect.top;
151
- const progress = Math.max(0, Math.min(1, elapsed / total));
152
- const n = spans.length;
153
- for (let i = 0; i < n; i += 1) {
154
- const cp = i / Math.max(1, n - 1);
155
- const lo = cp - 0.1;
156
- const hi = cp + 0.05;
157
- let op = 0.2;
158
- if (progress >= hi) op = 1;
159
- else if (progress > lo) op = 0.2 + 0.8 * ((progress - lo) / (hi - lo));
160
- spans[i].style.opacity = String(op);
161
- }
162
- raf = requestAnimationFrame(update);
163
- }
164
-
165
- raf = requestAnimationFrame(update);
166
- return () => {
167
- stopped = true;
168
- cancelAnimationFrame(raf);
169
- };
170
- }, []);
171
-
172
- const chars = Array.from(text);
173
- return (
174
- <p ref={ref} class={className}>
175
- {chars.map((ch) => (
176
- <span class="about-letter">{ch === " " ? "\u00A0" : ch}</span>
177
- ))}
178
- </p>
179
- );
180
- }
181
-
182
- export function FadeIn({
183
- delay = 0,
184
- duration = 0.8,
185
- children,
186
- as = "div",
187
- className,
188
- }: {
189
- delay?: number;
190
- duration?: number;
191
- children: ComponentChildren;
192
- as?: "div" | "p" | "span" | "button";
193
- className?: string;
194
- }) {
195
- const style: JSX.CSSProperties = {
196
- opacity: 0,
197
- transform: "translateY(20px)",
198
- animation: `fadeUp ${duration}s cubic-bezier(0.16, 1, 0.3, 1) ${delay}s forwards`,
199
- };
200
- const Tag = as;
201
- return (
202
- <Tag class={className} style={style}>
203
- {children}
204
- </Tag>
205
- );
206
- }
207
-
208
- export function StaggerCard({
209
- index,
210
- className,
211
- id,
212
- children,
213
- }: {
214
- index: number;
215
- className?: string;
216
- id?: string;
217
- children: ComponentChildren;
218
- }) {
219
- const { ref, inView } = useInViewOnce<HTMLDivElement>("-100px");
220
- const [shown, setShown] = useState(false);
221
-
222
- useEffect(() => {
223
- if (inView && !shown) {
224
- const t = window.setTimeout(() => setShown(true), index * 150);
225
- return () => window.clearTimeout(t);
226
- }
227
- }, [inView]);
228
-
229
- return (
230
- <div ref={ref} id={id} class={`${className ?? ""} ${shown ? "in" : ""}`}>
231
- {children}
232
- </div>
233
- );
234
- }
@@ -1,48 +0,0 @@
1
- export function ArrowRight({
2
- size = 18,
3
- className,
4
- }: {
5
- size?: number;
6
- className?: string;
7
- }) {
8
- return (
9
- <svg
10
- width={size}
11
- height={size}
12
- viewBox="0 0 24 24"
13
- fill="none"
14
- stroke="currentColor"
15
- strokeWidth="2"
16
- strokeLinecap="round"
17
- strokeLinejoin="round"
18
- class={className}
19
- >
20
- <line x1="5" y1="12" x2="19" y2="12" />
21
- <polyline points="12 5 19 12 12 19" />
22
- </svg>
23
- );
24
- }
25
-
26
- export function Check({
27
- size = 14,
28
- className,
29
- }: {
30
- size?: number;
31
- className?: string;
32
- }) {
33
- return (
34
- <svg
35
- width={size}
36
- height={size}
37
- viewBox="0 0 24 24"
38
- fill="none"
39
- stroke="currentColor"
40
- strokeWidth="2.5"
41
- strokeLinecap="round"
42
- strokeLinejoin="round"
43
- class={className}
44
- >
45
- <polyline points="20 6 9 17 4 12" />
46
- </svg>
47
- );
48
- }
@@ -1,16 +0,0 @@
1
- /**
2
- * Fixed media assets for the page — part of the design scaffold, never
3
- * touched during content population.
4
- *
5
- * Icons are inlined as data URLs (160px webp, a few KB each) so the page
6
- * has no external asset dependencies: it renders inside a sandboxed iframe
7
- * that cannot reach authenticated daemon routes, and hotlinking third-party
8
- * hosts is not an option for a seeded template.
9
- */
10
-
11
- /** One abstract icon per feature card, applied by card index. */
12
- export const CARD_ICONS = [
13
- "data:image/webp;base64,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",
14
- "data:image/webp;base64,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",
15
- "data:image/webp;base64,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",
16
- ];
@@ -1,20 +0,0 @@
1
- <!doctype html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8" />
5
- <meta
6
- name="viewport"
7
- content="width=device-width, initial-scale=1, viewport-fit=cover"
8
- />
9
- <title>Your Page</title>
10
- <link rel="preconnect" href="https://fonts.googleapis.com" />
11
- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
12
- <link
13
- href="https://fonts.googleapis.com/css2?family=Almarai:wght@300;400;700;800&family=Instrument+Serif:ital@1&display=swap"
14
- rel="stylesheet"
15
- />
16
- </head>
17
- <body>
18
- <div id="app"></div>
19
- </body>
20
- </html>
@@ -1,7 +0,0 @@
1
- import { render } from "preact";
2
-
3
- import { App } from "./components/App";
4
-
5
- import "./styles.css";
6
-
7
- render(<App />, document.getElementById("app")!);