pip install -U cycls.
v0.0.2.149
Chats named for what they are about. A new chat is named by the agent’s own
model from what the person wrote, in their language, in a few words. The call runs
beside the first reply, so the reply never waits for it, and the name appears in
the chat list as the reply ends. A greeting alone keeps its first line until the
next message says more. A name the person gives is never written over, and chats
from before keep their titles.The chat list. The open chat stands out from the others. Double-click a title
to rename it, and a row’s menu now shows on touch screens, in the chat, share,
file and app lists. Titles in Arabic read right to left.Other, written in. A question the agent asks with options ends with a line
where the person writes their own answer. With several choices, it joins what they
picked. See Built-in tools.Files in a narrow panel. A file’s size and date show under its name when the
panel is too narrow for their columns, with dates in the interface’s language.
v0.0.2.148
Voice input from any service.
Web().voice(model, base_url=, api_key=, headers=)
picks the model that turns the mic’s recordings into text, on your server. Any
service that speaks OpenAI’s audio transcriptions API works, and through OpenRouter
one key reaches ElevenLabs, Deepgram, AssemblyAI and Google models, and open ones
such as Whisper, Voxtral and Nemotron. The browser gets only the text, never the
model or its key, and a recording that could not be made out says so in the
reader’s language. Without .voice(), the mic uses OpenAI’s gpt-4o-transcribe
whenever OPENAI_API_KEY is set, as before. See Voice input.A tool’s pictures reach the model once. A screenshot or a rendered page goes
with the call right after the tool ran, and a short line stands in for it after
that, so a chat full of pictures no longer slows every step. Pictures the user
attaches stay. See Long conversations.One host per chat behind a router. Every request through the Chat Completions
adapter carries the chat’s id as x-session-id, so OpenRouter keeps a chat on one
host and its cache warm. Limit hosts with provider.only, not provider.order. The
usage log names the host that answered each step. See
Choosing a model.An hour per request. Apps and agents ask for a 3600-second request timeout, so
a long run ends at its own 45-minute cap instead of being cut at 20 minutes. See
Limits.Approvals above the message box. A call that needs approval asks above the
composer, where the agent’s other questions appear, and the conversation keeps one
line where it asked: waiting, then how it went. See Approvals.The plans as the last free reply ends. The reply that uses the last of a limit
opens the plans window as it ends, once a period on each device. The thank-you note
now appears only in a reply the limit refused. See
Meter usage per user.The queue. Send now is gone: stopping a run to send at once gave its prompt
back to the composer, or was refused while the run was still stopping. Queued
messages go as a reply ends, and after Stop a tap brings one back. When the
connection drops while the agent is still working, queued messages wait for it
instead of being refused.Smaller changes. A picture on its own is 20rem on its long side, and the frame
it is made in carries a sweep of light while it works. A markdown document in the
canvas opens its links to other workspace files in the canvas. Names in the agents
dropdown use the display font.v0.0.2.147
Approval cards in the conversation. A call that needs approval shows a card
where its step was. It asks about the action in plain words, shows the
connector’s logo, the file’s tile or the step’s icon, with a trash can for a
delete, and offers the action’s own button and Not now. Not now reaches the
model as
declined, and an answered card stays as a one-line receipt.
Don’t ask again sets the tool to allow, but a destructive call still asks, and
an API connector’s request tool now follows allow, ask and never. A connector’s
CMS record can word its calls with actions. See
Approvals.Citations from any tool. Every tool’s sources now come back after a reload,
not only web search’s. A source can carry a label for its chip, and a quote,
status and note for its card, and a phone’s first tap opens the card. See
Returning UI components.Pictures in the chat. A picture a tool opens with open_canvas shows in the
conversation as itself, and pictures next to each other form a grid that opens in
the canvas. .on(picture=) holds a frame of the picture’s shape while the tool
runs. Two images or two tables in a row no longer lose the second. See
Presentation.Usage limits. Yield {"type": "usage", "used", "limit", "per", "resets"} with a
reply. In the limit’s last fifth a line above the composer counts down, and once
nothing is left the reply is a note that thanks the person and offers the plans.
See Meter usage per user.The plans window. One button leads, for the plan on offer, already chosen,
with its price, a local amount and “cancel anytime” under it. The payment marks,
the choice of plan and billing period, and the features follow. Opened by a limit,
the window thanks the person first, and it opens by itself once per period on each
device. Web().plans(pay=, currency=, words=) sets the marks, a fixed-rate local
currency and plan words in other languages. paywall_shown now fires only when
the window opens, and plan_modal_opened and checkout_start carry why. See
The plans window.Continue after a lost run. A run the server lost, to a deploy, a crash or the
45-minute cap, ends with “Stopped before it finished” and a Continue button.The queue. Messages written while the agent works sit in one list above the
composer, each with Send now, edit and remove. Attach, the language switch and
voice stay open while a reply streams.Shared pages. A signed-out visitor gets Sign in, which returns to the share,
and Google One Tap when one_tap is on. A signed-in visitor gets Go to the
agent. The page follows the system’s light or dark theme, and the sign-in page
plays a white video in light mode.v0.0.2.146
One icon for steps. A step without an icon of its own, such as a skill, a
status line or a custom tool without an
icon, shows the Cycls star where the
circle and check mark were. Cycls blue and orange move through it while the step
runs, and it turns gray when the step finishes, or red when it fails. The
collapsed steps line uses the same star.v0.0.2.145
Skills people publish. Skills now come from four places: the ones you ship,
the organization’s, a team workspace’s and each person’s own, and the most
specific wins a shared name. People add them from a new Skills tab, by uploading a
.zip, filling in a form or asking the agent, and the agent publishes one with the
skill tool’s publish action. Every publish is kept as a version that can be
made current again, and org admins can switch a skill off for everyone. See
Skills.Breaking: skills/<name>/SKILL.md folders in a workspace no longer load.
Publish each one from the Skills tab, or ask the agent to publish it.Your own document parser. read can send PDF, Office, OpenDocument, RTF and
EPUB files to a parser you deploy as a function, named by CYCLS_PARSER, and give
the model the Markdown it returns. A PDF’s pages come back as images whenever the
model asks for them with pages, at any size. See
Document parser.Regenerate and take back. Regenerate is one request that runs the saved turn
again, with its files and selection, and replaces only what came after it.
Stopping a run before its reply puts the prompt back in the composer.
context.replay is true on a regenerate or retry. See the
agent HTTP API.Approvals in Settings. The Auto and Manual switch moved from the composer to
Settings, General, Approvals. It is still sent with each request as auto.Workspace members. The navbar’s workspace menu switches workspaces, and owners
and admins manage members, roles, the name and the icon in Settings, Workspaces.
Every member loads, people who left the organization show as Former member, and
each workspace keeps its open chat and canvas tabs. See
Workspaces.Plan properties from the token. Analytics person properties are now
user_plan, user_is_paid, org_plan and org_is_paid, read from the token’s
pla claim. The plan_* and is_paid properties and the subscription events are
gone, so update any dashboard that uses them. A failed run is tracked as
run_failed with its reason and error id, and a dropped connection is no longer
counted as a failure. See Analytics and push.Tool settings no longer need the secret key. A person’s built-in tool
settings, set from the card’s Always allow, the tool permissions in Settings or
the Memory switch, are stored as plain settings instead of encrypted credentials.
They work on every deployment. Before, a deployment without CYCLS_SECRET_KEY
failed every message from anyone who had clicked Always allow. The key is now
needed only for connectors.A missing key degrades. Without CYCLS_SECRET_KEY, stored connector grants
read as absent and a warning is logged, the same as after a key rotation. Storing
a grant still fails.Stricter bash reads. A command counts as a read only when every command in
the line reads and nothing is redirected into a file, so ls; touch notes.txt is
a write. find -delete always asks, because it deletes without going through the
trash.Built-in tool settings saved before this release are not carried over, so anyone
who chose Always allow is asked again until they choose it once more.Also in this release- The agent reaches an app’s data only through the
databasetool, underapps/<slug>/, andcycls.setin an app writes at once. - The
browsertool opens a page the agent wrote, by its workspace path. Web().mobile_app(ios=, android=)points phone visitors to the Cycls app.- Connectors has a recipe for Microsoft 365 over Microsoft
Graph. A REST connector’s
{name}_requestsends a body as JSON, withcontent-type: application/json. - A failed run’s
reasoniserror, and itserror_idis the same reference on the error callout, theon_runrecord and thelevel=runlog record. turn_completedis tracked once per run. A finished chat sent it again on every refocus, so web turn counts from September 21 are about 2.7 times too high.- A shared file opens full screen under one bar, and someone outside an org-only link is told so instead of being sent to sign in.
- An agent’s own fonts, through the
--font-displayand--font-sansCSS variables. - Voice input records in Firefox and shows a live waveform.
- The markdown editor has a right-click menu for tables, and its
/menu follows Arabic. - Canvas tabs keep their state when you switch between them, a long tool call shows what it is writing, the Files panel lists large folders faster, and an open tab reloads into a new deploy.
- A new sign-in screen.
v0.0.2.144
Compaction in two steps. Past 70% of the context window, the loop first
replaces old tool results and long tool arguments with a placeholder, which needs
no model call. It summarizes only when that would free less than 25% of the
window, and keeps the most recent 30% verbatim either way. The trigger also leaves
room for
.max_tokens(). Clearing never touches a loaded skill, and the system
prompt and AGENT.md are never cleared or summarized. See
Long conversations.Cheaper summaries. A summary is requested as the loop’s own next turn, with
the same system prompt and tools, so the provider serves the conversation from its
cache. A run that ends past the trigger summarizes before it closes, while the
answer is being read, so the next message does not wait. A chat that resumes after
20 minutes idle is cleared at a lower bar, since the provider’s cache has mostly
expired by then.Bounded reads. One read returns at most 50,000 characters and says where to
continue. Attachments go through read, so a single large file can no longer
overflow the window before the model runs.Instructions. AGENT.md opens from an Instructions tile in the dock at the
bottom of the right panel, in the canvas editor, and no longer appears in Files.
An empty file opens with examples to start from.Memory in Settings. Settings, General, Memory lists what the database tool
has saved for the person as plain-text notes they can edit, or delete with Undo.
The switch that turns the tool off moved here from Tool permissions. New routes:
GET /memory, PUT /memory/{key} and DELETE /memory/{key}.A markdown editor. Markdown files edit in a rich editor with a toolbar, a /
menu, drag handles, checklists and tables. Images and videos added to a document
are saved in a media/ folder beside it, and the markdown view shows them. Each
block takes its direction from its own text, so Arabic and English mix. Markdown
with math, HTML or footnotes still edits as plain text.Also in this release- Every compaction writes a
level=compactionlog record with its tier, reason, size and outcome, and the web client tracks it ascontext_compacted. - A summary that fails, returns no text or runs past five minutes drops the older turns, so the next request still fits.
- SGLang’s context-full error is recognized, so the loop compacts and replays instead of showing the raw error.
compact()in the custom loop kit now takeskeepandmax_tokens.session.compact(provider)works as before.- Dark mode styles follow the theme chosen in the app, not the operating system.
v0.0.2.143
Ships the current web client again. The 0.0.2.142 package carried a browser
bundle from before the 0.0.2.141 client changes, so apps could not reach their
data through the bridge and Settings showed Connectors on deployments that declare
none. Upgrade if you deployed 0.0.2.142.
v0.0.2.142
Fewer approval cards. A destructive command now has to be an actual
invocation, so
shredder.py or a grep for the word truncate no longer asks.
dd asks only when it names an of=. 2>/dev/null no longer counts as
destructive, and a redirect into a disk device now does.v0.0.2.141
Connectors. A user connects an account once and the tools that need it get a
live credential.
cycls.OAuth2 runs authorization code with PKCE, cycls.Key
takes a pasted key, and cycls.Endpoint accepts a private URL that is itself the
credential. Declare them on Web().connectors() for the directory and
LLM().connectors() for the loop. Credentials are encrypted at rest, calls are
audited with the grant they acted under, and an organization admin can switch a
connector off for everyone.Per-tool approvals. Every call is classified as read, write or destructive.
Reads always run, writes follow the composer’s Auto switch, and destructive calls
ask in both modes. An approval binds to the exact arguments shown on the card, so
a changed call asks again. Each person can set allow, ask or never per tool.MCP on every provider. The harness speaks Streamable HTTP itself, so
cycls.MCP servers work on any model rather than Anthropic only. Tools are
discovered once and cached, and a session opens only when a tool is called.
.server_side() still hands a server to Anthropic’s connector.Tools load on demand. A connector contributes one index line to the system
prompt, and the model calls find_tools to pull in the schemas it needs. Loaded
tools stay for the rest of the conversation.Background runs. A run now records its own state, survives a dropped
connection, and can be stopped from another container. The client watches a run
it started, reconnects to it, and shows a background indicator. Web().on_run()
fires once when a run reaches any terminal status.Durable turns. The user’s message is written to disk before the model is
called, turn writes refuse to overwrite, and a reader normalizes in memory while
only the writer repairs on disk.Browser automation. Adding "Browser" to allowed_tools gives the model a
real Chrome session: open, read, click, type, press, back, evaluate, screenshot
and download, with page state persisting across calls in a turn. Chrome runs in a
shared service configured by BROWSER_URL and BROWSER_SECRET, not in the agent
image. Includes per-site proxy routing and a stealth engine.Office files on the canvas. Spreadsheets render as an interactive grid and
.docx as a formatted document, both in the browser. Presentations render as a
slide viewer and other office formats as a read-only PDF through the shared
render service. Any failure falls back to the download card.Apps. The Apps tool bundles a source folder into one self-contained HTML
file installed in the Apps tab. App data is rows in a dedicated slot with
compare-and-swap writes and a 25 MB cap, and an app can call a connected
connector’s REST API with the credential attached server-side.Analytics as plugins. One canonical event pipe in the client fanned out to
cycls.PostHog and cycls.GTM, each scoped by an optional event allowlist.
cycls.OneSignal adds web push.Also in this release- Large tool results spill to a file to compute over instead of being read back as text.
.tmp/{chat}is the agent’sTMPDIR, and/tmpfails with an explicit reason.- Tool handlers can declare a second parameter to receive a
ToolContextwith the user, workspace and chat. - Google One Tap on the signed-out page, opt in with
cycls.Clerk(one_tap=True). - Search results collapse into one sources row at the end of an answer.
- A shared chat forks into the forker’s active workspace.
- The base image is pinned to bookworm, because the sandbox needs a syscall the newer runtime lacks.
shipped in v0.0.2.141
- Mini apps end to end. A
build_apptool, a sandboxed bridge scoped toapps/<slug>/, and an Apps launcher. - Canvas. Deliverables open while they are being written, tabs share the strip and shrink like a browser’s, the rail folds to icons, and the composer keeps working while the agent does.
- Example gallery.
Web().examples()turns real share links into cards on the empty chat screen, with an optional tutorial video card. - Suggest and Ask. One follow-up chip above the composer, and a question card with up to three questions that ends the turn.
- Apple IAP per product.
productstakes a mapping of product id to plan, and verification hands back the payload. - Transient 500s from model servers are retried, since capacity failures surface that way.
web_searchsearches before answering, with Brave country and language passthrough.- Provider clients are cached on the full configuration,
.headers()carries proxy authentication, and unmapped.thinking()values print a warning.
v0.0.2.133 to v0.0.2.140
- Workspaces.
Web().workspaces()gives every user a personal workspace and lets organizations share team workspaces with role-based access. Each workspace is a full context: files, chats,AGENT.md, skills and key-value store. The active one is selected per request with theX-Workspaceheader. - Skills.
LLM().skills("skills")ships skill folders with the agent, and anyskills/<name>/SKILL.mdin a user’s workspace joins the catalog automatically. Only names and descriptions sit in the system prompt. - Unified reasoning.
.thinking("low" | "medium" | "high" | "adaptive" | None)is translated into each vendor’s dialect, with.extra_body()as the escape hatch. - Faster import.
import cyclswent from about 1.0s to about 40ms through lazy imports and private subpackages. - Agent routes cross the pickle boundary as data rather than router instances.
- Failures that affect a user surface as a callout with a reference id, while plumbing goes to structured logs.
- The file picker and browser share one cached walk, with matching and ordering moved server-side.
shipped in v0.0.2.133
- Document canvas. In-chat canvas with media, PDF and sheet renderers, a Finder-like workspace view, and safe uploads.
- Composer mentions and paste. Reference workspace files with
@, and paste images directly. - Affiliate tracking.
Web().affiliate(key)loads a referral tracker and reports conversions at checkout. - The canvas tool opens finished deliverables only, not transient files.
v0.0.2.127 to v0.0.2.132
- Cost tracking. Per-turn usage, per-chat aggregates, and
cycls costwith--since,--monthand--by user|chat|model. cycls sql. SQL over thelogsandbillingtables, scoped to the deployments your key owns.- Structured logging.
cycls.log()emits one record with user and chat attribution. Errors carry a reference id shown to the user, and every tool dispatch emits atool_callrecord. - Cheaper prompts. Three cache breakpoints on the system prompt, the last tool and the last user turn, which cut cached input cost substantially on Anthropic.
- Providers moved to one file each behind a neutral shape, with per-provider context window and max output tables.
- Loop events became dict factories, which is why
cycls.to_uiis now an identity function kept for compatibility. - Chat storage moved to a one-folder layout with glob scans.
v0.0.2.120 to v0.0.2.126
- The primitives API.
cycls.Image,cycls.Webandcycls.LLMreplaced keyword arguments on the decorators. Each is an immutable fluent builder. - The CLI.
cycls run,deploy,ls,rm,logs,initandversion. - OpenAI adapter. One Chat Completions path covering every OpenAI-compatible endpoint, alongside the native Anthropic path.
- Custom tool handlers.
LLM().on(name, handler)registers an async function whose return value reaches both the UI and the model. - HTTP routes on agents.
@my_agent.server.api_route(...)adds routes to the same deployment. cycls.Sandbox. Bubblewrap execution as its own primitive, with secure defaults.- Live shares. Stateless signed URLs replaced frozen share snapshots.
- PDF page extraction for large files, and cost plus duration in the usage footer.
v0.0.2.117 to v0.0.2.119
- The chat client. A React interface with streaming, file uploads, a file manager, session list, rename and delete.
- Share links. Conversations render as public pages with generated social preview images, including Arabic and right-to-left layouts.
- Arabic. Full interface translation with a language toggle, plus localized sign-in.
- Voice input. Dictation in the composer, with long-press to record.
- Plans and organizations. Plan badges, an organization switcher, a pricing
modal, and
?plan=to open checkout directly. - Branding from a CMS. Logo, name and description pulled at startup.
- LaTeX rendering, drag and drop uploads, auto-retry on connection errors, and an explore dropdown for related agents.
v0.0.2.103 to v0.0.2.116
- The native agent harness. A direct Anthropic loop with history, prompt caching and compaction, replacing the subprocess approach.
- Sandboxed bash. Bubblewrap isolation with environment stripping, and editor paths restricted to the workspace.
- Sessions and files API. List, load, rename and delete chats, plus full file endpoints.
- Attachments. Upload limits, media sniffing, and recovery from API errors inside the loop.
- Organizations in the JWT. Organization claims reach
context.user. - Per-turn token usage and cost display, and incremental saves during a turn.
v0.0.2.63 to v0.0.2.102
- Streaming UI components. Yield dicts for thinking bubbles, code blocks, tables, callouts, status lines and images.
- The function runtime.
@cycls.functionwith local Docker execution, hot reload on save, and pre-built base images. - Volumes and state. Persistent key-value and filesystem storage for deployments.
- Automatic dotenv loading. A
.envbundled with the image is loaded at import, with no extra dependency. context.last_messageand theMessageswrapper with.raw.- Moved from Poetry to uv, added the test suite and CI.
v0.0.2.62
- Deployment moved to a two-step upload flow, which lifted the practical limit on build context size.
- Error propagation from the container reached the interface instead of failing silently.
v0.0.2.52
- The chat interface was redesigned and gained image attachments.
- Cycls Cloud went live for serverless deployment.
- A single
keyparameter replaced the several per-service keys. copy_publicmade it simple to ship static assets.
first public release
- Cycls became open source at github.com/Cycls/cycls.
- Decorator configuration for dependencies and authentication.
- Every deployed agent served a streaming OpenAI-compatible endpoint.
- Automatic web chat interface, with user and context management for multi-turn agents.