17 KiB
status, created, owner
| status | created | owner |
|---|---|---|
| draft | 2026-04-25 | cfl |
Office: Inbox, Approvals & Activity Log
Why
When agents work autonomously, they will encounter situations that require human judgment: hiring a new agent, exceeding a budget, completing a task that needs review, or hitting an error they cannot resolve. Without a centralized inbox, users must poll individual agent statuses and task lists to discover what needs attention. Without an approval system, agents cannot request permission for high-impact actions. Without an activity log, there is no audit trail of what happened and who did it.
Office adds an inbox that aggregates all items requiring human attention, an approval system for gating agent actions, and a full activity log for audit and debugging.
What
- The inbox at
/office/inboxis the user's single view of everything that needs attention. - The inbox is a computed view over pending approvals, budget alerts, agent errors, review requests, and clarification requests - not a separate table.
- Each inbox item shows: type icon, summary text, related agent/task, timestamp, action buttons (approve/reject, view task, dismiss).
- A badge count on the sidebar "Inbox" link shows unresolved items; resolved items move to an archive view.
- New inbox items trigger notifications via existing providers (Local/WebSocket, System/OS, Apprise).
- Approvals SHALL gate high-impact agent actions (hire, budget increase, board approval, task review, skill creation).
- Tasks SHALL support an
execution_policydefining ordered review and approval stages. - An activity log SHALL record every significant action with actor, target, and details for audit.
Data model
Inbox (computed)
Inbox is not a table. It is a read-side aggregation over:
- Pending approvals: hire requests, budget increase requests, board approval requests.
- Budget alerts: agents approaching or exceeding limits.
- Agent errors: sessions that failed with unrecoverable errors.
- Review requests: tasks with pending reviewer/approver decisions.
- Clarification requests: agents waiting on a question response.
Items ordered by recency, unresolved first.
office_approvals
| Field | Type | Notes |
|---|---|---|
id |
string | PK |
workspace_id |
string | FK |
type |
enum | hire_agent | budget_increase | board_approval | task_review | skill_creation |
requested_by_agent_instance_id |
string | null for system-generated |
status |
enum | pending | approved | rejected |
payload |
JSON | type-specific data (see below) |
decision_note |
text | optional, from reviewer |
decided_by |
string | user ID |
decided_at |
timestamp | nullable until resolved |
created_at |
timestamp |
Payload shapes:
hire_agent: proposed name, role, profile, skills, budget.budget_increase: current budget, requested budget, reason.board_approval: action description, context, impact.task_review: task ID, completion summary, deliverables.skill_creation: skill name, slug, SKILL.md content preview, requesting agent.
Task execution_policy (JSON)
Optional field on each task defining ordered review/approval stages.
execution_policy: {
stages: [
{ type: "review", participants: [{type: "agent", id: "security-agent"}, {type: "agent", id: "qa-agent"}], approvals_needed: 2 },
{ type: "approval", participants: [{type: "user", id: "cfl"}], approvals_needed: 1 }
]
}
- Reviewers (
type=review): N agents/users review output in parallel; each provides feedback. - Approvers (
type=approval): M agents/users must approve beforedone; requiresapprovals_needed. - Stages run sequentially. Within a stage, participants run in parallel. Sequential reviews use multiple single-participant review stages.
A sibling execution_state JSON field tracks current stage, which participants have responded, and the outcome.
office_activity_log
| Field | Type | Notes |
|---|---|---|
id |
string | PK |
workspace_id |
string | FK |
actor_type |
enum | user | agent | system |
actor_id |
string | user ID, agent instance ID, or "system" |
action |
string | verb (see table below) |
target_type |
enum | task | agent_instance | routine | project | approval | skill | budget_policy |
target_id |
string | entity ID |
details |
JSON | action-specific context |
created_at |
timestamp |
Activity actions
| Action | When logged |
|---|---|
task.created |
A task is created (by user, agent, or routine) |
task.assigned |
A task's assignee changes |
task.status_changed |
A task's status changes (including completion) |
task.commented |
A comment is posted on a task |
agent.created |
A new agent instance is created |
agent.hired |
A hire approval is approved and agent activates |
agent.paused |
An agent is paused (manually or budget) |
agent.resumed |
A paused agent resumes |
agent.stopped |
An agent is deactivated |
agent.error |
An agent session fails with an error |
approval.created |
An approval request is submitted |
approval.resolved |
An approval is approved or rejected |
routine.triggered |
A routine fires and creates a task |
routine.skipped |
A routine fires but skips due to concurrency policy |
budget.alert |
A budget threshold is crossed |
budget.exceeded |
A budget limit is exceeded |
budget.reset |
Monthly budget counters reset |
skill.created |
A skill is added to the registry |
skill.updated |
A skill is modified |
cost.recorded |
A cost event is recorded (summarized, not per-token) |
wakeup.processed |
A wakeup is claimed and processed |
API surface
Notifications
A new event type office.inbox_item is available alongside
session.turn_finished and session.clarification_requested. Users subscribe
per provider at /settings/general. Defaults: Local (browser) and System (OS)
auto-subscribed when Office is enabled. Notification content: item type,
summary, deep link to /office/inbox.
Approval flow
- Agent submits an approval request via tool call during a session, or scheduler submits for system events (budget alerts).
- Approval created with
status=pending; appears in inbox. - Requesting agent's session completes (agents do not block - they exit and are woken later).
- User reviews and approves/rejects with optional note.
- On resolution,
approval_resolvedwakeup queued for the requesting agent instance. - Agent's next session receives the result:
hire_agentapproved: CEO assigns tasks to new agent.hire_agentrejected: CEO finds an alternative.budget_increaseapproved: budget updated, agent resumes.task_reviewapproved: task ->done.task_reviewrejected: task ->in_progresswith feedback.
Review/approval flow (task execution_policy)
When an assignee agent marks a task done:
- If review stage exists: status ->
in_review. All reviewer participants woken in parallel. Each reviews independently and posts comments with approve/reject verdict. - Task stays
in_reviewuntil allapprovals_neededresponses collected. No action on individual responses. - All reviewers approve: advance to next stage (approval, or
doneif none). - Any reviewer rejects: wait for all remaining reviewers; then return to
in_progress. Assignee receives a single wakeup with ALL review comments aggregated, avoiding thrash on individual reviews. - Approval stage: inbox item per approver. When
approvals_neededmet, task ->done. - Any approver rejects: same pattern - wait for all, then
in_progresswith all feedback. - No policy: task ->
done.
Downstream blockers resolve only when task reaches done (after all stages pass).
Per-task and workspace defaults
- Users set reviewers/approvers when creating a task (properties panel or new task dialog).
- Agents can set reviewers/approvers during subtask creation (e.g. CEO creates "build" subtask with QA agent as reviewer).
- Workspace setting
require_approval_for_task_completion(defaultfalse): all office tasks default to having the user as an approver. Overridable per task. - The blocker system handles sequencing: subtask 2
blocked_by: [subtask 1]. When subtask 1 reachesdone(after all stages), blocker resolves andtask_blockers_resolvedwakeup fires for subtask 2's agent.
Approval configuration (workspace settings)
require_approval_for_new_agents: defaulttrue. If false, hire requests auto-approve.require_approval_for_task_completion: defaultfalse.auto_approve_budget_under_cents: threshold below which budget increase requests auto-approve (default 0 = all require approval).require_approval_for_skill_changes: defaulttrue. If false, agent-created skills bypass approval.
UI routes
/office/inbox- inbox with type icons, summaries, action buttons; archive view for resolved items./office/company/activity- chronological feed; filter by actor type, action, target type, time range; click target to navigate./officedashboard - "Recent Activity" with last ~10 entries.
State machine
Approval states: pending -> approved | rejected. Once resolved, terminal.
Task states relevant to inbox: in_progress -> in_review -> (back to in_progress on rejection, or forward to done after all stages pass). Stages run sequentially; participants within a stage run in parallel.
Permissions
- Workspace users can approve/reject items in the inbox.
- Agents submit approval requests via tool calls; they cannot resolve their own requests.
- The CEO receives
budget_alertwakeups for agent-scoped budget alerts. - See
agents/spec for thecan_manage_own_skillspermission gatingskill_creationapprovals.
Failure modes
- Agent submits approval but kandev restarts: pending approval persists in DB; inbox item reappears on next read.
- All reviewers approve but one rejection arrives late: rejection wins only if it arrives before the count is met; otherwise the task has already advanced and the late rejection is ignored.
- Assignee agent missing when blocker resolves:
task_blockers_resolvedwakeup remains queued until the assignee comes online or the task is reassigned.
Persistence guarantees
The inbox listing itself is a computed view — office_inbox does not exist. On every fetch DashboardService.GetInboxItems re-aggregates the underlying sources, so a kandev restart never loses inbox state and never reorders existing rows: identical inputs produce identical output. The badge count (GetInboxCount) is computed the same way.
Durable (survives restart):
- Approval requests persist in
office_approvalswithstatus = pending|approved|rejected. Pending rows stay in the table until decided; decided rows stay forever for audit.decided_by,decided_at, anddecision_noteare written atomically with the status flip inDecideApproval. - Activity log entries persist in
office_activity_log(append-only, no TTL). Theapproval.created/approval.resolvedentries are written byApprovalServicefrom inside the decide flow; failures to write activity are logged but never bubble back to the caller. - Budget alerts and agent errors surfaced in the inbox are activity rows with
action ∈ {budget.alert, agent.error}— the inbox re-reads up to 20 most-recent entries per type on every fetch. They do not have a separate "resolved" state; the rows persist forever in the log. - Failed-run and auto-paused-agent rows come from
FailureInboxSource(failedoffice_runsrows +office_agents.status = paused). They persist across restart and disappear from the inbox only when the user "Mark fixed"-dismisses them, when the run is re-queued, or when the agent is un-paused. - Inbox dismissals persist in
office_inbox_dismissalskeyed by(user_id, item_kind, item_id). Single-user kandev writesuser_id = "default". The inbox filters dismissed rows out on every fetch; the underlying activity/run rows are NOT deleted. - Reviewer/approver participants for task
execution_policystages persist inworkflow_step_participants; only rows withdecision_required = trueproducetask_review_requestinbox items. Runner/assignee rows never produce review inbox items. Decisions persist inworkflow_step_decisionsand are superseded (never deleted) when re-recorded, so the "viewer needs decision" check is deterministic across restarts. - Approval
task_blockersrows (intask_blockers) persist; blocker resolution on completion triggers atask_blockers_resolvedwakeup queued in the office scheduler queue, which itself persists inoffice_run_requests. - Notification subscriptions persist in
notification_subscriptions(event typeoffice.inbox_item). User preference about which providers receive inbox notifications survives restart.
Transient (recomputed on every read):
- The inbox listing array, badge count, item titles, item descriptions, and the per-task
task_review_requestitems are all recomputed per-request. There is no caching layer. - Item IDs for synthetic types are deterministic strings rebuilt at fetch time:
review:<task_id>:<viewer_agent_id>for review-request rows.- Inbox row IDs for failed runs / paused agents are the
run_id/agent_idfrom the source row, so dismissals key against stable identifiers.
- WS notifications fired by
notifications.HandleInboxItemforoffice.inbox_itemevents are at-most-once: if a delivery worker is mid-flight when kandev restarts, the in-flight delivery is lost. The inbox item itself is still discoverable on next dashboard fetch because the underlying source row persists. - The frontend Zustand inbox slice hydrates from the SSR fetch; it holds no durable client-side state beyond what the server returns. A hard reload reseeds from the server.
- Per-agent inbox views (
GetAgentInboxItems) are computed on demand for agent wakeups; agents do not hold inbox state between sessions.
There is no retention policy or archival job: dismissed/decided rows accumulate in their respective tables indefinitely. Out-of-scope for this iteration (see Out of scope).
Scenarios
-
GIVEN the CEO agent submits a hire request for a new "QA Bot" worker, WHEN the approval is created, THEN it appears in the inbox with type
hire_agent, showing the proposed name, role, profile, skills, and budget. The inbox badge increments. -
GIVEN a pending hire approval in the inbox, WHEN the user clicks "Approve" with the note "Looks good, start with the login tests", THEN the approval status moves to
approved, the agent instance activates, anapproval_resolvedwakeup is queued for the CEO, and activity entries are logged for both the approval resolution and the agent hire. -
GIVEN a worker agent's budget crosses 80%, WHEN the budget check fires, THEN a budget alert appears in the inbox with the agent name, current spend, and limit. The CEO also receives a
budget_alertwakeup. -
GIVEN a user on the activity log page, WHEN they filter by
actor_type=agentandaction=task.created, THEN they see only tasks created by agent instances, with links to each task. -
GIVEN the workspace setting
require_approval_for_new_agents=false, WHEN the CEO submits a hire request, THEN the agent is auto-approved and activates immediately. An activity entry is still logged. -
GIVEN an agent session that fails with an error, WHEN the error is detected, THEN an
agent.errorinbox item appears with the error message and a link to the failed session. -
GIVEN a task with reviewers [security-agent, qa-agent] (approvals_needed=2) and approver [user], WHEN the assignee agent completes the task, THEN the task moves to
in_review. Both reviewer agents are woken in parallel. Each reviews the changes and posts comments. When both approve, the task advances to the approval stage and an inbox item is created for the user. -
GIVEN a task in review stage where the security-agent rejects, WHEN the rejection is recorded, THEN the task returns to
in_progress. The assignee agent receives a wakeup with the security agent's feedback. The QA agent's pending review is cancelled. -
GIVEN a parent task "Add auth" with subtasks Spec (requires_approval=true) -> Build (blocked_by Spec) -> Review -> Ship, WHEN the spec agent completes the Spec subtask, THEN Spec moves to
in_reviewand an inbox item appears. WHEN the user approves, THEN Spec moves todone, the blocker on Build resolves, and the build agent receives atask_blockers_resolvedwakeup. -
GIVEN a new inbox item (any type), WHEN the item is created, THEN a browser notification is shown (if Local notifications enabled) and an OS notification fires (if System provider enabled).
-
GIVEN a task with
requires_approval=falsecreated by an agent, WHEN the agent marks it done, THEN the task moves directly todonewithout creating an inbox item. Downstream blockers resolve immediately.
Out of scope
- Approval workflows with multiple reviewers or escalation chains beyond the staged execution policy.
- Activity log retention policies or archival.
- Batch approval (approve/reject multiple items at once).
- Custom approval types defined by users.