Tasks, schedules, and agents API
These endpoints cover session-scoped task inspection plus managed-agent orchestration. Daemon-owned projects and project tasks now have their own separate surface. Read Projects API for that model. Sessions that are channel members can also receive daemon-created runs of kindChannelDelivery. Those runs still execute through the normal runs API and session queue. The public conversation itself lives in the separate Channels API.
Schedules, sidechains, and reviewer results can also feed public channel threads indirectly through durable channel stimuli and root-thread bindings. The schedule, task, and run themselves still remain session- and run-scoped; the channel only owns the public conversation projection.
Task endpoints
GET /v1/sessions/{session_id}/tasksGET /v1/sessions/{session_id}/tasks/{task_id}GET /v1/sessions/{session_id}/tasks/{task_id}/outputPOST /v1/sessions/{session_id}/tasks/{task_id}/stop
List and inspect tasks
GET /v1/sessions/{session_id}/tasks supports:
status: filter bypending,in_progress,blocked,completed,failed, orcancelled
TaskRecord with fields such as:
idtitledescriptionstatusowner_agent_idblocked_byblocksoutputmetadatacreated_at_msupdated_at_ms
Read task output
GET /v1/sessions/{session_id}/tasks/{task_id}/output supports:
wait: wait for output or state change before returningtimeout_ms: wait duration whenwait=truetail_bytes: excerpt size, defaulting to a small tail viewfull: include full captured output text when available
TaskOutputView:
retrieval_statustaskoutput_file_pathoutput_excerptoutput_textoutput_truncatedoutput_size_bytesoutput_total_bytesoutput_rotatedoutput_rotation_count
full=true returns the full retained output body. After output rotation, the retained body starts with a daemon marker and contains the latest retained output window, not bytes that were already discarded by retention.
Current retrieval_status values include:
successnot_readytimeout
retrieval_status: "success" means the daemon read the task output view. It does not mean the task succeeded. For daemon-managed shell tasks, treat command success as task.status: "completed" plus task.metadata.exit_code: 0. If task.status is failed or cancelled, clients and agents must not report the shell task as successful even when output retrieval succeeded.
Example:
task.status: "failed" with task.metadata.terminal_reason: "daemon_restarted" and task.metadata.recovered_on_boot: true; the output endpoint still returns partial captured output when available.
Daemon-managed shell-task metadata also records shutdown evidence when a stop or restart recovery has to terminate a process tree:
process_tree_shutdown_confirmed: whether no tracked process remained after bounded signal escalationprocess_tree_shutdown_matched_target: whether the persisted process identity matched before signallingprocess_tree_shutdown_signal_sent: whether at least one process or process group signal was sentprocess_tree_shutdown_supported: whether the current platform has daemon process-tree shutdown supportprocess_tree_remaining_pids: bounded remaining process ids when shutdown could not be confirmed
POST .../stop is called while a live shell task is still shutting down, the response may remain in_progress with metadata.stop_requested=true. Use task_output?wait=true or fetch the task again to observe the terminal state.
Stop a task
POST /v1/sessions/{session_id}/tasks/{task_id}/stop accepts:
Schedule endpoints
GET /v1/schedulesPOST /v1/schedulesGET /v1/schedules/{schedule_id}POST /v1/schedules/{schedule_id}/cancelPOST /v1/schedules/{schedule_id}/pausePOST /v1/schedules/{schedule_id}/resumePOST /v1/schedules/{schedule_id}/trigger
GET /v1/schedules supports:
session_id
Create a schedule
POST /v1/schedules accepts a ScheduleCreateRequest.
Required fields:
nametarget_session_idcadence- exactly one of:
requestobservation_materialization
target_agent_idowner_session_idowner_agent_idcreated_by_run_idmax_executionsoverlap_policymisfire_policy
requestis a fullSubmitInputRequest- when the nested schedule request uses only
input_items,contentcan be omitted
Cadence variants
once:
interval:
cron:
- interval cadence must be at least 5 seconds
- cron timezone defaults to
UTC max_executions, when set, must be greater than zero- when
observation_materializationis used, itstarget_session_idmust match the scheduletarget_session_id
Schedule policy fields
overlap_policy:
skipqueue_oneparallel
misfire_policy:
{ "type": "coalesce_once" }{ "type": "skip_missed" }
Schedule creation example
ScheduleView. Important fields include:
schedule_idnametarget_session_idstatuscadenceoverlap_policymisfire_policymax_executionsnext_fire_at_msqueued_fire_at_msin_flight_fire_at_msin_flight_run_idlast_fire_at_mslast_dispatched_run_idlast_errorlast_scheduler_errorscheduler_retry_after_msscheduler_retry_attemptexecution_countconsecutive_failuresrecent_executionsrequest
ScheduleView.misfire_policy is returned with the same tagged-object shape.
recent_executions is a bounded daemon-owned history of recent scheduler fires. Entries include the scheduled fire_at_ms, optional run_id, execution status, retry attempt, queued-fire marker, claim/dispatch/settle timestamps, retry deadline, and error text. The list is intentionally bounded so schedules remain cheap to list while still exposing enough evidence for duplicate-dispatch, retry, and overlap investigations.
Scheduler dispatch retry policy is daemon-global. CLI serve exposes --scheduler-retry-base-delay-ms, --scheduler-retry-max-delay-ms, --scheduler-retry-jitter-ms, and --scheduler-retry-max-attempts with matching KHEISH_SCHEDULER_* environment variables. The effective policy is exposed through GET /v1/runtime and the runtime.scheduler_policy object in status output. Retry state is persisted on the schedule view so restart does not erase operator-visible backoff/error context.
Pause, resume, cancel, and manual trigger all return:
Agent endpoints
GET /v1/agentsGET /v1/agents/summariesGET /v1/agents/{agent_id}POST /v1/agents/{agent_id}/sidechainsGET /v1/agents/{agent_id}/mailboxPOST /v1/mailboxes
GET /v1/agents and GET /v1/agents/{agent_id} return ManagedAgentSnapshot records. These include the static agent record plus live state such as pending approvals, pending questions, the last assistant message, and the last runtime error.
GET /v1/agents/summaries returns lightweight AgentSummaryView records for list-style inventory. It does not fan out to every live runtime for full snapshots, so operators and agent tools can list large agent trees without pulling assistant messages, journals, or pending request payloads. Use GET /v1/agents/{agent_id} for authoritative full state on one agent.
The summaries endpoint supports additive filters:
root_agent_idorroot: restrict to one root agent tree.session_id: restrict to one agent session.status:idle,running,waiting_for_approval,waiting_for_user_input,failed, orcompleted.has_runtime: restrict by live runtime presence.
page=true or cursor, it returns a paginated envelope with items, pagination.total_count, and exact counts for the selected scope after active-run overlays. Summary rows include cheap daemon-run overlays such as active_run_id, queued_run_count, pending request counts, pending_parent_clarification_count, bounded parent-clarification run ids, last_error, bounded last_output_preview, and last_activity_at_ms.
When a child agent has escalated a parent clarification, its summary status is projected as waiting_for_user_input until the parent answer, decline, expiry, or dead-letter settlement is durably recorded. This keeps agents summaries, agents get, and wait_agent from treating a child as settled while it is blocked on its parent.
The CLI keeps agents list on the full snapshot contract and exposes the lightweight path as agents summaries, including --root, --session-id, --status, --has-runtime, and pagination flags. Inside agent tools, use list_agent_summaries for routine coordination and list_agents only when full runtime snapshots are needed.
Public channel participation does not introduce a second agent runtime. A member session still uses the normal agent snapshot, mailbox, and run queue while the daemon separately keeps the public channel log and turn leases.
Spawn a sidechain
POST /v1/agents/{agent_id}/sidechains accepts SpawnSidechainRequest.
Top-level fields:
session_idthread_idroute_policyproviderpermission_moderetentionnicknamespawn_request_idfork_contextgenerationtool_surfacecapability_scopecredential_scopesubtask
fork_context shape is:
parent_assistant_messageinherited_tool_call_idssystem_prompt
fork_context fields:
team_nameisolationprompt_merge_modeprovidergenerationtool_surfaceworktree_path
route_policyis the preferred modern form- compatibility
providerplusgenerationmay still be supplied - conflicting combinations are rejected with
400 Bad Request
- child sessions inherit the parent’s bound persona snapshot at spawn time
- child sessions also inherit the parent’s persisted capability scope
- child sessions also inherit the parent’s persisted credential scope
- child sessions inherit the parent’s effective permission mode unless
permission_moderequests a narrower mode - explicit
permission_mode: "default"is pinned as a child session override instead of falling through to the daemon global mode - sidechain spawn rejects
permission_modevalues that would widen the parent, for examplebypassPermissionsfrom adefaultparent - parent session-scoped permission updates are copied to the child session at spawn time
- when the spawn request omits
credential_scope, delegated children keep route access but lose connector credentials and credentialed MCP access by default - child
capability_scope,credential_scope, andtool_surfacecan narrow the inherited surface further but cannot widen it
Mailbox delivery
POST /v1/mailboxes posts one typed JSON envelope and returns 202 Accepted.
Example:
GET /v1/agents/{agent_id}/mailbox returns the currently queued mailbox messages without draining them. Agent-side mailbox processing drains messages through the runtime control path; this HTTP inspection endpoint is safe to repeat during debugging.
- it returns the currently queued mailbox messages
- repeat reads can return the same messages until an agent/runtime path consumes them
Evidence Note
- Code verified:
crates/kheish-daemon/src/api/handlers.rs,crates/kheish-daemon/src/main.rs,crates/kheish-daemon/src/control_tools.rs,crates/kheish-daemon/src/control_tools/tasks.rs,crates/kheish-daemon/src/services/task.rs,crates/kheish-daemon/src/state/subagent_spawn.rs,crates/kheish-daemon/src/state/session_state.rs,crates/kheish-types/src/model.rs,crates/kheish-agent/src/supervisor.rs. - CLI/API verified: task output semantics checked against
TaskOutputView; mailbox endpoint checked against HTTP handler and supervisor peek behavior. - Daemon live tested for this note: no; deterministic service tests cover failed-task output retrieval.
- Provider-specific tested for this note: no; task output retrieval is daemon-local.
