Rensei docs
Nodes

Agent Action Nodes

agent.invoke, agent.dispatch, agent.dispatch_stage, file management, and session state nodes.

The layered execution model - how agents are packaged, what kits and credentials they receive, and how the runner processes work items - is documented at donmai.dev/docs/layered-execution-model. This page covers the platform workflow nodes that dispatch and manage agents.

Agent action nodes bridge the workflow engine and the donmai runtime. They dispatch work to agents, manage concurrent session caps, track session state, and reserve files across parallel agents.

Node summary

Node IDPurpose
agent.invokeDispatch work and wait for session completion (synchronous)
agent.dispatchExplicit A2A dispatch to a registered remote agent by card + skill
agent.dispatch_stageSDLC stage dispatch - reads canonical stage CloudEvent (deprecated; use agent.invoke)
agent.dispatch_count.incrementIncrement the per-issue dispatch counter
agent.file.reserveAcquire pessimistic file locks for a session
agent.file.releaseRelease file locks held by a session
agent.session.get_stateRead current session status and metadata

agent.invoke

The primary dispatch node. Accepts agent composition refs - either a pre-composed AgentDispatchRef from an upstream agent foundational node, or individual slot refs wired directly - dispatches work, and waits for the session to complete before routing to success or fail.

agent.invoke accepts two composition paths:

  • Path 2 - full composition: Wire a foundational agent node's output port to the composition input. The agent node fan-ins all refs upstream.
  • Path 3 - individual slots: Wire agent-definition, llm-model, kit-provider, credential-provider, capacity-pool, and capability nodes directly to the matching slot ports on agent.invoke.

Input ports

PortTypeRequiredDescription
agentRefstringyesAgent card $ref URI
stageEventobjectnoAuto-bound from trigger output; passed into the agent as initial context
partialsarraynoSkill partial refs to attach ([{ id, condition?, order? }])
modelProfileIdstringnoLLM model profile override
budgetOverrideobjectno{ maxDurationSeconds, maxSubAgents, maxTokens }
compositionobjectnoFull AgentDispatchRef from an upstream agent node (Path 2)
definitionobjectnoAgentDefinitionRef (Path 3 slot)
modelobjectnoModelRef (Path 3 slot)
kitsarraynoKitRef[] (Path 3 slot)
credentialsobjectnoCredentialRef (Path 3 slot)
sandboxobjectnoSandbox ref (Path 3 slot)
capacityPoolobjectnoCapacityPoolRef (Path 3 slot)
capabilitiesarraynoCapabilityRef[] (Path 3 slot)
organizationIdstringnoOverride org context
projectNamestringnoOverride project context
sessionIdstringnoParent session ID for sub-agent tracking
agentIdstringnoPin to a specific registered agent ID
repoPathstringnoWorking repository path
userIntentstringnoFree-text intent passed to the agent
prioritynumbernoQueue priority for this dispatch

Output ports

PortTypeDescription
successInvokeResultSession completed successfully
fail{ error: string }Session failed or could not be dispatched

InvokeResult shape

{
  sessionId: string
  stageId: string
  queuePosition?: number
  dispatched: boolean
  parked?: boolean
  replaced?: boolean
  agentCardId?: string
  budget?: {
    maxDurationSeconds: number
    maxSubAgents: number
    maxTokens: number
  }
}

Dynamic contract ports (B4)

When you select an agent card in the config panel and that card declares completion_contract.outputs, the editor projects additional named output ports onto the node. For example, a card with { outputs: { summary: "string", pr_number: "number" } } produces summary and pr_number output ports that downstream nodes can wire to typed inputs.


agent.dispatch

Explicit A2A dispatch node. Sends a work item to a registered remote agent by targeting either an exact agent card + skill (targetAgentRef) or the best-matching registered agent for a given skill description (skillMatcher). Unlike agent.invoke, this node does not wait for the session to complete - it routes to output immediately after queuing.

Use agent.dispatch when routing work to a remote registered agent at a known address. For dispatching work to a local-pool agent within your own SDLC, prefer agent.invoke.

Input ports

PortTypeRequiredDescription
orgIdstringyesOrg owning the registered remote agents
targetAgentRefobjectnoExplicit pointer: { agentCardId: string, skillId: string }. When set, skill-matching is skipped.
skillMatcherobjectnoSkill-based matcher: { workType?: string, capabilities?: string[] }. The highest-scoring registered agent receives the dispatch.
argsobjectnoPayload forwarded to the remote agent's skill
sessionIdstringnoOptional session ID for correlation and dedup
timeoutMsnumbernoOverride the outbound message timeout (default: 30,000 ms)

Output port

PortTypeDescription
outputDispatchResultDispatch result including agentCardId, skillId, sessionId, capabilityScore, and latencyMs

Example: dispatch to an explicit agent

- id: dispatch_reviewer
  type: action
  nodeId: agent.dispatch
  config:
    orgId: "{{ $workflow.orgId }}"
    targetAgentRef:
      agentCardId: "abc123"
      skillId: "code-review"
    args:
      issueId: "{{ $trigger.data.issueId }}"
      prUrl: "{{ nodes.create_pr.success.htmlUrl }}"

agent.dispatch_stage

agent.dispatch_stage is deprecated. Use agent.invoke for new workflows. Existing workflows using dispatch_stage continue to function, but the canvas displays a deprecation banner. The canonical migration path is to wire an agent-definition foundational node into agent.invoke's definition slot and move inline promptArtifact content there.

Dispatches a named SDLC stage. Reads the canonical stage CloudEvent payload, renders the stage prompt with issue context, and queues work with budget metadata. Used by legacy SDLC templates to dispatch individual phases (research, backlog-creation, development, QA, acceptance) while preserving stage-level context.

Input ports

PortTypeRequiredDescription
stageEventobjectyesCanonical stage CloudEvent data payload from the trigger normalizer
stageIdstringnoStage ID (e.g. "research", "development"). Falls back to the inbound CloudEvent's stageId when omitted.
budgetOverrideobjectno{ maxDurationSeconds?, maxSubAgents?, maxTokens? } - absent fields use stage defaults
sessionIdstringnoOptional session ID; generated if omitted
organizationIdstringnoLinear org ID for tenant isolation
projectNamestringnoLinear project name - drives model cascade and worker routing
modelstringnoModel identifier override
subAgentModelstringnoModel override for sub-agents spawned by the thinking agent
prioritynumbernoQueue priority (lower = higher priority)
repoPathstringnoRepository path hint
agentIdstringnoLinear agent ID that owns the session

Output port - output ({ sessionId, stageId, queuePosition?, dispatched, parked?, replaced?, budget? })


agent.dispatch_count.increment

Increments the per-issue dispatch counter. Pair with the agent.dispatch_count.under_cap condition node to enforce a maximum number of agent sessions per Linear issue.

Input ports

PortTypeRequiredDescription
issueIdstringyesLinear issue ID

Output port - output ({ count: number }): the new counter value after increment.


agent.file.reserve

Acquires pessimistic locks on one or more file paths for the duration of an agent session. Prevents concurrent agents from editing the same files simultaneously. Returns both successfully reserved paths and any conflicts (files already held by another session, with holder details).

Input ports

PortTypeRequiredDescription
sessionIdstringyesSession that will hold the locks
filePathsstring[]yesPaths relative to the repo root to reserve
reasonstringnoOptional human-readable reason stored on the reservation

Output port - output ({ reserved: string[], conflicts: FileConflict[] })

FileConflict shape: { filePath: string, heldBy: { sessionId, repoId, filePath, reservedAt, reason? } }

Conflicts do not cause the node to route to fail - the output always carries both reserved and conflicts arrays. Wire a downstream condition to check conflicts.length > 0 if you need to branch on conflicts.


agent.file.release

Releases all file locks held by a session. Call this at the end of every branch - including error paths - to unblock downstream agents.

Input ports

PortTypeRequiredDescription
sessionIdstringyesSession whose reservations to release

Output port - output ({ released: number }): count of reservations released.

agent.file.release always releases all reservations for the session. There is no per-file release - to release a subset, use agent.file.reserve on a new session with only the files you want to continue holding.


agent.session.get_state

Reads the current state of an agent session. Returns status, work type, model, branch, PR info, cost, and token counts. Useful in polling loops or condition checks that need current session metadata.

Input ports

PortTypeRequiredDescription
sessionIdstringyesAgent session ID

Output port - output (SessionState)

SessionState shape

{
  status: 'queued' | 'running' | 'completed' | 'failed' | 'stopped'
  workType?: string
  provider?: string
  model?: string
  branch?: string
  prUrl?: string
  prNumber?: number
  cost?: number
  tokenCount?: number
  startedAt?: string    // ISO-8601 timestamp
  completedAt?: string  // ISO-8601 timestamp
}

Usage pattern: SDLC stage dispatch with agent.invoke

The SDLC v2 template wires agent.invoke with a composed agent foundational node:

linear.agent_session.created
  → linear.agent_session.acknowledge
  → agent.dispatch_count.under_cap  (condition: issueId)
      → true  → agent.dispatch_count.increment
                   → agent.invoke (composition from agent foundational node)
                       → success → linear.agent_session.close
                       → fail    → linear.comment.create ("Stage failed")
      → false → linear.comment.create ("Dispatch cap reached")

Usage pattern: parallel file reservation

Reserve files before dispatching concurrent agents to prevent edit conflicts:

steps:
  - id: reserve
    type: action
    nodeId: agent.file.reserve
    config:
      sessionId: "{{ $trigger.data.sessionId }}"
      filePaths:
        - src/lib/core.ts
        - src/lib/utils.ts
      reason: "Development stage"

  - id: invoke_agent
    type: action
    nodeId: agent.invoke
    config:
      agentRef: "agent://my-org/dev-agent"
      stageEvent: "{{ $trigger.data }}"

  - id: release
    type: action
    nodeId: agent.file.release
    config:
      sessionId: "{{ $trigger.data.sessionId }}"

On this page