- 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.
Pi-ai ships $0 pricing for github-copilot (25 models), google-gemini-cli
(6 models), zai (13 models), and kimi-coding (2 models). This made cost
tracking produce $0 for every run through these providers, rendering
budget enforcement useless.
Added resolveModelCostFallback() in model-resolver.ts with two strategies:
1. Cross-provider lookup: subscription providers (github-copilot,
google-gemini-cli) use the same models as their API counterparts.
Look up the model in anthropic/openai/google/xai catalogs using
normalized ID matching (dots→hyphens) to find real pricing.
2. Per-provider fallback: API-key providers with no cross-provider
equivalent (zai, kimi-coding) get a static per-provider cost.
CostWriter now calls resolveModelCostFallback after resolveModel, so
all consumers get patched pricing without touching the pi-ai library.
Three fixes for the non-functional budget system:
1. Fireworks models hardcoded cost {input: 0, output: 0}, making all
Fireworks/Fire Pass runs report $0 and budget enforcement useless.
Added per-model pricing to FIREWORKS_CURATED_MODELS with a fallback
for unknown models. Pi-ai known providers (Anthropic, OpenAI, etc.)
already had correct per-model pricing via getModels().
2. saveAgent IPC handler passed the form's submitted status through
without checking whether the agent was budget-paused and the new
budget exceeds spent. Now auto-resolves paused → idle when the user
raises budget_monthly_usd above spent_monthly_usd.
3. checkAutoResolve in budget-enforcement-service resolved policy
incidents but never touched agents.status. Added
unpauseBudgetBlockedAgents() that scans for paused agents whose
legacy budget now has headroom and flips them to idle.
Agents always replied with channel="direct" regardless of the original
message's channel, creating new DM conversations instead of replying
in the existing group chat (department/company/project/incident).
Root cause: three layers of the system guided agents toward direct-only
replies:
1. Prompt instructions (wake-reasons, IC/manager operating instructions)
hardcoded channel="direct" and toAgentId=fromAgentId for all replies
2. Standup API omitted toAgentId from unread messages, removing context
about who the original message was addressed to
3. Inbox projection contained no reply-routing metadata, so the agent's
LLM had no signal about which channel to respond in
Fixes:
- routes-agents-standup.ts: include toAgentId in unread message payload
- wake-reasons/en.ts: differentiate direct vs group channel reply format
- operating-en.ts: same channel-preserving guidance for IC and manager
- inbox-projection.ts: embed reply-to routing context in message header
so the LLM sees channel + target alongside the message body
The heartbeat path (canRunAgent → startAgentHeartbeatRun) only checked
isAgentPendingApproval (hire gate), completely bypassing plan_review,
draft_review, strategy_review and other blocking approval types. Agents
in Co-Founder mode continued running despite pending approvals.
Root cause: task-runner.prepareTaskRun had the full getBlockingApproval
check, but heartbeat runs never went through prepareTaskRun — they
created runs directly, skipping the approval gate entirely.
Three fixes:
- heartbeat.canRunAgent: add getBlockingApproval(companyId, agentId, null)
for agent-level blocking approvals
- heartbeat.startAgentHeartbeatRun: add getBlockingApproval with task id
after task resolution, before run creation
- task-runner.resumeQueuedRuns: add getBlockingApproval check so app
restart does not dispatch runs past pending approvals
Also replaced the hardcoded type list in hasPendingReviewApprovalForAgent
with a dynamic join on approval_policies.blocks_execution, eliminating
the sync risk when new blocking approval types are added.