Ctrl K
ComunidadGuías…

CHANGELOG.md

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# Changelog

All notable changes to Todd are documented here. The format is based on
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the project aims to follow
[Semantic Versioning](https://semver.org/spec/v2.0.0.html) (pre-1.0: minor versions may include breaking
changes).

## [Unreleased]

### Added

- **A critic watches every short before the human does:**
  - `short_critique` renders the cut clean, and the media service watches it (`/watch`): where the picture freezes or
    goes black, where the sound drops out, and what the soundtrack actually says, transcribed locally.
  - It also makes a filmstrip every half second (`/filmstrip`), each frame labelled with the beat, the words being
    said then and the text on screen.
  - A separate critic agent with fresh context (a hidden `critic` toolset: `critic_watch`, `critic_verdict`) looks
    at that next to the format card's real examples. It scores the hook, what's said and written against what's
    shown (counted double), how organic it looks, the timing and the fit to the format, and returns up to six
    concrete fixes the producer applies.
  - Todd's own checks come with it: a line the voice says differently from the script, a picture that doesn't move,
    dead air, nothing to read or hear at the start, every shot the same length.
  - Iterating is free, and it can't loop: at most three critiques per cut and five per producer (a rebuilt short
    doesn't reset that), only after something changed, none after a pass, and the producer is told to stop as soon
    as the score stops improving. A critic that doesn't
    answer within ten minutes is stopped, and the producer judges the filmstrip itself.
  - `short_review` needs a critique first (`without_critique` when the human wants to see it now), and shows the
    critic's score.
  - A lesson the critic draws for a format, not a product, is kept on the format card (`lessons`) for later
    producers.
- **Shorts learn the edit, not just the words:** `trend_analyze` decodes each reference shot by shot. It finds the
  cuts (hard cuts, and jump cuts as spikes against the frames around them, so a fast scroll isn't a string of cuts),
  shows the agent a sheet with a frame from every shot (and every 2.5 s of a long one), and lines the words up with
  the shots. It measures how much of the video is speech. Before this, the agent wrote cards from transcripts and
  never looked at the frames.
- **Format cards v2:** `format_save` takes the shot list as it really is (each shot's type from a fixed list: screen,
  screenshot, talking-head, person, pov-hands, place, object, text-only, reused), the audio mode (voiceover,
  to-camera, skit, text + sound, natural) and the text style. Todd attaches the examples' measured pace (shots,
  average shot length, speech share, length) and what the card needs on camera. `format_search` flags older cards
  that have no shot list.
- **Trend scans by search phrase:** `trend_scan(queries=…)` searches TikTok the way the niche does. Hashtags still
  work, and `related=[ids]` follows TikTok's related videos from the best on-niche results. Captions in another
  language are dropped (`language`, default "en"). Every Apify run is capped at the approved amount, or $0.50 when
  that's less, Apify's smallest allowed cap.
- **Shorts that aren't all voiceover over a screen recording:**
  - A beat can be silent: no `vo`, a `seconds` length. A reveal can sit between lines, and a whole short can be
    text + sound, rendered silent for the trending sound to be added in the app. That needs no voiceover at all.
  - A beat can hold up to four quick cuts (`shots`, with a `share` each, and their own text).
  - A clip can punch in on what matters (`focus`: a spot and a zoom, optionally from a moment `at_s`).
  - Stock and generated clips are toned down to sit with phone footage (`grade`).
  - On-screen text uses the app's own typeface (TikTok Sans, OFL, in `services/media/fonts/`), in its two looks
    (`text_style` "box" or "outline", positions top, middle and low).
  - Captions default to phrases rather than per-word karaoke colour.
  - Plans report the number of shots and the average shot length, next to the format's examples (`vs_format`).
- **Stock b-roll:** `short_stock` searches Pixabay or Pexels videos and shows a numbered sheet of candidates.
  `short_stock_pick` downloads one into `video/stock/` and credits it in `video/stock/CREDITS.md`. The free
  `PIXABAY_API_KEY` is asked for by the tool itself. The media service fetches the clips (`/fetch/video`, checked as
  a real video) and the candidate previews (`/thumbs`), from its host allow-list only (now including
  videos.pexels.com).
- **A different flow for short-video goals:** trend research and a product scout work side by side. The scout looks
  through the product and films only a key moment or two. Then one producer per video, each on its own card,
  writes the shot list from the card and gets exactly that footage. Several videos vary their format and audio, and
  at most one is a screen recording with a voiceover. The shorts guide maps each shot type to a source and gives a
  phone-video prompt for AI shots. The editing rules (shot lengths, something changing every 2–3 s, hard cuts) and
  the honesty rules (no stock or AI "customers", no fake posts or numbers) come from TikTok's and Meta's creative
  guidance and from an eye-tracking study of cut pace.
- A tool can show the agent several images with one result (`push_image` collects them), e.g. a sheet per reference
  video.

- **Slideshow videos (video, step 1):** a `video` toolset (`video_new`, `video_find_shots`, `video_pick`,
  `video_render`) that writes a storyboard to `video/<slug>/storyboard.json`, searches each shot by meaning across stock
  photos (Pixabay, Pexels) and the run's own images (`video/library/`, or paths the agent names), ranks them by fit to
  the shot and to the look of the shots already picked (`text_score`, `style_score`, a small same-photographer bonus),
  and renders a 1080×1920 MP4 (`video/<slug>/<slug>.mp4`: H.264, 30 fps, a slow zoom on each slide or `motion="none"`,
  optional music from the run folder trimmed and faded to fit), the captioned slides it's made of
  (`video/<slug>/slides/NN.png`, also a TikTok photo post; stock photos fill the slide, the run's own screenshots are
  framed on a blurred backdrop, a tall phone screenshot large enough to read: it keeps its top part and fades out at
  the bottom), a contact sheet (`preview.png`) and `CREDITS.md`. Stock photos need a free
  `PIXABAY_API_KEY` in the vault (Pexels has paused new API keys; an existing `PEXELS_API_KEY` works too, and both are
  searched when both are there). Pixabay search responses are cached for 24 hours and its expiring download links are
  renewed when a photo is picked, as its API terms ask. Both are in the integrations catalog.
- **Feedback on the free scaffold before anything is spent:** `short_review` renders a numbered scaffold version
  (beats labelled b1, b2… in the corner) and asks the human through the usual question card: approve (with the
  generation price), "Slower", "Faster", or anything in their own words; every answer is kept with the version it
  was about, presets are applied, and the agent acts on the rest and reviews again. `short_pace` sets how fast a
  short moves: voice speed, a pause after every line, the least time any shot stays on screen (short lines are held
  automatically) and words per caption page. The default pace is calmer than before.
- **What's working, as data (`trends` toolset):** `trend_scan` pulls recent TikToks for a niche's hashtags through
  Apify (`APIFY_API_TOKEN` in the vault; one priced spend per scan, the actual cost recorded) and ranks them by reach
  (plays ÷ the creator's followers) and share rate, so videos that broke out of small accounts come first;
  `trend_analyze` studies a few: TikTok's subtitles or a free local transcription, caption, sound, metrics and a frame
  contact sheet, with the downloaded video deleted once its frames are taken; `format_save` / `format_search` keep a
  shared library of format cards (hook, beats, why it works, how a product fits in without becoming an ad, the real
  examples with their numbers). Apify's token only ever goes to api.apify.com. Recordings can start positioned on a
  section (`start_at`).
- **Todd films its own products:** `short_record` opens a 9:16 phone tab (or a desktop one) in the agents' browser,
  runs simple steps (wait, scroll, scroll to, tap, type, go to, mark) with real touch gestures, and saves a 30 fps
  recording with the time of every step, so a shot can land a recorded moment on a spoken word. Todd built the
  product, so it records the real pages and flows (the live site, or a repository running in the sandbox, filmed at
  `http://localhost:PORT`). A recording runs in a fresh browser with nobody signed in, so it goes through a flow like
  any visitor (answers a quiz, submits a demo form); it never pays, subscribes, deletes, deploys or publishes, never
  types a password, and never acts on social or payment sites. `signed_in=true` films pages behind the human's
  sign-in, and then only reads and makes safe taps. The media service assembles the frames (`/screencast/put`,
  `/screencast/assemble`).
- **Recordings that don't get stuck, and agents that can see them:** `short_look` opens a page the way a recording
  would and returns a screenshot plus an outline (headings, things to tap, fields, each with how many screens down),
  so steps find their texts the first time; a step that fails says what the page shows instead, with a screenshot.
  Every recording comes back with a contact sheet (a frame just after each step), and `short_render` /
  `short_review` with one frame per beat, shown to the agent as an image. A recording no longer waits on its own
  agent's browser session (which used to hang it), says who has the browser when it's busy, and gives up after three
  minutes; taps find clickable text that isn't a button (a quiz answer in a `<div>`); scrolling past the end of a page
  is a no-op instead of a failure; pages that keep polling load in 8 s instead of 12.
- **Sharp recordings:** Chrome's screencast only delivers frames at CSS-pixel size (414×736), so recordings were
  upscaled 2.6×. They're now filmed with full-resolution screenshots, one at a time (two in flight make Chrome
  misplace later taps), and use the screencast only while scrolling; the assembler sizes the video by its largest
  frame. A screen recording a little short for its slot holds its first or last frame (up to 3 s) instead of asking
  for a re-record (`hold_s` in the timeline).
- **Shorts look human-made and stay short:** the shorts guide now starts with the look (real screens full-frame or
  real phone footage, one line of native-style text, hard cuts; never designed backgrounds, slides, mockups, logos,
  end cards or pages built for the video) and the length (most 8–20 s; 30 s is a ceiling: the plan says cut past it
  and `short_review` won't show a cut over 35 s).
- **Tools ask for their own keys:** a trend scan without `APIFY_API_TOKEN`, or an ElevenLabs voiceover without
  `ELEVENLABS_API_KEY`, shows the human a card with a password field (where to find the key, what it costs) and
  carries on once it's saved, instead of failing and relying on the agent to ask; calls side by side share one card.
- **Short videos in the planner:** a video goal gets trend research and product footage as two agents side by side,
  then one producer per video for the script and the scaffold review; a product that's only a repository is run in
  the sandbox and filmed on localhost, never deployed for a video. The sandbox image now includes ffmpeg.
- **A free scaffold:** `short_voiceover` uses a local voice by default (Piper, timed by a local faster-whisper
  recogniser; caption words come from the script, only the times from what was heard), so a whole scaffold (voice,
  captions, Todd's recordings, priced AI placeholders) costs nothing. ElevenLabs (`provider="elevenlabs"`) is the final
  voice, after the human approves. Both models are baked into the media image.
- **Shorts timed from the voice (video, step 2 foundation):** a `shorts` toolset (`short_new`, `short_edit`,
  `short_voices`, `short_voiceover`, `short_plan`, `short_render`). The voiceover is rendered first (ElevenLabs, one
  take per hook variant with character timestamps, one charge for all takes) and is the master clock: the planner
  (`shorts_plan.py`) cuts 2 frames ahead of each line, adds holds in the pauses between lines without stretching the
  voice, lands a clip's moment on a chosen word (`shot.sync`), pages captions from the same timestamps, fits each
  recording (trim, slight slow-down, short freeze, or sped up to fit) and gives every AI shot an exact length and
  price. A sync report flags anything outside the limits. The media service renders the plan (`/render/timeline`):
  exact-frame segments joined video-only, one continuous voice track placed by sample offsets (a test checks the cut
  and the voice agree within one frame), word-highlighted captions and on-screen text via libass, an optional music
  track ducked under the voice, and labelled placeholders for the animatic. Also `/files/put` (the API hands over the
  voiceover bytes) and `/probe`. ElevenLabs is in the integrations catalog. No AI video is generated yet; see
  `docs/video-step2-plan.md`.
- **Videos play in the Files view:** `.mp4`, `.webm`, `.mov` and `.m4v` files get a player, streamed by
  `GET /api/runs/{id}/files/media?path=` with byte ranges (Safari needs them).
- **`media` service:** a new container for image embeddings (CLIP ViT-B/32 via fastembed, ONNX on CPU, models
  baked into the image), slide composition (Pillow) and MP4 rendering (FFmpeg). Own network to the API, token auth,
  the shared workspace, no vault; downloads only from `MEDIA_FETCH_HOSTS`. Embeddings are cached in a new
  `MediaAsset` table (a stock photo is embedded once, whichever run finds it) and searched in Python, or with
  pgvector when the database has the extension (the compose Postgres image is unchanged for now: see
  media_index.py).

- **Launch plan** for website goals: at the start, one card asks where it should live (your domain, a new domain,
  or a free address for now), whether the GitHub repository is private (default) or public, and whether to ask
  before deploying. Agents keep building meanwhile. Enforced in code: deploys (previews too), connecting a domain,
  Git connections that deploy every push and Deploy/Publish clicks on hosting dashboards wait for your OK unless
  you chose "when it's ready"; new repositories are private unless you chose public. Tools: `plan_launch`,
  `go_live`.
- **Ready for real visitors?** checklist on a finished website run, read from the project's files: titles and
  descriptions, social preview, icons, robots.txt, sitemap.xml, llms.txt, 404 page, security headers (plus manifest
  and privacy page, recommended). Tap a missing item, or "Add the N missing", to ask Todd for it.
  API: `GET /runs/{id}/launch-check`.
- **Right now in plain words:** each agent's line says what it's doing in everyday language ("Building the site to
  make sure it works", "Writing the streak badge part of the page", "Putting up a preview on Vercel"), with its own
  words and the actual command or file shown smaller underneath.
- **Questions you can tap:** `ask_human(options=[…])` shows the choices as buttons, with "Something else…" for your
  own answer.

- **Usage** for every run and across runs: model tokens (in, out, cached) and calls per agent and model, tool
  calls, browser steps, time, and cost (billed on the API engine; on your Claude plan, what it would cost at API
  prices). A Usage view on the run page and a Usage page with tokens per day, the runs that used the most and
  per-model totals. API: `GET /runs/{id}/usage`, `GET /usage?days=`.
- **Right now**: a live card on a running run showing what each agent is trying to do (its latest thought) and
  what it's doing this moment (the tool it's running, in plain words), and who's waiting on you or paused.
- **Suggested next steps** under a finished run: the planner's own "Try next" prompts plus production-readiness
  steps that fit what was built (security headers and rate limits, Firestore rules, Supabase row-level security,
  live-mode Stripe with verified webhooks, store review, CI, domain email, backups). Tap one to put it in the
  Continue box.
- **Sign in with Squarespace** for domains (Settings → Domain registrant, and the Accounts page): Squarespace
  Domains (where Google Domains went) has no API, so agents set up DNS in the browser with your sign-in, following
  a DNS playbook from `find_integrations` (e.g. Vercel's A and CNAME records). Goals that mention Squarespace or
  Google Domains ask for the sign-in up-front.
- **Previews on localhost:** `http://localhost:PORT` in the agents' browser reaches the same port in the sandbox
  (ports 3000, 3001, 4173, 4321, 5000, 5173, 8000, 8080, 8081, 19006, relayed through the API), so agents look at
  what they're building without deploying it. Agents are told never to deploy just to look at something.
- **`git` tool**: normal git (`push -u origin main`, `pull`, `clone`, `status`…) signed in to GitHub for commands
  that talk to a remote. `ask_human(secret_name=…)` collects a secret into the vault through a password field.
  `shell` and `api_request` take `save_to_vault` to store a secret they return without the model seeing it.
  `browser_type` types `{{secret:NAME}}`.

- **Files** view for every run: a read-only browser over the run's folder in the sandbox (what its agents made or
  changed), live while the run works. Recently changed files, a folder tree (installed packages and build output
  listed but not opened), code with line numbers, Markdown formatted (or its source), images, and downloads.
  Vault values are masked everywhere, `.env` values are hidden until you show them, and private key files are
  never shown. Nothing outside the run's folder is reachable, symlinks included. API: `GET /runs/{id}/files`,
  `…/files/view`, `…/files/download`; sandbox: `/files/tree`, `/files/view`.
- Vault keys by run: each secret remembers the run and agent that saved it (or that you added it, or that it came
  from a CLI sign-in). Settings → Vault groups keys that way, the Files view lists the keys its run saved, and
  `GET /secrets?run_id=` filters by run.
- Plain-language recaps: the planner's summary starts with **In plain words** (what now exists and what you can do
  with it) and ends with **Terms** for any jargon; every agent is told to explain technical words the first time
  it uses them with you.

- Accounts up-front: when a run starts, Todd reads which accounts the goal needs (services you name, plus what
  the work implies, e.g. an iPhone app → Expo, App Store Connect, Apple Developer, GitHub), skips those it can
  already use through a key or connected CLI, and pauses on one **Sign in to continue** card: a Sign in button per
  service (opens the login page in the live browser), live status, and Later / Continue. The card closes itself
  once everything is signed in; Todd then connects those CLIs and tells the planner what was left for later.
  `request_signins` shows the same card.
- Expo signs in once: `cli_login("expo")` (and signing in to Expo on the Accounts page) approves `eas login --browser`
  with the browser session; the `eas` tool uses it, with `EXPO_TOKEN` as a fallback.
- Keys Todd sets up itself: `find_integrations` has a `setup` route with the steps for services whose keys are
  created in the browser. App Store Connect: generate a Team API key and save the Issuer ID, Key ID and `.p8`
  straight into the vault.
- `browser_save_download` (and `save_download_to_vault` / `save_to_vault` for the browse() agent): click a Download
  button and put the file's contents in the vault without the model seeing it or it landing in Downloads.
- iPhone and Android apps: an `eas` tool runs the Expo EAS CLI in the sandbox, signed in with `EXPO_TOKEN` from the
  vault. When the vault has an App Store Connect API key, EAS gets it for one command at a time (written to a
  private file and removed afterwards) so it can sign iOS builds and upload to TestFlight. `cli("expo", …)` routes
  to it. The sandbox image now includes `eas-cli`; `find_integrations` knows Expo (and "iphone", "react native")
  and asks for the token when it's missing; the planner prompt has a mobile playbook. `EXPO_TOKEN` is protected
  like `GITHUB_TOKEN`.
- Sign in once: signing in to GitHub, Vercel, Netlify, Railway, Cloudflare, Stripe or Firebase (Accounts page,
  Setup) also signs in that service's CLI with the same session. Todd approves it in the browser itself and only
  asks you for a password/2FA page. Agents can do the same with `cli_login`.
- `cli` tool: run a connected CLI (`cli("vercel", "deploy --prod --yes")`) with its saved sign-in.
- `gh` tool: the GitHub CLI in the sandbox, signed in per command from the vault. The sandbox image now includes `gh`.
- `browser_console`: JavaScript errors, console warnings and failed requests on the current page, for agents
  checking the sites they build. The prompts now say verifying your own work is a normal use of the browser.
- `browser_read_text`: copy the full text of an element or page (shadow DOM and field values included, never
  password fields) into the agent's work.
- `browser_save_secret`: save a key a page shows straight into the vault without the model seeing it.
- Continue a finished run: message its planner (in its window, the run summary or the timeline) and it picks its
  conversation back up, on both engines.
- Rename, run again, copy the prompt of, stop or delete a run from the sidebar or the runs list; **Edit** in the
  sidebar deletes several at once. Deleting keeps ledger entries and workspace files.
- Finished, failed and stopped agents fold into a compact "Finished agents" tray on the run page. Open one to see
  its window again, or hide it.
- **Take control** opens the agents' browser in a large window you can click and type in. Sign-in prompts and CLI
  rows that need you have an **Open browser** button.

### Changed

- **Content goals skip the website checks:** a goal that makes content (a TikTok, reels, posts) about something that
  already exists no longer gets the launch plan card (where to host it), the GitHub/Vercel or app-store sign-ins, or a
  social sign-in unless it asks to post there (`goal.py`).

- The sandbox's `agent` user now has an explicit uid (1001, the same it had), which the `media` container shares so
  both can write run folders.

- **Agents work side by side without touching the same code:** one agent writes the code; helpers run alongside it
  for work that feeds it without editing it (content or data in their own files, like a questions file the site
  reads; research; assets); reviews and fixes come after the build and deploying is last. Todd refuses a write to
  a file another running agent is working on. Default limits: 6 agents at once, 16 per run.
- **Deploying asks first and comes last:** every deploy, previews included, waits for your OK (unless the launch
  plan says "when it's ready"), and a deploy is refused while another agent is still working on the project.
- **Production readiness is part of every site build** (the builder's last tasks: SEO and sharing, robots.txt,
  sitemap.xml, llms.txt, 404, security headers, accessibility, Lighthouse), and "Try next" leads with what's left
  for production. Suggested next steps keep up to three production-readiness items so feature ideas can't crowd
  them out.
- `git_push` pushes like a developer would: to `origin` (set from `repo` when given) with upstream tracking, the
  current branch instead of renaming it to main, a default .gitignore when the project has none (node_modules,
  .env, build folders, key files) and `--force-with-lease` for force. Before, agents couldn't push with plain git
  (the sandbox has no GitHub credentials) and fell back to odd workarounds.
- Secrets in context (security review): tool results are scrubbed string by string before serializing (JSON
  escaping hid multi-line keys), including encoded forms and CLI sign-in tokens; secret-shaped strings are hidden
  from models and the timeline; events are scrubbed before they're stored; the browser blurs secret-looking text
  before every screenshot and redacts page text, console output and URLs; checkouts give the browser agent no
  page-reading actions or form values and blank the checkout tabs afterwards; `read_file` hides `.env` values;
  `fetch_url`/`api_request` refuse internal and private addresses and don't follow redirects with a secret; the
  CLI blocklist sees subcommands after global flags; the vault shows only the last 4 characters. See SECURITY.md.
- The completed-run screen is redesigned: the recap renders as Markdown (lists, bold, code, links) in sections:
  In plain words, What got done, What you got (clickable link cards), What's next for you, Who did what, Words to
  know and How. Technical words explain themselves on hover or tap (a built-in glossary plus the recap's own
  Terms). Agent recaps in windows use the same renderer, including older one-line recaps.
- The **Sign in to continue** card says why: a short "Why these accounts" line from the goal (e.g. "You're making
  an iPhone app, so Todd needs to build it, sign it and upload it to the App Store."), and each account's row says
  what Todd uses it for ("Builds the app in the cloud").
- Agent windows are a little bigger (taller, wider minimum) with slightly larger text.
- `spawn_agent` runs agents in the background by default, and `wait_for_agents` ends early when you message the
  planner, so it can act on the message while agents keep working.
- Sending a message to a paused agent (or the planner) resumes it.
- The planner can't finish while agents it spawned are still running.
- A signed-in account's CLI now connects automatically when the Accounts page or Setup loads (one at a time, once per
  start; the button retries). Disconnecting a CLI turns this off for it.
- CLI approval: when a site keeps its final Authorize/Allow button disabled until a person interacts, Todd scrolls to
  it, outlines it and asks you for that one click instead of waiting. After you finish a password or 2FA page, Todd
  takes over again on the next page.
- `find_integrations` routes a service that isn't connected but has a browser-session login to `connect`
  (`cli_login`). Agents are told never to create tokens in the browser or copy credentials through their context.

### Fixed

- The planner no longer blocks on a sub-agent: messages sent while it waited weren't seen until the agent finished.
- `find_integrations` reported GitHub as "ready" without a `GITHUB_TOKEN` (the built-in `github` toolset was
  mistaken for a plugin).
- Sandbox commands that left a process running in the background (dev servers, CLI logins) hung until their
  timeout, because leaked descriptors kept the output stream open.
- A sandbox command that timed out lost everything it had printed; the output is now kept.
- The sandbox runs with an init process, so exited background processes are reaped.
- **Take control** didn't let you control the browser (only the new-tab view did): noVNC treats `view_only=0` as
  on. The Setup and Accounts browsers were affected too.
- Vercel's device-code field (`autocomplete="one-time-code"`) was mistaken for a 2FA prompt, so the Vercel CLI
  sign-in stopped for you right away.

### Security

From an outside review of spending and public-action controls, plus what checking it turned up:

- **The live view had no password,** so any website you visited could connect to `localhost:6080` (browsers don't
  limit WebSockets to the page's own site) and use the signed-in browser. It now always has one: random unless
  you set `VNC_PASSWORD`, shared with the API and put in the dashboard's live-view URL. The raw VNC port listens only
  inside the browser container, and Chromium no longer accepts debugging connections from web pages.
- **The dashboard only serves its own pages.** Its API route checks the Host (localhost, or `TODD_ALLOWED_HOSTS`),
  which stops DNS rebinding and Todd's own containers calling `http://web:3000`, and refuses requests a browser marks
  as coming from another site or origin (CSRF, including "simple" GETs and body-less POSTs like sign-out). Signing
  in a CLI after an account is signed in moved from `GET /api/accounts` to `POST /api/accounts/auto-connect`.
- **Purchases and public actions are checked in code** (`gates.py`), not only by instructions: purchase-looking
  clicks, payment and billing pages, and known purchase APIs and commands need `authorize_purchase` (new, every
  agent; human-approved, in the Ledger); posting/messaging/email sites need `request_approval(sites=[…])`; known
  posting APIs and publishing commands (npm publish, docker push, eas submit, public GitHub repos, releases and
  gists, live-mode Stripe changes) show the exact request and wait. Scripts can't click or submit on websites.
- **Card checkouts:** card details only on the exact approved sites (no subdomains); a shared payment page such as
  checkout.stripe.com must be approved together with the merchant and gets the card only when the tab came from the
  merchant; the page's total must fit the approved amount.
- **Signed-in commands run in their own container** (`signedin`, same image and workspace): git push/pull, `gh`,
  deploy CLIs, EAS and CLI sign-ins. Code running in the sandbox can't read their tokens or sign-in files anymore.
  npm-based CLIs run from the runner's own install, not the project's `node_modules` (`npx` picks the project's).
- **One browser lock for every run** and the Accounts page's CLI sign-ins (it was per run, so two runs could drive
  the browser at once).
- **Web tools check the address again when connecting,** so DNS rebinding can't reach internal services.
- Docs: SECURITY.md's "What needs a person, enforced in code" and known limits (recognition-based gates, a
  deploy running the project's own code with a sign-in, the GitHub sign-in's scope).

## [0.4.0] - 2026-09-25

First public release.

### Added

- Dynamic multi-agent planner: `spawn_agent` designs agents around groups of related work and runs them in
  parallel, with `wait_for_agents`, `message_agent` and `cancel_agent`.
- Two engines: **Your Claude plan** (headless Claude Code sessions, the default) and **API keys** (any
  LiteLLM model per tier, including local models via Ollama).
- Toolsets: code sandbox, signed-in browser (browser-use over CDP with a noVNC live view), Vercel, GitHub,
  web/API requests, vault, accounts, plugin files and MCP servers.
- API-first routing through `find_integrations`, with the browser as a last resort (`why_not_api`).
- Accounts catalog of about 70 services, with login detection and a setup wizard.
- Spend policy and ledger: auto-approve limit, per-run budgets, duplicate-charge guard, and human approval
  for all card payments.
- Approval flow for public actions (posting, messaging, publishing).
- Dashboard with one live window per agent, including reasoning, tool calls, browser steps, pause/resume,
  steering messages and summary cards.
- Crash-safe runs via LangGraph + Postgres checkpoints, with Resume.
- Editable prompts (`./prompts` or the dashboard).

### Fixed

- The API image failed to install its dependencies: `browser-use` 0.13.10 pins `mcp==2.1.1`, which
  conflicts with `langchain-mcp-adapters` 0.3.2 (`mcp<2`). Pinned `browser-use` to 0.13.9.

[Unreleased]: https://github.com/bobby-burns/todd/compare/v0.4.0...HEAD
[0.4.0]: https://github.com/bobby-burns/todd/releases/tag/v0.4.0