Commit Graph
147 Commits
Author SHA1 Message Date
ZacharyZhang-NYandClaude Opus 5.5 56b0594f91 Move Composio, logo.dev and Antigravity keys into Settings
The three third-party keys were hardcoded or read from the environment.
Operators now enter them in Settings > Integrations; they are stored
encrypted in the app-wide integration_keys table (migration 058).
An agent holding connector grants fails loudly when no Composio key is
saved. The goal-review test no longer depends on absolute dates.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 01:34:14 -04:00
ZacharyZhang-NY 327de38e78 Kimi Code joins as an OAuth plan provider: RFC 8628 device flow, verified against the live endpoints
- New oauth/kimi-code-flow.ts mirrors the official kimi-cli: form-encoded device_authorization + token polling at auth.kimi.com with the required X-Msh-* device headers, pure poll interpreter, 15-minute tokens refreshed via refresh_token grant
- Agent loop runs chat completions at https://api.kimi.com/coding/v1 with the Bearer access token; default model kimi-for-coding (kimi-for-coding-highspeed for Allegretto+ plans)
- Live model listing via /coding/v1/models; credential refresh wired into the turn credential resolver
- Migration 057 seeds the provider (disabled) into existing organizations
- Wire protocol probed live: device code issued, authorization_pending poll observed, 401 envelope on the coding API confirmed
2026-07-10 17:18:31 -04:00
ZacharyZhang-NY 813276de71 Kimi and Kimi CN join the provider catalog: OpenAI-compatible endpoints, verified live
- kimi -> https://api.moonshot.ai/v1 (platform.kimi.ai keys), kimi-cn -> https://api.moonshot.cn/v1 (platform.kimi.com keys)
- Adapters via openAiCompatibleAdapter; agent loop via makeChatCompletionsTurn; live model listing
- Curated fallback models per current docs: kimi-k2.7-code, kimi-k2.7-code-highspeed, kimi-k2.6
- Migration 056 seeds both providers (disabled) into existing organizations
- Both endpoints probed live: OpenAI-style 401 error envelope confirmed
2026-07-10 17:12:16 -04:00
ZacharyZhang-NY 26de3129b1 Goals end at achieved, not at an empty board: the goal-review loop
The board draining to 280/280 done left the company idle under a
still-active goal, with 24 markdown files and zero code as its life's work.
Two structural gaps caused it: goals had no lifecycle (status was written
once as 'active' and never read again — nothing re-engages anyone when the
last task closes), and nothing anywhere distinguished plans from product,
so every "engineering" objective closed on documents.

The loop, closed:
- tasks.origin (migration 055) records provenance: chat_wake for mention
  vehicles (backfilled from their runs), goal_review for checkpoints, null
  for real work. Chatter and checkpoints can therefore never count as
  progress.
- spawnGoalReviews (each worker tick): an active goal with zero open tasks
  and real progress since the last review gets ONE review task for the goal
  owner. The review demands evidence — list the workspace, read the code,
  run the tests; documents are how work starts, not proof it happened — and
  must end in exactly one of: complete_goal, or the next wave of tasks with
  requires_filesystem engineering. A review closed with neither spawns
  nothing: termination is structural, not a counter.
- complete_goal (CEO or goal owner) finally lets someone say a goal is
  achieved, audited.
- CEO and department-head voices now state the doctrine: a goal that
  promises software closes on code that builds and passes tests in the
  workspace, and implementation is delegated with requires_filesystem: true.

On the live profile this arms one review: the active platform goal has 0
open tasks and 21 real completed ones, so the CEO gets the checkpoint on
next launch and the company starts building instead of stopping.
2026-07-09 20:29:42 -04:00
ZacharyZhang-NY 2526633bfb Chat-wake tasks live and die with their reply run
Of the 202 tasks stranded in the backlog on July 9, 200 were "Reply to ..."
wake vehicles: every agent @mention creates a task plus a queued run, the
reply goes out, and nothing ever closed the task — a task with a completed
run is never enqueued again, so each one became a permanent zombie row. On
top of that, every message in an active thread minted a fresh wake even when
the target already had one queued, multiplying runs that would each read the
same conversation.

The lifecycle contract, made explicit:
- The reply run IS the wake task's deliverable. Completing a chat_mention
  run now closes its task (backlog/in_progress -> done, via the audited
  status path). Tasks an agent moved to review are left alone.
- One pending wake per (agent, thread): wakeFromMention returns the queued
  run instead of minting a duplicate — that run reads the thread's newest
  messages when it executes anyway.
- Migration 054 heals existing rows: wake tasks whose runs are all spent
  close as done; in-flight wakes keep working. This also stops Fix 1's
  retry-budget healing from resurrecting 31 stale replies on next launch.

The chat-governance loop test now completes each pending wake run between
posts, matching how the exchange actually interleaves in production.
2026-07-09 19:11:02 -04:00
ZacharyZhang-NY cb71afa6b7 Avatar photo uploads and a global handbrake for the agent engine
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.
2026-07-09 00:09:02 -04:00
ZacharyZhang-NY 1f89ea7089 Stop shipping megabytes of skill prompts in every snapshot refresh
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)
2026-07-08 23:20:56 -04:00
ZacharyZhang-NY 2bb2acd635 Grow a real OKR company: officers, departments, ranks - and a stable chart
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
2026-07-08 21:47:07 -04:00
ZacharyZhang-NY e55b8b85b0 Make every identity editable: agent and operator names plus avatars
- 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
2026-07-08 21:33:23 -04:00
ZacharyZhang-NY d404e1e72a Pin the UI locale to the language setting and add theme + language selectors with a dark palette 2026-07-07 12:28:06 -04:00
ZacharyZhang-NY 00e05054ca Mirror the Composio project's auth configs as the connector catalog with logo.dev icons 2026-07-07 11:06:07 -04:00
ZacharyZhang-NY a2de132083 Embed the Composio key in the app bundle instead of shipping cred.txt
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.
2026-07-07 01:40:19 -04:00
ZacharyZhang-NY 07d0b4587d Promote AgentCompany-New to the repository root and retire the old app
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.
2026-07-07 00:04:13 -04:00
ZacharyZhang-NY 810e2d5967 update 2026-05-15 19:40:20 -04:00
ZacharyZhang-NY b92cf62c4c fix(data): canonicalize operation json fields 2026-05-15 02:36:19 -04:00
ZacharyZhang-NY 66e24b1060 fix(security): scope managed path opening 2026-05-15 00:26:20 -04:00
ZacharyZhang-NY bde35c3518 fix(security): scope social account references 2026-05-15 00:15:37 -04:00
ZacharyZhang-NY 51d468e4c3 fix(security): scope work product creation 2026-05-15 00:00:04 -04:00
ZacharyZhang-NY d1a3d23c70 fix(security): scope skill assignments by company 2026-05-14 23:37:58 -04:00
ZacharyZhang-NY 41e1dec647 fix(security): enforce company scoped updates 2026-05-14 23:28:29 -04:00
ZacharyZhang-NY a532120649 feat(connectors): add Ollama and LM Studio as a local auth kind
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.
2026-05-14 14:55:38 -04:00
ZacharyZhang-NY 0e1710065f fix(messaging): self-read receipt at send time so authors don't inflate unread counts
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.
2026-05-14 14:36:10 -04:00
ZacharyZhang-NY 1e36259c5d fix(orchestration): enforce approval gates on heartbeat and recovery paths
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.
2026-04-29 20:17:33 -04:00
ZacharyZhang-NY c9deca5454 fix(database): project only live active runs 2026-04-29 00:10:23 -04:00
ZacharyZhang-NY 7dbaaf0022 fix(worker): recover tasks after worker crash 2026-04-28 23:37:24 -04:00
ZacharyZhang-NY 49ea508ee1 fix(scheduler): drain pending wakeup requests 2026-04-28 20:26:29 -04:00
ZacharyZhang-NY 749ad3a231 fix(scheduler): wake agents with assigned todo work 2026-04-28 20:16:08 -04:00
ZacharyZhang-NY 31aac7c0de fix(messages): normalize escaped markdown newlines 2026-04-24 10:35:11 -04:00
ZacharyZhang-NY eabd304a45 fix(database): normalize baseline schema ddl 2026-04-24 09:39:44 -04:00
ZacharyZhang-NY 9ee1425a71 refactor(approvals): move execution policy into database 2026-04-24 00:33:56 -04:00
ZacharyZhang-NY f8eb57ad8c refactor(database): move task runtime ownership to leases 2026-04-24 00:18:51 -04:00
ZacharyZhang-NY 1f4af89be0 refactor(database): squash schema migrations to v56 baseline 2026-04-24 00:00:13 -04:00
ZacharyZhang-NY 67ccfc1160 fix: enforce work product taxonomy 2026-04-23 22:44:41 -04:00
ZacharyZhang-NY d2e94959e0 fix: make run log creation atomic 2026-04-23 22:14:01 -04:00
ZacharyZhang-NY 88c905c381 fix: make artifact projection durable 2026-04-23 21:50:49 -04:00
ZacharyZhang-NY 84c00e0011 fix: validate and compact session entries 2026-04-23 21:21:59 -04:00
ZacharyZhang-NY c850d70bfc fix: enforce work product primary invariant 2026-04-23 21:08:07 -04:00
ZacharyZhang-NY a4af4a866a Prevent self-directed agent messages 2026-04-22 20:28:49 -04:00
ZacharyZhang-NY d13c9d83e8 Resolve filesystem work product targets by existing file 2026-04-22 20:06:01 -04:00
ZacharyZhang-NY fa006524c2 refactor(runtime): centralize lifecycle constants 2026-04-22 16:58:36 -04:00
ZacharyZhang-NY 775598ef4c fix(db): make task row state authoritative 2026-04-22 16:24:57 -04:00
ZacharyZhang-NY 3aaa64ddfd refactor(runtime): enforce source file size boundaries 2026-04-22 15:56:09 -04:00
ZacharyZhang-NY eefb515aa2 fix(runtime): classify run usage status 2026-04-22 15:41:19 -04:00
ZacharyZhang-NY 43583ef30b fix(runtime): gate cofounder checkpoints 2026-04-22 14:05:26 -04:00
ZacharyZhang-NY 7a1dd647aa fix(api): make task updates transactional 2026-04-22 14:00:24 -04:00
ZacharyZhang-NY 19f8494d09 fix(db): make filesystem lock intent explicit 2026-04-22 13:51:41 -04:00
ZacharyZhang-NY 0785c9ba47 fix(db): make task checkout atomic 2026-04-22 13:38:54 -04:00
ZacharyZhang-NY 9fba8dd6c3 fix(projector): stop writing null workspace_id on work_products
Two sites in upsertFilesystemArtifact leaked null workspace_ids
into the work_products table and produced the orphan rows the read
path now has to heal:

1. Create path unconditionally forwarded ctx.workspaceId, which was
   null whenever a run was created without a workspace binding
   (historically cognitive runs, or runs that lost their link through
   the observationPath regression fixed in 1f2f977).

2. Update path passed {workspaceId: ctx.workspaceId} as part of the
   patch. updateWorkProduct treats any key present in patch as "write
   this value", so a run touching a previously-well-linked row with
   ctx.workspaceId=null would clobber the existing workspace_id back
   to null — actively destroying data.

Resolve both at the projector layer:

  - Add workspacesMod.findWorkspaceIdByLocalPath(companyId, localPath)
    as a reverse-lookup. workspacePath is the argument the projector
    is already walking; it came from some workspace row's local_path,
    so finding that row by path is deterministic.
  - Compute resolvedWorkspaceId = ctx.workspaceId ?? reverseLookup(...)
    once, and use it for both the identity lookup
    (findWorkProductByExternalId) and the subsequent write.
  - On the update path, only include workspaceId in the patch when
    resolvedWorkspaceId is non-null. Null now means "no info — leave
    the existing column alone" instead of "clobber to null".

Together with the read-path heal in cfb8ba5 this closes the loop:
new files never enter the table with a null workspace_id if we can
possibly resolve it, old files keep their links, and truly unlinkable
rows still fall back to the observation workspace on open.

Typecheck and build:verify clean.
2026-04-21 14:52:38 -04:00
ZacharyZhang-NY cfb8ba5dc1 fix(docs): heal orphan work_products by falling back to observation workspace
Work-product rows written before the artifact projector tracked
workspace_id end-to-end can lose the link through all three joins:
workspace_id is NULL, task_id is NULL, and the row's run exists but
its runs.workspace_id is also NULL. Those orphans were readable in
the DocumentCenter (the listing doesn't need a workspace root) but
could not be opened — resolveWorkProductTarget returned null and the
toast now reports workspace_unresolved.

Add a fourth resolution tier in resolveWorkProductTarget: if the
three-step chain yields nothing, look up the company's observation
workspace (primary first, then oldest) and check whether the
external_id resolves to a real file inside that root. If it does,
return that absolute path and the row opens. If the file isn't
there, the error falls through with an extended detail line naming
the observation root that was tried, so the operator sees exactly
why the heal also failed.

This is deterministic — exactly one fallback workspace — and
side-effect-free on the read path: the row itself is left alone,
no stealth write-back. A follow-up commit will stop the projector
from writing NULL workspace_id in the first place.

Typecheck and build:verify clean.
2026-04-21 14:51:06 -04:00
ZacharyZhang-NY 390f76fa76 fix(docs): split work-product open errors into actionable reasons
The previous handler folded every null target into one error code
("WORK_PRODUCT_NOT_FOUND: Work product not found or its workspace
is unresolved"). When a user couldn't open a file we had no way to
tell whether the row was archived, the provider/external_id was
empty, the workspace had no local_path, or the external_id escaped
the root.

Widen resolveWorkProductTarget's return from (target | null) to
(target | error) where error carries one of five reasons:

  not_found             row missing for id+company
  archived              status == "archived" tombstone
  no_target             provider != filesystem and no url
  workspace_unresolved  all three workspace_id / task_id / run_id
                         lookups returned no local_path
  path_escape           external_id is absolute or steps outside root

Each reason includes the concrete detail (ids checked, paths tried)
so the toast on the renderer side tells the user exactly which slot
is null without having to open the DB.

The IPC handler maps each reason to its own failure code and passes
the detail through, so WorkProductRow's toast now reads e.g.
"workspace_id=null task_id=null run_id=null — none led to a
workspace with a local_path" instead of a generic unresolved.

Typecheck and build:verify clean.
2026-04-21 14:45:59 -04:00