Commit Graph

8517 Commits

Author SHA1 Message Date
Nathaniel Parson Koroso
a5020b0b69 feat(agent): upload the canvas with sends to seed the server draft
postMessage now carries the serialized active graph when it changed
since the last upload (2MB cap, re-upload after failed sends or thread
switches), per the agreed cloud contract; current servers ignore the
field. The ack binds the uploaded tab to the thread's workflow id, so
agent patches apply in place on the user's own tab. Empty drafts park
everywhere, and the conflict dialog still guards modified tabs.
2026-07-09 13:41:18 -07:00
Nathaniel Parson Koroso
c4fc77b514 fix(agent): keep the panel title on one line beside the tab strip 2026-07-09 12:50:25 -07:00
Nathaniel Parson Koroso
497c8e18e6 style(agent): enlarge chip remove buttons to a usable hit target 2026-07-09 12:48:17 -07:00
Nathaniel Parson Koroso
8aba29b72f feat(agent): inline tagged node definitions in the selection payload
The server-side draft never mirrors the live canvas (it starts empty
even for saved workflows), so @-tagged node_ids resolved to nothing and
the agent replied that the canvas was empty. Each tagged node now rides
the POST with its serialized definition from the graph the canvas is
showing (root or an open subgraph), plus a context note that claims
inline definitions only for tags that resolved, names the gap when some
did not, and nudges a save when the tab has no server-side workflow.
2026-07-09 12:04:53 -07:00
Nathaniel Parson Koroso
b7c4256653 feat(agent): @ node picker, park empty unbound drafts
- The composer @ button now opens a dropup listing the active graph's
  nodes (title + id); picking one stages it as a selection tag.
- An unbound draft patch with no nodes no longer opens a blank tab;
  the server-minted empty draft parks until a patch carries content.
- Selections sent from a tab with no server-side workflow carry a
  context note so the agent asks for a save instead of guessing.
2026-07-09 11:35:38 -07:00
Nathaniel Parson Koroso
b6326c3e52 feat(agent): keep sent @-tags on the transcript and move the tab name into the header
Sent node tags now persist as small pills on the user message (same
rationale as attachments: proof they were sent), with the node ids still
riding the POST selection. The tag titles travel through sendMessage
alongside attachments, replacing the session's selection dep.

The active tab name moves out of its own row into the panel header next
to the ALPHA badge (truncating), reclaiming a strip of vertical space.
2026-07-09 11:11:02 -07:00
Nathaniel Parson Koroso
0157adeb51 fix(e2e): make the mocked workspace token schema-valid
A bare {token} fails WorkspaceTokenResponseSchema, sending workspace auth
into its recovery/retry loop whose error toasts stack over the docked
panel and swallow the suggested-prompt click (the intermittent cloud
failure). The mock now carries expires_at/workspace/role/permissions, so
the app authenticates cleanly and nothing toasts.
2026-07-08 21:15:34 -07:00
Nathaniel Parson Koroso
260b7d1a8f test(e2e): widen the two long agent specs to 30s
Boot + panel open + a full streamed turn can exceed the cloud project's
15s budget on a loaded CI runner (observed: test-timeout at the chip
click with everything before it slow but passing). Same precedent as
versionMismatchWarnings.spec.ts.
2026-07-08 20:56:35 -07:00
Nathaniel Parson Koroso
f4fc05f35a fix(agent): release transcript previews and harden the feature-set tests
Review pipeline hardening: attachment object URLs now revoke on
conversation reset/hydrate (sent previews no longer outlive their
conversation); the conflict dialog's dropdown trigger reuses Button
instead of hand-copied variant classes; the dead useCanvasContext
composable is removed (ActiveTab lives with the strip).

New regression tests: draft-patch coalescing under a slow canvas apply,
the full preview-revoke lifecycle (dismiss, upload-fail, reset, hydrate,
never on submit), two-turn attachment keying, tab-strip reactivity,
dialog X-close semantics, uploading send-block, UserMessage render arms,
and loadThread dropping the prior draft binding.
2026-07-08 20:42:48 -07:00
Nathaniel Parson Koroso
e1a13ae9c2 feat(agent): name the active tab in the panel strip
Renders the ported ActiveTabStrip under the session bar with the active
workflow's display name, so it is clear which tab the agent acts on.
2026-07-08 19:04:32 -07:00
Nathaniel Parson Koroso
9e9f63ad1a feat(agent): restyle the edit-conflict dialog to DES-502
Compact card with the design's copy ("Agent changes are ready") and a
horizontal action row: Cancel/close defer the decision and the next turn
re-asks; "Keep my changes" retires the pending draft version so only a
newer agent edit asks again; "Accept agent changes" is the primary with
"Open in new tab" in its attached dropdown.
2026-07-08 18:50:51 -07:00
Nathaniel Parson Koroso
3048178d1b feat(agent): keep sent attachments in the transcript and show upload progress
Sent images now stay visible as a thumbnail grid on the user message (they
previously vanished on send, reading as lost). Attachment chips stage the
moment a file is picked, spin while uploading, and block the send until the
ref exists server-side; failures remove the chip and toast. Dismissed chips
release their local preview buffers.
2026-07-08 18:38:23 -07:00
Nathaniel Parson Koroso
939529a109 feat(agent): bind drafts to tabs, send workflow_id per turn, @-tag nodes
Draft patches now route to the canvas tab bound to their workflow_id:
in place when the tab is active (was: a new tab per patch), lazily on
refocus when backgrounded, and through the merge-conflict dialog when
the user edited the tab (keep mine / let agent continue / open new tab,
per B16). An unbound workflow opens and binds its own tab on first patch.

Every message POST carries the active tab's workflow_id (bound id, else
the saved workflow's uuid; a 403 on a speculative id retries once
without). Selected canvas nodes stage removable @-chips above the
composer and ride the turn as selection.node_ids, per B7.
2026-07-08 17:46:51 -07:00
Nathaniel Parson Koroso
fe466d8d88 fix(e2e): mock billing and assets so error toasts cannot cover the agent panel
Unmocked, both reject in CI and their sticky toasts stack over the docked
panel, swallowing chip and Send clicks. Reuses the shared mockBilling helper
and the shared jsonRoute in place of a local duplicate.
2026-07-08 15:28:31 -07:00
Nathaniel Parson Koroso
5b11576778 fix(e2e): seed agent onboarding as seen so the coach scrim does not block clicks 2026-07-08 14:29:58 -07:00
Nathaniel Parson Koroso
b2fe62d5b4 fix(e2e): install agent boot mocks before the app boots in cloud specs
The mocks lived in a sibling fixture with no ordering guarantee against
comfyPage, so in CI the app booted against the real /api/features (no
PostHog token) and the flag gate kept the panel button hidden. Override
the page fixture instead, the same pattern as workspaceSwitcher.spec.ts.
2026-07-08 13:45:52 -07:00
Nathaniel Parson Koroso
a505890f5a refactor(agent): single source for draft-apply failure copy
The PromptError message now reuses the catalog desc that renders; drops the
duplicate agent.draftApplyFailed string.
2026-07-08 12:32:42 -07:00
Nathaniel Parson Koroso
fe0cf34dd1 feat(agent): surface draft-apply failures through the workflow error overlay
A failed draft-to-canvas apply is a workflow error, so it now uses the
existing production error surface instead of a bespoke toast: the panel
writes lastPromptError (type agent_draft_apply_failed, details = the
zod/load failure) and opens the error overlay, whose View details opens
the Errors tab. Once per failure streak, reset when a draft applies; a
loadGraphData rejection surfaces the same way. Chat/session errors
(failed send, malformed event, cancel, history) keep their existing
inline/notice paths.

- promptErrorResolver: register agent_draft_apply_failed; catalog copy
  under errorCatalog.promptErrors
- useErrorGroups: pass prompt-error details through to the card item so
  View details shows the raw failure (TabErrors expectation updated)
2026-07-08 12:07:10 -07:00
Nathaniel Parson Koroso
5022ee8979 test: fix CI failures from the agent-panel seams
- agentPanel.spec: the bare main.json import crashed Playwright's Node
  loader at collection time, failing every project; add the
  'with { type: json }' attribute (same as WorkflowTabs.test)
- WorkflowTabs.test: mock the agent panel store like the component's
  other stores; its new useAgentPanelStore() call threw no-active-pinia
2026-07-08 11:43:41 -07:00
Nathaniel Parson Koroso
a02279ec92 refactor(agent): purge comments to the AGENTS.md bar
Comment-only sweep of the subtree (zero executable lines changed,
verified by diff filter): 424 -> 107 comment lines across 18 files.
Deleted every restatement of the code, symbol-paraphrasing JSDoc,
design-doc narration, and story references. Survivors are 1-2 line
facts the code cannot express: wire-contract behavior (409 = settled,
usage null on cancel, title empty until server names the thread),
prevented bug classes (send/ack race, stale-highlight race, IME,
drop navigation, id collisions, reactivity traps), and safety
invariants (fail-closed flag gate, cancel-on-unmount billing).
2026-07-08 11:05:52 -07:00
Nathaniel Parson Koroso
517d6d3df8 fix(agent): address CodeRabbit review findings
- CodeBlock: cancel superseded async highlights via watch onCleanup (a
  stale resolution could overwrite a newer one during streaming) and
  sanitize shiki output with DOMPurify before v-html, matching the
  markdown pipeline
- AgentPanelRoot: unsubscribe the event source only after the unmount
  cancel settles (stopTurn().finally(stop))
- ConversationView: replace the deep entries watch (O(all entries) per
  streamed token) with a signal over the last entry only
- ApprovalCard: guard Approve/Deny against double-click double-emit
- useAttachment: pending spans the whole batch instead of flickering
  per file; add a mixed-batch test (fail one, keep the rest)
- Lightbox: muted + playsinline so video autoplay works cross-browser
- agentApiSchema: zAgentMessage tolerates additive fields like its
  sibling wire objects
- Composer.test: type the mount helper via ComponentProps
- README: fix the stale opening (right dock, not a sidebar tab)
2026-07-08 10:52:55 -07:00
Nathaniel Parson Koroso
128e8f7bd7 refactor(agent): apply AGENTS.md rule-audit findings
Adversarially verified audit of the agent subtree against the repo rules:

- MarkdownStream, UserMessage: compress comments that restated the
  Tailwind classes below them; keep only intent and provenance
- useCloudAssets: replace the hand-rolled Set dedup loop with
  es-toolkit uniqBy (same keep-first-by-id semantics)
- agentPanel.test: move the mutable mock state the vi.mock factories
  close over into vi.hoisted, drop the agentClose alias for the
  store's own vi.fn
2026-07-08 10:37:36 -07:00
Nathaniel Parson Koroso
63690cca50 Merge remote-tracking branch 'origin/main' into nathaniel/fe-1187-agent-side-panel-fe-implementation
# Conflicts:
#	src/components/LiteGraphCanvasSplitterOverlay.vue
2026-07-07 21:29:36 -07:00
Nathaniel Parson Koroso
887e3a587a style(agent): composer caption says workflow, not graph
"The agent edits your graph as a draft." -> "The agent edits your
workflow as a draft." — user-facing surfaces say workflow; graph is
internal vocabulary. Still a single line at the 420px panel width.
2026-07-07 20:14:54 -07:00
Nathaniel Parson Koroso
37a5554962 fix(agent): pin the thread-list schema to the live envelope
GET /api/agent/threads returns {threads, pagination}, not a bare array,
so the speculative z.array schema written before the endpoint existed
failed at the root and Chat History rendered empty against a live 200.
Validated against a real response:

- zAgentThreads: {threads: [...]} envelope; rows are {id, title,
  preview, last_message_at, updated_at, created_at, ...} with title
  "" until the server names the thread
- listThreads unwraps the envelope and returns the rows
- toChatSession drops the guessed field spellings; an unnamed thread
  titles its row from preview (the first prompt), then untitledChat
- refreshHistory logs a fetch/parse failure instead of swallowing it
2026-07-07 20:14:23 -07:00
Nathaniel Parson Koroso
a0983fd2a4 fix(agent): surface schema-rejected drafts instead of dropping them
A draft_patch whose content fails the host workflow schema (e.g. a
graph missing its required version field) was dropped with only a
console.warn: the agent narrated a finished graph while the canvas
stayed stale and the user saw no error. Raise an error toast on
rejection (new agent.draftApplyFailed key), once per failure streak
rather than per patch, reset when a draft applies.
2026-07-07 20:06:11 -07:00
Nathaniel Parson Koroso
375e808973 fix(agent): name the back action on the history screen
The history screen's "<- Chat history" control is the current screen
title with a back arrow, but clicking it returns to the chat, so the
label reads like a destination. Add a "Back to chat" hover tooltip
(new agent.backToChat key), mirroring the session bar's "Show chat
history" tooltip.
2026-07-07 19:41:21 -07:00
Nathaniel Parson Koroso
5ee3576062 feat(agent): untitled session default and history tooltip (DES-455)
- Session-bar default title "New chat" -> "Untitled"; it still renames
  to the first prompt on submit. agent.untitledChat is consolidated to
  "Untitled" so the titleless fallback reads the same everywhere.
- Session-bar hover tooltip "Chat history" -> "Show chat history" (new
  agent.showChatHistory key) so the pill names its action; the
  new-chat action stays on the header icon.

Matches the DES-455 chat-history flow (Figma node 2442-9503).
2026-07-07 19:33:34 -07:00
Nathaniel Parson Koroso
7aee27ebfb style(agent): keep secondary text on agent-fg-subtle
CodeBlock and MessageFeedback drifted to text-agent-fg-muted while
every sibling secondary-text run uses text-agent-fg-subtle, the alias
agentTheme.css documents for secondary text. Both aliases resolve to
muted-foreground today, so this is a visual no-op, but it keeps the
panel on a single token if the aliases ever diverge.
2026-07-07 19:27:35 -07:00
imick-io
010389903d feat(website): sitewide announcement banner (#13481)
## Summary

Adds a **sitewide announcement banner** to the website, rendered above
the navbar on every page. It has a two-layer visibility model:

- **Build-time gate** — a pure, unit-tested `evaluateBannerVisibility()`
decides whether the banner mounts at all (active flag + optional date
window + locale/section targeting), driven by a typed config
(`src/config/banner.ts`). No CMS; copy resolves through i18n.
- **Client-side dismissal** — persisted in `localStorage`, keyed by a
**content hash** so editing the copy re-shows the banner (per-locale, so
an en edit doesn't re-show it for zh-CN).

Current content points the CTA at **Comfy MCP** (`/mcp`).

## Highlights

- **Full-width branded bar** above a now-`sticky` navbar; reuses the
design system (`Button`, gradient tokens, new reusable `IconButton`).
- **Flash-free** on load: an inline pre-hydration script hides an
already-dismissed banner before paint (no pop-in, no layout shift);
`close()` sets the same signal after the leave animation to stay
flash-free across ClientRouter navigations.
- **Open/close transition**: grid-rows height collapse + fade,
respecting `prefers-reduced-motion`.
- **i18n**: copy in `en` + `zh-CN`.

## Where to edit later

- **Copy**: `apps/website/src/i18n/translations.ts` →
`launches.banner.text` / `launches.banner.cta`
- **Link / on-off / dates / targeting**:
`apps/website/src/config/banner.ts` (`bannerConfig`)

## Notes

- On a static site the `startsAt`/`endsAt` window is evaluated at
**build time** (documented in `banner.ts`).
- Changing the copy or link changes the content hash, so
previously-dismissed visitors will see the banner again — by design.

## Test plan

- `pnpm test:unit` — evaluator + version-hash unit tests pass.
- `pnpm typecheck` + lint clean.
- On the preview: banner shows on `/`, `/launches`, and a `zh-CN` page;
CTA -> `/mcp`; dismiss animates and stays dismissed on reload (no
flash); reduced-motion disables the animation.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 01:56:48 +00:00
Nathaniel Parson Koroso
30ef8b3b8c style(agent): correct code-block sizing
- body: 11px -> text-sm, p-3 -> p-4
- frame radius: rounded-lg -> rounded-md
- header label and copy button: 11px -> text-xs

Colors were already correct (secondary-background-hover header,
border-default frame, base-background body).
2026-07-07 18:30:01 -07:00
Nathaniel Parson Koroso
c931e75550 style(agent): empty-state greeting spacing and type
- Session-bar fallback title "New Chat" -> "New chat"
- Greeting block: tile-to-text gap-6 -> gap-4
- Greeting wrapper: tracking-tight, snug line-height at both text
  sizes, gap-2 between the two lines, max-w-sm
2026-07-07 18:22:02 -07:00
Benjamin Lu
684b0b08b0 feat(telemetry): register client + deployment platform axes on PostHog (#13469)
## Problem

Filtering PostHog for the three product surfaces — **desktop local**,
**desktop cloud**, **web cloud** — currently requires a different hack
per pipe. For this repo's cloud build, the desktop-embedded frontend and
a plain browser are indistinguishable except by sniffing `Electron` in
`$raw_user_agent` (~124K desktop-cloud vs ~844K web-cloud execution
events/week get separated that way today).

## Change

Register the two standardized platform axes as PostHog super properties
at SDK init in `PostHogTelemetryProvider`:

- **`client`** — which surface emitted the event: `'desktop'` when the
desktop preload bridge (`window.__comfyDesktop2`) is present, else
`'web'`. The bridge is injected by Electron before any page script runs,
so detection is deterministic — unlike the existing utm-based
`source_app` attribution, which only covers sessions that *entered* via
a desktop link.
- **`deployment`** — which backend runs the work: pinned to `'cloud'`.

The register happens before the pre-init event queue flushes, so events
captured during the posthog-js dynamic-import window carry the axes too.

## Why pinning `deployment: 'cloud'` is safe (including
embedded-in-desktop)

The cloud bundle also runs **embedded in Comfy Desktop** — a cloud
install loads this same bundle in Electron, where `isCloud` and the host
bridge are both true. Two things happen there:

1. `main.ts` runs `initHostTelemetry()` *after* `initTelemetry()`, and
(when remote config `enable_telemetry` is on) it **replaces** the
registry with `HostTelemetrySink` — so tracked events
(`execution_start`, …) route through the desktop main process, bypassing
this provider. Those are tagged the same `client`/`deployment` values
main-side from the install's source category
([Comfy-Desktop#1229](https://github.com/Comfy-Org/Comfy-Desktop/pull/1229)).
2. posthog-js keeps capturing independently of the registry (pageviews,
web vitals, identify) — those are what these super properties cover in
the embedded case, and `deployment: 'cloud'` is correct for them because
the cloud bundle always talks to the cloud backend regardless of
embedding; the embedding itself is what `client: 'desktop'` captures.

The only way a cloud build runs against a non-cloud backend is a dev
setup, where `window.__CONFIG__.posthog_project_token` is absent
(injected by the cloud server) and the provider disables itself before
registering anything.

The locally-served frontend (desktop/localhost builds) never runs this
provider: `__DISTRIBUTION__` is a compile-time define, so the
`initTelemetry()` call folds away, with a runtime `IS_CLOUD_BUILD` guard
as backstop.

With both PRs, the three platforms become clean property filters:

| Surface | Filter |
|---|---|
| Desktop local | `client=desktop, deployment=local` |
| Desktop cloud | `client=desktop, deployment=cloud` |
| Web cloud | `client=web, deployment=cloud` |

## Testing

- `vitest run` on `PostHogTelemetryProvider.test.ts` — 45 passing,
including new coverage: web default, bridge-present → `client=desktop`,
and register-before-queue-flush ordering. The two desktop-entry tests
that asserted `register` is never called were narrowed to assert no
`source_app` register call.
- `pnpm typecheck` + eslint/oxlint on touched files — clean.

Ref
[MAR-51](https://linear.app/comfyorg/issue/MAR-51/foundation-desktop-sdk-dual-send-to-posthog-alongside-mixpanel)

---------

Co-authored-by: AustinMroz <austin@comfy.org>
2026-07-08 01:11:01 +00:00
Nathaniel Parson Koroso
749e473192 fix(agent): surface send failures once, inline only
A failed send raised both a host toast (pushError) and an inline
conversation notice (recordFailedSend), rendering one error twice; the
top-right toast also overlapped the right-docked panel. Session notices
are reserved for errors with no inline row, so drop the toast on the
send path and keep the inline row. Test (c) now asserts a send failure
raises no session notice.
2026-07-07 18:06:34 -07:00
Benjamin Lu
95b121bed9 fix(website): unbreak website-e2e on main (card count, CTA overflow, stale goldens) (#13496)
## Summary

Fix the website-e2e job, red on main since 2026-07-07 15:19 UTC, by
updating the cloud model-card count test and regenerating stale visual
screenshot goldens.

## Context

website-e2e runs Playwright tests against the marketing site
(`apps/website`). It broke on main in two independent ways, so every PR
since — however unrelated — has shown a red website-e2e check:

1. **Model-card count.** #13431 added a sixth model card (GPT Image 2)
to the /cloud "AI models" section but didn't update the test that pins
the card count at 5. CI: `locator resolved to 6 elements`.
2. **Stale screenshot goldens.** #13431 also swapped the ProductCard CTA
to the shared `Button` (`whitespace-nowrap`, so e.g. "SEE ENTERPRISE
FEATURES" renders on one line instead of wrapping) and committed
matching `home-product-cards-*` goldens. Minutes later #13445 — a branch
cut from main *before* #13431 — ran the screenshot-regen workflow, which
checks out the raw PR branch, not the branch merged with main. Its
regenerated lg/xl goldens therefore depict the **old** pre-#13431 card
(wrapped label; pixel-identical layout to the pre-#13431 golden), and
overwrote #13431's correct ones at merge. #13445's own website-e2e was
red at merge time for exactly this reason. The sm/md goldens (last
captured by #13431, without #13445's `ppformula-text-center`
0.19em→0.1em nudge) went stale by ~900 px the moment #13445 landed.

## Changes

- **What**: `cloud.spec.ts` — model-card count assertion and test title
5 → 6 (verified against the 6 entries in `AIModelsSection.vue`; passes
locally).
- **What**: `ProductCard.vue` — `h-auto whitespace-normal` on the CTA
`Button`. The shared Button's `whitespace-nowrap` made long labels ("SEE
ENTERPRISE FEATURES") overflow past the card edge at lg/xl (live on prod
since #13431); labels now wrap inside the card as they did before
#13431.
- **What**: regenerated `home-product-cards-*` goldens via the `Update
Website Screenshots` workflow, run on this branch, so they capture the
fixed rendering rather than enshrining the overflow.

## Review Focus

- At lg, "SEE DESKTOP/CLOUD FEATURES" now also wrap to two lines: #13431
raised the CTA font from `text-xs` to `md:text-sm`, so those labels no
longer fit one line in the 200px content box either (pre-fix they
silently consumed the card padding). If design prefers one-liners,
shrinking the CTA font is a follow-up.
- Process gaps this incident exposed (follow-ups, not in this PR): the
regen workflow captures against the raw branch instead of the
main-merged result, and website-e2e was red on #13445 at merge without
blocking it.

---

*Six cards where five once stood,*
*a button's text sat where it should —*
*but pixels pinned in amber lied,*
*so Linux looked, and rectified.*

---------

Co-authored-by: github-actions <github-actions@github.com>
2026-07-07 17:59:21 -07:00
Nathaniel Parson Koroso
d5aae1a24c style(agent): conversation-detail design parity
- ThinkingStatus: brain icon and a shimmer on the "Thinking…" label
  (new agent-shimmer keyframes; static muted fallback under
  prefers-reduced-motion and .disable-animations)
- CodeBlock: bordered chrome; header gains a file-code icon, the
  language, and a bordered copy button; mono body
- MessageFeedback: gap-0.5 row, size-6 buttons, size-3.5 icons
- Composer: send button shows a loader-circle spinner while the turn
  is thinking, via a new submitting prop threaded
  AgentPanelRoot -> AgentPanel -> Composer
2026-07-07 17:45:01 -07:00
Wei Hai
baeb6df662 Gate signup submit on Turnstile being enabled, not enforced (#13463)
## Summary
- Shadow-mode Turnstile never blocked the signup Submit button on the
async Cloudflare challenge resolving, so most real submits raced ahead
of the widget and reached the backend with an empty token. This defeated
the point of shadow mode, which needs real tokens to measure the
false-positive rate before flipping to enforce.
- Submit is now blocked while the widget is enabled (shadow or enforce)
and has no token yet, in both modes.
- To keep a broken or slow Cloudflare load (network issue, ad-blocker,
CDN outage) from permanently blocking a legitimate signup,
`TurnstileWidget` now reports itself "unavailable" on a script-load
failure, a challenge error, or a 9s load timeout, and the form treats
that the same as shadow previously did: proceed without a token.

## Test plan
- [x] `vitest run` on `TurnstileWidget.test.ts` + `SignUpForm.test.ts`
(unit tests updated/added, all passing)
- [x] `pnpm typecheck` / eslint / oxlint / stylelint / oxfmt via
pre-commit hooks
- [ ] Manual click-through on staging to confirm no perceptible UX
regression during normal-latency challenge solves
- [ ] Confirm the 9s fallback timeout against real p95 Turnstile
challenge-solve latency

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-07-08 00:39:58 +00:00
CodeJuggernaut
e58b231664 fix: suppress the stray focus ring on the sidebar splitter panel (#13482)
Clicking empty space in a workspace panel focuses the whole PrimeVue
SplitterPanel (tabindex=-1 makes it click-focusable), and any following
non-chord keypress (e.g. Shift) trips the browser focus-visible
heuristic, painting the default blue ring around the entire panel.

The wrappers are not Tab-reachable (Tab lands on the controls inside,
never the panel box) and nothing focuses them programmatically, so the
ring conveys nothing. `focus-visible:outline-hidden` suppresses it while
keeping a forced-colors (High Contrast) indicator. Confirmed as noise
with design (Alex Tov).

Covers every click-focusable panel in
`LiteGraphCanvasSplitterOverlay.vue`: the sidebar panel (both
locations), the properties-side panel (both branches), and the bottom
panel. The center and graph-canvas panels are left untouched - they
inherit `pointer-events-none`, so a click can never focus them.

## Repro / QA

Ring trigger: click an empty, non-interactive spot inside the panel,
then press solo Shift. On main the browser paints a blue ring around the
whole panel; on this PR nothing appears. (Ctrl+Shift only triggers when
Shift lands first, hence the original "sometimes".)

| Panel | How to open | Fixed |
| --- | --- | --- |
| Sidebar (left, default) | Any rail icon, e.g. Assets | yes |
| Sidebar (right) | Settings > Sidebar Location > right | yes |
| Properties-side panel | Toggle properties panel / builder mode | yes |
| Bottom panel | Toggle Logs/Terminal | yes |
| Canvas / center | n/a | untouched - pointer-events-none, cannot be
click-focused |

- [ ] Each fixed panel: click empty spot, press Shift, no ring
- [ ] Same steps on main/prod show the ring (before-state)
- [ ] Tab still reaches controls inside each panel and their own focus
rings still show
- [ ] Media Assets shortcuts unchanged (Ctrl/Cmd+A, marquee modifiers) -
PR is CSS-only

- Surfaced during design review of #13323
2026-07-08 00:33:41 +00:00
Comfy Org PR Bot
545b48ee5b [chore] Update Ingest API types from cloud@421de6d (#12777)
## Automated Ingest API Type Update

This PR updates the Ingest API TypeScript types and Zod schemas from the
latest cloud OpenAPI specification.

- Cloud commit: 421de6d
- Generated using @hey-api/openapi-ts with Zod plugin

These types cover cloud-only endpoints (workspaces, billing, secrets,
assets, tasks, etc.).
Overlapping endpoints shared with the local ComfyUI Python backend are
excluded.

---------

Co-authored-by: mattmillerai <7741082+mattmillerai@users.noreply.github.com>
Co-authored-by: Benjamin Lu <benjaminlu1107@gmail.com>
Co-authored-by: GitHub Action <action@github.com>
2026-07-08 00:29:59 +00:00
Nathaniel Parson Koroso
b83df7704e feat(agent): server-backed Chat History via GET /api/agent/threads
Kishore shipped the list-threads endpoint, so Chat History is no longer a
single local 'current' row.

- New listThreads() on the REST client + a tolerant zAgentThreads schema
  (every field optional + passthrough): the endpoint shipped after the openapi
  extract, so the client normalizes id / title / timestamp across the likely
  spellings and a shape drift degrades to a best-effort row rather than a hard
  zod failure that would blank the list.
- useAgentSession gains listThreads() and loadThread(id): loadThread cancels
  any in-flight turn, adopts + persists the thread id, and hydrates its
  transcript (reusing the B17 resume path; a stale id 404s and is forgotten).
- AgentPanelRoot fetches the list on mount and each time history opens
  (best-effort: a failed fetch keeps the last-known list), maps threads to
  ChatSession rows, tracks the active row off the live threadId, and loads a
  picked chat via loadThread. Removed the old local 'current row' mirror.
- History store gains replaceAll() for the authoritative server list.

Verified live: the panel calls GET /api/agent/threads and, because the
endpoint 404s on the pr-4432 preview env (Kishore's build not deployed there
yet), the history screen shows 'No conversations yet' with no crash — the
graceful-degradation path. Real rows will populate on an env with the
endpoint; the field mapping should be reconfirmed against the live shape.
Typecheck/knip clean; agent tests 180/180 (added loadThread + listThreads +
server-history-population coverage).
2026-07-07 17:24:04 -07:00
Nathaniel Parson Koroso
944ff2c009 fix(agent): working in-panel history, clean new chat, textarea border
Three reported bugs:

- History did nothing: it was a reka Dialog teleported to <body> that opened
  behind the docked panel's z-999 stacking context, so it never appeared.
  Replace it with an in-panel Chat History screen (new ChatHistoryScreen)
  that the session bar swaps in over the conversation, with a back arrow;
  picking a chat or starting a new one returns. No teleport, no stacking
  fight. Deleted ChatHistoryDrawer + its DropdownMenu* deps.
- No way to make a new chat: the header new-chat button opened the
  out-of-V0-scope 'starting point' onboarding modal instead of resetting.
  It now just clears to the empty state; deleted StartingPointModal.
- Textarea border wrong: the panel-scoped Preflight reset in agentTheme.css
  covered <button> but not <textarea>/<input>/<select>, so the textarea kept
  its UA border/font inside the composer's own border (double border). Extend
  the reset to those elements (border 0, transparent bg, inherit font).

Verified live: session bar -> in-panel history -> back; new-chat -> empty
state; textarea border now 0 (single border on the composer container).
Typecheck/knip clean; agent tests 177/177.
2026-07-07 17:07:32 -07:00
Benjamin Lu
22ea53fb56 fix(ci): remove out-of-rootDir config files from package tsconfig includes (#13485)
## Summary

Type-aware oxlint rejects `packages/ingest-types/tsconfig.json` and
`packages/object-info-parser/tsconfig.json` as invalid (TS6059): their
`include` lists a root-level config file (`openapi-ts.config.ts` /
`vitest.config.ts`) that sits outside `rootDir: "src"`. This fails the
lint-and-format job with "Invalid tsconfig" on any PR that touches those
packages' src files — currently blocking every auto-generated
ingest-types sync (e.g. #12777).

## Changes

- **What**: Drop the out-of-`rootDir` config-file entries from the two
package tsconfig `include` arrays, matching the other workspace
packages. Repro: `pnpm exec oxlint --type-aware
packages/ingest-types/src/index.ts` fails before, passes after; the
config files themselves still lint clean.

## Review Focus

Neither package emits a build, so `rootDir`/`outDir` are
editor/lint-only; excluding the config files from the project has no
runtime effect (vitest/openapi-ts load their configs directly).
2026-07-07 17:03:12 -07:00
Nathaniel Parson Koroso
17bf3643e9 style(agent): round 2a conversation parity (bubble, markdown, tool row)
Computed-style diff of our conversation vs the design reference (prototype);
re-probed live after the fix for an exact match on the user bubble.

- User message: right-aligned pill, no avatar (dropped the Avatar and the
  name/userName props that fed it) — w-fit rounded-lg bg-agent-surface-raised
  px-4 py-3 text-xs (was rounded-agent bg-agent-pill max-w-sm px-3 py-2
  text-sm with an avatar; bubble was 14px/#303036/radius12, now
  12px/rgb(38,39,41)/radius8/py-3 px-4 to match).
- Markdown prose: h1 text-2xl/600, h2 text-base/600, p 14/1.625, decimal/disc
  lists, accent underlined links, 3px muted blockquote, bordered
  secondary-surface tables (were entirely missing), bordered inline code.
- Tool-call summary row: wrench + 'Ran N…' summary + right chevron on a
  borderless h-8 rounded-md muted row (was a bordered card with a left
  chevron); expanded steps render as a plain gap list.
- Conversation column: mx-auto max-w-[640px] p-4.

Verified live on localhost against the prototype. Typecheck clean; agent
tests 177/177.
2026-07-07 16:54:23 -07:00
Nathaniel Parson Koroso
37632ad3b8 style(agent): round 1 parity with the design reference
Iterative style pass against the design prototype (visual reference only; all
changes are tweaks to our own components):

- Dock: the panel is now a full-viewport-height right column beside the tab
  bar (was a splitter percentage panel below it), 420px default <-> 960px
  maximized with a drag handle on its left edge; 8px inset wrapper with a
  rounded interface-stroke border.
- Entry: the 'Ask Comfy Agent' button moves from the actionbar row into the
  workflow tab bar (plum-bordered ink pill, yellow Comfy-C, open state
  highlights); still flag-gated fail-closed. The extension is now gate-only.
- Header: h-12/px-4, regular-weight title, neutral ALPHA pill (no
  semibold/tracking), icons message-circle-plus + maximize-2/minimize-2 +
  x with tooltips.
- Session bar: compact h-6 rounded-sm pill (align-justify icon, truncated
  title, no chevron) instead of a full-width bordered row.
- Empty state: ink-700 tile with plum-600 border and size-6 mark; greeting
  scales text-base -> text-2xl via container query; suggested prompts are
  rounded-full pill chips (size-3 icons) that wrap centered at >=460px;
  4th prompt icon corrected to message-circle-warning.
- Composer: rounded-2xl container owning all padding, focus-within border
  brighten, min-h-20 textarea (px-4 py-3), toolbar px-3 py-2, Auto gets its
  chevron, send is rounded-xl light-on-dark with opacity-50 disabled state;
  placeholder is now 'Ask Comfy Agent…'.
- Footer: p-4 with a mx-auto max-w-[640px] column and my-0 caption.
- Tokens: agent-* aliases retuned to the reference's host tokens
  (base-background surface, secondary-background raised/hover,
  muted-foreground for all secondary text, component-node-border dividers).

Running mismatch list: temp/plans/fe-1187-figma-acceptance.md
2026-07-07 16:37:29 -07:00
Nathaniel Parson Koroso
ad150c88b3 fix(agent): resume the conversation across page reloads (B17)
The thread id lived only in the in-memory store, so a reload always opened a
blank panel and the next send even split the transcript onto a new server
thread. Per the backend contract, GET /api/agent/threads/{id}/messages is the
page-load history endpoint (seq ascending; 404 = thread not found).

- Persist the thread id (localStorage) when a send's ack adopts it; clear it
  on new chat.
- On session start with an empty store, restore the persisted thread and
  hydrate its transcript through the new conversationStore.hydrate(): rows
  pair by turn_id in seq order and arrive settled; a turn whose assistant row
  is missing still renders its user prompt. A 404 forgets the stale id
  silently; reopening the panel within the same page session leaves the live
  conversation untouched.
- Tests: hydrate-on-start, stale-404 forget, persist-on-send/clear-on-new-
  chat, no-clobber-on-reopen; the harness clears localStorage between tests.
2026-07-07 16:18:48 -07:00
cloud-code-bot[bot]
9fe5dd51b8 ci: bump cursor-review to github-workflows@df507e6 (#13493)
Automatic SHA bump — `cursor-review.yml` was updated in
`Comfy-Org/github-workflows` at
[`df507e6`](df507e6bae).
_Opened by the `bump-cursor-review-callers` workflow._

Co-authored-by: cloud-code-bot[bot] <234529496+cloud-code-bot[bot]@users.noreply.github.com>
2026-07-07 23:06:59 +00:00
Nathaniel Parson Koroso
88cdef79f5 feat(agent): dock the panel on the right, not in the left sidebar
The Figma docks the agent on the right of the canvas (pushing the canvas
left) and opens it from the top-bar "Ask Comfy Agent" button — the left rail
keeps Assets/Nodes/etc. and has no agent icon. It was previously a left
sidebar tab.

- New agentPanelStore holds the panel's { enabled, isOpen }. agentPanel.ts no
  longer registers a sidebar tab; the top-bar button toggles isOpen, and the
  PostHog flag gate drives enabled (fail-closed: flag off hides the button and
  closes the panel).
- GraphCanvas renders AgentPanelRoot in the host right-side-panel slot when the
  panel is enabled + open (taking precedence over the node-properties panel),
  and the splitter shows that offside panel while the agent is open, so the
  canvas shrinks to the left. The panel's close button closes the store.
- Update the unit + e2e specs to open via the top-bar button.

Verified live: opens/closes on the right, canvas pushes left, left rail
intact, no agent icon in the rail. Typecheck + unit tests pass.
2026-07-07 14:59:19 -07:00
Nathaniel Parson Koroso
500a6d9c00 fix(agent): match the finalized Figma for the panel empty state
Against the finalized "In-App Agent V0 - Design & Requirements" doc and its
Figma frames (fileKey G7ZjPEAFJB8cqb7halGEfa):

- Empty state: the Comfy mark sits on a dark rounded tile; both greeting
  lines are bold; all five suggested prompts are pinned just above the
  composer (mark + greeting center above them) instead of overflowing, each
  a light row with its Figma leading icon (lightbulb, list, search,
  message-circle-question, workflow).
- Composer bottom row (B6): add the @ (mention) button beside the paperclip,
  add the "Auto" model label, and make the send button white with a dark
  up-arrow when active / grey when empty (was always the blue accent).
- Header (B3): ALPHA is a neutral outline badge (was blue-tinted), new chat
  uses the speech-bubble icon, and the history clock is removed.
- Add the session bar (B4): a "New Chat" row under the header that opens
  Chat History (the history entry point the header clock used to be).
- Rename the sidebar tab label from "AI Agent" to "Comfy Agent" to match the
  panel title and branding.

Verified live against the Figma frames.
2026-07-07 14:47:38 -07:00
Nathaniel Parson Koroso
028b15c3c9 fix(agent): stop panel buttons rendering the UA grey buttonface
Tailwind Preflight is disabled host-wide (PrimeVue + litegraph coexistence),
so a raw <button> in the panel fell back to the browser default: a grey
buttonface fill and a 2px outset border. That is what made the suggested-
prompt rows, the header history/new-chat buttons, and the composer attach
button look like heavy grey cards instead of the clean rows in the Figma
frames.

Re-establish Preflight's button normalization (background transparent,
border 0 solid) in one @layer base rule scoped to #agent-panel-root and the
teleported reka surfaces (dialogs, dropdowns, drawer now carry .agent-scope).
The selector is wrapped in :where() so it stays at zero specificity and every
button's own bg-* / border utilities still win, so filled/bordered buttons
(the blue send button, bordered chips) render exactly as authored.

Verified live: no grey buttonface or outset border remains on any panel
button, and the send button keeps its accent background.
2026-07-07 14:24:33 -07:00
Nathaniel Parson Koroso
442b49b3e5 fix(agent): match the Figma empty state (mark color, title, prompt rows)
Bring the panel closer to the Figma design (it had been built to the
written requirements, not the frames):

- The Comfy mark renders in brand yellow (text-brand-yellow), not the
  generic blue accent, in both the header and the centered empty-state
  mark.
- The panel title is "Comfy Agent" (Figma) rather than "AI Agent" (the
  written doc, which disagreed with the design).
- Suggested prompts are light rows with a leading icon instead of heavy
  bordered cards, per Figma B5 ("leading icon + label"). The per-prompt
  icons are best-fit lucide icons pending the exact Figma icon set.

Typecheck, oxlint, and eslint clean; agent and core tests pass.
2026-07-07 14:00:48 -07:00