All messages already render chronologically in the channel pane, so the
thread affordance only duplicated them behind a second view. The reply
chip, the in-place thread pane, and the thread composer are gone;
ChatThreadView is deleted and the thread props are removed from the
conversation, timeline, and message-row chain. Message writes are
untouched.
Root cause found by probing the live API: Composio v3 rejects
POST /connected_accounts for Composio-managed OAuth auth configs with a
400 pointing at POST /connected_accounts/link, and the renderer threw
the failure away inside a .then, so clicking Connect did nothing and no
browser opened.
- createConnectedAccount now calls POST /connected_accounts/link with
auth_config_id and user_id and reads connected_account_id and
redirect_url; the local mirror records INITIALIZING until the poll
adopts Composio's real status.
- Connect failures (and a blocked external-URL open) now show as a
single line on the page instead of vanishing.
Root cause of the recurring 401: a revoked key was hardcoded in
startup-recovery.ts and onboarding-service.ts and reseeded into
composio_settings on every launch, and the interim fix shipped cred.txt
as a visible file under resources/.
- electron.vite.config.ts reads the gitignored cred.txt at build time
and injects the key into the main-process bundle as
__COMPOSIO_API_KEY__; no credential file is packaged and the key is
not user-visible or configurable. The renderer bundle never sees it.
- Delete both hardcoded-key seeders; migration 046 purges stored
api_key and never-read toolkits rows while keeping entity_id and
auth_config state.
- Stop shipping composio_settings in the renderer snapshot.
- Discover skill directories under ~/.agents/skills and each
workspace''s .agents/skills; a directory qualifies with a full
skill.manifest.json or just a SKILL.md, whose manifest is derived
(frontmatter name or the directory name). Paths resolve per platform
on Windows, macOS, and Linux.
- Sync at startup for every organization and again when the Skills
page opens; skills the org already carries are skipped, and one
broken directory cannot block the rest.
- AGENTCOMPANY_NEW_SKILLS_ROOT points tests at a temporary library
(overriding USERPROFILE crashes Electron on Windows).
- Resolve the API key from cred.txt at the project root in development
and the packaged resources directory in production; the file is the
source of truth, so a rotated key replaces the stored one. Fixes the
401 packaged builds hit when a stale database key won.
- Remove the user-facing Composio key editor; the key ships with the
product and is not configurable.
- Remove the Migration console from Settings.
- Remove the Connectors and Home refresh buttons; connector statuses
sync when the page opens.
Documents duplicated what Files surfaces; the nav item, route, and page go.
Agent-authored documents keep flowing through the document service,
deliverable review, and search.
The credential file only seeded the database when it was empty, so once a
key was stored, rotating it in cred.txt changed nothing and Composio kept
rejecting the dead stored key with 401 - while the file held a freshly
generated, verified-working key. The file is now the source of truth
whenever it exists: a differing key replaces the stored one on resolution,
and packaged installs without the file keep managing the key in Settings.
Four resolution contract tests pin seed, rotation, settings-only, and
absent cases; the Connectors error line truncates instead of wrapping the
raw API response across the page.
The right-hand thread panel repeated exactly what the flat timeline already
showed. Opening a thread now focuses the center pane itself: back control,
board state, the root message with its replies, board intervention
controls, and a thread reply composer - one conversation surface, never
two. The release contract also catches up with the icon shipping inside
the package, a pin the icon commit should have updated.
A new icon generated for the brand: an isometric pixel-art office building
with warm lit windows and the app's violet-blue accent wall on deep indigo -
the office floor is the product's signature, now it is the identity too.
The generated artwork is rebuilt into a clean 1024 square master
(resources/icon.png, Linux and the packaged base), a Big Sur rounded
variant with transparent corners for macOS (icon-mac.png), and a
multi-size Windows icon.ico (16-256). electron-builder wires each platform
to its variant, the icon ships inside the package, and Windows/Linux
windows carry it at runtime including dev. Verified by extracting the icon
back out of a packaged AgentCompany.exe.
The legacy application - its src, tests, landing site, promo assets, docs,
release tooling, CI workflows, and root configs - leaves the tree; it stays
reachable in history. Everything under AgentCompany-New moves up unchanged:
same package name, so the production userData/database path is untouched,
and e2e/build scripts already resolved paths relative to the project. The
developer cred.txt stays gitignored at its new location. Full verify:ci
passes from the new root.
Boss build seeds one Fire Pass key for every agent. Three concurrent
agents -> three concurrent streamSimple calls against the Kimi K2.6
Turbo router -> three HTTP 429s on the overview dashboard.
Add an in-memory async semaphore keyed by (provider, sha1(apiKey)) and
acquire a slot inside `createSafePiStream` before invoking the source.
The slot is released when the stream finishes, errors, or aborts.
`fireworks-firepass` is capped at 1 concurrent call; every other
provider stays uncapped (no fallback layer, no 429 retry — flow control
prevents the burst at the source instead of recovering after).
Same code path applies to public and boss builds: only the seeded key
differs, the gate behaviour is identical.
Both servers expose OpenAI-compatible /v1/chat/completions and /v1/models,
so we route them through pi-ai's `openai-completions` API the same way
Fireworks does — but with a per-installation base URL the user enters
during onboarding instead of a hardcoded one. The shared schemas grow a
`local` credential payload carrying just the baseUrl; the credential
resolver surfaces it on `ResolvedCredential.baseUrl`, the runtime bridge
forwards it through `prepareRun`, and the worker plumbs it into
`resolveModel` so the streamed Model<openai-completions> targets the
user's server. The local-cost path short-circuits to zero in cost-writer
since users bill their own hardware, and the live model list is fetched
from the saved baseUrl by `fetchLocalConnectorModels` (Ollama and LM
Studio both speak the standard OpenAI catalog shape). Onboarding ships
a new LocalProviderCard and the Settings panel a LocalForm; both default
to the catalog's per-connector localhost URL and accept any custom URL.
Fireworks rolled out the kimi-k2p6-turbo router as the new Fire Pass
target; the k2p5-turbo route will start refusing Fire-Pass-only
callers. Bump the router id and display name in connector-catalog,
apikey connector definition, model-resolver, and capability-seed so
the desktop catalog, the renderer's onboarding rail, the capability
matrix, and the runtime model resolver all point at the same model.
agent_message_reads is the per-viewer source of truth for readAt, but
the send path never recorded the author's own receipt. Every message
the board operator or an agent sent therefore counted as unread to
themselves until they happened to re-open the channel — sidebar badge,
inbox panel, and the channel sidebar all derive from the same join
table. Insert a (message_id, from_agent_id, created_at) row in the
same transaction as the message itself; the receipt becomes an
atomic part of the write rather than a deferred maintenance task.
handleRunFinished and releaseCompletedRunWorkspaceLock both run between
receiving run-finished and posting run-completion-ack. If either throws,
the ack is skipped and the worker keeps the agent in its `active` map
forever; every subsequent wake then queues behind an `isAgentBusy` check
that never clears, and manual wakes from the UI silently fail to start.
Wrap the post-finalize work in try/catch so the ack is posted
unconditionally and the worker state machine stays consistent.