| Files | Last commit | Last update |
|---|---|---|
Do not count the agent database host start against the first statement (#5044) * Do not count the agent database host start against the first statement The supervisor sent the first statement as soon as it started the host process, so process start and module load counted in that statement's time and in its 5 s deadline. On slow Windows computers this ended the first statement after app start with the "ran longer than allowed" message, and it set the db_request_ms p95 and max values. - The host sends a ready message after it loads. The supervisor sends no statement before it, so the statement deadline and the reported time start at the send. - A host start has its own 15 s deadline. A host that does not report ready in time, or exits first, fails the waiting requests with a start message and is ended. It is not started again in a loop. - After the first window loads, OpenBot starts the host early when the shared database file exists. - Add the agent-database-host-start E2E test to the provider fakes job. Host start time is not reported to analytics: that needs a schema change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Harden the agent database host start review items - Drop a prewarm start that throws as a failure, not as a defect. - Give the host start E2E test wider deadlines for a loaded CI runner. - Name the changelog file after the branch. - Say in the CI job comment that it runs the host start test. - Make the start deadline comment exact about a request that waited. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 5 days ago | |
Let an agent find work from its other chats (#5543) * Let an agent find work from its other chats An agent now reads its own chat and its side threads with openbot.history_search and openbot.history_read. A search with no taskId searches all of its chats in one call. A search with no query lists the chats with their last request and reply. A messaging chat searches only itself, and a channel gets no history. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Say in the handoff text that history tools read all chats Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 3 days ago | |
Let an agent find work from its other chats (#5543) * Let an agent find work from its other chats An agent now reads its own chat and its side threads with openbot.history_search and openbot.history_read. A search with no taskId searches all of its chats in one call. A search with no query lists the chats with their last request and reply. A messaging chat searches only itself, and a channel gets no history. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Say in the handoff text that history tools read all chats Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 3 days ago | |
Let agents list and close open waits (#2758) * Let agents list and close open waits An agent at the limit of 20 open waits could not find wait IDs, so it could not close finished waits or open new ones (#2753). - Add openbot.list_waits: wait ID, note, open time, routine, last wake. - close_wait and list_waits take an optional agentId, as the routine tools do, so a coordinator can close a finished wait of another agent. - list_agents shows openWaits for each agent with open waits. - await_event returns the count and limit, and warns near the limit. At the limit, the refusal lists the open waits. - Add scripts/agent-waits-tools-e2e.ts. Closes #2753 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Apply review fixes to the open-wait tools - Run scripts/agent-waits-tools-e2e.ts in CI as test:agent-waits-tools, and upload its report. - close_wait and list_waits name the bad field when arguments are not valid. Before, a bad agentId gave "this wait is not open". - Count the open waits with COUNT(*) after await_event. - Remove the limit refusal text that repeated the localized message. - Assert where the near-limit warning starts. - Move the docs paragraph after the run route's status table. - Update comments that named only two wait tools. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 19 days ago | |
Measure the storage of a hosted server against its plan (#6264) * Measure the storage of a hosted server against its plan The account server sends the plan storage of a hosted server in the host list. The desktop storage card and Server settings > Storage use it. They do not count the 22 GB base image, and the card shows when less than 10 GB of the plan is free. Agents on a hosted server get one short line about the plan storage. * Correct the release notes for the plan storage warning * Read the hosted state of a host from its plan, with one lookup | 14 hours ago | |
Give each browser profile its own proxy (#5145) * Give each browser profile its own proxy A named browser profile can use a saved proxy: socks5, http or https, with an optional sign-in. The password is encrypted and shows only as a mask. A SOCKS5 proxy with a sign-in goes through a relay on 127.0.0.1. When the proxy cannot be set, the tab does not open, so pages never go out without the proxy. A badge shows when the proxy does not answer or refuses the sign-in, and the page error names the proxy. A member can also share the connection of their own computer as the proxy of a profile on a joined server. The computer opens only public internet addresses. Owners and admins manage proxies from the desktop app and the web (browser-proxies-v1, browser-egress-v1). Migration 56 adds the browser_proxies and browser_profile_proxies tables. Closes #3443 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix proxy relay races and egress edge cases from review - Apply one profile proxy at a time, so two tabs that open together do not open two relays and leave one open. - Stop the relay of the old proxy when a proxy change closes the tabs. - Keep a saved password only when the address still has a user name. - Connect to a SOCKS5 proxy at an IPv6 address without the brackets. - Refuse a site name that browser-egress-v1 cannot carry before it goes to the computer, which closes the channel on a bad frame. - Open a new egress channel when the last one is closing. - Stop the computer proxy muxes before the team WebRTC bridge. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Remove unused proxy relay and port exports Knip found five exports that only their own file uses. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Refuse a proxy address with a password and no user name The database refused such a proxy, so the save failed with a generic error. The parser now refuses the address, and the dialog shows that the address is not valid. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 5 days ago | |
Stop a queue loop that filled a server disk with chat updates (#5908) * Stop a queue loop that filled a server disk with chat updates A hosted server wrote 1.7 million `conversation.snapshot-updated` events (66 GB) in 22 hours. A Claude agent waited on a question in a turn that the CLI started itself. A routine turn in the same chat ended and set the chat to idle. The queue then asked the busy CLI for a turn again and again, and each try saved every queued message again in the chats of the agent and of each sender. - A turn end keeps another turn that still runs in the same chat active. - After `ProviderTurnActiveError`, the lane waits until the next drain of the agent, for example the end of any of its turns. - A change of a queue position alone is shown, but not saved. Each reader takes positions from the mailbox again. - A chat update that the next update replaces whole is removed from the event log. A replay of the log gives the same chat. Adds scripts/queue-restore-loop-e2e.ts and scripts/conversation-update-supersede-e2e.ts. * Fix the review findings of the queue loop change - A turn end clears the chat's active turn as before, except when that turn is another turn that still runs. It no longer takes a different running turn, so a turn that never ended cannot hold the chat. - A start is not held after `ProviderTurnActiveError` when a drain call arrived during the start: that call can be the end of the turn. - The activity row of an update has a key from its sequence, so the removal finds it by key and does not read the whole table. - An update goes only when its command id has the generated form, and when the later message has the same author and, with no delivery, the same time, as a replay keeps them. - A page read takes the queue positions from the mailbox, because a move of the queue alone is no longer saved. - The loop check waits for the hold that the drain logs, not for a time. * Do not save queue moves on page reads The page read compares stored messages with the mailbox. It counted a moved queue position as a change and saved every queued message again. Both checks now compare messages with no queue position, through one helper. A page read takes the positions from the mailbox. | 1 day ago | |
Let a pinged person answer an agent's Slack post without a mention (#5910) * Let a pinged person answer an agent's Slack post without a mention When an agent's messaging_post pings a person with <@U…> in Slack, OpenBot records the person in the new projection_messaging_pings table (migration 59). A reply from that person in the post's thread now reaches the Slack Orchestrator without a mention. The Orchestrator gets the post and the name of the agent that sent it as the message that the reply answers. Replies of other people still need a mention until the thread has its link. The link deletes the thread's pings, and deleting an agent deletes its pings. A thread message event that mentions OpenBot is now left to its app_mention event, so the order of the two events does not decide the route. In Slack threads an agent can answer only NO_REPLY when a message needs no answer. OpenBot then deletes the "Working on it…" post and keeps the reaction. Fixes #5565 * Hear only the pinged people in a thread that a ping opened In a Slack thread where an agent's post pinged people, a reply without a mention now counts only from a pinged person, also after the first reply gives the thread its link. Other people reach the Orchestrator only with a mention. The ping rows now stay with the link, and deleting an agent deletes the pings of its threads and its pings of threads that no reply opened. A thread that a person opened with a mention still hears everyone. Refs #5565 * Fix the local review findings of the Slack ping replies - An answer in a thread that a ping opened records the people that it pings, so they can reply without a mention too. - MessagingThreads.recordPings does the link check and the save in one Effect.try, and logs a failure. A database error after the post no longer fails the tool call. - The Orchestrator reads its own pinging post as its own, not as a teammate's post. - The stored post uses the prompt's REPLY_CHARACTERS limit. - The E2E reads each dropped reply again after a later reply arrives. Refs #5565 * Give a pinged person's first reply with a mention the post as context A first reply that mentions OpenBot comes only as app_mention, so the receive path did not read the thread's pings. The reply went to the workflows, and the Orchestrator did not get the post that it answers. The receive path now reads the pings for each Slack thread message with no link. The E2E covers this case. Refs #5565 | 1 day ago | |
Test the upgrade from every released pre-registry schema (#2435) * Test the upgrade from the released v3, v4 and v6 schemas The old tests made a v3 database from the current schema. These fixtures use the DDL that v0.1.12, v0.1.15 and v0.2.1 shipped, with data in each table and the marker sets that a new install and an upgrade wrote. They check that each upgrade to version 43 keeps the rows, matches a new install, passes the foreign-key and integrity checks, and rolls back and retries after a failure. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Test the foreign-key and history guards of the migration runner No test made the foreign-key check before commit reject a migration. The new test puts a broken foreign key in a v27 and a v42 database. Migration 28 (which turns foreign keys off) and migration 43 must roll back, keep every row, and turn foreign keys on again. A table-driven test also rejects a history with a gap at each version from 8 to 42. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Test migration 21 with case variants and a failed run The old test seeded only `computer_use` and `computer_use_saved`. The version 20 name index is case-sensitive, so `Computer_Use` and `COMPUTER_USE_SAVED` could also exist. The new test checks that the migration renames each case variant to a different free name, keeps the configuration, and rolls back and retries after a failure. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Test the workspace root rewrite of migration 13 No test checked the `OpenBot/Bots` to `OpenBot/Agents` rewrite. The new test stores POSIX, Windows and JSON-escaped paths in JSON and raw columns. It checks that each stored root moves once, that `Botsy` and a user's own `Projects/OpenBot/Bots` stay, and that a failed run rolls back and retries. It also records that an agent which keeps its id gets the new root. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Test the upgrade from the released v0.1.18 schema v0.1.18 to v0.2.0 kept schema version 4 but added the thread_messages_page_order index. Add its DDL as a released schema and upgrade it with markers [4] and [3, 4]. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 23 days ago | |
Skip linked agents after a quiet routine run (#5030) A scheduled, script or webhook run whose agent has nothing to report keeps no answer. The routine flow still handed it on, so each linked agent started with "(no answer text)". The flow now skips these agents, as routine results and workflows already send nothing for a quiet run. A Test or manual run with an empty answer still hands on. Fixes #5022 Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 5 days ago | |
Add workflows: connect agents and steps on a canvas (#3062) * Add workflow IPC contracts Types and guards for workflows: graph, summary, detail, runs, steps, versions and their inputs. Add the input limits and the workflows-changed agent event. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Add the workflow tables (migration 46) Migration 45 only creates tables and indexes: workflows, nodes, edges, versions, triggers, trigger webhooks, webhook receipts, runs, steps and output deliveries. No row of an existing table changes. The same SQL is the tail of the latest schema for new databases. - workflow-migration.test.ts covers every schema from 8 to 44, rollback and retry, newer and incomplete histories, foreign keys and cascades. - The older migration tests expect version 45 as the latest. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Add the workflow store and graph rules The store saves graphs with a revision check, keeps up to 50 versions, and keeps trigger rows. Deleting an agent removes it from workflow nodes and keeps the step history. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Add the workflow IPC endpoints The host answers list, get, create, rename, delete, save, versions and restore. A joined server says that it does not support workflows yet. The preview keeps workflows in memory. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Add workflow runs Run a workflow from the start, from a trigger, or from one node, and run one node alone. Agent steps go through the routine sender, so an agent answers in its own chat. Model and condition steps use a tool-less generation; a condition answers yes or no. Approvals wait for a decision, and a rejection stops the run. Notify outputs show a desktop notification. A sweep at startup settles agent steps from their delivery and fails model steps that a restart interrupted. Channel outputs fail with outputPending until the messaging-place output lands. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Add the workflow run view and rail button - WorkflowFeatures and the v1 set of node kinds, triggers and outputs. - A pure run view: pages of runs, run and step changes, and the run state of each node. - The Workflows rail button (Waypoints) and app.rail.workflows in all languages. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Move the workflow nodes into shared UI - The board, camera, picker, tool art and one file per node kind move to packages/ui/src/features/workflows, with their styles. Their icons go through the shared icon exports. - Nodes read agents, models, places, image models and files from WorkflowCatalogs on the board. No story data or simulated runs. - The agent node writes agentId; a channel output writes connectionId and placeId, as the contract says. - 199 English keys in the workflow module. Result times are ISO and formatted in the reader's locale. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Split the workflow canvas into shared UI Move the canvas from the prototype into packages/ui as a controlled component. The host gives the workflow, runs, versions and catalogs, and gets each graph change. Remove the run simulation, presence and local ids. Add the canvas keys in English, the Workflows stories over the preview mock, and the Storybook icon link. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Add the Workflows page to the desktop window The Waypoints rail button opens a titled page with the workflow canvas of the server. The page saves the graph 400 ms after the last edit, one save at a time on its base revision, and loads it again after a change from another device. A joined server without workflows shows a notice. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Add workflow triggers, place outputs and workflows-v1 - Schedule triggers: WorkflowTriggers is a routine due source. A missed time gives one run, and a time is skipped while a run is open. - Webhook triggers: a third webhook route owner. The first secret makes the route. Receipts stop a second run for the same delivery. - Place outputs: an output posts the result to a messaging place, once, and an interrupted post becomes uncertain at the next start. - workflows-v1: the frozen Team API routes, codecs and event, the host route with its grant rules, the team client and the remote IPC branches. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Translate the workflow catalog Add the 302 workflow keys in de, es, fr, it, ja, pl, pt, ru and tr. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Edit trigger schedules and webhook secrets on the canvas A trigger node uses the routine schedule picker and saves the schedule on the node. A webhook trigger without an address has a button that makes the first secret, and so the address. The preview gives the next run and the address. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Document workflows PRIVACY.md says what a workflow keeps on this computer and what an output and a webhook trigger send. The architecture notes describe the run engine, the timer source, the webhook owner and workflows-v1. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix the workflows page height and the canvas after a delete The canvas is a direct child of the window page, so it gets the height of the pane. The page reads its host once, without a tracked read. When the host opens another workflow after a delete, the canvas shows again; a switch while the Undo toast is open keeps the delete. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Name migration 46 in the workflow docs Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Scope workflow run and step keys to their workflow A copy of a workflow keeps its node ids, so its schedule and webhook keys were the same text as the first workflow's, and its runs were answered with the other workflow's run. The keys are now unique per workflow. Migration 46 is not released, so its tables change in place: steps check their kind, and the unused waiting status, trigger enabled and last_scheduled_for columns, delivery retry columns and the inactive receipt reason are gone. A refused delivery gets its own reason. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix workflow run starts, the sweep and schedule waits - A run, its version, its reused steps and its webhook receipt are one write. - A webhook request that the graph refuses gets an ignored receipt and 202. - A trigger of a workflow with an open run waits for the run to end. - One failing schedule trigger does not stop the others. - The sweep ends steps that a restart or an agent delete left open, and ends a stop that a restart cut off. - A run version is kept only when the graph changed. - Store errors carry a code; the Team API maps missing to 404 and a bad schedule to 400. The list reads every graph in one query. - The runs cursor names the run, so runs of the same millisecond stay. - The routine timer plans again only when a trigger changes or a run ends. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Share the workflow helpers with routines Use one placeholders, receipt cutoff, result text, node agent, node timezone, CHECK column and coalesced sweep helper. Build the edge maps in place. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix workflow canvas bugs in keys, drags, uploads and approvals - Agent and LLM pickers set model, provider and effort as one change. - Undo and redo stop a drag and cancel queued layout frames. - Keys, clipboard and Delete act only in the canvas, its portals or the page body. Menus and lists keep their letter keys. Delete and cut do nothing on a frozen or deleted workflow. - Drag and pointer listeners are removed on cancel, restore and unmount. - Slots and ports stop the resize observer when they go. - Asset uploads belong to the board. Their blob URLs are freed at unmount, a file is found by id, and the upload button follows features.uploads. - The canvas label names the workflow. The minimap is hidden from screen readers. The live region tells only the step status changes. - Ports set aria-pressed only while a line is drawn. - A new note saves the name of its author in its fields. - Picker tiles get the colour of their kind. - A new node does not cover a node at the centre of the view. - Search reads the text fields of the board, not the DOM. - An approval answer is for one step, and shows again when sending fails. - Each trigger gets its own new schedule object. - The search fields show a grey focus ring. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Make the workflow canvas do less work for each change - Selected, removed and locked nodes are projections. A node runs again only when its own state changes. - The list of nodes on the canvas and the trigger lookup are memos. - Fit to view and the minimap use the sizes from the resize observer. Fit reads the element only for a node that has no size yet. - A line that the user draws adds points in place. A move does not copy the line or put it in a store. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Open the workflows-v1 names and add the version graph read - The wire carries node kinds, edge types, statuses, sources, trigger types and version kinds as short names. A client maps a name it does not know: a node becomes a locked note, its edges go, and the workflow is read-only. - New workflows.versionGraph read (IPC and Team API). The host hides a version that names an agent the member cannot see. - The IPC parse keeps only known graph keys and refuses a graph over 1 MiB with its own sentence. - Align wire limits: runs limit 1-50, run number and attempt >= 1, runs cursor up to 64 characters. - Move the workflow decoders to remote-workflow-decoding.ts. Delete the unused team-client and web-runtime workflow requests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Ask for the manage level to change a shared workflow - A member needs manage on each agent of the graph, before and after, to save, rename, delete or restore a workflow or to make a new webhook secret. A run, a stop and a decision still need chat. - Retry, stop and decide also check the agents of the run's own graph: a retry ran an agent that the current graph no longer names. - An owner or admin sees a workflow whose agent was deleted. - A save conflict answers as missing when the newer graph names an agent that the member cannot see. - Run answers empty the oldest step texts to fit one frame, as the run page does. A single step of a hidden agent answers 404, not 500. - Reuse runEventsEffect from route-events.ts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Share one workflows controller between the page and stories - Move the save queue, resets, restore, preview and delete into workflows-controller.ts. The page and the stories use it. - A version preview reads its graph from the host. - A conflict or a restore keeps the canvas edits on undo, and a toast tells the user. - A failed save keeps the edit for the next save. A restore or a delete saves the waiting edit first. A failed delete shows the workflow again. - A member without "Can manage" on each agent gets a read-only canvas. Only an admin can rotate a webhook secret. The channel output hides when there are no places. - Add the Korean workflow catalogs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Document workflow permissions and test agent steps Docs and the release note give the manage rule, the version-graph route and the sweep rules. A new e2e checks that an agent delete fails the open step, and that a restart ends a stopping run. The webhook e2e checks that a graph this version cannot run gives a refused receipt. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix workflow canvas halt on restore and new node placement A restore that removed a node wrote the slot size store in a list cleanup, which halts Solid. The reset now drops the sizes of the removed nodes. A new node now moves past each card that it would cover, not only an exact corner. The controller reads the server role untracked. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Read the first agent fields of an agent step untracked The catalog agent is a store, so a read of its model and provider in the component body warned that the value does not update. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Remove unused workflow exports and fix optional props for the type ratchet Knip found 13 exports that only their own file uses. The optional props that take an undefined value now say so, as exactOptionalPropertyTypes requires. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Document that workflows with no agent need no agent level Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Keep the workflow canvas and its toolbars on whole pixels Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fade workflow canvas parts in out of a blur instead of a move Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Remove the entry animations of the workflow canvas Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Match the workflow title to the other page titles and remove its icon Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Balance the paddings of the workflow title and HUD buttons Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Give the workflow top row the button size of the other pages' top rows Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Make the workflow canvas icons and node toolbars sharp On a screen with one device pixel for each CSS pixel, the canvas icons get a line of 2, so a line fills whole pixels. The node toolbar icons are 16px, as in the tool bar. After the camera, a node or the selection moves, the node toolbars and result strips move onto whole device pixels. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fill the icon of a pressed workflow canvas tool Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Snap the workflow node toolbars to whole pixels and enlarge their icons The earlier sharpness commit added the snap offsets in the canvas but not the CSS that reads them. The node toolbar and the result strip now add the offsets to their position, and the toolbar icons show at 16px. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fill the pressed Select arrow fully The part-fill rule matched the svg element too, so it had more specificity than the arrow rule, and the arrow stayed at 35%. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Give workflow sticky notes the corner radius of the other cards A note had a 6px corner that shrank with the zoom while its selection ring and resize arc kept their screen gap, so the corners did not look alike. Notes and their drop preview now use the card radius. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Allow loops in workflows A connection back to an earlier step is a loop. The run takes it as the next pass: the earlier step runs again with the loop's answer as one more input, and the steps after it follow. A loop has no pass limit; a condition, a failed or rejected step, or a stop ends it. The canvas draws a loop under its steps with a "Loop" badge, and with an "Endless loop" warning when no condition is in the loop. While the user connects, the draft line says that the connection makes a loop. A retry now runs every step after the retried step again, not only the skipped ones. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Do not select a workflow step on a press of its port A press on a port, a portal or the edge actions selected the step under it, so a new connection also moved the selection. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Show why a workflow Output or Condition step failed These cards have no result field, so a failed step showed only its status. They now show the error of the step. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Retry a workflow step with the node's current settings A retry used the settings that the run copied, so a fix made after the failure, such as a new output place, did not apply. A retry now uses the current node, and the run's copy only when the node is gone or is now another kind. The Team API already checks the agents of both graphs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Reuse a warm provider for workflow model steps and count their usage A model or condition step started a new provider process each time and recorded no tokens. Now one process without tools per provider stays for 5 idle minutes; each step still starts a new ephemeral thread and closes it after the turn. The process stops when the main client stops or changes, after a failed step, and at shutdown. The tokens of these steps go to one reserved usage row, WORKFLOWS_USAGE_ID, which the host report, the Team API filter and the usage panel accept. Agent steps still count under their agent. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Report workflow runs and actions to product analytics The host sends system_workflow_run when a run ends (outcome, source, duration, step counts by kind) and workflow_count in the daily inventory. The window sends workflow_action for each user action on a workflow. No name, text, graph or ID is sent. Host generation 11, desktop 12. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Translate the workflow text to Simplified Chinese main added zh as a 12th language. Add the workflow and workflow error catalogs, and the Workflows rail, usage and notification keys, so zh has every key. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix workflow review findings before merge - Reuse steps in graph order, so a reused step does not make a node behind it due. - Build the input of a retried agent step again from the current task. - Show the Workflows usage row only to a peer with workflows-v1. - Keep the warm client state correct when a generation is interrupted, and wait for idle stops at shutdown. - Send the duration of a workflow run up to 30 days. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 16 days ago | |
Let workflow outputs post into OpenBot channels (#5572) * Let workflow outputs post into OpenBot channels Add the output target "chat-channel". The result shows in the channel as a note of the workflow: author kind "coordinator", id "workflow:<workflowId>", no task. No task starts and no agent answers it. The step id is the operation id, so a resumed step posts no second note. Only this computer offers the target. Over the Team API only an owner or admin can add or change one. Older clients show the workflow read-only (needsNewerApp). The phone shows the target and does not choose it. Rename the "Channel" target to "Slack, Discord or Telegram channel". Fixes #5559 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Name the messaging output target "Messaging place" The target also lists WhatsApp places, so "Slack, Discord or Telegram channel" was not correct. Use the name that the mobile app already uses. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Show one Channel target for OpenBot channels and messaging places "Send to" of a workflow output has one "Channel" choice again. Its channel list shows the OpenBot channels first, then the places of each messaging connection under the connection name, with a search field. An OpenBot channel stores the chat-channel target and a place stores the channel target, so saved workflows and the backend do not change. An OpenBot channel shows "Posts as: A note; no task starts". * Use a live OpenBot channel when an output goes back to Channel When the channel of the output is not in the list (for example, it is archived), the output takes the first open channel. --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 2 days ago | |
Fix documentation drift from the code (#2436) * Fix documentation drift from the code Make the docs agree with the code: commands and flags, file paths, module ownership, IPC groups, CI lanes, release targets, and protocol versions. Correct the settings paths and the client list in PRIVACY.md. Code is not changed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Correct four statements that the local review found State the v7 capability limit, say that no CI job runs the plugin catalog check, name the owner of the plugin types, and add the Muse state folder. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 23 days ago | |
Show the agent's models in Check agent (#2689) * Share one fake ACP agent helper and run provider fakes in CI Move the fake agents of six provider E2E tests out of inline CommonJS strings into typed files under scripts/provider-fakes/agents/. acp-agent.ts holds the NDJSON loop and the FAKE_LOG writer; host.ts installs a fake as an executable and holds the thread, turn, log and cipher helpers. test:custom-agents failed on main since #1368, which added a folder tag to routed thread ids. It now reads <agent>:<tag>:<session>. The new provider-fakes-e2e CI job runs the six tests. The other five reports are the same as before the move. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Show the agent's models in Check agent Check agent for a custom ACP agent now opens one session/new in the empty temporary folder, with no MCP servers, and returns the model names that the session lists. A failed session/new (an agent that needs sign-in first) gives no models and does not fail the check. The registry install check still sends initialize only. The check result has a models field (max 256 names). The dialog shows the count and the first six names. E2E: test:custom-agents (install check sends initialize only; Settings check lists Fast and Deep and stops the process; sign-in agent passes with no models). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Give the model wait in the Check agent semaphore comment Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 19 days ago | |
Start provider CLIs with the login shell PATH (#2713) * Start provider CLIs with the login shell PATH An app that Finder, the Dock, or a login item starts has a short PATH. An npm Codex is a `#!/usr/bin/env node` script, so `codex --version` and the app server failed with exit code 127, and OpenBot reported that Codex was not found or could not start. - Move the cached login-shell PATH (loginSearchPath) and agentLaunchPath to cli.ts, and use them for CLI lookup instead of `command -v`. - Run the version probe with the CLI's launch PATH, and keep it in the CLI info (launchPath). The Codex app server, sign-in and sign-out, Muse, Pi and ACP processes start with it. - On macOS, use $SHELL when it is a known shell, not always zsh. - Search ~/.npm-global/bin, ~/.volta/bin, ~/.bun/bin and the pnpm home. - Keep the resolver's catalog message in the provider status, and say whether the managed copy or the user's copy of Codex or Claude did not start. Error codes and states do not change. - On Windows, pick a `where` result that PATHEXT can run. Add the test:codex:login-path E2E script. Fixes #2686 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Keep the CLI folder on PATH for Claude sessions and xterm sign-in - Claude sessions start with the CLI's launch PATH, so a system Claude found in a fallback folder keeps its own folder, as in the version check. - The Pi sign-in in xterm on Linux gets the launch PATH. - A folder with the CLI's name is not a CLI: lookup now checks for a file. - Run test:codex:login-path in CI. - Do not export loginShellCommand. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 19 days ago | |
Migrate service runtimes to Effect 4.0.0 (#1283) * refactor: migrate service runtimes to Effect 4 * refactor: make Effect the canonical service runtime * fix: preserve typed Effect failures and align CI test boundaries * test: await Effect test boundaries and lower lint baseline * fix: handle early provider exit and retain remote send errors * Simplify Effect composition and failure handling * Fix review findings in hosted sites, updates and WebRTC requests - Release a hosted-site idempotency claim on any failure, defects included. - Keep update downloads running after stop(), as before the migration. - Clean up a pending WebRTC request when the send is interrupted. - Compose the account switch in one Effect, without nested runPromise. - Remove single-effect generators in the WebRTC client transport. - Put the atomic-write JSDoc on writeFileAtomically; reuse writeJsonFileAtomically. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Run Effects at Promise boundaries through one runCauseEffect Replace the inline Effect.runPromise(x.pipe(Effect.mapError((error) => error.cause))) calls and the per-domain runners (runBrowserEffect, runMcpEffect, runArchiveEffect, runRemoteWorkflow, runDesktopEffect, runProviderClientEffect, runTestEffect) with runCauseEffect in src/backend/effect-boundary.ts. Behavior is unchanged: expected failures reject with their cause, and defects and interruption reject as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Mark concurrent test starts as not awaited runCauseEffect returns a Promise, so Biome now finds the operations that these tests start and do not wait for. Mark each with void. The start stays concurrent, and a rejection still fails the run. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Send CentralAuth requests through the network-block detector The merge of main wrapped the options fetch, but CentralAuth sends its requests through the Effect transport. Build the transport from the wrapped fetch, and run the new test through Effect. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Run each account API entry point as one Effect Route handlers, the browser API, the Worker pre-routing and the cron now run one Effect each. Request body readers, the SMTP socket close and the remaining R2 and D1 calls are Effects. The skill icon read is now part of its Effect; before, the Effect returned a Promise. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Run the remaining service runtime in Effect Main: the automation server, Team API server start and stop, duplicate requests, Dynamic Island, the Computer Use highlight, trace flushes, update background work, sibling scans, upload slots, server mode, window bounds, attachment archives and uploads, and remote transport callbacks are Effects forked into their owner's scope. Backend: usage limit hooks and the channel requeue are Effects. Team client: request frames, Signal notices and connection failures run in the peer's work scope, and recovery loops inside its run. Site router: one Effect per request. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Run the picture-in-picture and permission help windows in Effect Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Remove the unused analytics runner The trace file forks its flush into its own scope now, so nothing calls runAnalytics. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Keep cron jobs, session ends and host starts complete after a failure - The cron waits for every job before it reports the first failure, so a failed delivery does not stop retention or a site cleanup midway. - A connection failure ends the logical session in an uninterruptible fork, so dispose does not stop it. - A dependency that throws while the Team API listener starts fails the start, so the host stops the listener. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 1 month ago | |
Show a plain message when Grok ends a turn with only reasoning (#6124) Grok CLI fails the prompt with `Internal error: "empty response from model (reasoning_only)"` when the model sends no answer. OpenBot showed that text in the chat. The Grok flavor now names this failure, and the turn ends as a turn with no answer does: the error.provider.acpNoAnswer message, or no error when the turn does not need an answer. The cause stays empty_answer. Fixes #6073 Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 17 hours ago | |
Show the models of the Mistral account in the model picker (#5750) * Show the models of the Mistral account in the model picker Mistral Vibe 3.1.0 lists only Mistral Medium 3.5 and the models of ~/.vibe/config.toml. Its model catalogue gets the models of the account from Mistral, but it is off by default. - Start each vibe-acp process with VIBE_EXPERIMENTAL_ENABLE_MODEL_CATALOG=true. - Keep the Mistral model probe session open for 5 s. A close stops Vibe's catalogue request, and then Vibe saves no catalogue for the next session. - Read the Mistral model list once more 5 s after each start (catalog.relistAfterStart), so the picker gets the catalogue on a cold cache. partialListAtStart keeps agents on a catalogue model. - Keep model_catalog_cache.json read-only for confined processes, as managed_config_cache.json. Refs #5712 * Correct the Mistral catalogue cache path and its confinement note PRIVACY.md now names the cache in ~/.vibe or $VIBE_HOME. The confinement comment says which process saves the catalogue and which reads it. | 2 days ago | |
Show the first crash line when an ACP agent stops (#3472) A crash handler such as the one in the Gemini server ends its report with "RAW: Raising signal 6". OpenBot showed only this last stderr line, so the error did not say why the agent stopped. The exit detail is now the first line of the crash report, when there is one. Refs #3468 Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 13 days ago | |
Show the Gemini service message when a proxy returns an HTTP status line (#5760) * Show the Gemini service message for a proxy's HTTP 502 status line Antigravity can end a turn with "Agent execution error: iterateResponseStream: invalid stream chunk: "HTTP/1.0 502 Bad Gateway ...". The 5xx pattern needed the code directly after "HTTP", so the "/1.0" version stopped the match, and the chat showed the raw text. Let an HTTP version come before the code, and match "bad gateway" and "gateway timeout". Bare numbers still do not match. Fixes #5752 * Match an HTTP version in Antigravity 429 status lines; remove unanchored gateway words A 429 status line with the HTTP version, such as "HTTP/2 429", now shows the rate limit message. The words "bad gateway" and "gateway timeout" can come from a tool or an MCP server, so they do not select the service failure message. The version form is the same as in classifyFailure. | 1 day ago | |
Show an error when a custom ACP agent ends a turn with no answer (#5880) * Show an error when a custom ACP agent ends a turn with no answer A custom ACP agent can end a prompt as done (`end_turn`) with no text, no tool call and no reasoning, for example when its context is full. OpenBot recorded a successful turn, so the chat showed nothing and the user could not know why. Custom agents and Oh My Pi now fail such a turn with "<agent> ended the turn with no answer", as OpenCode already does. A turn with reasoning, a tool-only turn and a turn that may stay silent (`answerOptional`) still succeed. The failure does not start a backup model. The audio and image fakes now answer with text, because an empty answer is now a failed turn. Fixes #5110 * Keep stopped, plan and compaction turns of custom agents successful Review fixes for the empty-answer rule of custom ACP agents: - A stopped turn ends as interrupted, also when the agent answers the cancel with `end_turn`. Before, it showed the no-answer error. - The custom rule fails a turn only when the agent sent no update for it. A plan, a thought, a tool call or a compaction shows in the chat, so that turn succeeds. A `/compact` turn whose answer is only a compaction now counts as answered. OpenCode keeps its own rule and messages. | 1 day ago | |
Show a plain message when Grok ends a turn with only reasoning (#6124) Grok CLI fails the prompt with `Internal error: "empty response from model (reasoning_only)"` when the model sends no answer. OpenBot showed that text in the chat. The Grok flavor now names this failure, and the turn ends as a turn with no answer does: the error.provider.acpNoAnswer message, or no error when the turn does not need an answer. The cause stays empty_answer. Fixes #6073 Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 17 hours ago | |
| 2 days ago | ||
Let agents save pasted API keys and passwords to the Vault (#4986) * Let agents save pasted API keys and passwords to the Vault Add the openbot.save_api_key tool. It saves a key that the agent has, such as one the user pasted in the chat, to the Vault. Every agent then can read the key file. The agent's own key with the same name is deleted. Let save_vault_item kind=login add a login with website, username and password, and change the password of a saved login. Both tools register the value for redaction when the call arrives, show a Vault note in the chat, and do not work in an external chat. Closes #4874 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Let only a person's chat message save a key or password Review fixes for the Vault save tools: - save_api_key and a save_vault_item password work only when a person in this computer's OpenBot chat started the turn. A team member, a routine, another agent or a web page cannot replace them. - Register the value for redaction before the arguments are checked, also for a value shorter than 8 characters. - Report a failure when the agent's own key cannot be deleted. - Report "unchanged" and show no note when a login keeps its password. - Say that another agent's own key with the same name still wins. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Make the Vault save texts shorter and raise the context budget - Make the new tool descriptions, schema fields, instructions and guidance shorter. - Raise tools/agent-context/budget.json by the bytes that remain for the new save_api_key tool and the save_vault_item password fields. - Correct the header of vault-service.ts: agents can now save a password or an API key that they have. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Redact a short value only when the Vault saves it A refused save_api_key or save_vault_item call registered a short value, such as one character, for redaction. That value then hid unrelated text in each log line until restart. The early registration now skips values shorter than 8 characters. The Vault registers a short value when it saves it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 5 days ago | |
| 19 hours ago | ||
Let a pinged person answer an agent's Slack post without a mention (#5910) * Let a pinged person answer an agent's Slack post without a mention When an agent's messaging_post pings a person with <@U…> in Slack, OpenBot records the person in the new projection_messaging_pings table (migration 59). A reply from that person in the post's thread now reaches the Slack Orchestrator without a mention. The Orchestrator gets the post and the name of the agent that sent it as the message that the reply answers. Replies of other people still need a mention until the thread has its link. The link deletes the thread's pings, and deleting an agent deletes its pings. A thread message event that mentions OpenBot is now left to its app_mention event, so the order of the two events does not decide the route. In Slack threads an agent can answer only NO_REPLY when a message needs no answer. OpenBot then deletes the "Working on it…" post and keeps the reaction. Fixes #5565 * Hear only the pinged people in a thread that a ping opened In a Slack thread where an agent's post pinged people, a reply without a mention now counts only from a pinged person, also after the first reply gives the thread its link. Other people reach the Orchestrator only with a mention. The ping rows now stay with the link, and deleting an agent deletes the pings of its threads and its pings of threads that no reply opened. A thread that a person opened with a mention still hears everyone. Refs #5565 * Fix the local review findings of the Slack ping replies - An answer in a thread that a ping opened records the people that it pings, so they can reply without a mention too. - MessagingThreads.recordPings does the link check and the save in one Effect.try, and logs a failure. A database error after the post no longer fails the tool call. - The Orchestrator reads its own pinging post as its own, not as a teammate's post. - The stored post uses the prompt's REPLY_CHARACTERS limit. - The E2E reads each dropped reply again after a later reply arrives. Refs #5565 * Give a pinged person's first reply with a mention the post as context A first reply that mentions OpenBot comes only as app_mention, so the receive path did not read the thread's pings. The reply went to the workflows, and the Orchestrator did not get the post that it answers. The receive path now reads the pings for each Slack thread message with no link. The E2E covers this case. Refs #5565 | 1 day ago | |
Add essential memories and local memory search (#1779) * Add essential memories and local recall * Keep memory negotiation within released capability limits * Keep memory editor controls visible in long lists * Align memory fixtures and optional fields with the new behavior * Apply staged selection to saved duplicate memories * Preserve user changes when remembering a pinned duplicate * Apply memory merge selection to the surviving entry | 1 month ago | |
Review agent memories in the background (#3747) * Review agent memories in the background Agents now save lasting facts from their chats and side threads. A background review starts after a number of completed turns, after a pause, when the user clears a chat, and when a side thread closes. It runs on the provider's smaller default model when the agent's model costs more. Memory search now also finds other forms of a word. Server settings > General > Memory review sets the cadence: every 10, 20 or 40 turns, or Off. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Raise the default memory limit per agent to 256 Background reviews add memories without a request. The old default of 64 fills fast with one fact per entry. Only essential memories enter each prompt, within their own budget, so a larger limit adds no prompt cost. A limit that the user set stays as it is. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Keep the memory review from changing what it cannot see - A review cannot forget or change a memory that it sees only redacted. - A review cannot add the text of a saved memory again. This would change the inclusion that the user set. - When a user turn takes the spare client while the model list loads, the review waits and reads those turns later. It does not lose them. - A review does not read the turn that is active when it starts. It reads that turn after the turn ends. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Say in PRIVACY.md that a review can use the lower-priced default model Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Remove the exports that only the memory review uses Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Review memories on the agent's own provider account - The review client signs in to the agent's provider account, as the side question does. Before, it used the provider's default sign-in. - Make the memory search instruction and tool description shorter, so that the agent context stays in its byte budget. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Let an admin set the memory review of a joined server Add the memory-review-v1 Team API capability. An owner or admin of a joined, hosted or self-hosted server reads and sets how often its agents review their memories, or turns the reviews off. The setting row uses the scoped host-admin calls for this computer and for a joined server, in place of the unreleased app-level calls. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 12 days ago | |
Run agent routines in the main chat again, with a switch for a separate thread (#5156) * Run agent routines in the main chat again, with a switch for a separate thread Since 0.61.0 each agent routine ran in its own thread, so a routine that must report back hid its report from the main chat. Agent routines now run in the main chat again. A new "Run in a separate thread" switch moves one routine to its own thread. - Migration adds projection_agent_routines.run_in ('chat' or 'thread'). All saved routines get 'chat'. - The scheduler, the digest and the "Open thread" link use the routine thread only for 'thread' routines. - The switch shows only on the local host. The Team API does not carry the field, so a save from a remote client keeps the saved value. - The create and update routine tools accept runIn. Fixes #5073 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Use the run place of each routine run in all routine paths - Give the routine thread the reply digest only when the run ran in that thread. - Read the routine record one time for each run. - Let the webhook routine tools set `runIn`. - Find webhook routines too when the chat decides on the "Open thread" link. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fit the routine run place to the agent context budget and the routine E2E tests The runIn text in the four routine tool schemas is shorter. The schema baseline goes up by 588 bytes, for one optional field in four tools. The routine session and routine failure report E2E tests check runs in a routine thread, so their routines now set runIn to thread. The routine session test also checks that a routine with no run place answers in the chat and gets no thread. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 5 days ago | |
Measure the storage of a hosted server against its plan (#6264) * Measure the storage of a hosted server against its plan The account server sends the plan storage of a hosted server in the host list. The desktop storage card and Server settings > Storage use it. They do not count the 22 GB base image, and the card shows when less than 10 GB of the plan is free. Agents on a hosted server get one short line about the plan storage. * Correct the release notes for the plan storage warning * Read the hosted state of a host from its plan, with one lookup | 14 hours ago | |
Let a pinged person answer an agent's Slack post without a mention (#5910) * Let a pinged person answer an agent's Slack post without a mention When an agent's messaging_post pings a person with <@U…> in Slack, OpenBot records the person in the new projection_messaging_pings table (migration 59). A reply from that person in the post's thread now reaches the Slack Orchestrator without a mention. The Orchestrator gets the post and the name of the agent that sent it as the message that the reply answers. Replies of other people still need a mention until the thread has its link. The link deletes the thread's pings, and deleting an agent deletes its pings. A thread message event that mentions OpenBot is now left to its app_mention event, so the order of the two events does not decide the route. In Slack threads an agent can answer only NO_REPLY when a message needs no answer. OpenBot then deletes the "Working on it…" post and keeps the reaction. Fixes #5565 * Hear only the pinged people in a thread that a ping opened In a Slack thread where an agent's post pinged people, a reply without a mention now counts only from a pinged person, also after the first reply gives the thread its link. Other people reach the Orchestrator only with a mention. The ping rows now stay with the link, and deleting an agent deletes the pings of its threads and its pings of threads that no reply opened. A thread that a person opened with a mention still hears everyone. Refs #5565 * Fix the local review findings of the Slack ping replies - An answer in a thread that a ping opened records the people that it pings, so they can reply without a mention too. - MessagingThreads.recordPings does the link check and the save in one Effect.try, and logs a failure. A database error after the post no longer fails the tool call. - The Orchestrator reads its own pinging post as its own, not as a teammate's post. - The stored post uses the prompt's REPLY_CHARACTERS limit. - The E2E reads each dropped reply again after a later reply arrives. Refs #5565 * Give a pinged person's first reply with a mention the post as context A first reply that mentions OpenBot comes only as app_mention, so the receive path did not read the thread's pings. The reply went to the workflows, and the Orchestrator did not get the post that it answers. The receive path now reads the pings for each Slack thread message with no link. The E2E covers this case. Refs #5565 | 1 day ago | |
Add fast mode for Claude agents (#6261) * Add fast mode for Claude agents Claude agents can use fast mode from the model picker when Claude Code reports that the model supports it. The turn sets the fastMode flag setting of the claude process. Before fast mode turns on for a Claude agent, a dialog asks the user to confirm the higher price. When Claude Code runs a turn at the standard speed, the chat shows the reason once. * Turn off fast mode on a provider change A provider change sets fastMode to false, so a Codex agent with fast mode does not use Claude fast mode without the price confirmation. The first Claude turn of a process also sends fast mode off, because a Claude settings file can start the process in fast mode. A pending confirmation ends when a different agent opens. The chat warning is marked as sent only when it was sent. The release notes file now has the branch name. * Turn off fast mode one time for Claude agents Before this release only Codex read fastMode. A Claude agent has the value true only from a provider change, and the user never confirmed the Claude price for it. At the first start of this release, the agent store sets fastMode to false for each Claude agent. The receipt of the command is the marker that stops a second run. * Add the fast mode resets to the release note * Keep fast mode across a backup model, and hide Claude fast mode from members | 14 hours ago | |
Let a pinged person answer an agent's Slack post without a mention (#5910) * Let a pinged person answer an agent's Slack post without a mention When an agent's messaging_post pings a person with <@U…> in Slack, OpenBot records the person in the new projection_messaging_pings table (migration 59). A reply from that person in the post's thread now reaches the Slack Orchestrator without a mention. The Orchestrator gets the post and the name of the agent that sent it as the message that the reply answers. Replies of other people still need a mention until the thread has its link. The link deletes the thread's pings, and deleting an agent deletes its pings. A thread message event that mentions OpenBot is now left to its app_mention event, so the order of the two events does not decide the route. In Slack threads an agent can answer only NO_REPLY when a message needs no answer. OpenBot then deletes the "Working on it…" post and keeps the reaction. Fixes #5565 * Hear only the pinged people in a thread that a ping opened In a Slack thread where an agent's post pinged people, a reply without a mention now counts only from a pinged person, also after the first reply gives the thread its link. Other people reach the Orchestrator only with a mention. The ping rows now stay with the link, and deleting an agent deletes the pings of its threads and its pings of threads that no reply opened. A thread that a person opened with a mention still hears everyone. Refs #5565 * Fix the local review findings of the Slack ping replies - An answer in a thread that a ping opened records the people that it pings, so they can reply without a mention too. - MessagingThreads.recordPings does the link check and the save in one Effect.try, and logs a failure. A database error after the post no longer fails the tool call. - The Orchestrator reads its own pinging post as its own, not as a teammate's post. - The stored post uses the prompt's REPLY_CHARACTERS limit. - The E2E reads each dropped reply again after a later reply arrives. Refs #5565 * Give a pinged person's first reply with a mention the post as context A first reply that mentions OpenBot comes only as app_mention, so the receive path did not read the thread's pings. The reply went to the workflows, and the Orchestrator did not get the post that it answers. The receive path now reads the pings for each Slack thread message with no link. The E2E covers this case. Refs #5565 | 1 day ago | |
Let a pinged person answer an agent's Slack post without a mention (#5910) * Let a pinged person answer an agent's Slack post without a mention When an agent's messaging_post pings a person with <@U…> in Slack, OpenBot records the person in the new projection_messaging_pings table (migration 59). A reply from that person in the post's thread now reaches the Slack Orchestrator without a mention. The Orchestrator gets the post and the name of the agent that sent it as the message that the reply answers. Replies of other people still need a mention until the thread has its link. The link deletes the thread's pings, and deleting an agent deletes its pings. A thread message event that mentions OpenBot is now left to its app_mention event, so the order of the two events does not decide the route. In Slack threads an agent can answer only NO_REPLY when a message needs no answer. OpenBot then deletes the "Working on it…" post and keeps the reaction. Fixes #5565 * Hear only the pinged people in a thread that a ping opened In a Slack thread where an agent's post pinged people, a reply without a mention now counts only from a pinged person, also after the first reply gives the thread its link. Other people reach the Orchestrator only with a mention. The ping rows now stay with the link, and deleting an agent deletes the pings of its threads and its pings of threads that no reply opened. A thread that a person opened with a mention still hears everyone. Refs #5565 * Fix the local review findings of the Slack ping replies - An answer in a thread that a ping opened records the people that it pings, so they can reply without a mention too. - MessagingThreads.recordPings does the link check and the save in one Effect.try, and logs a failure. A database error after the post no longer fails the tool call. - The Orchestrator reads its own pinging post as its own, not as a teammate's post. - The stored post uses the prompt's REPLY_CHARACTERS limit. - The E2E reads each dropped reply again after a later reply arrives. Refs #5565 * Give a pinged person's first reply with a mention the post as context A first reply that mentions OpenBot comes only as app_mention, so the receive path did not read the thread's pings. The reply went to the workflows, and the Orchestrator did not get the post that it answers. The receive path now reads the pings for each Slack thread message with no link. The E2E covers this case. Refs #5565 | 1 day ago | |
Let a pinged person answer an agent's Slack post without a mention (#5910) * Let a pinged person answer an agent's Slack post without a mention When an agent's messaging_post pings a person with <@U…> in Slack, OpenBot records the person in the new projection_messaging_pings table (migration 59). A reply from that person in the post's thread now reaches the Slack Orchestrator without a mention. The Orchestrator gets the post and the name of the agent that sent it as the message that the reply answers. Replies of other people still need a mention until the thread has its link. The link deletes the thread's pings, and deleting an agent deletes its pings. A thread message event that mentions OpenBot is now left to its app_mention event, so the order of the two events does not decide the route. In Slack threads an agent can answer only NO_REPLY when a message needs no answer. OpenBot then deletes the "Working on it…" post and keeps the reaction. Fixes #5565 * Hear only the pinged people in a thread that a ping opened In a Slack thread where an agent's post pinged people, a reply without a mention now counts only from a pinged person, also after the first reply gives the thread its link. Other people reach the Orchestrator only with a mention. The ping rows now stay with the link, and deleting an agent deletes the pings of its threads and its pings of threads that no reply opened. A thread that a person opened with a mention still hears everyone. Refs #5565 * Fix the local review findings of the Slack ping replies - An answer in a thread that a ping opened records the people that it pings, so they can reply without a mention too. - MessagingThreads.recordPings does the link check and the save in one Effect.try, and logs a failure. A database error after the post no longer fails the tool call. - The Orchestrator reads its own pinging post as its own, not as a teammate's post. - The stored post uses the prompt's REPLY_CHARACTERS limit. - The E2E reads each dropped reply again after a later reply arrives. Refs #5565 * Give a pinged person's first reply with a mention the post as context A first reply that mentions OpenBot comes only as app_mention, so the receive path did not read the thread's pings. The reply went to the workflows, and the Orchestrator did not get the post that it answers. The receive path now reads the pings for each Slack thread message with no link. The E2E covers this case. Refs #5565 | 1 day ago | |
Let a pinged person answer an agent's Slack post without a mention (#5910) * Let a pinged person answer an agent's Slack post without a mention When an agent's messaging_post pings a person with <@U…> in Slack, OpenBot records the person in the new projection_messaging_pings table (migration 59). A reply from that person in the post's thread now reaches the Slack Orchestrator without a mention. The Orchestrator gets the post and the name of the agent that sent it as the message that the reply answers. Replies of other people still need a mention until the thread has its link. The link deletes the thread's pings, and deleting an agent deletes its pings. A thread message event that mentions OpenBot is now left to its app_mention event, so the order of the two events does not decide the route. In Slack threads an agent can answer only NO_REPLY when a message needs no answer. OpenBot then deletes the "Working on it…" post and keeps the reaction. Fixes #5565 * Hear only the pinged people in a thread that a ping opened In a Slack thread where an agent's post pinged people, a reply without a mention now counts only from a pinged person, also after the first reply gives the thread its link. Other people reach the Orchestrator only with a mention. The ping rows now stay with the link, and deleting an agent deletes the pings of its threads and its pings of threads that no reply opened. A thread that a person opened with a mention still hears everyone. Refs #5565 * Fix the local review findings of the Slack ping replies - An answer in a thread that a ping opened records the people that it pings, so they can reply without a mention too. - MessagingThreads.recordPings does the link check and the save in one Effect.try, and logs a failure. A database error after the post no longer fails the tool call. - The Orchestrator reads its own pinging post as its own, not as a teammate's post. - The stored post uses the prompt's REPLY_CHARACTERS limit. - The E2E reads each dropped reply again after a later reply arrives. Refs #5565 * Give a pinged person's first reply with a mention the post as context A first reply that mentions OpenBot comes only as app_mention, so the receive path did not read the thread's pings. The reply went to the workflows, and the Orchestrator did not get the post that it answers. The receive path now reads the pings for each Slack thread message with no link. The E2E covers this case. Refs #5565 | 1 day ago | |
Show the tokens and cost of each skill in the Usage page (#5237) * Show the tokens and cost of each skill in the Usage page Add a Skill tab to the Usage breakdown. It shows the loads, cost, share, tokens and agents of each skill. - The backend records each skill load in the new table agent_usage_skill_loads (migration 58). A Claude Skill call names the skill. Other providers read the SKILL.md file with a read tool or a shell command. The tokens come from the size of SKILL.md. - The Claude client sends the call input again with the result, so the completed step names the skill or the command. - Only the local host sends skill rows. The Team API v1 decoder stays the same. - A short breakdown table does not keep the height of the tallest tab. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Count only real skill reads in the Skill tab - Count a SKILL.md path only from a read: the Claude Read tool, an ACP read, or a shell command that shows the file. A write, an edit, a sed -i, an output redirection or a grep is not a load. - Find the read command after a quote, as in the Codex `zsh -lc "sed ..."` form. - Match a whole SKILL.md path for a read tool, and do not match `myskills/`. - Remove only the `openbot:` and `agents:` plugin prefixes. Keep the name of a skill from another plugin; it has no size. - Read the skill files first, then find the agent again and save all loads of the step. - Keep only the skill, command and file fields of an open Claude tool call. - Stack the hidden Breakdown tables over the open one, so they fade out and add no height. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Keep skill loads off the Team API host analytics wire The Team API route sent the whole local report, so remote peers got the new skills field with no capability. Remove it from the route response, and do not read it from a remote report. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Expect the skill loads date index in the usage migration test Migration 58 adds `agent_usage_skill_loads_occurred`. A host-wide skill report also filters by date only, so the test now expects this index with the other two. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 4 days ago | |
Let a pinged person answer an agent's Slack post without a mention (#5910) * Let a pinged person answer an agent's Slack post without a mention When an agent's messaging_post pings a person with <@U…> in Slack, OpenBot records the person in the new projection_messaging_pings table (migration 59). A reply from that person in the post's thread now reaches the Slack Orchestrator without a mention. The Orchestrator gets the post and the name of the agent that sent it as the message that the reply answers. Replies of other people still need a mention until the thread has its link. The link deletes the thread's pings, and deleting an agent deletes its pings. A thread message event that mentions OpenBot is now left to its app_mention event, so the order of the two events does not decide the route. In Slack threads an agent can answer only NO_REPLY when a message needs no answer. OpenBot then deletes the "Working on it…" post and keeps the reaction. Fixes #5565 * Hear only the pinged people in a thread that a ping opened In a Slack thread where an agent's post pinged people, a reply without a mention now counts only from a pinged person, also after the first reply gives the thread its link. Other people reach the Orchestrator only with a mention. The ping rows now stay with the link, and deleting an agent deletes the pings of its threads and its pings of threads that no reply opened. A thread that a person opened with a mention still hears everyone. Refs #5565 * Fix the local review findings of the Slack ping replies - An answer in a thread that a ping opened records the people that it pings, so they can reply without a mention too. - MessagingThreads.recordPings does the link check and the save in one Effect.try, and logs a failure. A database error after the post no longer fails the tool call. - The Orchestrator reads its own pinging post as its own, not as a teammate's post. - The stored post uses the prompt's REPLY_CHARACTERS limit. - The E2E reads each dropped reply again after a later reply arrives. Refs #5565 * Give a pinged person's first reply with a mention the post as context A first reply that mentions OpenBot comes only as app_mention, so the receive path did not read the thread's pings. The reply went to the workflows, and the Orchestrator did not get the post that it answers. The receive path now reads the pings for each Slack thread message with no link. The E2E covers this case. Refs #5565 | 1 day ago | |
Let a pinged person answer an agent's Slack post without a mention (#5910) * Let a pinged person answer an agent's Slack post without a mention When an agent's messaging_post pings a person with <@U…> in Slack, OpenBot records the person in the new projection_messaging_pings table (migration 59). A reply from that person in the post's thread now reaches the Slack Orchestrator without a mention. The Orchestrator gets the post and the name of the agent that sent it as the message that the reply answers. Replies of other people still need a mention until the thread has its link. The link deletes the thread's pings, and deleting an agent deletes its pings. A thread message event that mentions OpenBot is now left to its app_mention event, so the order of the two events does not decide the route. In Slack threads an agent can answer only NO_REPLY when a message needs no answer. OpenBot then deletes the "Working on it…" post and keeps the reaction. Fixes #5565 * Hear only the pinged people in a thread that a ping opened In a Slack thread where an agent's post pinged people, a reply without a mention now counts only from a pinged person, also after the first reply gives the thread its link. Other people reach the Orchestrator only with a mention. The ping rows now stay with the link, and deleting an agent deletes the pings of its threads and its pings of threads that no reply opened. A thread that a person opened with a mention still hears everyone. Refs #5565 * Fix the local review findings of the Slack ping replies - An answer in a thread that a ping opened records the people that it pings, so they can reply without a mention too. - MessagingThreads.recordPings does the link check and the save in one Effect.try, and logs a failure. A database error after the post no longer fails the tool call. - The Orchestrator reads its own pinging post as its own, not as a teammate's post. - The stored post uses the prompt's REPLY_CHARACTERS limit. - The E2E reads each dropped reply again after a later reply arrives. Refs #5565 * Give a pinged person's first reply with a mention the post as context A first reply that mentions OpenBot comes only as app_mention, so the receive path did not read the thread's pings. The reply went to the workflows, and the Orchestrator did not get the post that it answers. The receive path now reads the pings for each Slack thread message with no link. The E2E covers this case. Refs #5565 | 1 day ago | |
Show each Gemini usage limit for its own models (#3073) * Read the Gemini usage of the agent's model group Google can give a Gemini model a weekly limit of its own, such as Gemini 4 Argon. The /usage parser put all Gemini groups together into one reading, so a spent Argon limit showed for Flash, or Argon showed room that it did not have. The parser now reads each quota group with its "Models within this group" list. A read for a model takes the group that lists the model's family. A model that no group lists has no reading. The dock read, with no model, keeps the "Gemini Models" group. Fixes #3055 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Show each Gemini limit on its own usage line The Antigravity usage reading now carries every quota group of the plan by name, with the models that Google lists for it. The usage popover shows one line for each group. The ring stays on the "Gemini Models" group. The message box warning uses the group of the agent's model. The Team API codecs do not send the groups, so remote clients keep one line. Refs #3055 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Keep the usage ring and warning on the right Gemini limit The rail ring shows the windows of the reading itself, not the quota group lines. The warning above the message box uses the reading itself when the agent has no model or no group lists models, as the backend does. A kept account reading is read again when the window of one of its groups resets. Refs #3055 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Use the model's Gemini group in a kept account reading An Antigravity account after the first keeps only the reading taken with no model. The usage limit check now takes the windows of the model's quota group from that reading, so a spent Argon limit holds an Argon agent. The composer uses the same rule. Refs #3055 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 16 days ago | |
| 19 hours ago | ||
Retry a held Windows transfer folder rename before an attachment send fails (#5463) On Windows, antivirus or the search indexer opens the files that a send has just copied into its temporary transfer folder. Windows then refuses to rename the folder with EPERM, and the send failed at once with "OpenBot does not have permission to complete this action". The rename now waits and tries again for about 3 s on EPERM, EACCES and EBUSY, as atomic JSON writes already do. Other systems and other errors still fail at once. Fixes #5458 Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 3 days ago | |
Migrate service runtimes to Effect 4.0.0 (#1283) * refactor: migrate service runtimes to Effect 4 * refactor: make Effect the canonical service runtime * fix: preserve typed Effect failures and align CI test boundaries * test: await Effect test boundaries and lower lint baseline * fix: handle early provider exit and retain remote send errors * Simplify Effect composition and failure handling * Fix review findings in hosted sites, updates and WebRTC requests - Release a hosted-site idempotency claim on any failure, defects included. - Keep update downloads running after stop(), as before the migration. - Clean up a pending WebRTC request when the send is interrupted. - Compose the account switch in one Effect, without nested runPromise. - Remove single-effect generators in the WebRTC client transport. - Put the atomic-write JSDoc on writeFileAtomically; reuse writeJsonFileAtomically. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Run Effects at Promise boundaries through one runCauseEffect Replace the inline Effect.runPromise(x.pipe(Effect.mapError((error) => error.cause))) calls and the per-domain runners (runBrowserEffect, runMcpEffect, runArchiveEffect, runRemoteWorkflow, runDesktopEffect, runProviderClientEffect, runTestEffect) with runCauseEffect in src/backend/effect-boundary.ts. Behavior is unchanged: expected failures reject with their cause, and defects and interruption reject as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Mark concurrent test starts as not awaited runCauseEffect returns a Promise, so Biome now finds the operations that these tests start and do not wait for. Mark each with void. The start stays concurrent, and a rejection still fails the run. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Send CentralAuth requests through the network-block detector The merge of main wrapped the options fetch, but CentralAuth sends its requests through the Effect transport. Build the transport from the wrapped fetch, and run the new test through Effect. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Run each account API entry point as one Effect Route handlers, the browser API, the Worker pre-routing and the cron now run one Effect each. Request body readers, the SMTP socket close and the remaining R2 and D1 calls are Effects. The skill icon read is now part of its Effect; before, the Effect returned a Promise. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Run the remaining service runtime in Effect Main: the automation server, Team API server start and stop, duplicate requests, Dynamic Island, the Computer Use highlight, trace flushes, update background work, sibling scans, upload slots, server mode, window bounds, attachment archives and uploads, and remote transport callbacks are Effects forked into their owner's scope. Backend: usage limit hooks and the channel requeue are Effects. Team client: request frames, Signal notices and connection failures run in the peer's work scope, and recovery loops inside its run. Site router: one Effect per request. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Run the picture-in-picture and permission help windows in Effect Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Remove the unused analytics runner The trace file forks its flush into its own scope now, so nothing calls runAnalytics. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Keep cron jobs, session ends and host starts complete after a failure - The cron waits for every job before it reports the first failure, so a failed delivery does not stop retention or a site cleanup midway. - A connection failure ends the logical session in an uninterruptible fork, so dispose does not stop it. - A dependency that throws while the Team API listener starts fails the start, so the host stops the listener. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 1 month ago | |
Retry a held Windows transfer folder rename before an attachment send fails (#5463) On Windows, antivirus or the search indexer opens the files that a send has just copied into its temporary transfer folder. Windows then refuses to rename the folder with EPERM, and the send failed at once with "OpenBot does not have permission to complete this action". The rename now waits and tries again for about 3 s on EPERM, EACCES and EBUSY, as atomic JSON writes already do. Other systems and other errors still fail at once. Fixes #5458 Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 3 days ago | |
Let a pinged person answer an agent's Slack post without a mention (#5910) * Let a pinged person answer an agent's Slack post without a mention When an agent's messaging_post pings a person with <@U…> in Slack, OpenBot records the person in the new projection_messaging_pings table (migration 59). A reply from that person in the post's thread now reaches the Slack Orchestrator without a mention. The Orchestrator gets the post and the name of the agent that sent it as the message that the reply answers. Replies of other people still need a mention until the thread has its link. The link deletes the thread's pings, and deleting an agent deletes its pings. A thread message event that mentions OpenBot is now left to its app_mention event, so the order of the two events does not decide the route. In Slack threads an agent can answer only NO_REPLY when a message needs no answer. OpenBot then deletes the "Working on it…" post and keeps the reaction. Fixes #5565 * Hear only the pinged people in a thread that a ping opened In a Slack thread where an agent's post pinged people, a reply without a mention now counts only from a pinged person, also after the first reply gives the thread its link. Other people reach the Orchestrator only with a mention. The ping rows now stay with the link, and deleting an agent deletes the pings of its threads and its pings of threads that no reply opened. A thread that a person opened with a mention still hears everyone. Refs #5565 * Fix the local review findings of the Slack ping replies - An answer in a thread that a ping opened records the people that it pings, so they can reply without a mention too. - MessagingThreads.recordPings does the link check and the save in one Effect.try, and logs a failure. A database error after the post no longer fails the tool call. - The Orchestrator reads its own pinging post as its own, not as a teammate's post. - The stored post uses the prompt's REPLY_CHARACTERS limit. - The E2E reads each dropped reply again after a later reply arrives. Refs #5565 * Give a pinged person's first reply with a mention the post as context A first reply that mentions OpenBot comes only as app_mention, so the receive path did not read the thread's pings. The reply went to the workflows, and the Orchestrator did not get the post that it answers. The receive path now reads the pings for each Slack thread message with no link. The E2E covers this case. Refs #5565 | 1 day ago | |
Let an agent wait for an event and continue the request (#1910) * Let an agent wait for an event and continue the request An agent can stop a request and wait for something outside its turn, such as a build or a nightly run. It opens a wait with openbot.await_event and closes it with openbot.close_wait. The next routine run of the agent, or a local script run that names the wait, wakes it. The input of that run repeats the request and the agent's note. The chat shows the open waits above the composer. The menu of each wait shows its request or cancels it. Migration 34 adds the agent_waits table. Closes #1837 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Refuse a wait when the agent has no routine Only a routine run wakes a wait. An agent with no routine could open a wait that stays open until the user cancels it. await_event now tells the agent to create a routine first. The instruction and the tool text no longer name a reply as a reason to wait, because a reply does not wake a wait. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 28 days ago | |
| 4 days ago | ||
Add a Vault for saved logins, cards and contact info (#3047) * Show focus with a grey border, not a blue glow Focused fields get a grey border with no ring. Focused buttons and other controls get a 1px grey ring. Invalid fields keep their red ring. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Add the Vault: saved logins, cards and contact info for agents Server settings has a Vault page after General. It keeps logins, payment cards and contact info on this computer, encrypted by the operating system. Agents sign in with saved logins, fill a saved card (also in the card frames of payment providers) and type saved contact info. Agents never see a password, a card number or a security code. Agents can add and change contact info, change the non-secret fields of logins and cards, and delete items. Save to Vault on the secure password card keeps the password that the user types. The Vault is on the local server only. A click on a label whose decorative layer covers its radio button now selects the radio. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix Vault review findings - Read a saved website such as example.com:8443 as a host and port, so a login saved from a page with a port can be edited. - Give agents the Vault instructions only when the Vault can be read. - Use the same rule for a complete contact in the dialog and in main. - Use one card brand rule in the dialog and in main. - Register imported secrets for redaction with one pattern rebuild. - Refuse a delete of an item that is not in the Vault. - Route the agent Vault tools with a switch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Remove Vault exports that no other file uses Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Add the Korean Vault catalogs Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Keep each saved login bound to its website An agent can no longer change the website of a saved login. It could move a login to a different site and then fill its password there. An agent can still change the username. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Ask the owner before an agent fills a card on a new site The first fill_card of a card on an HTTPS site waits for the server owner's approval. The card keeps the approved page and the origins of its card frames, so later fills there need no approval. Team members, messaging services and local scripts cannot give this approval. The Vault card dialog lists the approved sites, and the user can remove one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Keep the tab recording when a card fill waits for approval The recording of a tab is discarded only after the owner's approval lets the fill type the card. A fill that waits, or that the owner declines, keeps the recording. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 16 days ago | |
Make the agent browser read as Chrome to bot checks (#5525) * Make the agent browser read as Chrome to bot checks Bot checks found the built-in browser from page values and input: an outer size smaller than the page in hidden or minimized tabs, an empty window.chrome and none in new frames, a fixed 1200x800 hidden viewport, typing with no key events, and clicks with no pointer path. - Add a tab preload (src/preload/browser-page.ts) with no IPC and no exposed world. It sets outer size, screen position and window.chrome in the page world of each frame. verify:preload checks the boundary. - Give hidden tabs the panel size, or the main display size before the panel has one. - Type text up to 128 characters as key events with US key codes. Empty and long text still use Input.insertText. - Move the pointer along a short path and hold the button 30-60 ms. A path move that is not acknowledged in 100 ms ends the path, so hidden tabs with no frames are not slower. - Add the browser fingerprint E2E and run it in CI. Update the smoke and fill-fields E2E for key events. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Keep agent tab values native across frames and in code editors - Frames of one page share one map of replaced functions. Before, the toString of one frame showed the source of another frame's chrome.loadTimes and an unnamed outerWidth getter. - Remove the Proxy trap frame from the stack of a replaced function that throws, so the stack is the same as in Chrome. - Insert text with a line break, a bracket, a quote or "<" in one piece. Code editors add a closing bracket or tag for a typed key. - Correct the backgroundThrottling comment: a hidden tab runs at full rate until the idle sweep puts it to sleep. - Add three fingerprint E2E cases for these changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Remove an unused export and report page state when background typing fails knip found that `pointerPath` has no use outside its file. The browser smoke background check failed on CI with no reproduction in Docker (arm64 and x64, Xvfb), so its error now gives the keys, value, focus and size that the page saw. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Report focus that moves while targeted text is typed Short text now arrives one key at a time, so a key or input handler can move focus to another element part way through. The focus check now runs after the last key, and its error says that part of the text can be in another element. The submit error no longer says that the text is in the field. The new error is in English and Arabic; the other languages show English until a translator adds it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Insert typed text when key events do not reach a tab On Linux under Xvfb, Chromium can drop the key events of a hidden tab with no error, but it accepts inserted text. After the first key, the type action now checks that the key reached the page or changed the text. If not, it inserts the rest of the text, as before keys. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Check typing focus between keys, not after the last key A field that moves focus after its last character, such as a one-time code box, made type fail after all the text was typed. Focus is now checked before each key or insert after the first one, and before Enter. The submit error has its text from main again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Save the field text again when text starts with no US key Text such as "Émile" inserts "É" before the first key. The key check then saw changed text and read a dropped key as arrived, so the rest of the text was lost with no error. The field text is now saved again before that first key. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Give the browser smoke backstop room for human-like clicks Each agent click now moves along a path and holds the button, which adds about 100 to 150 ms. The main flow took 114 to 120 s on CI, at the 120 s backstop. The backstop is for a hung phase, not a budget, so it is now 180 s. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Check the deadline between typed keys Targeted type now sends short text one key at a time. When the deadline passed part way, the action only said that it timed out, and a caller that typed again entered the text twice. Each key now checks the deadline first and reports how many characters the page already has, as focused typing does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 3 days ago | |
Name the candidates of an ambiguous browser target (#4278) * Name the candidates of an ambiguous browser target When a browser action target matches more than one element, the engine still sends no input. The error now lists up to five candidates. Each line has the ref from the last snapshot when there is one, the role or tag, the label, and the text of the candidate's item when labels repeat. The CSS error now also lists candidates. Before, it gave none, and the semantic error gave internal node IDs that the agent could not use. Candidate text is visible page text and goes through the card scrub. Add an E2E on a loopback lesson calendar, where five lessons share a layout attribute, and two calendar tasks to bench:browser-tools that score the registration that the server recorded. Fixes #4264 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Make the candidate list of an ambiguous target safer and less expensive - wait_for drops the error of each poll, so it does not describe the candidates. - After the deadline, the error names the candidates without their text. - A candidate that cannot be described or resolved is left out. The others keep their text. - A candidate that is not rendered shows no text. innerText gives all of textContent for it. - The twin check compares each peer one time, and only when it has the same shape. - The CSS and text paths use one helper to describe a match. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Keep the numeric bench task selection on the public-site tasks --tasks=<n> selected the calendar tasks, because they are first in TASKS. The default and a number now select from the public-site tasks only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Let the bench stop when its setup fails after the calendar starts The calendar server starts before the try that closes it. When a truth fetch, the Auth API or the app launch failed, the server kept the process alive. The server is now unref'd, so it does not keep the process alive. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 9 days ago | |
Fix copy and paste in the browser of a remote server (#1607) * Fix copy and paste in the browser of a remote server The live view of a remote tab sent Ctrl/Cmd+C and V to the host as plain keys with no key code, so the page did no edit command. Also, the host clipboard is not the clipboard of the member. - Add the optional `browser-view-clipboard` capability. The client sends `paste` text and a `copy` request. The host inserts the text with `Input.insertText` and sends the selection back as `copied`. The host does not read or write its own clipboard. - Do not prevent the clipboard shortcuts in the live view, so the client window fires its own copy, cut and paste events. - Send the `selectAll` command for Ctrl/Cmd+A, and forward Cmd/Ctrl+A from the desktop window to a focused live view. - Show a message when the host is older, or when the text is too long. Fixes #1597 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Keep the user's clipboard safe in the remote browser copy - Start a copy in the key press with a ClipboardItem that waits for the host answer. Do not cancel the copy event: an empty cancelled copy clears the clipboard. The host answers every copy, and an empty answer does not change the clipboard. - Send Ctrl/Cmd+C, V and X to the page as keys again, so a web terminal still gets Ctrl+C. Match the shortcut letter by key position on layouts that are not Latin. - Fire a paste event on the focused element before the text is inserted, and insert it only when the page does not cancel it. - Do not delete a cut that is too long to send, and stop a too-long selection in the page before CDP sends it. - Read the selection in an open shadow root. - Send only the key down for the desktop select-all, so the real key up is not sent two times. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Delete a cut only after the text is on the clipboard - Split a cut into a copy and a `cut` input. The client sends `cut` only after the clipboard write succeeds. The host deletes only when the selection is still the same text. - Run the inputs of one live view one at a time, in order, so a key typed after a paste does not land first. - Find focus in frames of the page's own origin, so the paste event, copy and cut reach a field there. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Follow focus into cross-origin frames for remote paste and copy The automation world of the page cannot enter a frame of another origin. The host now asks CDP for the frame ID of the focused frame element with `DOM.describeNode`. It then uses that frame's target session when the frame runs in another process, or an isolated world of that frame when it runs in the same process. It does this again for each nested frame. The paste event, copy and cut now reach a field in such a frame. The live-view smoke test clicks into a cross-origin frame and checks the paste event, copy and cut there. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 1 month ago | |
Fill saved authenticator codes into code fields with no one-time-code mark (#6092) * Fill saved authenticator codes into code fields with no one-time-code mark A Vault, 1Password or Bitwarden code filled only fields with autocomplete="one-time-code". Older Keycloak pages (before 2024-09) and many other sites name the field otp with autocomplete="off", so the code card opened each time. A text, tel or number input with a strong code word (otp, totp, 2fa, mfa, authenticator, one-time code, verification code, two-factor) in its id, name or aria-label now counts as a code field. Backup and recovery code fields do not. These attributes are in the target fingerprint, so a change before the fill still stops it. The origin, agent-script and URL navigation guards do not change. Fixes #6040 * Match joined code words such as totpmfa in vault code fields The public TOTP test page authenticationtest.com names its field totpmfa, which the whole-word match missed. Match totp anywhere, as Bitwarden does, and joined otp, 2fa and mfa words. Fixes #6040 * Keep vault codes out of SMS, email, phone and recovery fields A name check found otp in smsOtp, twoFactorPhone and totpbackup. Reject names that also have backup, recovery, mail, phone, sms, name or label anywhere, and give the agent the full rule when it skips a fill. Fixes #6040 | 18 hours ago | |
Detect AWS WAF only on its interstitial page (#6020) Sites that use the AWS WAF integration SDK, such as Sketchfab, load challenge.js on normal pages. The detector counted any awswaf.com script on a short page as a check, so the agent got "needs-user" on a normal page, and a takeover could not clear it. Now only the interstitial layout counts: the script in the head and #challenge-container or #captcha-container as a direct child of the body (as in apify/crawlee PR 4184). Fixes #5999 | 20 hours ago | |
Refuse cookie banners in agent browser tabs (#5366) * Refuse cookie banners in agent browser tabs An agent tab now clicks the reject button of a cookie banner, so the agent does not spend turns on it. The script is our own code. It runs in an isolated world of the main frame, never accepts, and stops after one click or 15 seconds. Tabs that a person opens or controls, and secret tabs, keep their banners. Add a cookie-banner scenario to the browser E2E, with a late banner, a banner in shadow roots, an accept-only banner and a person's tab. Add two bench tasks that run only by id. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Stop cookie refusal when a person takes the tab - A takeover, user control or secret entry stops the script of the current page. - The script also watches open shadow roots that fill in later. - Only the word "cookie" counts as banner text, so sign-in and terms dialogs keep "Decline". - The smoke imports CONSENT_WORLD and checks that a takeover stops the script. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Run the cookie-banner scenario in the full browser smoke CI runs test:browser with no scenario, so the full run must call it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 4 days ago | |
Split the tab operations and control sessions out of BrowserHost (#949) - browser-tab-operations.ts: the per-tab queue and the deadlines that bound one unresponsive renderer (enqueue, bounded engine operations, unwind, snapshot, agent actions, page evaluation). - browser-control-sessions.ts: the agent control sessions, their idle timers and listeners. - browser-host-tab.ts: the host's tab record and the pure tab helpers. BrowserHost keeps the tabs, the views, the session setup, the navigation and popup policy, and secure input. browser-host.ts goes from 2354 to 1917 lines. The types ratchet for browser-host.ts goes from 19 to 10. | 1 month ago | |
Let the agent answer page dialogs that its action opened (#4148) * Let the agent answer page dialogs that its action opened When an agent input action opens a page alert or confirm, the action now returns the dialog: an opaque id, the type, and the redacted message. The new handle_dialog tool answers it through CDP, then closes Electron's box with the internal -cancel-dialogs event. A run_script step stops with status "dialog". A person's dialog, a dialog under an external debugger, and a dialog that is open when the turn ends stay in the native box for a person. Close and shutdown detach the debugger and close the box, also when Electron shows the box after the close. scripts/browser-dialogs-e2e.ts (21 cases) runs in the CI browser job. It fails if an Electron upgrade changes -run-dialog or -cancel-dialogs. The agent context budget increases because of the new tool description and schema (catalog.descriptions +123, catalog.schemas +245, visible-tools +31). Fixes #4015 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Close a page dialog box when its tab closes Electron keeps its native dialog box open after the page closes, and nobody can answer it then. The engine now cancels every box of the tab when it is destroyed, also a box that shows after the close. A new fixture case checks a person's dialog. Refs #4015 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Keep the dialog watch attached for the whole agent action The engine keeps its debugger attached with Page on while a dialog watch runs. Thus it sees a dialog that a page load or a timer opens after the input command returns. A tab queue drain ends the watch, also after an interruption. Other review fixes: - A dialog that opens after the tab starts to close goes to the native box and does not hold the tab queue. - releaseDialogs signals the waiters. - handle_dialog uses Effect.timeoutOption for the next dialog. - A handoff signals only when something changed. The E2E fixture adds two cases: a dialog from a timer after the click, and a dialog during a navigation. Refs #4015 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Remove the export of the dialog type that only browser-cdp uses Knip reports BrowserDialogType as an unused export. Refs #4015 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 10 days ago | |
Add the agent's browser to the mobile app (#1654) * Add the agent's browser to the iPhone app The phone shows an agent's browser tab live, and the user can use it with touch: - Pointer that shows the page's cursor. Trackpad mode is on by default. - Clicks, drags, scroll, pinch zoom, and right-click with the page's menu on the phone. - Keyboard, and copy and paste with the phone's clipboard. - Back, forward and reload. A tab list to switch, open and close tabs. - Help sheet with each gesture. Host changes, each an optional Team API capability that only a client that asks for it gets: - browser-view-cursor: the page's cursor as a text message. - browser-view-context-menu: the menu of a right-click goes to the phone, not to the host's screen. - browser-view-viewport: the page keeps the size the phone asks for while the phone watches. - A drag from a live view holds the mouse button. The phone uses the browser-view-clipboard messages of main. The account server's "browser" mobile feature flag turns the phone's browser off for an app version. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix review findings, gesture state and Android menu of the mobile browser - The touch callbacks of the live view run as worklets. From the JS thread, the gesture state calls did nothing and logged a Reanimated warning for each touch. - The input queue moved into the live view hook. Queued moves and scroll steps go before the phone reports a frame as drawn: the host forgets older frames then, and dropped that input. A copy request goes after the input that waits. - On Android, the page's right-click menu is the native @expo/ui menu, opened from code at the pointer, with all actions. Before, an alert showed only the first two. - The browser flag is on for Android up to 1.2.0, as for iOS. - Exports that nothing else uses are no longer exported (knip). - The release note files have the branch name, and the notes say mobile app, not iPhone app. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Keep the copy answers in order after a timeout A copy that timed out removed its waiter. The host answers each copy in order, so its late answer went to the next copy, and the phone copied the old selection. The waiter now keeps its place, and its rejected promise ignores the late answer. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Send one Backspace for each character a reader sees The phone keyboard compared the hidden field by code points. A deleted flag or joined emoji sent one Backspace for each code point, and the page, which deletes a whole character for each Backspace, also deleted the text before it. The comparison now uses graphemes. The chat's grapheme helper moves to the shared library, so both use one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 1 month ago | |
Make browser tool results smaller so web work compacts less (#4115) * Make browser tool results smaller so web work compacts less - Input actions return a short status. Pass snapshot: true for a snapshot. - A snapshot gives each element as one line and leaves out successful requests and actions. - Browser guidance is split: sign-in and vault rules come only with their tools. - A screenshot can capture one element by ref. - select_option follows a select that the page replaces. - A request that the page cancels (net::ERR_ABORTED) is info, not an error. - An automatic snapshot image is skipped when a current full-page image exists. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix the local review findings for the browser tool results - Action status errors go through the card scrub, as snapshots do. - A status reports an error once. The next snapshot still lists it. - pageStatus fails on a closed tab, so the action returns the closed-tab result. - A replacement select must contain the old center point. - A native select keeps its tag in the element line. - A cancelled main-frame load stays an error. - An image is current only for the same address, when the page is not loading, and for 15 s. An element screenshot does not pass it on. - Snapshot entries are delivered only after the last policy check. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Filter snapshot history before its limit A snapshot left out successful requests and plain successful actions only in its text, after it took the newest 20 entries. On a busy page, successful requests filled the window, and the delivery cursor then marked an error as sent that no snapshot showed. The filter now runs before the limit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Deliver snapshot history only after the image capture The snapshot image now runs in the release step of the snapshot read. When the capture fails, the console, network and action entries stay undelivered, so the next snapshot still shows them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 10 days ago | |
Make browser tool results smaller so web work compacts less (#4115) * Make browser tool results smaller so web work compacts less - Input actions return a short status. Pass snapshot: true for a snapshot. - A snapshot gives each element as one line and leaves out successful requests and actions. - Browser guidance is split: sign-in and vault rules come only with their tools. - A screenshot can capture one element by ref. - select_option follows a select that the page replaces. - A request that the page cancels (net::ERR_ABORTED) is info, not an error. - An automatic snapshot image is skipped when a current full-page image exists. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix the local review findings for the browser tool results - Action status errors go through the card scrub, as snapshots do. - A status reports an error once. The next snapshot still lists it. - pageStatus fails on a closed tab, so the action returns the closed-tab result. - A replacement select must contain the old center point. - A native select keeps its tag in the element line. - A cancelled main-frame load stays an error. - An image is current only for the same address, when the page is not loading, and for 15 s. An element screenshot does not pass it on. - Snapshot entries are delivered only after the last policy check. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Filter snapshot history before its limit A snapshot left out successful requests and plain successful actions only in its text, after it took the newest 20 entries. On a busy page, successful requests filled the window, and the delivery cursor then marked an error as sent that no snapshot showed. The filter now runs before the limit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Deliver snapshot history only after the image capture The snapshot image now runs in the release step of the snapshot read. When the capture fails, the console, network and action entries stay undelivered, so the next snapshot still shows them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 10 days ago | |
Migrate service runtimes to Effect 4.0.0 (#1283) * refactor: migrate service runtimes to Effect 4 * refactor: make Effect the canonical service runtime * fix: preserve typed Effect failures and align CI test boundaries * test: await Effect test boundaries and lower lint baseline * fix: handle early provider exit and retain remote send errors * Simplify Effect composition and failure handling * Fix review findings in hosted sites, updates and WebRTC requests - Release a hosted-site idempotency claim on any failure, defects included. - Keep update downloads running after stop(), as before the migration. - Clean up a pending WebRTC request when the send is interrupted. - Compose the account switch in one Effect, without nested runPromise. - Remove single-effect generators in the WebRTC client transport. - Put the atomic-write JSDoc on writeFileAtomically; reuse writeJsonFileAtomically. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Run Effects at Promise boundaries through one runCauseEffect Replace the inline Effect.runPromise(x.pipe(Effect.mapError((error) => error.cause))) calls and the per-domain runners (runBrowserEffect, runMcpEffect, runArchiveEffect, runRemoteWorkflow, runDesktopEffect, runProviderClientEffect, runTestEffect) with runCauseEffect in src/backend/effect-boundary.ts. Behavior is unchanged: expected failures reject with their cause, and defects and interruption reject as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Mark concurrent test starts as not awaited runCauseEffect returns a Promise, so Biome now finds the operations that these tests start and do not wait for. Mark each with void. The start stays concurrent, and a rejection still fails the run. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Send CentralAuth requests through the network-block detector The merge of main wrapped the options fetch, but CentralAuth sends its requests through the Effect transport. Build the transport from the wrapped fetch, and run the new test through Effect. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Run each account API entry point as one Effect Route handlers, the browser API, the Worker pre-routing and the cron now run one Effect each. Request body readers, the SMTP socket close and the remaining R2 and D1 calls are Effects. The skill icon read is now part of its Effect; before, the Effect returned a Promise. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Run the remaining service runtime in Effect Main: the automation server, Team API server start and stop, duplicate requests, Dynamic Island, the Computer Use highlight, trace flushes, update background work, sibling scans, upload slots, server mode, window bounds, attachment archives and uploads, and remote transport callbacks are Effects forked into their owner's scope. Backend: usage limit hooks and the channel requeue are Effects. Team client: request frames, Signal notices and connection failures run in the peer's work scope, and recovery loops inside its run. Site router: one Effect per request. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Run the picture-in-picture and permission help windows in Effect Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Remove the unused analytics runner The trace file forks its flush into its own scope now, so nothing calls runAnalytics. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Keep cron jobs, session ends and host starts complete after a failure - The cron waits for every job before it reports the first failure, so a failed delivery does not stop retention or a site cleanup midway. - A connection failure ends the logical session in an uninterruptible fork, so dispose does not stop it. - A dependency that throws while the Team API listener starts fails the start, so the host stops the listener. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 1 month ago | |
feat(browser): fill inspected text fields in one call (#2139) * feat(browser): fill inspected text fields in one call * Remove the browser fill-fields step from CI The maintainer keeps the E2E script for local runs only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Remove the source digest from the fill-fields E2E report The digest read a fixed list of source paths. A rename of one of these files stops the script. The report keeps the commit SHA and the run ID. Write the changelog item for users. State in the browser architecture document that the E2E script is a local check. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Report a fixed reason when a fill-fields target fails before input A hidden or changed ref failed with a plain error in the preflight, so the result gave input_failed although no input was sent. Map these failures to invalid_target, and fingerprint failures in verify to target_changed. Use deadline when the deadline is past. The E2E script now checks the reason for a hidden field. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Send no Backspace when a fill-fields field is already empty An empty replacement sent Backspace also to an empty field. Page key handlers then ran, for example to remove the last chip of a tag input. Send Backspace only when the field had a value at preflight. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Sniezka1927 <dygons14@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 26 days ago | |
Make the agent browser use fewer turns (#5589) * Make the agent browser use fewer turns - Find bot checks in a snapshot. Wait up to 15 s for a check that passes by itself, and tell the agent to ask for takeover or to report a check that needs a person. - Wait in a snapshot while the page shows loading placeholders. - Load a crashed agent tab again on the next call, and stop a running script at once. - Return the first snapshot from open. - Give the browser tool schemas with the first browser result in a thread, so that the agent can call open before tool_search. - Accept more wait forms in run_script, and add sleep and setTimeout. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix review findings in the agent browser changes - Run sleep and setTimeout in browser scripts as host timers, so that a timer does not stop the steps after it. - Stop the settle wait and the bot check wait before the caller's timeout. - Keep a snapshot when the page changes its URL with history.replaceState during the settle wait. - Report a crashed page that OpenBot did not reload, and do not wait for its snapshot. - Find Cloudflare and Akamai checks only with their scripts or on a short page, and skip pages that are not http or https. - Remove the old "search for them first" browser instruction. - Read the open result in the card smoke as a snapshot. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Do not export the settle probe type Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix the settle and bot check waits from the review - The settle wait takes its reserve from the time left, not from the cap. - A loaded or stopped page with no controls does not wait. - All reads of one document share one 15 s bot check wait. - Cut the agent context text that the branch added. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Do not export the empty page settle wait Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Report a bot check that is still running as checking - A page waits for its first content once, also when a stopped navigation keeps the old page. - A read that stops its bot check wait at its own deadline reports checking, not needs-user, while the shared wait has time left. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Show the timed-out navigation smoke values when it fails Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Read the bot check over CDP in the automation world On Linux, executeJavaScriptInIsolatedWorld does not return for a document whose navigation was stopped, so each snapshot of it waited for the 1 s check timeout. The check now uses Runtime.evaluate in the isolated automation world, as the snapshot and the settle probe do. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 2 days ago | |
Keep agent browser pages drawn while the Windows window is minimized or hidden (#6009) * Keep agent browser pages drawn while the Windows window is minimized or hidden Windows draws no page of a minimized or hidden window, like macOS. A snapshot image or screenshot of an agent tab then gave an empty image or waited until its 10 second deadline. The macOS background window (#5604) now also holds the tab views on Windows. There it is a tool window, so it is not in Alt+Tab or the taskbar, and a minimized window places it at the restored bounds, because Windows reports a tiny content size off screen. The macOS-only calls stay macOS only. An empty capture threw a plain error inside the generator. That was a defect, so Codex showed only "dynamic tool request failed". It is now a typed browser failure that tells the agent why the image is empty, and an empty image no longer counts as a current image for the next snapshot. Fixes #5991 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Make the empty screenshot reason less certain and rename the platform flag An empty capture has other causes than a hidden window, so the agent now reads that a hidden window can cause it. USES_BACKGROUND_WINDOW names what the flag selects. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 20 hours ago | |
Send the same browser brands in the header as the page reads (#4116) * Send the same browser brands in the header as the page reads Cloudflare rejected the built-in browser because the Sec-CH-UA header named only Chromium, but navigator.userAgentData.brands also has a GREASE brand in a version-specific order. Make the header from the same rule as Chromium. The identity smoke now compares the header with the page brands. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Say in the release note that only Cloudflare checks are fixed Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 10 days ago | |
Keep the browser on a page that goes to its next page while it loads (#4076) * Keep browser navigation when the page starts its next page A page that goes to its next page while it loads aborts its own load with ERR_ABORTED. Electron rejects loadURL and emits a main-frame did-fail-load (-3), and the navigation wait failed. The agent open tool then closed the tab. Now the wait continues while the main frame loads the next page. The same deadline applies. The redirect-navigation browser scenario now requires that each navigation succeeds. Fixes #4055 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Keep the open tool's tab when the page starts its next page BrowserHost.open called loadURL directly, so an ERR_ABORTED from a page that goes to its next page while it loads still failed the open and closed the new tab. The first load now goes through navigateAndWait. A null timeout keeps the load without a deadline, as before. The redirect-navigation scenario now also opens the redirecting page. Fixes #4055 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Name the release notes file for the branch Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Follow the next page only after the requested page commits Accept ERR_ABORTED only when the requested document committed and the main frame then started a new cross-document navigation. A load that another navigation replaces before it commits fails as before. When a wait with no deadline follows a next page, it gets a 10 second deadline, so a redirect loop ends. The redirect-navigation scenario now checks that open ends on the redirected page. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 10 days ago | |
Keep the browser request hook working for URLs that Node cannot parse (#3825) Chromium requests some URLs that the WHATWG parser of Node rejects, for example "https://%20example.com/logo.png". The onBeforeSendHeaders listener called new URL() on them, threw before it called callback, and the main process stopped. Use URL.parse, so an unparsable URL keeps the session User-Agent and gets no client hints. Fixes #3775 Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 12 days ago | |
Let a pinged person answer an agent's Slack post without a mention (#5910) * Let a pinged person answer an agent's Slack post without a mention When an agent's messaging_post pings a person with <@U…> in Slack, OpenBot records the person in the new projection_messaging_pings table (migration 59). A reply from that person in the post's thread now reaches the Slack Orchestrator without a mention. The Orchestrator gets the post and the name of the agent that sent it as the message that the reply answers. Replies of other people still need a mention until the thread has its link. The link deletes the thread's pings, and deleting an agent deletes its pings. A thread message event that mentions OpenBot is now left to its app_mention event, so the order of the two events does not decide the route. In Slack threads an agent can answer only NO_REPLY when a message needs no answer. OpenBot then deletes the "Working on it…" post and keeps the reaction. Fixes #5565 * Hear only the pinged people in a thread that a ping opened In a Slack thread where an agent's post pinged people, a reply without a mention now counts only from a pinged person, also after the first reply gives the thread its link. Other people reach the Orchestrator only with a mention. The ping rows now stay with the link, and deleting an agent deletes the pings of its threads and its pings of threads that no reply opened. A thread that a person opened with a mention still hears everyone. Refs #5565 * Fix the local review findings of the Slack ping replies - An answer in a thread that a ping opened records the people that it pings, so they can reply without a mention too. - MessagingThreads.recordPings does the link check and the save in one Effect.try, and logs a failure. A database error after the post no longer fails the tool call. - The Orchestrator reads its own pinging post as its own, not as a teammate's post. - The stored post uses the prompt's REPLY_CHARACTERS limit. - The E2E reads each dropped reply again after a later reply arrives. Refs #5565 * Give a pinged person's first reply with a mention the post as context A first reply that mentions OpenBot comes only as app_mention, so the receive path did not read the thread's pings. The reply went to the workflows, and the Orchestrator did not get the post that it answers. The receive path now reads the pings for each Slack thread message with no link. The E2E covers this case. Refs #5565 | 1 day ago | |
Let a pinged person answer an agent's Slack post without a mention (#5910) * Let a pinged person answer an agent's Slack post without a mention When an agent's messaging_post pings a person with <@U…> in Slack, OpenBot records the person in the new projection_messaging_pings table (migration 59). A reply from that person in the post's thread now reaches the Slack Orchestrator without a mention. The Orchestrator gets the post and the name of the agent that sent it as the message that the reply answers. Replies of other people still need a mention until the thread has its link. The link deletes the thread's pings, and deleting an agent deletes its pings. A thread message event that mentions OpenBot is now left to its app_mention event, so the order of the two events does not decide the route. In Slack threads an agent can answer only NO_REPLY when a message needs no answer. OpenBot then deletes the "Working on it…" post and keeps the reaction. Fixes #5565 * Hear only the pinged people in a thread that a ping opened In a Slack thread where an agent's post pinged people, a reply without a mention now counts only from a pinged person, also after the first reply gives the thread its link. Other people reach the Orchestrator only with a mention. The ping rows now stay with the link, and deleting an agent deletes the pings of its threads and its pings of threads that no reply opened. A thread that a person opened with a mention still hears everyone. Refs #5565 * Fix the local review findings of the Slack ping replies - An answer in a thread that a ping opened records the people that it pings, so they can reply without a mention too. - MessagingThreads.recordPings does the link check and the save in one Effect.try, and logs a failure. A database error after the post no longer fails the tool call. - The Orchestrator reads its own pinging post as its own, not as a teammate's post. - The stored post uses the prompt's REPLY_CHARACTERS limit. - The E2E reads each dropped reply again after a later reply arrives. Refs #5565 * Give a pinged person's first reply with a mention the post as context A first reply that mentions OpenBot comes only as app_mention, so the receive path did not read the thread's pings. The reply went to the workflows, and the Orchestrator did not get the post that it answers. The receive path now reads the pings for each Slack thread message with no link. The E2E covers this case. Refs #5565 | 1 day ago | |
Remove most exactOptionalPropertyTypes type errors (#2496) * Remove exactOptionalPropertyTypes errors in contracts and team-client Add `| undefined` to optional fields that callers already set to `undefined`: the channel read cursor, the marketplace query fields and the v4 base adapter option. These are type changes only; the wire format does not change. The team client now sends `contentType` only when it has an upload. A transfer ID exists only with an upload, so the request is the same. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Remove exactOptionalPropertyTypes errors in the account service Add `| undefined` to optional inputs that the routes already set to `undefined`: skill marketplace queries, host registration and Mobile Connect tickets, and the article figure props. The R2 list and the footer route link now leave out a missing cursor or hash. Both APIs read a missing value and `undefined` the same way. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Remove exactOptionalPropertyTypes errors in scripts Add `| undefined` to optional options that the scripts already set to `undefined`. The renderer dev server port is now left out of the Vite config when it is not set; Vite skips an undefined value the same way. `ConversationMessage` fields now include `undefined`, as `isConversationMessage` already accepts. The seed scripts and the backend build messages with `undefined` fields. Two renderer helpers that read these messages accept the same fields. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Remove exactOptionalPropertyTypes errors in the backend Optional fields that the backend sets to undefined now say so in their type. The MCP server bridge leaves out missing options instead of passing undefined, and update_profile forwards only the profile fields that the agent sent. Two errors stay: the MCP SDK transport classes declare onclose and sessionId as accessors that can return undefined, and its Transport interface does not allow it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Remove exactOptionalPropertyTypes errors in the main process Widen optional fields that receive undefined, and use conditional spreads for DOM, Electron and fetch option objects. The Team protocol options are coerced with `=== true`; the adapters read them only by truthiness. The ICE server schema drops absent credentials so parsed servers match `IceServer`. Two MCP SDK transport errors stay in github-mcp-proxy.ts: the SDK typings are not compatible with the flag. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Remove exactOptionalPropertyTypes errors in shared UI Widen optional props that callers set to undefined. Pass external props that can be undefined through conditional spreads, or with the default that the library uses. Add selectValue for a Select option that can be missing. Keep the Switch input id error: an explicit undefined removes the Kobalte generated id today. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Remove exactOptionalPropertyTypes errors in the renderer Add `| undefined` to optional props and fields that callers already set to `undefined`: the shared agent and message models, renderer state, and helper parameters. In contracts and team-client, this applies to `AgentModelChoice.providerAccountId`, the direct conversation `readState`, the host analytics `agentId`, the remote request `upload` and `timeoutMs`, and the parameters of the agent policy and chat visual helpers. These are type changes only; the wire format does not change. Portal mounts use `?? document.body`, which is the Solid default. The skill switch and the story select pass `false` in place of `undefined`. The preview search mock omits absent filters. Stories share named render functions and decorators. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix Settings and model picker after the shared UI type changes A conditional JSX spread on a Kobalte component stops it from rendering in Solid 2.0.0-rc.0. Settings showed an empty page and the model picker with an effort select did not open. Pass disabled and step as plain props with the Kobalte defaults. Restore the plain value prop of the effort and display selects, and keep their two exactOptionalPropertyTypes errors in the baseline: no form that the checker accepts keeps an undefined value uncontrolled. Remove selectValue. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Use plain Link props for the footer hash A conditional JSX spread can stop a Solid 2.0 component from rendering. The footer link now uses a plain `hash` prop when the link has a hash, and no `hash` prop when it has none. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Let linkFavicons take undefined `MarkdownMessageText` passes `props.linkFavicons` through, and the value can be undefined. The prop type now says so, as the other optional props of the branch do. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 22 days ago | |
Remove unit tests and keep E2E and protected tests (#2051) * Remove unit tests and keep E2E and protected tests Delete 591 vitest unit test files and their test-only harnesses. Keep E2E and smoke scripts under scripts/, and keep vitest files only for migrations, schema parity, the renderer-to-main trust boundary, secret redaction, and check-rule fixtures. Remove the test runners that have no tests now (site-router, remote/api, contracts, brand, auth-api client), their CI jobs, and the transfer budget report. Remove code and dependencies that only the deleted tests used. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Set the E2E-only test policy and remove references to deleted tests AGENTS.md now permits vitest files only for migrations, the trust boundary, secret redaction and check-rule fixtures. All other behaviour gets an E2E script under scripts/. Remove references to deleted tests, harnesses and commands from docs, AGENTS.md files and skills. The release-upgrade-safety gates that ran a deleted test now use a manual check. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Keep migration tests and fix CI checks after the test removal Move the migration cases out of the deleted mixed test files into three migration test files: the v15 agent usage upgrade and rollback, the read-state baseline from schema v2, and the remote control plane and permanent invitation D1 migrations. Keep error.agent.noStartingModel for the text that older hosts send, and let i18n-check accept it. Lower two type baseline entries. Correct comments and docs that named removed tests or test commands. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Remove the empty test patterns from the knip configuration Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Add E2E checks for Signal secrets, IPC input decoding and analytics redaction - scripts/signal-security-e2e.ts starts the real Signal service with throwaway secrets. It checks the Telegram secret header, the Slack signature, signed account events, remote and route tickets, webhook headers, and that no frame or log line holds the bot token. CI runs it in the new Signal E2E job. - scripts/ipc-input-e2e.ts sends malformed requests from a running dev app and checks that the main process decoders refuse them. - scripts/analytics-redaction-e2e.ts checks that no captured analytics or failure report request holds a seeded secret. For this, OPENBOT_ANALYTICS_CAPTURE_URL sends analytics to a loopback capture server. Only an unpackaged build reads it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Check the renderer analytics path and path traversal in the E2E scripts The analytics E2E now loads the window again and waits for the renderer event, so the renderer send path cannot pass with no request. The IPC input E2E now sends an attachment id and a shared file path that try to leave their folder. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Remove an unused optional parameter from the read-state migration test The parameter gave one exactOptionalPropertyTypes error, which the type ratchet refuses for a new file. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 27 days ago | |
Accept more browser argument forms that agents write (#5885) * Accept more browser argument forms that agents write The browser tools now repair argument forms with one meaning before the schema decodes them: a ref, snapshot line or CSS selector as a string, Playwright wait states, tab_id, open with a tabId, type replace, and fill_fields value. run_script accepts role exact, waitFor with a target, text or state, and a waitFor function that calls evaluate. An evaluate step can return 1 MiB to the script. The bench reads queue.welcome, because #5773 hides the first message. * Keep secret fills, open and the user-control check safe in the browser repair - type with replace or clear false appends, because the default mode replaces. - open drops a tabId and opens a new tab. It does not navigate the tab away. - submit_secret and fill_card get no repair: their targets must come from a snapshot. - The user-control check reads the repaired tabId, so tab_id waits too. - run_script helpers send a string target as it is, so a ref string works in a script. - A sandbox waitFor uses the clock for its timeout and ignores document.evaluate. * Keep all Vault, secret and payment tool arguments as the agent sent them save_vault_item registers the raw password for log redaction before the decoder runs. A repaired wrapped call skipped that registration. | 1 day ago | |
Make the agent browser use fewer turns (#5589) * Make the agent browser use fewer turns - Find bot checks in a snapshot. Wait up to 15 s for a check that passes by itself, and tell the agent to ask for takeover or to report a check that needs a person. - Wait in a snapshot while the page shows loading placeholders. - Load a crashed agent tab again on the next call, and stop a running script at once. - Return the first snapshot from open. - Give the browser tool schemas with the first browser result in a thread, so that the agent can call open before tool_search. - Accept more wait forms in run_script, and add sleep and setTimeout. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix review findings in the agent browser changes - Run sleep and setTimeout in browser scripts as host timers, so that a timer does not stop the steps after it. - Stop the settle wait and the bot check wait before the caller's timeout. - Keep a snapshot when the page changes its URL with history.replaceState during the settle wait. - Report a crashed page that OpenBot did not reload, and do not wait for its snapshot. - Find Cloudflare and Akamai checks only with their scripts or on a short page, and skip pages that are not http or https. - Remove the old "search for them first" browser instruction. - Read the open result in the card smoke as a snapshot. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Do not export the settle probe type Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix the settle and bot check waits from the review - The settle wait takes its reserve from the time left, not from the cap. - A loaded or stopped page with no controls does not wait. - All reads of one document share one 15 s bot check wait. - Cut the agent context text that the branch added. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Do not export the empty page settle wait Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Report a bot check that is still running as checking - A page waits for its first content once, also when a stopped navigation keeps the old page. - A read that stops its bot check wait at its own deadline reports checking, not needs-user, while the shared wait has time left. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Show the timed-out navigation smoke values when it fails Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Read the bot check over CDP in the automation world On Linux, executeJavaScriptInIsolatedWorld does not return for a document whose navigation was stopped, so each snapshot of it waited for the 1 s check timeout. The check now uses Runtime.evaluate in the isolated automation world, as the snapshot and the settle probe do. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 2 days ago | |
Show the full edit menu on a right-click in chat text fields (#3902) * Show the full edit menu on a right-click in chat text fields The chat right-click menu showed only Select All in an editable field. It now shows Undo, Redo, Cut, Copy, Paste, Delete and Select All, and Paste and Match Style on macOS. Each item is enabled from the field's edit state. Spelling suggestions stay. The items do not use Electron roles: a role label is English on Windows and Linux, and a role acts on the focused window. Each item runs on the main window's contents, with a label from the menu catalog. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Resolve the menu translator once for each right-click Get the translator one time before the chat menu items are built, not one time for each item. Make the comments say why the edit entries are not Electron roles. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 11 days ago | |
Let a user tag browser tabs and threads in chat (#5021) * Let a user tag browser tabs and threads in chat The @ picker of the chat composer now has sections for agents, browser tabs and threads. A tag becomes a chip in the message. - A tab tag lists only the tabs of the agent of the chat. When the user sends it, the host shares that tab with the target thread of the same agent, so a side thread can use a tab of the main chat. - A thread tag gives the agent one short line: the title, the task id, the state and the start of the result. The agent reads more with history_read or history_search and the new optional taskId. - The stored text keeps the marker. The marker expands at delivery, so the agent gets the current title and state. - A click on a chip opens the tab page or the thread. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Share a tagged tab when its prompt is built - Share the tab with the public thread that gets the prompt, not when the message is queued. A queued message can move or change. - Use the strict name rule for tab and thread titles. - A popup keeps the shares of its opener tab. - A tag from a teammate shows as a name only. - The picker lists only tabs that have an owner agent. - Use browserDisplayUrl in the picker. - Make the thread question in chat-tags-e2e exact. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Keep TaggedThreadState private to delivery-content Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 5 days ago | |
Accept more browser argument forms that agents write (#5885) * Accept more browser argument forms that agents write The browser tools now repair argument forms with one meaning before the schema decodes them: a ref, snapshot line or CSS selector as a string, Playwright wait states, tab_id, open with a tabId, type replace, and fill_fields value. run_script accepts role exact, waitFor with a target, text or state, and a waitFor function that calls evaluate. An evaluate step can return 1 MiB to the script. The bench reads queue.welcome, because #5773 hides the first message. * Keep secret fills, open and the user-control check safe in the browser repair - type with replace or clear false appends, because the default mode replaces. - open drops a tabId and opens a new tab. It does not navigate the tab away. - submit_secret and fill_card get no repair: their targets must come from a snapshot. - The user-control check reads the repaired tabId, so tab_id waits too. - run_script helpers send a string target as it is, so a ref string works in a script. - A sandbox waitFor uses the clock for its timeout and ignores document.evaluate. * Keep all Vault, secret and payment tool arguments as the agent sent them save_vault_item registers the raw password for log redaction before the decoder runs. A repaired wrapped call skipped that registration. | 1 day ago | |
Add a Vault for saved logins, cards and contact info (#3047) * Show focus with a grey border, not a blue glow Focused fields get a grey border with no ring. Focused buttons and other controls get a 1px grey ring. Invalid fields keep their red ring. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Add the Vault: saved logins, cards and contact info for agents Server settings has a Vault page after General. It keeps logins, payment cards and contact info on this computer, encrypted by the operating system. Agents sign in with saved logins, fill a saved card (also in the card frames of payment providers) and type saved contact info. Agents never see a password, a card number or a security code. Agents can add and change contact info, change the non-secret fields of logins and cards, and delete items. Save to Vault on the secure password card keeps the password that the user types. The Vault is on the local server only. A click on a label whose decorative layer covers its radio button now selects the radio. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix Vault review findings - Read a saved website such as example.com:8443 as a host and port, so a login saved from a page with a port can be edited. - Give agents the Vault instructions only when the Vault can be read. - Use the same rule for a complete contact in the dialog and in main. - Use one card brand rule in the dialog and in main. - Register imported secrets for redaction with one pattern rebuild. - Refuse a delete of an item that is not in the Vault. - Route the agent Vault tools with a switch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Remove Vault exports that no other file uses Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Add the Korean Vault catalogs Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Keep each saved login bound to its website An agent can no longer change the website of a saved login. It could move a login to a different site and then fill its password there. An agent can still change the username. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Ask the owner before an agent fills a card on a new site The first fill_card of a card on an HTTPS site waits for the server owner's approval. The card keeps the approved page and the origins of its card frames, so later fills there need no approval. Team members, messaging services and local scripts cannot give this approval. The Vault card dialog lists the approved sites, and the user can remove one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Keep the tab recording when a card fill waits for approval The recording of a tab is discarded only after the owner's approval lets the fill type the card. A fill that waits, or that the owner declines, keeps the recording. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 16 days ago | |
Accept more browser argument forms that agents write (#5885) * Accept more browser argument forms that agents write The browser tools now repair argument forms with one meaning before the schema decodes them: a ref, snapshot line or CSS selector as a string, Playwright wait states, tab_id, open with a tabId, type replace, and fill_fields value. run_script accepts role exact, waitFor with a target, text or state, and a waitFor function that calls evaluate. An evaluate step can return 1 MiB to the script. The bench reads queue.welcome, because #5773 hides the first message. * Keep secret fills, open and the user-control check safe in the browser repair - type with replace or clear false appends, because the default mode replaces. - open drops a tabId and opens a new tab. It does not navigate the tab away. - submit_secret and fill_card get no repair: their targets must come from a snapshot. - The user-control check reads the repaired tabId, so tab_id waits too. - run_script helpers send a string target as it is, so a ref string works in a script. - A sandbox waitFor uses the clock for its timeout and ignores document.evaluate. * Keep all Vault, secret and payment tool arguments as the agent sent them save_vault_item registers the raw password for log redaction before the decoder runs. A repaired wrapped call skipped that registration. | 1 day ago | |
Add two-step codes to Vault logins (#5933) * Add two-step codes to Vault logins A Vault login can keep an authenticator setup key. Main makes the live code, the Vault list shows it, and agents can enter it with submit_secret method=authenticator. Agents can save a key from a person's message. Joined servers get the codes through the new vault-v3 capability. * Load two-step codes again after a key change or a failed load The Vault list loads the codes again when a saved change gives a new list, so a replaced key shows its own code. A failed load tries again after 30 seconds. A draft code with no Web Crypto shows no code and does not leave an unhandled rejection. The key input uses TWO_STEP_KEY_LIMIT, and the preview mock uses the catalog error text. * Keep the two-step key decoder private to the Vault input file * Add the two-step key of a CSV row to a saved login that has none * Keep an absent two-step key out of the Vault save body | 23 hours ago | |
Use one causeHelpers adapter for typed cause errors (#1472) * Use one causeHelpers adapter for typed cause errors Add causeHelpers(ErrorClass) to src/backend/effect-boundary.ts. It returns io, sync and rewrap. Replace 81 module-local Effect.try and Effect.tryPromise wrappers and 405 mapError calls that moved a cause into a new typed error. Each error class, tag and wrapper name stays. The AbortSignal variants (runtimeIO, cuaIO, githubCall, onePasswordCall) keep their names and still forward the signal. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Move the no-span comment to the slackIo adapter Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Make two error classes private that only their module uses knip found AttentionOperationFailed and AvatarFileFailed unused outside their files after the rewraps moved to the to* helpers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 1 month ago | |
Make channels feel like chats and run channel turns beside the own chat (#5790) * Make channels feel like chats and run channel turns beside the own chat - Show progress notes as separate messages when they are complete, and slide new channel messages in. - Group "Assigned to" rows, remove own-name labels from member messages, and show a sent message once. - Send mentions after a quote, several typed names and late names to the named members. - Give each channel its own mailbox lane and side thread, so a channel turn runs beside the member's own chat. An agent runs one channel turn at a time. Providers that run one turn at a time keep the serial order. * Keep channel turns apart from the own chat in failure, clear and stop - A failed channel turn no longer marks the own chat as failed. - Clear, delete history and compact wait for channel work only when the provider runs one turn at a time. - interrupt_agent tells the caller that a channel task still runs. - An empty streaming row that holds only the member's name stays hidden. - After a reconnect, channels load again so that no missed event stays lost. * Make the channel_assign description shorter to keep the context budget The task prompt already tells a member to do its assignment itself. * Give each sent channel message its own row key When a channel guess took the wrong stored message, the send's own id later mapped to the same pending row key, and two rows shared one key. | 1 day ago | |
Let a person give a channel a picture (#4679) * Let a person give a channel a picture A channel can now show an uploaded image, or one of 20 icons on one of 8 colors, in place of the member faces. Open channel settings and click the picture to change it. A reset button shows the member faces again. The image file stays on the computer that runs OpenBot, in userData/avatars/channels. The channel record keeps only the icon. The Team API v1 channel codec does not send the icon or the image, so remote clients continue to show the member faces. Closes #4274. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Keep the channel image when an icon save fails The editor now saves the icon first and removes the image after it. Before, it removed the image first, so a failed save lost the image. Other fixes from the local review: - The image change runs to the end during a shutdown, so no file stays that the channel does not name. - A saved icon that this version cannot read shows the member faces. Before, the channel became unreadable after a downgrade. - The picture editor shows only when the server is known to be this computer. - The remote error and the color labels use catalog keys. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Make the channel image store a required argument ChannelService has one caller, and it always gives the image store. The checks for a missing store could not run, so they are removed. The store is the third argument and the hooks stay last, so the hook block in agent-service.ts keeps its indentation. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Use the Plus icon in the channel picture editor Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 7 days ago | |
Let a channel lead cancel its own runs at a person's request (#4981) * Let a channel lead cancel its own runs at a person's request - Add the channel_cancel tool. At a person's request, a member cancels a whole run that it owns, with its running and queued subtasks. It refuses the run of another owner, its current run, an ended run, and a run that another open run depends on. The chat shows a note that names the agent and the person. - Add Stop to each row of "Waiting for sub-tasks" in a channel. It sends the existing stop command. - Give each channel packet the stopped and cancelled work of the member (stoppedTasks), so that a lead does not wait for work that a person cancelled. Closes #4937. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Stop the turns of a retried channel cancel - Read the receipt of channel_cancel in the channel queue. A copy of a call that waited behind the first is accepted, not refused. - On a receipt, stop the turns that the first call did not stop, as a retried command does. - Use one helper for the cancel of the channel controls and of channel_cancel. - Give stoppedTasks for each run of a recent message, also when the message is from a sub-task. - Name the task in the note from its first line with text, and cut it by characters. - Send one row stop at a time in the awaiting replies. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Make the channel_cancel description shorter Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Raise the agent context budget for channel_cancel The new tool adds 331 description bytes, 140 schema bytes and 24 visible-tool bytes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 5 days ago | |
Give messaging agents workspace memories, a history tool and named mentions (#1950) * Give messaging agents workspace memories, a history tool and named mentions An agent that answers in Slack, Discord or Telegram now keeps facts for each connection. It saves a fact with openbot.messaging_remember and deletes one with openbot.messaging_forget_memory. Every prompt of every conversation of that workspace, server or chat shows the facts, with their ids, as untrusted data. A connection keeps at most 32 facts. A fact from a direct message is not saved. The agent's own memories stay private to the OpenBot user. openbot.messaging_history reads the newest messages of the channel or thread that the conversation is in, and no other place. A Slack rate limit fails the read at once and does not pause the connection. Mentions of people in Slack and Discord messages, and in their context, now show names, not ids. Discord role mentions keep their ids. Migration 35 adds projection_messaging_memories. It only creates a table. Disconnect keeps the facts. Refs #1911 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix messaging history reads and bound mention lookups Discord channel history keeps the newest window that it read to its end. Before, a longer window that it did not read to its end replaced it, and the agent got the oldest messages of the day. Slack thread history reads up to 5 pages, so a long thread keeps its newest replies. Discord thread history adds a clock margin, so a slow local clock does not drop the newest replies. Mention lookups stop waiting after 1.5 s and keep the ids. The lookups go on in the background and fill the name cache. Before, slow lookups could pass the prompt context deadline, and the prompt lost its earlier messages. The author, mention and place lookups of a new message now run at the same time. A saved fact cannot close the workspace memories block with another spelling of the closing tag. The docs say that facts from private channels reach every channel, and that a direct message can delete a fact. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Keep the Slack thread pages that were read when a later page fails Until the Slack Marketplace approves OpenBot, Slack allows one thread read a minute, so the second page of a long thread is often refused. Before, that refusal failed the whole history read, and the agent got nothing. Now only a failed first page fails the read; a later failure keeps the pages already read. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Pass memoryId only when the tool call gives one The ratcheted exactOptionalPropertyTypes check refused an undefined memoryId on an optional field in messaging-threads.ts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 28 days ago | |
Let channel members reply to each other (#5167) * Let channel members reply to each other Add the channel_reply tool. A member posts a short reply to a message of another member, and that member gets one turn to answer it. The answer shows as a reply to the reply. The caller does not wait for it. A run allows two member replies. A reply after the caller's result, in a run that ended, to a person or to the caller, and to a member that takes no work is refused. A reply turn cannot cancel a run or change the channel. No migration and no Team API change: the reply task is known by its request id prefix. Add scripts/channel-reply-e2e.ts. Reactions are not part of this change. Fixes #4847 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Make a member reply wait for its answer The caller of channel_reply now waits for the reply turn, as for channel_assign, and continues with the answer. Before, a member that had to agree on something answered the person first and promised to report back, and the late reply back was refused in the chat. A reply turn only answers: it cannot reply, assign, transfer, cancel or manage the channel. Refusals tell the model not to mention them. Text that the caller writes after its reply in the same turn folds into its steps. Refs #4847 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Close the gaps in channel member replies A failed reply turn lets the caller continue without the answer. A result is refused while a reply of the task waits. A reply turn cannot change the channel memory, and its assignment names the sender and says it is not the user. The reply limit counts the reply tasks of the whole request. A person's message that resumes a reply task makes it an ordinary task. The commentary of a turn that posted a reply is left out of the transcript that models read. Refs #4847 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Let a failed member reply release its caller on every path Only the task end path queued a waiting parent again. A reply that failed on delivery or at the queue left its caller waiting until a person acted. A shared helper now releases the parent on all three paths. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Raise the agent context budget for channel_reply The new channel_reply tool adds its description and schema to the tool catalog, and its name to the send_message guidance. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 5 days ago | |
Cancel a stopped or failed channel task (#4257) * Cancel a stopped or failed channel task A stopped or failed channel task stayed above the composer until the user continued or reassigned it. Add the channel command `cancel`. It sets the task and every open task of the same root to `cancelled`, interrupts active work, and keeps the messages. The host advertises `channel-task-cancel-v1` only with its channel service and rejects `cancel` from a peer without it. Desktop, the web client and mobile show Cancel task, with no confirmation, only with the capability. Fixes #4240 Refs #4200 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Put the Cancel condition of the stopped task card in a named function A ternary in a component prop makes a memo on each read. A child that reads the prop in a tracked effect then halts the renderer. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Refuse Cancel for an active task and record a cancelled routine run - The host refuses Cancel when the task is not stopped or failed. - A routine run whose root task is cancelled records "cancelled", not "succeeded". - Mobile shows Cancel on only one card of each run, because Cancel ends the whole run. - The release notes and the architecture doc say that Cancel ends the whole run. Refs #4240 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 9 days ago | |
Let script and webhook routine runs stay quiet, and keep quiet answers in a closed run (#1948) * Keep the source of a routine run and let script and webhook runs stay quiet Store the source of each routine run (migration 35). Script and webhook runs no longer get the Test run sentence, so the agent can call routine_no_update. The run history shows Script and Webhook runs. A quiet run keeps its text answers as routine_quiet_answer messages. Unread counts, the preview, the runtime snapshot and the Team API do not include them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Open and close the messages of a routine run with its row The messages of a routine run's turn now follow the run's row in the chat. The run toggle shows and hides them. A finished quiet run starts closed. A run that is not finished, or that reported something, starts open. An open run group shows each run and its messages as rows. Quiet answers do not add to the new message count or the unread state. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Document quiet routine answers and keep them from Team API clients in tests The Team API conversation test now has a quiet routine answer. It checks that no client gets it and that each page and read request asks the store to exclude it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Leave quiet routine answers out of message search Search returned a quiet answer to Team API clients, which never get it, and to the desktop chat, which cannot show it inside a closed run. Also update the comments that still said only scheduled runs can be quiet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Keep an open routine run out of a run group A reported run starts open, and its messages follow its row. When it came after closed runs, it joined their group, and an open group drew its messages two times. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Show the earlier states of an open routine run A run with messages lost its history toggle. An open run now shows its earlier states above its messages. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Keep a turn that a routine run shares in place A turn can also run a message from a person or a second run. Its messages now stay where they are, so a reply to a person does not move under a run. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Keep a turn that a teammate's message shares in place A message from a teammate also starts a turn with a routine run. Its turn now keeps its messages in place, as a turn with a person's message does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 28 days ago | |
Add shared channels and agent task coordination to Electron (#322) * feat: add shared group chats and task coordination * wip: channel rename and channel chat UI Temporary checkpoint so origin/main can be merged. Amend or squash later. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * Add channel-scoped memories and routines behind channel settings Rebuild channel settings out of the agent settings panel's own components and give the two nav rows something to open. - Schema: `CHANNEL_SETTINGS_SCHEMA_SQL` holds one memory table and three routine tables, shared by migration v16 and the separate latest schema. A new constant and a new version, because every profile on this branch already ran v15 and `CREATE TABLE` never re-executes. - Contract: a channel now has `title` and `instructions`. `purpose` reads as `instructions` in both decoders, so a row written earlier on this branch and a peer that still sends the old key both read cleanly. - Stores: `RoutineStore` and `MemoryStore` hold the SQL, keyed on an owner descriptor. `AgentRoutineStore` and `AgentMemoryStore` keep byte-identical public signatures; `Channel*` subclasses map the owner. - One `RoutineTimer` for both owners, armed from the earliest due time across every source, so neither scheduler can clobber the other. - A routine fires as a `request` command, never through a private path, so it inherits the per-channel serialisation, the retry receipt and the archived guard. It never takes `send`'s continuation heuristic: a routine must not hijack a human's open task. - `channelRunStatusForTasks` joins a run to its work on `requestMessageId`, which survives the routing merge; `rootTaskId` does not. Status is a pure function of durable rows, so a restart settles open runs from the database alone. - `channel_remember` and `channel_forget_memory` commit at call time under the per-call receipt, and the packet carries a `<channel_memories>` block. - Renderer: `AgentMemoriesModal` and `AgentRoutinesSettings` take an injected port instead of an agent id, so the channel panel reuses them as they are. Members list unchanged. Also carries this branch's earlier uncommitted sidebar and channel chat UI work, which was already in the tree. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: correct channel dispatch, request files, and remote events Review findings on the channel chats branch. - A task with an active assignment starts no second owner. Dispatch now rejects a task that any assignment holds, not only a task whose owner still matches: a transfer replaces the owner of a task while the previous owner runs its turn. - An assignment stores the resources it starts with. A transfer that lowers the resources of a running task can no longer release a reservation that the turn still holds. An assignment row written before this field reads as a host reservation. - Only the first dispatch of a request converts its attachment drafts and rewrites the stored message. A child task that inherits the request id keeps the text and the files of the request, and a resumed assignment sends the stored copies again. - `reconcile` keeps a routine run open while its request command is still in flight. The run row is written before the command commits, and a publish from other channel work can land in that window. Read as a state, that window looks like a request that every task has dropped. - Agent deletion and the provider switch use `hasUnfinishedDelivery`, which counts a queued channel delivery as well as a running one. - The WebRTC host peer envelopes `channels-changed` through the optional channel protocol. The frozen v1 event adapter rejects that type, and the frame was dropped without a trace. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: lift the New channel agent avatars onto the row text The drawn bloub overflows its own box downwards, so each avatar landed 14% of its size below the center of the row and read low beside the agent name and the checkbox. Measured in the running app: the shape center sat 3.36px below the row center at 24px, and 0.11px after the lift. The value is the one the other 24px avatar already uses. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat: cluster the channel member avatars in the sidebar A channel row drew its members as a row of small faces, which made the row twice as wide as an agent row and put the name in a different place from every other row in the list. The sidebar and the pinned rail now draw the members in the square one agent avatar occupies: one member is the whole face, two sit side by side, three read as a triangle, and four fill the quarters. Past four, the last cell carries the count of the members that have no face. The conversation title and the empty channel keep the row layout, because a quarter face and a count are not legible at 18px. The cluster geometry is two measurements: the pitch between face centres and the face box. A bloub draws about 1.16 times its own box wide, so a face box of 0.48 of the square draws 0.56 of it; the faces overlap, and the pair still ends inside the square the row expects. Storybook: Navigation/Sidebar Channels and ChannelsCompact cover one, two, three, four and six members, in the list, in the pinned rail and in the compact rail. No test assertions were added, so there is no break-it check to report; the change is layout, which tests do not assert. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor: share one settings panel between agents and channels The channel side panel was a second implementation of the agent side panel, so the two drifted: different header height, no divider, no resizer and different body padding. The shared shell, header, scroll body, field and link row now live in `components/SettingsPanel.tsx`, and the CSS moves out of `conversation.css` into `styles/settings-panel.css` under names without the `agent-` prefix. That prefix is what made a channel author write a copy instead of a second user. The channel panel adopts the agent panel's appearance and gains the resizer; both panels remember one width, because one replaces the other in the same slot. Member rows become `ChannelMemberRow`, used by settings and by the New channel dialog, and a task becomes a row of the shared item list with a state badge. `FieldContext` is exported from `components/ui/form.tsx` so `SettingsField` can hand its control an id: that is what lets a label point at a control it wraps rather than one it names. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: match the channel panel spacing to the agent panel The header was already the same 39px grid, but the channel form was not: `.channel-editor` was a flex column with its own 12px gap, which added to the bottom margin the shared field already carries. Every field stood 29px from the next where the agent panel puts 17px, and the member list stood 36px below the link group where the agent panel puts 25px. The editor now stacks as a block and adds no spacing of its own. The step between one section and the next becomes `--settings-panel-section-gap` on the panel itself, so the agent runtime card and the channel member list read the same distance from the link group. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: give both settings panels the same header buttons The agent panel closed with a light double chevron drawn at 20, the channel panel with a lucide X drawn at 24 with a heavier stroke, so the same action read as two different buttons. The header now draws its own glyphs and takes no icon from the caller, which is what stops a third panel from picking a third button. The two glyphs move out of `ConversationIcons.tsx` into the shared panel, along with the half-pixel nudge that keeps the back chevron on the axis. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat: give the channel chat the agent chat's transcript Both chats now render one shared ChatMessageRow, so the row around a bubble cannot drift a third time. The channel gains the entrance animation, the streaming reveal, the hover toolbar, the virtualizer, the scroll-to-latest button and the unread divider and banner. A coloured author name and a face stand over the first message of a run; own messages stay on the right with neither. A day separator is added to both chats, and two working agents read as one activity row. Channel reactions stay out: they need a react variant of ChannelCommand and a rewrite of the stored message_json. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * Polish channel conversations and add permanent deletion * Fix channel recovery, deletion races, and remote updates * Keep channels when provider starts remain unconfirmed * Fix channel queue ordering and restored routine scheduling * Remove message status labels from chat bubbles * Restore selected channels after restart * fix: keep the failed-action label and clear deleted routine history The status label removal took the name of the failed action with it, so an error bubble showed only the provider text: "Provider is offline." read as a sentence the agent said. A renderer-written error now carries kind "error" and keeps its label. The generic delivery labels stay off. Channel deletion collected routine ids from the projection table only. RoutineStore.delete() keeps the event log, so a routine deleted before its channel left its instruction text in SQLite after a permanent delete. The ids now also come from the event log and the run rows. The story play function queried the working sentence with findByText, but the sentence lives in the aria-label of a role="status" span. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: hide agent chats under an open channel from read tracking An open channel covers the workspace, but the agent under it stayed selected, so a reply that arrived while the channel was on screen was marked read without being shown. The shared chat-read predicate now asks whether a channel is open, next to the questions it already asks about the setup form, a direct message and the Usage report. The same gap ran the other way: Edit agent and a global-search result call selectAgent, which left the channel selected, so the chat they opened stayed hidden. Clearing the channel moves into selectAgent and selectDirectMember, and the sidebar wrappers that did it by hand go. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: keep both member removals when two channel saves overlap A channel save carries the whole draft, and the member controls stay enabled while one is in flight. Two removals started together both read the member list as it was before either of them, so the second save restored the member the first removed. Commits now run one at a time, and each draft is built after the previous save and its refresh. Break-the-behaviour check: with the chain replaced by `Promise.resolve()`, the new test in App.channels.test.tsx fails because "Remove Chief" is back on screen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: close the channel history gap a refresh could leave A refresh reads the newest window of a channel, not the whole history. When more messages arrived than that window holds, the loaded transcript no longer touched it, and the messages between the two blocks became unreachable: the loaded cursor names a sequence below the loaded block, so "load earlier" stepped over the gap rather than into it. The merge is now a pure module. The two blocks join only when the last loaded message sits right below the first fetched one; otherwise the fetched window replaces the transcript and brings its own cursor, which walks back through the gap. Break-the-behaviour check: with `joins` forced to true, the gap case in channel-page-merge.test.ts fails with [1, 2, 10, 11] against [10, 11]. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * perf: answer the channel host check with one query `mayDrain` runs on every normal queue check. It walked the channel list to look for an active assignment, and building that list parses every stored message of every channel to compute unread counts and previews that nothing in the check reads. `ChannelStore.hasAssignmentInState` answers the same question with one indexed query over the assignment rows. Behaviour is unchanged; the existing channel-service tests cover both answers. Break-the-behaviour check: with the guard inverted so the query always reports no active assignment, three channel-service tests fail, including "counts a normal request held behind another agent's channel work as unfinished". Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat: offer the way out of a task the channel stopped The service stops a task and writes the reason on it when the automatic assignment limit is reached, and that reason asks the reader to continue the task or to give it to another member. Neither action was on any screen, so the task stayed stopped. The channel transcript now shows a notice for every stopped task that carries a reason, with Continue, which sends `resume`, and Reassign, which sends `reassign` with the chosen member. An archived channel stops every task without a reason, so those stay out of the notice. Break-the-behaviour check: with the command type fixed to `resume`, the reassign test fails; with the filter reading `running`, both tests fail because the notice is absent. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: show one notice for a stopped run, at its root The assignment limit stops a root task and everything under it, and every stopped task carries the same reason. A card for each of them repeated the reason, and Continue on a child would have left the root stopped, because `resume` starts a task with its descendants only. The notice is now one for each root, and the actions name the root task. Break-the-behaviour check: with the deduplication removed, the new test in App.channels.test.tsx finds two notices instead of one. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: complete the recovery of a stopped channel run The review of 5dd1577d found three ways a channel run stays stuck: - `reassign` queued only the selected task. The assignment limit stops the root and each task under it, and a parent waits for every task it delegated, so a root queued alone waited for a stopped child for ever. Reassign now starts the stopped tasks of the branch as well, and gives the new owner to the selected task alone. A task that still runs keeps its turn. - The notice listed a paused task only. A failed task carries its own reason, its parent waits for it, and nothing but the reader starts it again, so it belongs in the same list. - An open channel covers a private conversation while the member stays selected, and each message that arrived was marked read behind it. Opening a channel now closes the private conversation, as selecting an agent already closes the channel. Break-the-behaviour checks: - with `reassign` back to the selected task alone, "starts the rest of a stopped run when one of its tasks is reassigned" in channel-service.test.ts finds no assignment for the child; - with the failed state out of the notice, "continues a task that failed, which nothing but the reader starts again" in App.channels.test.tsx finds no notice; - without `clearDirectSelection()` in `open()`, "keeps a private message unread while an open channel covers the conversation" in App.read-state.test.tsx reads the message as seen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: answer the second channel review round The review of 59051361 found four problems. Each is fixed at its source of truth: - A search result carries an agent id and a message, and opening one shows the normal conversation of that agent. A channel gives each member a thread under the same agent id, so a channel message could arrive as a result that opens a conversation without it. The conversation search now excludes the channel threads, the same way the agent roster does. - A channel assignment reserves the host, and every path that ends one must schedule the agents held behind it. Two paths did not: an assignment cancelled while its delivery was prepared, and one stopped before its delivery started. Both now release the held agents. - The Usage report covers the workspace and marks it inert, so a channel message that arrives behind it was never seen. The read now refuses while the report is open, and opening or creating a channel dismisses the report. - Only the save that creates a channel opens it, and only while the reader has stayed where the save started. A save that landed after the reader opened another channel pulled them back out of it. Break-the-behaviour checks, each confirmed to fail for its own reason and then restored: removing the channel-thread clause from both search queries; removing each `#releaseHeldAgents()` call; removing `!usage.state.serverId` from the channel read gate; and reducing the creation guard to `if (creating)`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * test: wait for the routine cancellation marker after a restart The restarted service reconciles the cancelled delivery while it starts, so the marker that reconciliation writes was read too early under CI load and the test went red. The read now waits for it. The count stays a plain assertion, so a second marker still fails the test. Break-the-behaviour check: with the marker persistence kept rejected, the test fails because no marker ever arrives, not because it arrives late. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: answer the third channel review round - A settings save waits for the one before it, and the reader can open another channel while it waits. The queued save now keeps the channel and the fields it was made in. It built its draft from whatever channel was open when it ran, so it could write one channel's members and text into another, or drop the save while the next page loaded. - One shared message is allowed to be larger than a whole summary input, so it never fits beside another one and the loop had nothing to summarize. It blocked every later request in that channel for good, because no shorter request removes a stored message. Such a message is now summarized alone, in parts that fit, and keeps its source ID. - Every completed turn of every normal chat wakes the channel scheduler, and the scheduler asked for the sidebar summary, which reads and parses every message of every channel. The scheduling paths now work from the channel ids and the archived set, each one query. Break-the-behaviour checks, each confirmed to fail for its own reason and then restored: building the queued draft from the open channel again; restoring the error that a message too large for one input raised; and the scheduling paths keep their behaviour, which the 38 channel service tests cover. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: answer the fourth channel review round A settings save that waits behind another one now carries its own text. It takes only the members and the lead from the save before it, because those build on each other, while the text is the value the reader left in the field. Before this, the queued save re-sent the earlier text. A routine run whose open tasks all wait behind a failed or paused task now reports that blocker, with its reason, instead of "queued". A task starts only when the work it delegated is complete, so nothing but a human moves that branch again. Break-the-behaviour checks: with the away branch built from the previous draft, "keeps the text a queued settings save carries when the reader opens another channel" fails with title "" in place of "Weekly sync"; with the blocker lookup replaced by null, "reports the failure that holds a delegated run instead of reading it as queued" fails with status "queued" in place of "failed". Both files pass again after the restore: 20 renderer tests and 12 scheduler tests. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * perf: summarize a channel without reading its history The sidebar summary read, parsed and validated every message of every channel. The renderer rebuilds it on each `channels-changed`, and a streaming reply emits those while it arrives, so one long channel made every frame of a stream parse the whole archive. Each summary now reads one message for the preview and counts the rest in SQL: the unread count runs over the (channel_id, sequence) index and the active tasks over a state filter. The duplicate author-navigation test of `ChatMessageRow` is removed. `App.channels.test.tsx` clicks both controls and asserts the agent conversation opens, which is the user consequence the removed test only saw as a spy call. Break-the-behaviour checks on "summarizes a channel from the read cursor without reading its history": without the author filter the unread count is 3 in place of 2, and without the state filter the active tasks are 2 in place of 1. 39 channel-service tests and 59 MessageRendering tests pass after the restore. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: keep shared channel data when a member agent is deleted A channel message carries the files of the mailbox delivery that sent it. The deletion of the recipient agent removed that delivery, its message and its transfer files, so the channel kept showing an attachment that no longer resolved. Files the agent generated inside a channel thread went the same way. Both now stay: they belong to the shared transcript, and `deleteChannelData` already removes them with the channel. The caller names the channel threads, because a generated file records the thread it was made in. A settings save also refused every draft that held an unavailable member, so after two member agents were deleted the reader could not remove either one, nor edit any other field: each removal sends the whole membership list, and the other deleted member was still in it. Only a member the draft adds must be available now. Break-the-behaviour checks. "keeps the shared files of a channel when a member agent is deleted": without the channel-message guard the uploaded file resolves to null, and without the channel-thread guard the generated file does. "removes a member the deletion of its agent left behind, and still refuses a new one": with the earlier guard the removal throws "A channel member is unavailable.". After the restore, 40 channel-service, 35 mailbox-store, 11 restart and 34 queue tests pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: keep a channel upload that waits for a free member A channel dispatches a request when a member becomes free, which can be after a restart, and a restart clears every attachment draft and deletes its files. The queued task kept only the draft ids, so its dispatch failed with "Attachment draft no longer exists" and no retry could bring the upload back. The send now commits its files before it is accepted: the mailbox stores them as a channel-owned message with no delivery, and the request keeps durable references that every later dispatch re-sends. A request that carries files is also no longer steered into a running turn, because a steer carries text only. Also index the tasks and the messages of a capture once instead of once for each message of the thread: a streaming turn calls it for every delta, so the cost was the square of the transcript. Break checks: - Restore `task.attachmentDraftIds = command.attachmentDraftIds` and drop the commit: the new test fails at the dispatch, which never creates a delivery. - Drop the text rewrite: the request text keeps the draft reference and both attachment tests fail on the committed id. - Drop the stored attachments: four tests fail with no file to send. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: answer the eighth channel review round Four findings, all in the paths the previous round touched. The upload copy of a send is now the first thing it does, before it reads the channel or its tasks. `archive` and `stop` do not queue behind the other commands, so a state read before that copy could be written back after an archive: the channel came back and its work started again. The send reads the channel after the copy and refuses to write into an archived one. A settings save now says that it edits an open channel. Settings save on every field, so a save can still be queued behind the deletion of its own channel, and a save carries the whole draft: it recreated the channel with its name, instructions and members. A save without the flag still creates, which is how a channel is made and how every released client saves. A channel read is coalesced: one read at a time, with at most one more behind it. A channel publishes on every streamed chunk, and a read is two calls, so starting a read for each event and keeping only the newest threw away every answer while a member was writing. The transcript stood still until the turn ended and an opening channel stayed on Loading. A failed routine run follows the retry of its task. Continue and Reassign restart the task under the request the run holds, but reconciliation read only the open runs, so the history reported Failed for work that later finished. Break checks: - Read the channel before the copy instead of after: the archive test finds the send accepted and the channel restored. - Drop the update flag from the save: the deleted channel comes back, and the settings saves of the renderer test no longer carry it. - Drop the coalescing: the held read is discarded and the message never appears in the transcript. - Reconcile the open runs only: the retried run stays failed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: answer the ninth channel review round Carry the `update` flag of a settings save through the channel wire codec. Both the remote transports and the host route encode a command with it, so a save that reached a host after the channel was deleted created the channel again with its old settings and members. End a channel command on a read that started after it. The read already in flight began before the write, so its answer holds the state the write replaced, and a settings panel that saves each field built the next draft from the member list it removed: two removals in a row put the first member back. Break checks: - Drop the flag from the codec: the encoded save no longer carries `update`. - End the command on the read in flight: the second removal saves Chief back into the channel. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: keep the routine timer to one pass at a time A fire writes, and every channel write arms the timer. The owner behind the one that is firing is still due at that moment, so the timer woke again and started a second pass over the list the first pass was still working through. The two passes then fired the same routine: the repeated command answered with the receipt of the first run, which carries no request id, so the run row took a request message that the channel never received and reconciliation cancelled a run whose work was still going on. Held open, the timer also armed a new zero-delay wake for each pass, so the passes multiplied. `arm()` is now a no-op while a pass is in flight. Every pass ends in `arm()`, which reads every source again, so a change that arrives during a pass is answered, not lost. Break check: without the guard, the owner that fires first is asked 1001 times in the second that its fire is held open, instead of once. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: answer the eleventh channel review round Bound the name of a channel message author by the account name, not by the agent name. The author of a channel message can be a person, and a person's name holds up to 120 UTF-16 units, so a member with a long name wrote a message that no read could decode. Unload every provider session of an agent when its profile or its memories change. A channel turn runs on a session of its own, and the standing instructions are written when a session loads, so a channel turn continued with the values the agent had before the change. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: answer the twelfth channel review round Report the provider as busy while a channel turn runs. A channel turn holds its turn id in an execution snapshot, not in the conversation of the agent, so the guard read the CLI as idle and a CLI update stopped the client under the running turn. Keep the queued channel work when the queue is reordered. The queue the user reads hides channel deliveries, so the order it sends names the normal messages alone and the mailbox refused it as stale. The channel work is merged back at the head. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat: make the channel coordinator cheap, deterministic, and visible A channel request with no named recipient used one routing turn of the lead's own model, sent up to 40 000 characters of transcript, read its answer out of prose, and told the user nothing unless it failed. - Two more selections need no model: a channel with one available member, and a reply to a member message that carries no task. Both stay silent, because a dispatch message exists to make a model's choice auditable and no model was asked. - The routing prompt sends the persisted channel summary in place of the transcript, then the last five messages the summary does not cover, each cut to 600 characters. The prompt budget is now three named constants. - New `structured-output.ts` renders a zod schema into the prompt and validates the reply against it. The four routing outcomes become a strict union, so a stray field or two outcomes at once are rejected as data, not counted by hand. `profile-generation.ts` uses the same helper for its own parse. - A model decision now posts one channel message authored by the lead agent itself, so it carries the lead's face and colour and the user can correct the choice. `idle` and the failure notice keep their behaviour. Note: `strictObject` rejects a decision that adds a key such as `reason`, where the old key count accepted and ignored it. If that appears in practice, add the key to the schema and put it in the dispatch line. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: keep a channel alive on the screen while its lead routes The routing turn moves no task out of `queued` and, before a member owns the work, writes no message. The transcript stood still for the whole turn, which reads as a channel that dropped the request. - The lead joins the working faces while a task has no owner and is `queued` or `waiting`. Routing that ends without an owner leaves the task `paused` with the coordinator's reason under the transcript, and that notice is the indicator from then on, so the face goes. - The channel indicator takes the agent chat's shifting label, so a reader who moves between the two surfaces meets one indicator. The announcement carries the subject and the label, and the tests match on the subject. - The 24px avatar lift and the working face size move to one rule each, so the New channel dialog and the activity row read them instead of restating them. A member row inside the create form no longer paints a second hover inside the label that already paints one. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: stop a chat from paging in its history under the newest message A transcript that is short against its viewport renders its first row at every scroll position, so the row index alone reported the history boundary while the reader sat at the newest message. The chat then asked for the next older page under a reply that was still arriving, and each page moved the transcript. `chatHistoryBoundaryReached` needs both signals: the reader near the top and a low first rendered row. The distance alone reports the boundary in a virtualized transcript that holds its first row far above the screen, which is why the two readers of the boundary each used one signal. Both now use the same function, and the scroll store has one writer for the boundary signal. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * chore: make the dev seed coherent and give it channels The seed dated every record from one fixed timestamp, gave each agent a hand-written preview that no message supported, wrote mentions as plain text, started threads with a reply that answered nothing, and had no channel at all. - One clock, created per run. Every conversation, team chat, routine run and channel message dates backwards from it, so the date separators read Today and Yesterday and the transcript no longer sits weeks before the routine runs. - Each preview is built from that thread's last request with `displayMessageReferences`, the same rule a real send applies. Mentions use `serializeChatTagReference`, so the composer's own token is seeded. - Every reply now follows a request, and each agent gains a later exchange. - Two channels: a launch room with a lead, four members, a delegated child task, a failed task, a routine-fired request, one generated file, a read marker that leaves two messages unread, three memories and two routines with two terminal runs; and a small archived channel, so the archived view is not empty. - No seeded task is `queued` and no routine run is open, so startup recovery begins no provider turn. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: address a channel member from the composer, not from a menu The channel composer carried a recipient menu beside the send button - "Let the channel choose" and one item per member - while the same composer already ran the agent chat's inline `@` picker over the channel members. Two affordances for one choice, and the menu won over the text. The menu was also the worse of the two. Its trigger drew only an `@` glyph, so the chosen recipient reached assistive tech and nothing else, and `clearSent` kept the recipient after a send: a reader who chose Chief once addressed every later request in that channel to Chief until the channel changed. A channel request is now addressed the way a normal chat message is: type `@`, and a request that opens with a member goes to that member. A mention anywhere else stays a reference the owner of the work can read. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: keep channel read state and panel counts after sign-in `HostService.channelActor` answers `local` while no account is signed in and a team member id after it. Channel read cursors were keyed by that id and never moved, so signing in turned every read channel unread. Direct threads already adopt their cursors this way; `ChannelStore.adoptReads` gives channels the same behaviour, and a reader that already holds a cursor keeps its own position. A message the reader wrote before signing in carries the signed-out id too, so the unread count read it as another person's message. Both the count and the transcript now treat that id as the reader, through one shared `SIGNED_OUT_CHANNEL_MEMBER_ID`. The channel settings row asked the memories and routines views for their counts, and those views render only after the reader opens them, so both counts stayed at zero while the data existed. The conversation loads them from the ports instead. The dev seed ran one routine twice, and the mailbox stamps its own clock on every request, so the two runs rendered as one instruction repeated at one time. The manual run now belongs to the Friday review routine. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> | 1 month ago | |
Continue a stopped subtask when a channel message says "continue" (#6114) * Continue a stopped subtask when a channel message says "continue" A restart or a failed turn can stop a subtask while its parent waits for it. The "continue" rule looked only at stopped top-level tasks, so the message started a new request and the stopped subtask stayed stopped. Stopped subtasks whose parent waits now count as stopped work of the request, as the Continue control already treats them. Fixes #6010 * Keep the reply rules of a held reply task that "continue" resumes A held reply task answers the member that waits for it. When "continue" resumed it, it got the new message as its request and the result rule of a normal task. Thus it was no longer a reply task: its answer did not go back to the member, and the reply tool limits stopped. It now keeps its reply message and its result rule, as the Continue control does. | 18 hours ago | |
Make channels feel like chats and run channel turns beside the own chat (#5790) * Make channels feel like chats and run channel turns beside the own chat - Show progress notes as separate messages when they are complete, and slide new channel messages in. - Group "Assigned to" rows, remove own-name labels from member messages, and show a sent message once. - Send mentions after a quote, several typed names and late names to the named members. - Give each channel its own mailbox lane and side thread, so a channel turn runs beside the member's own chat. An agent runs one channel turn at a time. Providers that run one turn at a time keep the serial order. * Keep channel turns apart from the own chat in failure, clear and stop - A failed channel turn no longer marks the own chat as failed. - Clear, delete history and compact wait for channel work only when the provider runs one turn at a time. - interrupt_agent tells the caller that a channel task still runs. - An empty streaming row that holds only the member's name stays hidden. - After a reconnect, channels load again so that no missed event stays lost. * Make the channel_assign description shorter to keep the context budget The task prompt already tells a member to do its assignment itself. * Give each sent channel message its own row key When a channel guess took the wrong stored message, the send's own id later mapped to the same pending row key, and two rows shared one key. | 1 day ago | |
Make channels feel like chats and run channel turns beside the own chat (#5790) * Make channels feel like chats and run channel turns beside the own chat - Show progress notes as separate messages when they are complete, and slide new channel messages in. - Group "Assigned to" rows, remove own-name labels from member messages, and show a sent message once. - Send mentions after a quote, several typed names and late names to the named members. - Give each channel its own mailbox lane and side thread, so a channel turn runs beside the member's own chat. An agent runs one channel turn at a time. Providers that run one turn at a time keep the serial order. * Keep channel turns apart from the own chat in failure, clear and stop - A failed channel turn no longer marks the own chat as failed. - Clear, delete history and compact wait for channel work only when the provider runs one turn at a time. - interrupt_agent tells the caller that a channel task still runs. - An empty streaming row that holds only the member's name stays hidden. - After a reconnect, channels load again so that no missed event stays lost. * Make the channel_assign description shorter to keep the context budget The task prompt already tells a member to do its assignment itself. * Give each sent channel message its own row key When a channel guess took the wrong stored message, the send's own id later mapped to the same pending row key, and two rows shared one key. | 1 day ago | |
Add fast mode for Claude agents (#6261) * Add fast mode for Claude agents Claude agents can use fast mode from the model picker when Claude Code reports that the model supports it. The turn sets the fastMode flag setting of the claude process. Before fast mode turns on for a Claude agent, a dialog asks the user to confirm the higher price. When Claude Code runs a turn at the standard speed, the chat shows the reason once. * Turn off fast mode on a provider change A provider change sets fastMode to false, so a Codex agent with fast mode does not use Claude fast mode without the price confirmation. The first Claude turn of a process also sends fast mode off, because a Claude settings file can start the process in fast mode. A pending confirmation ends when a different agent opens. The chat warning is marked as sent only when it was sent. The release notes file now has the branch name. * Turn off fast mode one time for Claude agents Before this release only Codex read fastMode. A Claude agent has the value true only from a provider change, and the user never confirmed the Claude price for it. At the first start of this release, the agent store sets fastMode to false for each Claude agent. The receipt of the command is the marker that stops a second run. * Add the fast mode resets to the release note * Keep fast mode across a backup model, and hide Claude fast mode from members | 14 hours ago | |
Show what fills the Claude context in the composer popover (#2314) * Show what fills the Claude context in the composer popover The Claude SDK context answer has categories. The backend maps them to fixed categories and sends them with the context usage. The popover shows one row and one bar segment for each category, and the free space. Other providers send only a total, so their popover does not change. Refs #1635 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Keep the Claude breakdown when the rows are more than the total The CLI gives the input of the last API answer as the total, but its messages row adds an estimate of the messages after that answer. The breakdown was dropped in that case. Now the excess comes out of the messages row. The guard also refuses a category that occurs two times. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 24 days ago | |
Show the cost of OpenCode turns in Usage (#3222) * Show the cost of OpenCode turns in Usage OpenCode sends the session's total cost in `usage_update` before the prompt result. OpenBot dropped it, and has no OpenCode price, so every OpenCode turn had no cost. Keep the last total in its own checkpoint for each session and book the difference for each turn. A session that has records but no cost checkpoint started before this change: its first total is only the start. Fixes #3135 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Look up older sessions only for a reported cost Run the records query for a missing cost checkpoint only when the sample has a cost. The E2E finds the session through the active provider session. The release note names each case with an unpriced first cost. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Read the history of one Claude session one scan at a time A Claude history scan holds a write transaction on its index while it awaits transcript reads. A second scan of the same session at the same time failed at once with "database is locked". The boot backfill and a backup-model continuation can read one session together, and the backup-model-failover E2E failed on Linux CI. Scans of one index now wait for the scan before them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 15 days ago | |
Keep Claude signed in after OpenBot restarts and updates (#5187) * Read the stored Claude sign-in before `claude auth status` At each provider start, OpenBot read the Claude account with `claude auth status`. When the access token is near its expiry, that command starts an OAuth refresh and exits after 2 s without saving the rotated tokens. The next Claude process then gets `invalid_grant` and clears the sign-in, so users had to connect Claude again after a restart or an update (anthropics/claude-code#105645, #95822). The account check now reads the sign-in that the CLI stored (the macOS Keychain item or `.credentials.json`, and `.claude.json` for the email and organization). With a refresh token stored, it starts no CLI process. With no usable refresh token, an unreadable store, or an auth variable in the environment, it runs `auth status` as before, which then has no token to spend. Add scripts/claude-stored-sign-in-e2e.ts to CI. Keep the login of the computer out of claude-api-key-e2e. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Ask the Claude CLI when more auth variables are set The CLI also reads gateway, AWS, Google Cloud and Mantle backends, a refresh token, identity tokens and the OAuth endpoint from the environment. With one of them set, the stored sign-in alone can give a wrong account, so the account check runs `auth status`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 4 days ago | |
Put the Workspace only sandbox in the Claude settings file (#5597) * Put the Workspace only sandbox in the Claude settings file When ~/.claude/settings.json has sign-in settings, OpenBot gives them to the Claude Agent SDK as a private settings file. The SDK refuses a settings file path together with its sandbox option, so each Workspace only Claude agent failed to start. The sandbox now goes in the same file, as the SDK error says. The keys stay off the command line. With no settings file, the sandbox option is the same as before. Fixes #5583 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Type the Workspace only sandbox as a Claude settings value The sandbox now goes in the Claude settings file. The `Settings` type of the SDK does not accept `undefined` for optional sandbox keys, and the CI type ratchet runs with exactOptionalPropertyTypes. The SDK `sandbox` option accepts this type too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 2 days ago | |
Add Mistral as a built-in provider through Mistral Vibe (#5582) * Add Mistral as a built-in provider through Mistral Vibe OpenBot downloads Mistral Vibe 3.1.0 as a managed runtime and runs vibe-acp over ACP. Users sign in with the browser or set MISTRAL_API_KEY. Disconnect signs the CLI out with _auth/signOut. The sandbox keeps Vibe state in VIBE_HOME and trusts the workspace. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix two comments about ACP settings and additional directories Move the timeout comment back to #timed, and say in the comment that a flavor can also send additionalDirectories. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 2 days ago | |
Show Cline's free models and run the selected Cline model (#2528) * Show Cline's free models and run the selected Cline model (#2519) `cline --acp` lists only the OpenRouter catalog bundled into the CLI. The Cline CLI's own picker reads the free tier from `api.cline.bot/api/v1/ai/cline/recommended-models`, so OpenBot never showed `cline-free/*` models. - Read Cline's public free-model list when the Cline catalog refreshes, and add those models after the CLI's own models, with a "(free)" name. On failure, keep the last good list, then a bundled list. - Send a selected model to the model option that lists it. Cline puts a `provider` option of the model category first, so the model was never sent and the agent stayed on Cline's default model. - Send a model that OpenBot added to the agent's `model` option. - Add `scripts/cline-free-models-e2e.ts` and a PRIVACY.md note. The `~...-latest` alias ids that Cline lists stay dropped. Fixes #2519 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Apply review findings to the Cline free models - Set the `cline` provider before a free model. The free tier is on Cline's own billing, and a session can start on `cline-pass`. - Give the free models to the profile client too, so side questions and profile generation run on the selected free model. - Keep the `acpNoModels` error when Cline lists no model. - Use one deadline for the response headers and the body. - Accept an empty free tier. Before, the picker kept the old list. - Find the model option in one lookup. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Set the provider back after a Cline free-model turn A free model sets the session's `provider` option to `cline`. A later turn on a listed model kept `cline`, so the listed model ran on the wrong provider. OpenBot now records the session's own value before the first change, and sets it back before a listed model. The E2E now runs a listed, a free, and a listed model in this order. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 21 days ago | |
Add workflows: connect agents and steps on a canvas (#3062) * Add workflow IPC contracts Types and guards for workflows: graph, summary, detail, runs, steps, versions and their inputs. Add the input limits and the workflows-changed agent event. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Add the workflow tables (migration 46) Migration 45 only creates tables and indexes: workflows, nodes, edges, versions, triggers, trigger webhooks, webhook receipts, runs, steps and output deliveries. No row of an existing table changes. The same SQL is the tail of the latest schema for new databases. - workflow-migration.test.ts covers every schema from 8 to 44, rollback and retry, newer and incomplete histories, foreign keys and cascades. - The older migration tests expect version 45 as the latest. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Add the workflow store and graph rules The store saves graphs with a revision check, keeps up to 50 versions, and keeps trigger rows. Deleting an agent removes it from workflow nodes and keeps the step history. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Add the workflow IPC endpoints The host answers list, get, create, rename, delete, save, versions and restore. A joined server says that it does not support workflows yet. The preview keeps workflows in memory. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Add workflow runs Run a workflow from the start, from a trigger, or from one node, and run one node alone. Agent steps go through the routine sender, so an agent answers in its own chat. Model and condition steps use a tool-less generation; a condition answers yes or no. Approvals wait for a decision, and a rejection stops the run. Notify outputs show a desktop notification. A sweep at startup settles agent steps from their delivery and fails model steps that a restart interrupted. Channel outputs fail with outputPending until the messaging-place output lands. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Add the workflow run view and rail button - WorkflowFeatures and the v1 set of node kinds, triggers and outputs. - A pure run view: pages of runs, run and step changes, and the run state of each node. - The Workflows rail button (Waypoints) and app.rail.workflows in all languages. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Move the workflow nodes into shared UI - The board, camera, picker, tool art and one file per node kind move to packages/ui/src/features/workflows, with their styles. Their icons go through the shared icon exports. - Nodes read agents, models, places, image models and files from WorkflowCatalogs on the board. No story data or simulated runs. - The agent node writes agentId; a channel output writes connectionId and placeId, as the contract says. - 199 English keys in the workflow module. Result times are ISO and formatted in the reader's locale. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Split the workflow canvas into shared UI Move the canvas from the prototype into packages/ui as a controlled component. The host gives the workflow, runs, versions and catalogs, and gets each graph change. Remove the run simulation, presence and local ids. Add the canvas keys in English, the Workflows stories over the preview mock, and the Storybook icon link. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Add the Workflows page to the desktop window The Waypoints rail button opens a titled page with the workflow canvas of the server. The page saves the graph 400 ms after the last edit, one save at a time on its base revision, and loads it again after a change from another device. A joined server without workflows shows a notice. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Add workflow triggers, place outputs and workflows-v1 - Schedule triggers: WorkflowTriggers is a routine due source. A missed time gives one run, and a time is skipped while a run is open. - Webhook triggers: a third webhook route owner. The first secret makes the route. Receipts stop a second run for the same delivery. - Place outputs: an output posts the result to a messaging place, once, and an interrupted post becomes uncertain at the next start. - workflows-v1: the frozen Team API routes, codecs and event, the host route with its grant rules, the team client and the remote IPC branches. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Translate the workflow catalog Add the 302 workflow keys in de, es, fr, it, ja, pl, pt, ru and tr. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Edit trigger schedules and webhook secrets on the canvas A trigger node uses the routine schedule picker and saves the schedule on the node. A webhook trigger without an address has a button that makes the first secret, and so the address. The preview gives the next run and the address. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Document workflows PRIVACY.md says what a workflow keeps on this computer and what an output and a webhook trigger send. The architecture notes describe the run engine, the timer source, the webhook owner and workflows-v1. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix the workflows page height and the canvas after a delete The canvas is a direct child of the window page, so it gets the height of the pane. The page reads its host once, without a tracked read. When the host opens another workflow after a delete, the canvas shows again; a switch while the Undo toast is open keeps the delete. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Name migration 46 in the workflow docs Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Scope workflow run and step keys to their workflow A copy of a workflow keeps its node ids, so its schedule and webhook keys were the same text as the first workflow's, and its runs were answered with the other workflow's run. The keys are now unique per workflow. Migration 46 is not released, so its tables change in place: steps check their kind, and the unused waiting status, trigger enabled and last_scheduled_for columns, delivery retry columns and the inactive receipt reason are gone. A refused delivery gets its own reason. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix workflow run starts, the sweep and schedule waits - A run, its version, its reused steps and its webhook receipt are one write. - A webhook request that the graph refuses gets an ignored receipt and 202. - A trigger of a workflow with an open run waits for the run to end. - One failing schedule trigger does not stop the others. - The sweep ends steps that a restart or an agent delete left open, and ends a stop that a restart cut off. - A run version is kept only when the graph changed. - Store errors carry a code; the Team API maps missing to 404 and a bad schedule to 400. The list reads every graph in one query. - The runs cursor names the run, so runs of the same millisecond stay. - The routine timer plans again only when a trigger changes or a run ends. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Share the workflow helpers with routines Use one placeholders, receipt cutoff, result text, node agent, node timezone, CHECK column and coalesced sweep helper. Build the edge maps in place. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix workflow canvas bugs in keys, drags, uploads and approvals - Agent and LLM pickers set model, provider and effort as one change. - Undo and redo stop a drag and cancel queued layout frames. - Keys, clipboard and Delete act only in the canvas, its portals or the page body. Menus and lists keep their letter keys. Delete and cut do nothing on a frozen or deleted workflow. - Drag and pointer listeners are removed on cancel, restore and unmount. - Slots and ports stop the resize observer when they go. - Asset uploads belong to the board. Their blob URLs are freed at unmount, a file is found by id, and the upload button follows features.uploads. - The canvas label names the workflow. The minimap is hidden from screen readers. The live region tells only the step status changes. - Ports set aria-pressed only while a line is drawn. - A new note saves the name of its author in its fields. - Picker tiles get the colour of their kind. - A new node does not cover a node at the centre of the view. - Search reads the text fields of the board, not the DOM. - An approval answer is for one step, and shows again when sending fails. - Each trigger gets its own new schedule object. - The search fields show a grey focus ring. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Make the workflow canvas do less work for each change - Selected, removed and locked nodes are projections. A node runs again only when its own state changes. - The list of nodes on the canvas and the trigger lookup are memos. - Fit to view and the minimap use the sizes from the resize observer. Fit reads the element only for a node that has no size yet. - A line that the user draws adds points in place. A move does not copy the line or put it in a store. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Open the workflows-v1 names and add the version graph read - The wire carries node kinds, edge types, statuses, sources, trigger types and version kinds as short names. A client maps a name it does not know: a node becomes a locked note, its edges go, and the workflow is read-only. - New workflows.versionGraph read (IPC and Team API). The host hides a version that names an agent the member cannot see. - The IPC parse keeps only known graph keys and refuses a graph over 1 MiB with its own sentence. - Align wire limits: runs limit 1-50, run number and attempt >= 1, runs cursor up to 64 characters. - Move the workflow decoders to remote-workflow-decoding.ts. Delete the unused team-client and web-runtime workflow requests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Ask for the manage level to change a shared workflow - A member needs manage on each agent of the graph, before and after, to save, rename, delete or restore a workflow or to make a new webhook secret. A run, a stop and a decision still need chat. - Retry, stop and decide also check the agents of the run's own graph: a retry ran an agent that the current graph no longer names. - An owner or admin sees a workflow whose agent was deleted. - A save conflict answers as missing when the newer graph names an agent that the member cannot see. - Run answers empty the oldest step texts to fit one frame, as the run page does. A single step of a hidden agent answers 404, not 500. - Reuse runEventsEffect from route-events.ts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Share one workflows controller between the page and stories - Move the save queue, resets, restore, preview and delete into workflows-controller.ts. The page and the stories use it. - A version preview reads its graph from the host. - A conflict or a restore keeps the canvas edits on undo, and a toast tells the user. - A failed save keeps the edit for the next save. A restore or a delete saves the waiting edit first. A failed delete shows the workflow again. - A member without "Can manage" on each agent gets a read-only canvas. Only an admin can rotate a webhook secret. The channel output hides when there are no places. - Add the Korean workflow catalogs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Document workflow permissions and test agent steps Docs and the release note give the manage rule, the version-graph route and the sweep rules. A new e2e checks that an agent delete fails the open step, and that a restart ends a stopping run. The webhook e2e checks that a graph this version cannot run gives a refused receipt. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix workflow canvas halt on restore and new node placement A restore that removed a node wrote the slot size store in a list cleanup, which halts Solid. The reset now drops the sizes of the removed nodes. A new node now moves past each card that it would cover, not only an exact corner. The controller reads the server role untracked. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Read the first agent fields of an agent step untracked The catalog agent is a store, so a read of its model and provider in the component body warned that the value does not update. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Remove unused workflow exports and fix optional props for the type ratchet Knip found 13 exports that only their own file uses. The optional props that take an undefined value now say so, as exactOptionalPropertyTypes requires. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Document that workflows with no agent need no agent level Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Keep the workflow canvas and its toolbars on whole pixels Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fade workflow canvas parts in out of a blur instead of a move Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Remove the entry animations of the workflow canvas Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Match the workflow title to the other page titles and remove its icon Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Balance the paddings of the workflow title and HUD buttons Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Give the workflow top row the button size of the other pages' top rows Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Make the workflow canvas icons and node toolbars sharp On a screen with one device pixel for each CSS pixel, the canvas icons get a line of 2, so a line fills whole pixels. The node toolbar icons are 16px, as in the tool bar. After the camera, a node or the selection moves, the node toolbars and result strips move onto whole device pixels. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fill the icon of a pressed workflow canvas tool Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Snap the workflow node toolbars to whole pixels and enlarge their icons The earlier sharpness commit added the snap offsets in the canvas but not the CSS that reads them. The node toolbar and the result strip now add the offsets to their position, and the toolbar icons show at 16px. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fill the pressed Select arrow fully The part-fill rule matched the svg element too, so it had more specificity than the arrow rule, and the arrow stayed at 35%. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Give workflow sticky notes the corner radius of the other cards A note had a 6px corner that shrank with the zoom while its selection ring and resize arc kept their screen gap, so the corners did not look alike. Notes and their drop preview now use the card radius. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Allow loops in workflows A connection back to an earlier step is a loop. The run takes it as the next pass: the earlier step runs again with the loop's answer as one more input, and the steps after it follow. A loop has no pass limit; a condition, a failed or rejected step, or a stop ends it. The canvas draws a loop under its steps with a "Loop" badge, and with an "Endless loop" warning when no condition is in the loop. While the user connects, the draft line says that the connection makes a loop. A retry now runs every step after the retried step again, not only the skipped ones. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Do not select a workflow step on a press of its port A press on a port, a portal or the edge actions selected the step under it, so a new connection also moved the selection. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Show why a workflow Output or Condition step failed These cards have no result field, so a failed step showed only its status. They now show the error of the step. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Retry a workflow step with the node's current settings A retry used the settings that the run copied, so a fix made after the failure, such as a new output place, did not apply. A retry now uses the current node, and the run's copy only when the node is gone or is now another kind. The Team API already checks the agents of both graphs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Reuse a warm provider for workflow model steps and count their usage A model or condition step started a new provider process each time and recorded no tokens. Now one process without tools per provider stays for 5 idle minutes; each step still starts a new ephemeral thread and closes it after the turn. The process stops when the main client stops or changes, after a failed step, and at shutdown. The tokens of these steps go to one reserved usage row, WORKFLOWS_USAGE_ID, which the host report, the Team API filter and the usage panel accept. Agent steps still count under their agent. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Report workflow runs and actions to product analytics The host sends system_workflow_run when a run ends (outcome, source, duration, step counts by kind) and workflow_count in the daily inventory. The window sends workflow_action for each user action on a workflow. No name, text, graph or ID is sent. Host generation 11, desktop 12. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Translate the workflow text to Simplified Chinese main added zh as a 12th language. Add the workflow and workflow error catalogs, and the Workflows rail, usage and notification keys, so zh has every key. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix workflow review findings before merge - Reuse steps in graph order, so a reused step does not make a node behind it due. - Build the input of a retried agent step again from the current task. - Show the Workflows usage row only to a peer with workflows-v1. - Keep the warm client state correct when a generation is interrupted, and wait for idle stops at shutdown. - Send the duration of a workflow run up to 30 days. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 16 days ago | |
Let a pinged person answer an agent's Slack post without a mention (#5910) * Let a pinged person answer an agent's Slack post without a mention When an agent's messaging_post pings a person with <@U…> in Slack, OpenBot records the person in the new projection_messaging_pings table (migration 59). A reply from that person in the post's thread now reaches the Slack Orchestrator without a mention. The Orchestrator gets the post and the name of the agent that sent it as the message that the reply answers. Replies of other people still need a mention until the thread has its link. The link deletes the thread's pings, and deleting an agent deletes its pings. A thread message event that mentions OpenBot is now left to its app_mention event, so the order of the two events does not decide the route. In Slack threads an agent can answer only NO_REPLY when a message needs no answer. OpenBot then deletes the "Working on it…" post and keeps the reaction. Fixes #5565 * Hear only the pinged people in a thread that a ping opened In a Slack thread where an agent's post pinged people, a reply without a mention now counts only from a pinged person, also after the first reply gives the thread its link. Other people reach the Orchestrator only with a mention. The ping rows now stay with the link, and deleting an agent deletes the pings of its threads and its pings of threads that no reply opened. A thread that a person opened with a mention still hears everyone. Refs #5565 * Fix the local review findings of the Slack ping replies - An answer in a thread that a ping opened records the people that it pings, so they can reply without a mention too. - MessagingThreads.recordPings does the link check and the save in one Effect.try, and logs a failure. A database error after the post no longer fails the tool call. - The Orchestrator reads its own pinging post as its own, not as a teammate's post. - The stored post uses the prompt's REPLY_CHARACTERS limit. - The E2E reads each dropped reply again after a later reply arrives. Refs #5565 * Give a pinged person's first reply with a mention the post as context A first reply that mentions OpenBot comes only as app_mention, so the receive path did not read the thread's pings. The reply went to the workflows, and the Orchestrator did not get the post that it answers. The receive path now reads the pings for each Slack thread message with no link. The E2E covers this case. Refs #5565 | 1 day ago | |
Remove unit tests and keep E2E and protected tests (#2051) * Remove unit tests and keep E2E and protected tests Delete 591 vitest unit test files and their test-only harnesses. Keep E2E and smoke scripts under scripts/, and keep vitest files only for migrations, schema parity, the renderer-to-main trust boundary, secret redaction, and check-rule fixtures. Remove the test runners that have no tests now (site-router, remote/api, contracts, brand, auth-api client), their CI jobs, and the transfer budget report. Remove code and dependencies that only the deleted tests used. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Set the E2E-only test policy and remove references to deleted tests AGENTS.md now permits vitest files only for migrations, the trust boundary, secret redaction and check-rule fixtures. All other behaviour gets an E2E script under scripts/. Remove references to deleted tests, harnesses and commands from docs, AGENTS.md files and skills. The release-upgrade-safety gates that ran a deleted test now use a manual check. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Keep migration tests and fix CI checks after the test removal Move the migration cases out of the deleted mixed test files into three migration test files: the v15 agent usage upgrade and rollback, the read-state baseline from schema v2, and the remote control plane and permanent invitation D1 migrations. Keep error.agent.noStartingModel for the text that older hosts send, and let i18n-check accept it. Lower two type baseline entries. Correct comments and docs that named removed tests or test commands. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Remove the empty test patterns from the knip configuration Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Add E2E checks for Signal secrets, IPC input decoding and analytics redaction - scripts/signal-security-e2e.ts starts the real Signal service with throwaway secrets. It checks the Telegram secret header, the Slack signature, signed account events, remote and route tickets, webhook headers, and that no frame or log line holds the bot token. CI runs it in the new Signal E2E job. - scripts/ipc-input-e2e.ts sends malformed requests from a running dev app and checks that the main process decoders refuse them. - scripts/analytics-redaction-e2e.ts checks that no captured analytics or failure report request holds a seeded secret. For this, OPENBOT_ANALYTICS_CAPTURE_URL sends analytics to a loopback capture server. Only an unpackaged build reads it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Check the renderer analytics path and path traversal in the E2E scripts The analytics E2E now loads the window again and waits for the renderer event, so the renderer send path cannot pass with no request. The IPC input E2E now sends an attachment id and a shared file path that try to leave their folder. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Remove an unused optional parameter from the read-state migration test The parameter gave one exactOptionalPropertyTypes error, which the type ratchet refuses for a new file. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 27 days ago |
| Files | Last commit | Last update |
|---|---|---|
| 5 days ago | ||
| 3 days ago | ||
| 3 days ago | ||
| 19 days ago | ||
| 14 hours ago | ||
| 5 days ago | ||
| 1 day ago | ||
| 1 day ago | ||
| 23 days ago | ||
| 5 days ago | ||
| 16 days ago | ||
| 2 days ago | ||
| 23 days ago | ||
| 19 days ago | ||
| 19 days ago | ||
| 1 month ago | ||
| 17 hours ago | ||
| 2 days ago | ||
| 13 days ago | ||
| 1 day ago | ||
| 1 day ago | ||
| 17 hours ago | ||
| 2 days ago | ||
| 5 days ago | ||
| 19 hours ago | ||
| 1 day ago | ||
| 1 month ago | ||
| 12 days ago | ||
| 5 days ago | ||
| 14 hours ago | ||
| 1 day ago | ||
| 14 hours ago | ||
| 1 day ago | ||
| 1 day ago | ||
| 1 day ago | ||
| 1 day ago | ||
| 4 days ago | ||
| 1 day ago | ||
| 1 day ago | ||
| 16 days ago | ||
| 19 hours ago | ||
| 3 days ago | ||
| 1 month ago | ||
| 3 days ago | ||
| 1 day ago | ||
| 28 days ago | ||
| 4 days ago | ||
| 16 days ago | ||
| 3 days ago | ||
| 9 days ago | ||
| 1 month ago | ||
| 18 hours ago | ||
| 20 hours ago | ||
| 4 days ago | ||
| 1 month ago | ||
| 10 days ago | ||
| 1 month ago | ||
| 10 days ago | ||
| 10 days ago | ||
| 1 month ago | ||
| 26 days ago | ||
| 2 days ago | ||
| 20 hours ago | ||
| 10 days ago | ||
| 10 days ago | ||
| 12 days ago | ||
| 1 day ago | ||
| 1 day ago | ||
| 22 days ago | ||
| 27 days ago | ||
| 1 day ago | ||
| 2 days ago | ||
| 11 days ago | ||
| 5 days ago | ||
| 1 day ago | ||
| 16 days ago | ||
| 1 day ago | ||
| 23 hours ago | ||
| 1 month ago | ||
| 1 day ago | ||
| 7 days ago | ||
| 5 days ago | ||
| 28 days ago | ||
| 5 days ago | ||
| 9 days ago | ||
| 28 days ago | ||
| 1 month ago | ||
| 18 hours ago | ||
| 1 day ago | ||
| 1 day ago | ||
| 14 hours ago | ||
| 24 days ago | ||
| 15 days ago | ||
| 4 days ago | ||
| 2 days ago | ||
| 2 days ago | ||
| 21 days ago | ||
| 16 days ago | ||
| 1 day ago | ||
| 27 days ago |