House style now follows the app language: Chinese UI forces the new
bundled humanizer-zh skill into every run, English forces humanizer -
one variant per run, picked by preference with cross-language fallback
so an older catalogue never silently loses house style. Both variants
are protected from grant/revoke, list_skills labels each with its
language, the bundle guard requires both, and startup sync imports the
new skill into existing orgs on next launch. Covered by a unit test
that flips app_state.language and asserts which humanizer compiles.
The chat split is now adjustable: drag the conversation list's right
edge (200-480px, persisted in localStorage) and the composer textarea
grew a native vertical resize grip capped at 40vh.
Two operator asks, one review gate:
Avatar portraits (migration 052): the avatar editor gains "Upload
photo" - the renderer downscales the chosen picture to a ~96px cover
crop and stores it as a small data URL (PNG keeps transparency, the
rest compresses to JPEG), so the snapshot stays light and the CSP needs
nothing new. The portrait wins over emoji and initials everywhere the
shared Avatar renders (chat, org chart, office card, sidebar), and
"Remove photo" falls back cleanly. The main process validates scheme
and size (base64 image data URL, <=300k chars) so a rogue caller cannot
re-bloat the snapshot. e2e uploads a real PNG through the profile
dialog and sees the portrait land in the sidebar.
Global pause (migration 053): when agents start talking in circles,
one topbar toggle - visible on every page - pulls the handbrake.
app_state.autorun_paused survives restarts; the queue worker checks it
each tick and neither enqueues nor claims while set (the run already in
flight finishes). Queued work simply waits and the paused state renders
as an amber "resume" pill. A unit test drives the real worker: paused
leaves a queued run untouched through many ticks, resuming claims it
within moments; e2e flips the toggle in the live shell.
A systematic review of the whole codebase (main process, renderer,
IPC/migration/packaging seams) surfaced four real defects; each is
fixed at its root:
- Legacy catalogue drift: the operator's profile still carried 89
active skills from the removed ~/.agents import era next to the 228
built-ins. Catalogue sync now mirrors the bundle - actives whose slug
the bundle does not ship retire (bindings keep resolving via their
pinned revisions; retired skills leave list_skills and cannot be
granted anew)
- Broken-bundle guard: staging now refuses a bundle without the
humanizer skill, so an empty or quarantined resources folder can
never trigger the mirror pass into retiring a healthy catalogue
- Duplicate agent names: @mentions, DM routing, and task assignment all
resolve by name, so createAgent and updateAgent renames now reject a
name already held by an active agent (case-insensitive); the mention
resolver still refuses to guess between legacy duplicates
- Stale DM titles: agent-to-agent conversation rows titled themselves
from the frozen creation-time space name, so renames never reached
the sidebar; direct titles now derive from members' current names
Verified clean in the same sweep: IPC surface (82 channels, handler +
preload + api aligned), per-migration transactions with rollback,
quota-parking month rollover, retry-budget counting, meeting spot
reuse across floor-plan rebuilds.
Every whip click now lands visibly, wherever the target is standing:
- simulation.whip() always registers a WhipCrackEffect at the struck
agent and starts their hit reaction; step() ages both away. The
discipline rule is unchanged - only snack-bar/restroom slackers are
sent hustling home; desk, CEO office, and meeting stay safe
- office-renderer draws the strike: a lash sweeping in from the upper
right that collapses into an impact starburst with an expanding ring
(~450ms), while the struck agent shakes with anime-style pain sparks
over their head (~600ms), fading as the flinch drains
- The office e2e now collects pageerror during the run and lets the
effect play out after a whip-mode click, so a throwing frame in the
rAF loop fails the suite instead of dying silently
The whole app turned to molasses because snapshot.load() shipped the
entire skill_runtime_snapshots table - unbounded, compiled_prompt
included - over IPC on every refresh. A real profile carried 142 rows
totaling 144MB (1.1MB each from the pre-cap CEO compile-all era), so
every coalesced refresh serialized ~144MB of JSON. The forced-humanizer
rework made it worse by adding a row per run for every agent.
- Drop skillRuntimeSnapshots, skillRevisions, skillTests, and
skillBindings from the renderer snapshot: no renderer surface reads
them; skill internals are main-process state. Only the lightweight
skillDefinitions catalogue still ships.
- Project provider_requests without raw_response_json (4.1MB across the
newest 100 rows; RunsPage only reads responseText)
- Migration 051 purges the bug-era compiled prompts from existing DBs
- resolveRuntimeSnapshot is now idempotent per run: a run re-queued by
startup recovery reuses its frozen snapshot instead of dying on the
uniqueness guard and burning real retry budget
- Regression test pins the contract: a 2MB database row must not add
bytes to the snapshot (payload asserted under 400KB)
- New bottom toolbar on the pixel office floor with a Whip tool;
toggling it turns the cursor to a crosshair, clicks whip instead of
opening the detail card, Escape puts the whip away
- Simulation.whip(): an agent inside the snack bar or restroom drops
whatever they are carrying and hustles back to their desk at 1.8x
speed for a few seconds; agents at their desks, in the CEO office,
or in the meeting room are working and stay untouched
- Room membership decides the outcome (rectContains against the same
floor-plan rooms the renderer draws), so the rule matches what the
player sees
- Unit tests cover the whip matrix; e2e drives the toolbar toggle in
the live shell
Researched how OKR-run companies cascade (company objective -> C-suite
officers per function -> department objectives -> individual key
results) and encoded that shape into how the CEO builds the org.
- hire_agent gains seniority (director/lead/staff; c_suite stays
reserved for the CEO's privileges) and a business-card title, so the
CEO hires a CTO/CFO/CMO as directors instead of a flat pool of ICs
- CEO persona + goal kickoff now direct the OKR cascade: hire 2-4
officers, hand each a department objective, let them build teams;
department heads get their own doctrine (hire leads/staff, cascade
key results as tasks, report up)
- Migration 050 adds Finance and Sales to every org's canonical
departments (backfilled for existing DBs) so a real C-suite has
somewhere to hire
- Org chart viewport is a fixed-height scroll region: zooming rescales
the diagram inside it instead of resizing the card
- Unit test drives a live three-level chain: CEO -> CTO (director) ->
Engineering Manager (lead) -> staff, with rank/title persisted
- The detail card now rides beside the clicked agent (flipping and
clamping at viewport edges via a pure cardPositionFor helper) instead
of pinning to the top-right corner; it follows a walking agent
- Every agent - the CEO and busy agents included - periodically visits
the coffee machine, snack bar, and restroom; work status no longer
recalls a walker mid-break
- The office convenes meetings around the meeting table every few
minutes: at least two settled attendees walk over, dwell together on
distinct table spots, and head back; a solo office never meets
- Meeting spots ship as lane-wired amenities so pathing stays correct
by construction; floor-plan invariants (collider-free, reachable)
cover them automatically
- e2e asserts the card opens beside the sprite, not in a corner
- Migration 049: avatar_emoji/avatar_color on agents and employees
- updateAgent accepts a new name (mention-name validated) and avatar;
new updateEmployeeProfile renames the operator and sets their avatar
- Avatars are emoji-on-color tiles (renderer CSP blocks remote images);
an AvatarDirectoryContext built from the snapshot lets the shared
Avatar component honor custom looks at every call site - chat rows,
conversation list, org chart, office card, sidebar - without
threading props through pages
- Team edit dialog gains Name + avatar editor (CEO included); the
sidebar foot opens an Edit-profile dialog for the operator
- e2e: rename an agent to a new mention handle with an emoji avatar,
edit the operator profile, both verified in the live shell
Skills no longer come from the user's ~/.agents library - most users have
none, and the CEO branch that compiled the entire catalogue into its
prompt overflowed the model context (1.1MB) and failed every CEO run.
- Bundle "built-in skills/" with the app (extraResources) and stage it
at startup into <profile>/builtin-skills as one dir-per-skill copy -
both bundle shapes (SKILL.md dirs and flat category .md files)
normalize into the same importable layout
- Import the staged catalogue per org at startup and inside onboarding,
so the CEO's first run already carries it
- Forced house style: resolveRuntimeSnapshot unions the humanizer skill
into every agent's runtime (CEO included) as a resolution rule - no
binding row to forget, delete, or bypass; revoke_skill refuses it
- CEO staffs hires with skills: hire_agent accepts a skills array,
grant_skill/revoke_skill adjust later, list_skills now shows
descriptions from skill frontmatter
- Kill the c_suite compile-all branch; everyone resolves bindings-only,
capped at 200k chars with omissions named in the prompt
- Remove the Skills settings page and its import/sync/test IPC surface;
drop ~/.agents discovery and the AGENTCOMPANY_NEW_SKILLS_ROOT seam in
favor of AGENTCOMPANY_NEW_BUILTIN_SKILLS_DIR
- Tests: staging/import/forced-resolution/cap units, real-bundle CI
check, e2e asserts the tab is gone and the catalogue self-imports
The heartbeat scheduler enqueued runs even while a provider's monthly
usage window was full, so every task burned its 3-attempt retry budget
on instant quota failures within seconds and stayed dead forever -
raising the limit (or restarting) never revived anything.
- Park tasks (skip enqueue) while their provider is at its
monthly_request_limit instead of creating runs doomed to fail
- Exclude quota-blocked failures from the 3-attempt retry budget,
retroactively reviving tasks abandoned by past filled windows
- Share the gate message as PROVIDER_MONTHLY_LIMIT_MESSAGE and log
quota-block transitions once per state change for diagnosability
- Ignore landing/ (self-contained Next.js sub-project) and
built-in skills/ (prompt content) in eslint so verify:ci lints only
application code
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.
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 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.