Let agents work when nobody is signed in
An agent runs on a schedule or when a message arrives, under a machine identity each organization chooses to grant it, and pauses for a reviewer before any unreviewed write.
- What it is
- Mechanism — something you declare.
- What it does
- Declaring an agent grants nothing. A turn runs in an organization only while an administrator there has bound one of the organization's OAuth clients to the agent, or while the organization admits, on a service-provider relationship, a firm's client bound to it. There is no application-wide autonomous agent.
- What you decide
- Declare the agent on a backend module under flowsActors.autonomousAgents, and give it a schedule if it runs on one. The engine registers the scheduled batch itself. Gate every write tool it uses on a reviewer route. Each customer organization then decides whether to bind one of its clients to it.
- Where it stops
- An agent started by an inbound message, and an agent running for a client through a service firm's admitted credential, are verified by the engine's own tests but have not yet run end to end in a running application.

An application declares an agent that can run with nobody at a keyboard: its actor system, the instruction it follows on every run, and whether a new run may start while an earlier one is still waiting. It runs on a schedule, or when an inbound handler hands it a message. In each organization it runs only after that organization’s administrator has granted it one of the organization’s own machine credentials.
The problem it solves
Much of a service firm’s work is not a conversation: overnight checks, month-end preparation, a message that arrives and needs handling. An agent that does this work has no person present, so three questions a chat answers by default need an answer here. Whose authority does it act under? Who notices before it changes something? And how does a customer switch it off?
The easy answers are the dangerous ones. An agent that runs across every customer under an application-wide identity holds standing access to all of them. An agent that writes without review because nobody is there to ask turns a scheduled job into an unsupervised actor.
What it does
Declaring an agent grants nothing. A turn runs in an organization only while an administrator there has bound one of the organization’s OAuth clients to the agent, or while the organization admits, on a service-provider relationship, a firm’s client bound to it. There is no application-wide autonomous agent.
The turn acts with that client’s current roles. They are read again every time a turn starts and again before an approved change runs. If the credential has been deactivated before the answer, the work does not run and the outcome says so.
Reads run, writes wait for a reviewer. Every autonomous turn may use tools declared as reads. A write is offered only when it is gated on a reviewer, so the proposal lands in a reviewer’s inbox and nothing changes until someone approves it. A tool gated only for its owner is denied, because nobody owns a machine’s turn.
The message is data, never an instruction. The instruction is declared with the agent. A message that starts a turn is what the instruction refers to, so its content cannot steer the agent into a different task.
One run at a time, when you want it. With the skip-if-active setting, a new occurrence does not start while an earlier turn in the same organization is still active or waiting on a reviewer, and the refusal is recorded.
Every change such an agent makes is recorded as the machine’s act, with the agent and the approving decision named on the audit row.
What you decide
Declare the agent on a backend module under flowsActors.autonomousAgents, and give it a
schedule if it runs on one. The engine registers the scheduled batch itself. Gate every write
tool it uses on a reviewer route. Each customer organization then decides whether to bind one of
its clients to it.
Selected from
overdue-triage-agent.ts;
the actor system and agent it names, declared earlier in the file, are omitted.
/** The ref an administrator binds one of the organization's OAuth clients to. */
export const TODOS_OVERDUE_TRIAGE_AGENT_REF = 'todos.overdue-triage';
/** Registered on the tasks-manager module's `flowsActors.autonomousAgents`. */
export const todosOverdueTriage: FlowsActors_AutonomousAgentDeclaration = {
ref: TODOS_OVERDUE_TRIAGE_AGENT_REF,
// The actor system whose chat actor's agent runs — here `todo-triage`, with read tools and the
// reviewer-gated `resource.todos.reschedule`.
systemRef: TODO_TRIAGE_SYSTEM_REF,
// Declared here, never taken from the trigger.
instruction: 'Review this organization\'s overdue todos and propose a new due date for the most overdue one.',
// A proposal still waiting for an administrator blocks the next morning's run: one open question at a time.
concurrency: AutonomousAgentConcurrency.SKIP_IF_ACTIVE,
// The engine registers the batch `autonomous-agent.todos.overdue-triage` from this.
schedule: { cronJobExpression: '0 7 * * *' },
};
The instruction is fixed in the declaration, so a run carries no task of its own choosing. The skip-if-active setting stops the next morning’s run while yesterday’s proposal still waits for an administrator, and the cron expression is all it takes for the engine to register the daily batch.
Limits
An agent started by an inbound message, and an agent running for a client through a service firm’s admitted credential, are verified by the engine’s own tests but have not yet run end to end in a running application. The scheduled path has.