0.1.7-67
·
5369 commits
to main
since this release
feat: slides 'mine' filter, shared slide types, design downloads (#663)
* feat(slides): 'created by me' deck filter end-to-end
End-to-end addition of a 'mine vs. all' filter for the deck list:
- `list-decks` action: new optional `--createdBy all|me` arg. When
`me` and an authenticated user is present, ANDs
`accessFilter(...)` with `ownerEmail = currentUser` so the
agent can answer 'list my decks' without re-filtering on the
client.
- `view-screen`: reports `deckFilter` (mirrors what the UI
toggle reflects) and filters the deck-list snapshot by owner
when the user has the toggle on, so the agent sees what the
user sees.
- `/api/decks` GET + `/api/decks/:id` GET: each row now carries
`createdByMe: boolean` so the UI can decide affordances without
a second lookup.
- `DeckContext`: `Deck` interface adds `createdByMe?`;
optimistic-create paths set it true.
- `use-navigation-state`: navigation state grows a
`deckFilter: 'all' | 'created-by-me'` field reading from the
`?createdBy=me` URL param.
- `Index` page: shadcn `ToggleGroup` switches between "All" and
"Mine" and writes the URL param via `useSearchParams`. Visible
decks are filtered client-side off `deck.createdByMe`.
- AGENTS.md updated with the new `--createdBy` arg and a 'List
my decks' common-task example.
* refactor(slides/shared-api): extract SharedDeckSlide types + toSharedDeckSlide() helper
Public-share and stored-deck slide shapes used to be hand-written
inline in three places (`shared/api.ts` request/response, the
public-deck loader, and the share handler), each missing one or
two of `transition` / `animations` / `splitByParagraph`. Result:
animations and transitions configured in the editor silently
dropped from the public share view.
- `shared/api.ts` introduces named exports:
- `SharedSlideTransition`, `SharedAnimationType`,
`SharedSlideAnimation` types so the shape is shared with
`DeckContext.SlideAnimation` and the editor.
- `SharedDeckSlide` interface that includes the optional
`transition`, `animations`, and `splitByParagraph` fields.
- `toSharedDeckSlide(slide, index)` normalizer that walks
unknown deck JSON and emits a fully-typed slide with sane
defaults — strips speaker notes (`notes: ""`), keeps
`id`/`content`/`layout`/`background`, and forwards the
transition / animations / splitByParagraph fields when
they're well-formed.
- `ShareDeckRequest` / `SharedDeckResponse` now reference
`SharedDeckSlide` instead of inlining the shape, so adding a
new shared field updates everywhere.
- `p.$id` loader and `share.ts` POST handler both replace their
hand-rolled mapping with `toSharedDeckSlide(slide, index)` so
the public viewer receives the editor's animations and
transitions.
- New `share.test.ts` covers the POST happy path + verifies that
the persisted `slides` JSON contains the normalized shape with
animations and transitions intact.
- `public-deck-route.test.ts` fixtures gain `transition`,
`splitByParagraph`, and an `animations` entry so the
end-to-end test catches regressions if the normalizer drops
any of them.
* feat(design): wire Download menu (HTML / ZIP / PNG) + preserve standalone index.html on export
Designs lacked a UI download path — users could only get HTML via
`export-html` action or by copying source. AGENTS.md was also
silent on this, so the agent occasionally suggested external
HTML-screenshot services.
- `export-html` action: when the design contains a standalone
`index.html` with its own `<!DOCTYPE>` / `<html>` tags, emit
that file verbatim and only inject the combined CSS as a
`<style data-agent-native-export>` block before `</head>`.
Previous behavior always wrapped HTML fragments in a boilerplate
shell, double-wrapping designs that were already complete
documents and breaking head/body content order.
Non-standalone designs still fall through to the legacy
body-concatenation path.
- `DesignEditor`: new Download menu with "Download HTML",
"Download ZIP", and "Download PNG" items, each wiring through
`useActionMutation` to the corresponding `export-html` /
`export-zip` / existing PNG export, then triggering a blob
download via a hidden `<a download>`. Toast on success / error
surfaces the action error message instead of a generic copy.
- Added `IconArchive` and `IconPhoto` to the lucide imports for
the new menu items.
- AGENTS.md now explicitly steers the agent to `export-html --id`
or the editor's Download menu when the user asks for a
download, instead of recommending external services.
* fix: address bot review on #663 — export bundling, CSS injection idempotency, PNG error handling, animation defaulting, DeckData typing
Five Builder review findings on #663:
- 🔴 `design/export-html`: when `index.html` is a standalone document,
merge the remaining HTML/JSX files into its body before
`</body>` instead of dropping them. Multi-file designs
(`components.html`, `page-*.html`, etc.) now ship in one
bundle again.
- 🔴 `design/export-html`: CSS injection idempotency — skip the
`<style data-agent-native-export>` block when one is already
present so repeated exports don't pile up duplicate styles.
Switched the insertion sites from `.replace("</body>", …)` /
`.replace("</head>", …)` to `lastIndexOf` so inline JS / comments
that happen to contain a closing tag don't pull the injection
to the wrong spot.
- 🔴 `design/DesignEditor.handleDownloadPng`: wrap the
`canvas.toBlob` callback body in try/catch. `triggerBlobDownload`
does DOM mutation + `URL.createObjectURL`, both of which can
throw inside the async callback — outside the outer try/catch.
Failures now surface a toast instead of going silent.
- 🟡 `slides/shared-api/normalizeSlideAnimation`: animations with
an `elementPath` but no `elementIndex` no longer default to
index 0. Derive the index from the path's leaf segment so
consumers that read `elementIndex` still hit the right element,
and add a comment spelling out the contract.
- 🟡 `slides/p.$id.DeckData.slides`: restore a specific type.
Loosening to `unknown[]` lost compile-time documentation; now
typed as `Array<Partial<SharedDeckSlide>>` with a comment
explaining why fields are partial (mixed template versions in
the persisted JSON) and that `toSharedDeckSlide` validates at
runtime.
* fix(core/auth): try every framework session cookie + clear host-only duplicates
When the same browser holds two `an_session` cookies (one host-only,
one domain-scoped — common after `COOKIE_DOMAIN` was introduced
post-sign-in, or after switching between two deployments on the
same domain), H3's cookie parser keeps only the first duplicate
name. The browser sends the older host-only cookie first because
its more-specific path matches earlier. `getSession` then sees
the stale token, the DB lookup returns no row, and the user
appears signed-out despite a perfectly valid fresh sign-in.
- New `getCookieValues(event, name)` parses the raw `Cookie` header
and collects every value for `name` (deduped), so duplicate cookies
are no longer silently dropped. Falls back to `getCookie` for mock
runtimes that don't expose the raw header.
- New `getFrameworkSessionCookieValues(event)` iterates each candidate
token through the existing session-lookup path and returns the
first that resolves to a live session.
- New `deleteCookieFromEveryScope(event, name)` clears both the
host-only and the domain-scoped variants on sign-out / session
invalidation so future sign-ins start clean and don't inherit the
same shadowing problem.
- `frameworkSessionCookieNamesToClear()` returns both the canonical
`an_session` and the app-slug variant (`an_session_<slug>`) when
`APP_NAME_SLUG` is set, so the rotation also clears legacy
app-scoped cookies left over from earlier deploys.
- Companion spec verifies that with `Cookie: an_session=stale-token; an_session=fresh-token`
the loader walks past the stale token and returns the
fresh-token session, instead of erroring on the first hit.
* fix(core/builder-card): stop telling users to enable Cloud Agents in Builder org settings
Builder Cloud Agents aren't self-serve from Builder's org settings,
beta features, 'Cloud Agents', or 'AI Agents' panels — but the
agent system prompt and the ConnectBuilderCard copy implied they
could be 'enabled', so the agent kept directing users on a dead
end to flip a switch that doesn't exist.
Reframe everything as 'not available yet' + waitlist:
- `ConnectBuilderCard` and `BackgroundAgentSection`: 'are not
enabled for this workspace yet' → 'are not available for this
workspace yet'. `BackgroundAgentSection` also adds an explicit
'they are not enabled from Builder org settings' clarification
so the user doesn't go hunting.
- `onboarding/default-steps` (Connect Builder step description):
same wording change — 'when Builder Cloud Agents are available'.
- `agent-chat-plugin` Builder-handoff section of the system
prompt:
- `connect-builder` tool description: switch to 'available'
phrasing and add an explicit rule — never tell the user to
enable Cloud Agents in Builder org settings or beta settings.
- Act-mode handoff guidance example: replace 'aren't enabled
here yet' with 'aren't available here yet — join the waitlist
in the card, or use the Agent Native Desktop app for local
code changes'.
- New paragraph spells out the rule: when `builderEnabled` is
false, the only allowed guidance is the card's waitlist /
local-dev fallback. No 'go to Builder org settings'.
- Code-changes section: fallback now mentions `npx agent-native
create`, Agent Native Desktop, and the waitlist card, plus
the same 'never tell users to enable Cloud Agents from
Builder org settings' guardrail.
* fix(core/agent-chat-plugin): extend never-send-to-Builder-org-settings rule to the connect-builder guidance blocks
Follow-up to df8909845. The two existing 'Connecting Builder.io'
sections of the agent system prompt (one compact, one full) only
explained how to call `connect-builder` — they were silent on
what to do when Cloud Agents aren't available. Adds the same
'never send the user to Builder org settings or beta settings;
use the card's waitlist/local-dev fallback' clause to both, so
the agent doesn't slip past the new guardrail through these
slightly earlier explanations.
* fix(desktop): keep Builder connect OAuth from replacing app webview
When Electron returns 'deny' to a window.open from the webview (after
opening the OAuth window itself), Chromium reports the call as null.
Previously the fallback assigned window.location, navigating the whole
app to the OAuth URL. Now we leave the app mounted on desktop UA and let
the status poll observe completion, and the webview's will-navigate
handler diverts any leaked OAuth nav for older bundles.
* fix(desktop): skip main frame in will-frame-navigate handler
The will-frame-navigate event fires for every frame including the main
one, but will-navigate now handles main-frame OAuth diversion. Without
this guard, both handlers preventDefault the same navigation and the
second call no-ops on the already-handled event, masking edge cases.
Limit will-frame-navigate to subframes only.
* feat(workspace-dev): surface app start failures on the loading page
When a workspace app fails to start (install error, build error, port
conflict, etc.), the gateway used to silently keep showing the generic
'Starting <app>' loading page while the child kept exit-restarting in
the background. Users saw only the spinner.
Now the gateway captures the last ~8KB of the app's stderr/stdout and
renders it in a red 'App failed to start: <name>' page with the retry
countdown, so failures are immediately visible in the browser. The
loading page falls back to the success layout once the app comes up.
* chore(changeset): add changeset for workspace-dev failure page
* fix(builder-frame): only log to console relay when no parent frame
When sendToBuilderChat ran in an iframe with a real parent (the normal
Builder embed case), it both posted the message to the parent AND
emitted the BUILDER_PARENT_MESSAGE console line. That console line is
specifically a fallback for Electron/webview relays that watch stdout
because they can't postMessage directly — duplicating it for in-iframe
sends just spammed the log with the full payload. Gate the console
relay to the no-parent case.
* feat(workspace): include shared skills + agent symlinks in workspace scaffold
The workspace-root scaffold now ships the same .agents/skills set as
the standalone templates (a2a-protocol, actions, … 30+ skills) and
runs setupAgentSymlinks against both the workspace root and the
packages/shared/core package so AGENTS.md / CLAUDE.md / cursor configs
get linked consistently. linkWorkspaceRootSkills symlinks
.agents/skills → packages/shared/.agents/skills when present, with a
copyDir fallback for environments that block symlinks.
Builder-chat tests cover the new no-parent console-relay branch from
the previous fix so the regression stays fenced.
The fuzzy-builder-chat-relay changeset describes the user-visible
effect: embedded app composers no longer double-submit prompts.
* test(workspace): add e2e coverage for shared skills symlink + doc reference
Adds a CLI e2e test that scaffolds a workspace and asserts
.agents/skills resolves into packages/shared/.agents/skills, plus
prettier-style line wrap on the symlinkSync call. README and root
AGENTS.md briefly mention where the shared skills live so a fresh
workspace user sees the path right away.
* fix(content): defend buildDocumentTree against self/cyclic parent refs
Two robustness improvements to the document tree builder:
- Self-recursive nodes (doc.parentId === doc.id) and parent cycles
(a→b→a) used to attach the node under itself / its ancestor, which
blew up the sidebar tree renderer with an infinite loop. Both cases
now demote the node to a root.
- Duplicate document ids in the input (same primary key reappearing)
used to overwrite the map entry mid-build; the second occurrence is
now ignored so the cached node keeps its already-attached children.
Adds a vitest spec covering both cases.
Bundled workspace-core skills now use placeholder owner emails
(owner@example.com) and generic provider language instead of
Steve / @builder.io specifics so the scaffolded examples don't
ship someone else's identity in fresh workspaces.
* fix(content): reject moves that would create document cycles + custom error boundary
Two related improvements to the document tree:
- move-document action and PATCH /api/documents/:id/move now walk
descendants of the document being moved and reject any parentId that
is itself a descendant. Previously a user (or agent) could move
document A under one of its own descendants, producing a cyclic
parent graph that buildDocumentTree handled defensively but the API
shouldn't allow in the first place.
- root.tsx exports a content-specific ErrorBoundary that renders a
themed 'Page not found' / 'Something went wrong' page with a 'Go to
pages' CTA instead of falling back to the core generic boundary.
Includes the standard 404 vs. Error / string discrimination via
react-router's isRouteErrorResponse.
- _app.page._index.tsx pulls in a small alignment update from the same
agent's pass.
* feat(AssistantChat): recover from assistant-ui stale message index errors
assistant-ui's MessageList renderer throws 'tapClientLookup: Index N
out of bounds (length: M)' when the underlying message array shrinks
between render and the next paint (e.g. agent retry, message
deletion, or a rapid stream-then-rollback). The error tore down the
entire AssistantChat tree and the user lost the open conversation.
Adds an AssistantMessageListErrorBoundary scoped to the message list
that:
- Recognises the specific error shape via isAssistantUiStaleIndexError
and only swallows that one — anything else rethrows unchanged.
- Captures it to Sentry with a 'recoverable' tag so the dashboard
still shows occurrences without paging.
- Schedules a 0-tick retry that resets the boundary so the next render
picks up the corrected message list. Falls back to a second-attempt
reset if the parent resetKey changes before retry.
Includes a display spec for the boundary plus an unrelated calendar
helper test for buildStatusEventFields (out-of-office / focus-time /
working-location native field shapes). The bundled slides and
calendar skill docs are refreshed in step with the changes.
* chore: prettier sweep + AssistantChat boundary changeset
Adds the missing changeset for the AssistantChat stale-index recovery,
reformats the long statusMessage string back to one line so the move
handler matches the rest of the file's style, and tightens the display
spec around the new boundary to match its actual render shape.
* test(calendar): mock server deps in event-action-helpers test
buildStatusEventFields is a pure helper but its module siblings import
@agent-native/core/server (which reads runtime context) and the google
calendar lib (which expects google-auth env vars). When vitest pulls
in the file it eagerly evaluates those imports, which threw during
the previous CI Test run. Stubbing the two modules at the top of the
spec gives vitest a clean import graph without needing to refactor
the helper into a separate module just to break the cycle.
* test(AssistantChat): drop unstable empty-DOM assertion on stale-index throw
The 'rendering an unrelated error rethrows' boundary spec was peeking
at container.textContent immediately after the throw, but JSDOM keeps
the last successful render until the boundary's componentDidUpdate
flushes. The spec already covers the user-visible outcome (the
boundary re-throws and the test wrapper catches it) — the empty-DOM
peek added flake without coverage.
* docs(agents): surface 'no inline LLM' rule in delegate-to-agent and workspace AGENTS
Two related doc tweaks so agents pick up the delegate-to-agent skill
when they're tempted to add a model call:
- Widens the SKILL description to mention 'user asks for agent
behavior or LLM-powered features' so the skill router matches
prompts like 'add an AI summary' or 'wire a chat that uses Claude',
not only the narrower 'sending messages to the agent' phrasing.
- Adds a top-level 'Core Agent Rule' to the workspace-root and
workspace-core AGENTS.md templates that scaffolded workspaces ship:
AI work must go through sendToAgentChat — never inline
generateText / streamText / model-provider SDK calls. Points at
the skill file for the longer explanation.
The same widening lands in both .agents/skills (framework copy) and
packages/core/src/templates/workspace-core/.agents/skills (the copy
that ships in fresh workspaces).