Commit Graph

51 Commits

Author SHA1 Message Date
realmroot[bot] 7ed77d2295 fix: harden v2 service boundaries (#243)
* fix: harden v2 service boundaries

* fix(sessions): validate observation configuration first

---------

Co-authored-by: jarvis <jarvis@agents.realmroot.dev>
2026-09-02 16:50:47 -04:00
jarvis a2fa54a729 feat!: rebuild Agent Kanban for Realmroot Toolbox
Replace the v1 Agent/Machine runtime and CLI architecture with the v2 Resource Server, AMA-backed projections, task lifecycle, session observation, BDD specifications, and a single-package layered codebase.

BREAKING CHANGE: remove the v1 CLI, internal Agent/Machine runtime entities, workspace layout, and legacy runtime coordination APIs.
2026-09-02 00:17:52 -04:00
saltbo 5d009cd8c2 fix(runtime): restore historical AMA session streaming 2026-08-21 21:55:44 -06:00
saltbo d2c6c6c5f6 fix(auth)!: restore AK-owned agent sessions
Use Realmroot only for human and machine authentication, authorize AMA through the browser user's multi-resource grant, and restore AK-managed Ed25519 Agent sessions.

Preserve Better Auth source tables while removing the mistaken Realmroot Agent binding and machine M2M paths.

BREAKING CHANGE: AK no longer accepts Realmroot Agent tokens or AMA machine credentials; active browser users must sign in again to authorize the AMA resource.
2026-08-21 01:55:28 -06:00
saltbo 972673aecd feat(auth)!: snapshot Realmroot native cutover
Replace Better Auth user, machine, and agent authentication with Realmroot native flows, migrate tenant ownership, and route AMA access through the cutover adapter. Preserve this deployed migration state as the baseline for the narrower user-auth-only redesign.
2026-08-21 00:44:07 -06:00
saltbo dc9d0a440e feat(maintainers): serialize webhook dispatch 2026-07-20 02:51:33 -04:00
saltbo 1140161b0b feat(agents): expand leader runtime support
Detect supported leader runtimes by environment marker and require a matching live ancestor process plus session identity. Keep leader resources immutable while separating leader runtimes from AMA-backed worker runtimes.
2026-07-17 19:56:37 -04:00
saltbo 2effaa042d refactor(ama): store session secrets by ref only 2026-07-04 20:49:05 -04:00
saltbo 9d00096ab5 feat(ama): isolate board maintainer secrets by vault 2026-07-04 19:43:08 -04:00
saltbo f7c324f268 fix(runtime): refresh AMA tokens with resource
Agent-Profile: https://agent-kanban.dev/agents/05f3d68684e84e89
2026-07-03 23:36:35 -04:00
saltbo 33bd22f236 fix(agent): clarify schedulable status and auth commands 2026-07-02 23:08:45 -04:00
saltbo 1dc2eb4de9 feat(maintainers): add scheduled heartbeat toggle
Add heartbeat_enabled persistence and enforce a 1 hour minimum heartbeat interval.

Keep webhook triggers active when scheduled heartbeat is disabled, and expose the toggle through the UI and CLI.
2026-06-26 14:58:31 -04:00
saltbo f848041499 feat(maintainers): add scoped auth sessions 2026-06-26 11:10:24 -04:00
saltbo 1b74518bc9 feat(maintainers): require maintainer agents 2026-06-26 02:02:10 -04:00
saltbo 59c88dffa0 feat(maintainers): taint maintainer agents 2026-06-26 00:26:27 -04:00
saltbo 32e16f5510 feat(maintainer): add board repository auth flow 2026-06-25 13:07:39 -04:00
saltbo 3807ee189d feat: wire maintainers to AMA events and memory 2026-06-25 01:28:57 -04:00
saltbo 464cf87b51 feat(github): wire repository lifecycle to the platform GitHub App
Connect repo onboarding to the GitHub App and complete the installation
webhook, so per-repo App coverage is tracked and surfaced end to end.

- migration 0027: github_installations + github_installation_repositories
- githubInstallations.ts: owner-scoped install tracking; repoAppStatus
  coverage computed on read (single source of truth, no denormalization)
- webhook: handle installation + installation_repositories events (was
  pull_request only); githubApp.ts gains getInstallation /
  listInstallationRepositories / recordInstallationFromSetup
- routes: new /api/github-app config|setup|repositories endpoints; repo
  create/list/get return app_status; the authenticated config reports the
  owner install status + accounts
- UI: install/connected state on Repositories + Account pages, dual-tab
  Add dialog (browse-and-import from GitHub | manual URL), status badges
- drop the GITHUB_AGENT_TOKEN push-credential fallback: the App
  installation token is the sole credential; an uninstalled repo fails
  dispatch loudly instead of using a shared long-lived PAT
- env: add GITHUB_APP_SLUG; vite allowedHosts for a stable dev tunnel
2026-06-19 19:11:29 -04:00
saltbo 8e0b23723d feat(ama): eager, feature-coupled AMA provisioning; only sessions stay lazy
Move AMA resource creation out of the dispatch path into the AK actions that own
them:
- Connect AMA -> provision the owner's AMA project + session-secret vault
  (POST /api/ama/provision, called once from the account page after linking).
- Add Machine -> create the matching environment: local computer = self_hosted
  (+ ama-runner via `ak start`), or Cloud Sandbox = a cloud environment
  (POST /api/machines/cloud). Unlimited; machines.hosting distinguishes them.
- Create Agent -> create the AMA agent first, then persist the AK agent with its
  ama_agent_id; if the AMA call fails the request errors and nothing is written.
- Dispatch only creates the session now: it reads the existing project and the
  stored agents.ama_agent_id, picks an environment among the owner's machines
  (cloud sandbox or local) by runtime, and 409s with "add a machine or cloud
  sandbox" when none fits. No more per-owner lazy cloud singleton.

Agent and machine creation require a linked AMA account (the agent subsystem is
AMA-backed); the kanban board and tasks still work without AMA. Adds migrations
0025 (machines.hosting) and 0026 (agents.ama_agent_id).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 19:27:57 -04:00
saltbo e1c9e965ea feat(ama): call AMA as the user's own linked account; drop runner federation
Each AK user connects their own AMA account via BetterAuth account-linking (AMA
registered as a generic OIDC provider, enabled only when AMA OAuth is configured).
AK→AMA calls now use that user's linked-account token (auto-refreshed via
getAccessToken), with ownerId threaded through the createAma* call graph, instead
of a faceless client_credentials token. Cloud-scheduling routes are gated on an
active AMA connection; standalone AK (no AMA env) stays a pure kanban, unchanged.

Self-hosted runners self-authenticate via ama-runner's native device login:
createAmaFederatedRunnerToken / signed-JWT / JWKS are removed, machine onboarding
returns only {origin, projectId, environmentId}, and `ak start` (unchanged usage)
spawns ama-runner with --api-server/--environment-id rather than minted tokens.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 15:39:05 -04:00
saltbo 6fb6bb5859 feat(dispatch): surface dispatch as a system action on the task timeline + runner lifecycle logs
Dispatch is the control plane's action on a task, so record it in task_actions as
a 'system' actor: 'dispatched' on success, 'dispatch_failed' on failure (deduped
by reason — one timeline entry per distinct error, not per retry). The reason
shows in the task detail Activity timeline; raw errors stay in logs. Adds the
'system' actor_type + the two actions (migration 0024) and ActivityLog rendering.

Bumps the bundled ama-runner to 0.3.4 (narrates lease lifecycle at INFO, stops
WARNing on normal lease end).
2026-06-17 23:59:01 -04:00
saltbo 427c662eb9 feat(server): replace the daemon loop with AMA dispatch sweeps, binding teardown, and GitHub App PR sync
- Dispatch policy: defer blocked/scheduled/at-capacity tasks (todo+assigned,
  no binding) instead of failing; 409 only when no machine supports the
  runtime; skip runners whose provider quota window is at 100%.
- Per-minute cron closes the loop: reconcileAmaBoundTasks releases tasks
  whose AMA session died and mops up leftover bindings; dispatchPendingAmaTasks
  re-dispatches assigned, unblocked, due tasks; the stale sweep stops the
  bound session before releasing.
- releaseTaskRuntimeBinding is the single teardown path: stop session,
  revoke the vault session secret (credential id stored via migration 0023),
  copy AMA usage totals into ama_agent_sessions, close the AK session, clear
  annotations. Complete/cancel transition locally first with best-effort
  teardown; reject falls back to restart when the session is already dead.
- PR state sync through a platform GitHub App: users install the app on
  their repos (no per-user setup); POST /api/webhooks/github-app verifies
  the app HMAC secret and maps PR merged→done / closed→cancelled in real
  time, routed by pr_url — replacing the old 30s gh poll.
- Dispatch passes per-agent GIT_AUTHOR_*/GIT_COMMITTER_* in runtimeEnv;
  machine registration returns a server-pinned runner version
  (AMA_RUNNER_VERSION); AMA client retries once on 401 with a fresh token;
  GET paths no longer provision AMA resources.
2026-06-11 12:06:23 -04:00
saltbo 2b47b40b12 refactor(runtime): consolidate AMA integration schema 2026-06-07 19:50:47 -04:00
saltbo 19eef6f419 feat(runtime): integrate AMA runner onboarding 2026-06-07 19:19:42 -04:00
saltbo 3bd8ad366e feat(runtime): dispatch AK tasks through AMA 2026-06-07 17:13:42 -04:00
saltbo db228e5177 feat(subagents): store subagents separately 2026-05-17 23:42:22 -04:00
Jasper Van b730fe4f19 feat: redesign account profile settings (#177)
Agent-Profile: https://agent-kanban.dev/agents/a318237412dd8b98
2026-05-05 07:56:12 -04:00
saltbo a4f8f76a0f feat(auth): require email verification 2026-05-04 23:35:30 -04:00
saltbo 39db878793 feat(web): split board settings and add metric badges 2026-05-04 16:41:27 -04:00
saltbo f930da2413 feat: add board labels for tasks 2026-05-04 11:03:29 -04:00
saltbo f5823fc95e feat(agent): add versioned agent publishing 2026-05-03 20:19:45 -04:00
saltbo 7fbbbfe2eb feat: add registered worker subagents 2026-05-03 10:55:55 -04:00
saltbo d3f533e2eb fix(api): enforce one leader per runtime 2026-04-14 15:35:04 -04:00
saltbo d0d323c989 feat(admin): add machines page with Analytics Engine observability
Per-machine QPS, error rate, and latency metrics powered by Cloudflare
Analytics Engine. Hono middleware writes data points per request,
AE SQL API aggregates over 5-minute windows. Admin machines page shows
all machines cross-tenant with owner, active agents, and live metrics.
2026-04-13 12:05:46 -04:00
saltbo c9e9339ec8 refactor: migrate from Pages+Functions to Cloudflare Worker
Unify the app into a single Worker deployment:
- Move functions/api/ → server/, add worker/index.ts entry point
- Merge relay-worker DO (TunnelRelay) into the main worker
- Replace Pages Functions with @cloudflare/vite-plugin build
- Integrate share SSR (OG meta injection) into Hono routes
- Remove dayjs dep from shared (native Date suffices)
- Simplify dev to single `vite dev`, deploy via CF Git integration
- Delete packages/relay-worker/ and Pages-specific _routes.json
2026-04-13 07:58:39 -04:00
saltbo 09b65eb041 fix(api): record session_id in task_actions to fix incorrect active_session_id
The getTask query derived active_session_id from the latest agent_session,
which returned the wrong session when an agent had multiple sessions.
Now session_id is stored directly on each task_action row and read from
the claimed action, giving the correct session for the chat relay.
2026-04-02 09:26:38 -04:00
saltbo ca7d7aceab feat(api): unify agent Ed25519 key — GPG subkey is single source of truth
Previously agents had two independent Ed25519 keys: one for JWT auth
(generated by crypto.subtle) and one for GPG commit signing (generated
by openpgp.js addSubkey). This violated the original design of one
cryptographic identity per agent.

Now the GPG subkey is the sole key source. Its Ed25519 material is
extracted as JWK for JWT auth. Agent ID = GPG subkey keyID. All agents
(including builtins) go through the GPG identity path.

Other changes:
- username is now required in CreateAgentInput (no more deriveUsername)
- username is globally unique (shared email domain), migration 0015
- Validation order: username check before GPG mutation to avoid orphans
- Runtime guards on openpgp.js internal field access
2026-03-28 16:30:45 -04:00
saltbo 7d18fac4f7 feat: agent mailbox inbox + GPG sync hardening 2026-03-28 11:22:55 -04:00
saltbo 575006906e feat: agent identity — username, GPG subkey, daemon signing
Complete agent commit identity implementation:

- Agent entity: add username (unique per owner, auto-derived from name)
  and gpg_subkey_id fields. Email computed as username@mails.agent-kanban.dev.
- GPG subkey: auto-generated on agent creation via root key addSubkey().
  Subkey ID stored on agent record for daemon use.
- Daemon injection: taskRunner sets up ephemeral GNUPGHOME with imported
  root+subkey chain, injects GIT_AUTHOR/COMMITTER identity and GPG signing
  config via GIT_CONFIG_COUNT env vars. Cleanup on process exit.
- API: GET /api/agents/:id/gpg-key (machine-only) returns armored private
  key + subkey ID for daemon GPG setup.
- CLI: ak create agent --username flag, auto-derive if omitted.
- Session persistence: gpgSubkeyId, agentUsername, agentName saved for
  resume flow.
2026-03-28 03:02:55 -04:00
saltbo 980747574a feat(task): add scheduled_at for deferred task scheduling
Tasks can now specify a one-time scheduled_at timestamp (ISO 8601 with
timezone, normalized to UTC via dayjs). The daemon skips tasks whose
scheduled time has not yet arrived. Released/timed-out tasks have their
scheduled_at cleared so they become immediately re-dispatchable.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 14:11:12 -04:00
saltbo cf3fa34f32 feat: add board type (dev/ops) with workspace abstraction
Boards now have a required `type` field — `dev` for development workflows
(git/PR/code review) and `ops` for operations workflows (no repo). This
drives per-type system prompts, workspace creation (worktree vs tmpdir),
and task validation (dev requires repository_id, ops forbids it).

Key changes:
- Workspace abstraction (workspace.ts) decouples workspace lifecycle from
  repo operations — scheduler and process manager stay unchanged
- System prompt generates per-board-type agent work protocol
- BoardType enum with isBoardType guard centralizes type validation
- gh auth downgraded to warning so ops-only daemons can run without it

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 11:51:12 -04:00
Jasper Van 0f4ef89811 feat: integrate Better Auth admin plugin with DB migration (#97)
- Add admin() plugin to betterAuth.ts with defaultRole "user" and adminRoles ["admin"]
- Create migration 0009_admin_fields.sql for role/banned/banReason/banExpires columns
- Restrict /api/auth/admin/* routes to user identity in auth.ts ROUTE_RULES
- Add 0008 and 0009 migrations to test helper applyMigrations list
- Document manual first-admin setup via D1 console in code comment

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 01:36:48 -04:00
saltbo 3d9f1d8daf feat(task): add per-board auto-incrementing sequence number
Add a `seq` field to tasks, similar to GitHub issue numbers — each board
maintains its own counter. Displayed as #N on cards and detail drawer.
Title in detail drawer is now read-only (no EditableText).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 01:00:35 -04:00
saltbo 5414ceddc6 feat(cli,api): device fingerprint for machine deduplication and multi-env credentials
Machines are now identified by a hardware fingerprint (sha256 of hostname + MAC),
eliminating duplicate machine creation on API key rotation or environment switching.
CLI config stores credentials per api-url host, enabling seamless environment switching
without re-entering keys.

- Add device.ts: generate deterministic device ID from hardware fingerprint
- Rewrite config.ts: credentials stored by host, auto-migrate legacy flat format
- POST /api/machines: upsert by (owner_id, device_id) via ON CONFLICT
- Heartbeat: bind unbound keys, reject keys bound to different machines
- Frontend reconnect: simplified to show `ak start --api-url` command only
- Migration 0006: add device_id column with NOT NULL + unique index

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 22:35:04 -04:00
Jasper Van 711a1bf57c Rename task_notes to task_actions with actor_type/actor_id (#89)
Replace the task_notes table and agent_id/session_id columns with
task_actions and actor_type/actor_id to track all identity types
(user, machine, agent:worker, agent:leader). This enables user and
machine actions to appear in the activity log and board animations.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 10:39:46 -04:00
saltbo 9115d21a64 feat(auth): add agent kind (worker/leader) and CLI runtime wrapper
Introduce agent `kind` field to differentiate worker agents (daemon-spawned)
from leader agents (CLI-launched via `ak claude/codex/gemini`). Leader agents
can complete, reject, cancel, and assign tasks; workers can only claim and
review. Session capabilities are granted based on kind.

- Add `kind` column to agents table (worker | leader)
- Split IdentityType: agent → agent:worker / agent:leader
- Runtime auto-detection via env vars (CLAUDECODE, CODEX_CI, GEMINI_CLI)
- `ak claude/codex/gemini` wraps runtime with session lifecycle
- `ak whoami` for agents to query their own identity
- JWT `aid` claim verified against session binding (prevent escalation)
- Auth middleware resolves kind in single JOIN query (no N+1)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 19:57:50 -04:00
Jasper Van 9d928951c0 refactor: rename task_logs to task_notes across API, database, and codebase (#59)
Breaking change: renames the task_logs concept to task_notes throughout
the entire stack — database table, API endpoints, shared types, CLI
client methods, SSE event types, and frontend components.

- Add migration 0002 to rename table task_logs → task_notes
- API: POST/GET /api/tasks/:id/logs → /api/tasks/:id/notes
- Types: TaskLog → TaskNote, TaskWithLogs → TaskWithNotes
- Repo: addTaskLog → addTaskNote, getTaskLogs → getTaskNotes
- CLI: addLog → addNote, getTaskLogs → getTaskNotes, formatTaskLogs → formatTaskNotes
- SSE: event type "log" → "note"
- Frontend: logs state → notes, initialLogs → initialNotes, sseLogs → sseNotes

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 14:01:40 -04:00
saltbo d84dc66373 refactor: replace prettier with biome for formatting and linting
Prettier's aggressive line-splitting (printWidth 100, single quotes) didn't
match the project's existing compact code style. Switch to Biome which
provides formatting + linting in one tool with better chain-call handling.

- Remove prettier, add @biomejs/biome
- Configure biome: double quotes, lineWidth 150, recommended lint rules
- Update lefthook pre-commit to use `biome check`
- Full codebase format pass with new config

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 19:51:41 -04:00
saltbo 5580e3818c refactor(ui): make board read-only, keep only reject/complete for review
Remove all human task management from UI — task creation (+ Task button,
onboarding Create Task step), status transitions (claim/cancel/release/assign),
delete button, and AssignDropdown component.

Board is now observation-only. Only reject and complete buttons remain,
shown exclusively for in_review tasks. Onboarding simplified from 3 to 2 steps.

Update CLAUDE.md with UI Principles and Post-Write Workflow to include
Playwright tests for frontend changes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 17:08:37 -04:00
saltbo de7d3dc3ae test: add unit tests for API layer (boardRepo, repositoryRepo, routes, sse)
Coverage improved from 36.73% to 47.33%. Four new test files with 115 test
cases covering board CRUD, repository CRUD, all HTTP routes, and SSE streaming.
Shared test infrastructure extracted to tests/helpers/db.ts. Updated CLAUDE.md
with test ownership rule: main agent owns source code, test-writer agent owns
test code.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 16:19:35 -04:00