Skip to content

0.1.7-67

Choose a tag to compare

@github-actions github-actions released this 12 May 21:13
· 5369 commits to main since this release
64792af
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).