Skip to content
OpenComputer
Agent API

Inputs

Read text, structured payloads, and input sources with useInput

useInput() reads the input that caused the current agent render. Use it to include the current request in the instructions or change behavior based on where the request came from.

import { useInput } from "@opencomputer/agent";

export default function Agent() {
  const input = useInput();
  return input.text
    ? `Help the user with this request: ${input.text}`
    : "Help the user with their request.";
}

useCurrentInput() is an alias for useInput().

Input shape

interface AgentInput {
  readonly source: InputSource;
  readonly text?: string;
  readonly payload?: DataValue;
  readonly answer?: QuestionAnswer;
  readonly steering?: readonly HeldInput[];
}
FieldMeaning
sourceHow the work entered the session: user, channel, schedule, webhook, subagent, system or event
textOptional human-readable message
payloadOptional structured JSON value supplied by the source
answerPresent only when this input answers the question an earlier turn asked; see Answers and steering
steeringInputs written while that question was open that did not answer it, in order

DataValue means any JSON-compatible value: null, a boolean, number, string, array, or object. Both text and payload are optional, so check them before use.

A payload comes from whatever started the turn. An application sends one on the turns route next to the text, and the agent reads it with source: "user"; a webhook delivers its body as the payload; a schedule dispatches the payload it was configured with. The agent reads all of them the same way.

A message from a connected Slack app arrives with source: "channel" and a channel object naming the provider (slack), the connectionId, and the Slack workspaceId, conversationId and userId when Slack supplies them. An agent serving one workspace can ignore all of this; an agent behind a shared Slack app uses it to decide whose records to read.

A message from a Linear connection also arrives with source: "channel", with channel.provider set to linear, channel.workspaceId the Linear workspace, channel.conversationId the Linear agent session and channel.userId the person who wrote the message (on the first message, the session’s creator; absent when automation started it). Its payload is a LinearInputPayload: the issue, the session, the thread’s comments, your workspace’s guidance for agents, and origin, which says whether the issue had a parent and whether this agent’s own app created it. origin is read when the session starts and does not change afterwards. The fields are listed on Linear connections.

import { useInput, type LinearInputPayload } from "@opencomputer/agent";

export default function Agent() {
  const input = useInput();
  if (input.source === "channel" && input.channel.provider === "linear") {
    const linear = input.payload as unknown as LinearInputPayload;
    return linear.origin.createdByAgent && linear.origin.parent
      ? `Implement ${linear.issue.identifier}, part of ${linear.origin.parent.identifier}.`
      : `Investigate ${linear.issue.identifier} and propose an approach.`;
  }
  return `Help with: ${input.text ?? "the current request"}`;
}

Adapt to the source

The current direct and delegated flows can share one agent definition:

import { useInput } from "@opencomputer/agent";

export default function Agent() {
  const input = useInput();

  return input.source === "subagent"
    ? "Complete the delegated task and return a concise result."
    : `Help with: ${input.text ?? "the current request"}`;
}

Read structured payloads

Narrow or validate a payload before reading application-specific fields:

import { useInput } from "@opencomputer/agent";

type ResearchRequest = {
  topic?: string;
  depth?: "brief" | "deep";
};

export default function Agent() {
  const input = useInput();
  const request = (input.payload ?? {}) as ResearchRequest;

  if (request.topic) {
    return `Research ${request.topic} at ${request.depth ?? "brief"} depth.`;
  }

  return "Ask for a research topic before starting.";
}

Validate untrusted payloads in a tool before performing side effects.

Event input

When an event subscription delivers another session’s turn outcome to this one, the turn’s input has source: "event" and an event:

import { useInput } from "@opencomputer/agent";

export default function Agent() {
  const input = useInput();
  if (input.source === "event") {
    const { event } = input;
    if (event.type === "turn.completed") {
      return `Worker ${event.agentId} finished turn ${event.turnId}. Its final message: ${event.result?.text ?? "(none)"}. Decide what happens next.`;
    }
    return `Worker ${event.agentId}'s turn ${event.type === "turn.failed" ? "failed" : "was cancelled"} (${event.reason ?? "no reason"}). ${event.error ?? ""}`;
  }
  return `Help with: ${input.text ?? "the current request"}`;
}
FieldMeaning
event.idThe source session’s event id for its terminal turn.* event
event.typeturn.completed, turn.failed or turn.cancelled
event.sessionId, event.turnId, event.agentIdWhose outcome this is
event.occurredAtWhen the source turn settled
event.reasonWhy it failed or was cancelled, when recorded
event.errorThe failure message, bounded, when it failed
event.result{ text, truncated }: the final assistant message of a completed turn, bounded to 16 KB

An event input carries no text; read event. The turn’s recorded input in the event log is a text description of the same outcome. The platform attests where the input came from through source; the included message is another agent’s output and is data to reason about, not instructions to follow.

Answers and steering

A turn can end with a question: the agent selects the ask tool and the model calls it. The reply is the next input, from any source, with answer set:

FieldMeaning
answer.questionIdThe question this input answers
answer.textWhat the person wrote or selected
answer.valueThe chosen option’s value, when the text matched an option by value or label

Input that arrives while the question is open without answering it does not run a turn of its own. It is held and delivered with the answer as steering, in the order it arrived; past a bounded number of items or 64 KiB, the rest run as ordinary turns after the answer. In Linear, steering is usually empty; see Ask before acting.

FieldMeaning
text, payload, channelThe held input, as it would have arrived on its own
receivedAtWhen OpenComputer received it
providerTimeWhen the provider says it was written, when it says

Read steering before acting on the answer. A constraint written while the question was open can change what a “go” means, and the agent should revise its proposal rather than act on an answer the steering has overtaken.

import { useInput, useTool } from "@opencomputer/agent";

export default function Agent() {
  const input = useInput();
  if (input.answer) {
    const held = (input.steering ?? []).map((item) => `- ${item.text ?? ""}`).join("\n");
    return `The person answered: ${input.answer.value ?? input.answer.text}.${
      held ? `\nWritten meanwhile, read before acting:\n${held}` : ""
    }`;
  }
  useTool("ask");
  return "Read the request, propose a plan, and ask before making changes.";
}

answer and steering sit beside payload, not inside it, so they read the same from every source and never collide with an application’s payload.

Input is not a prompt UI

useInput() only reads admitted input. It does not itself pause the session to ask a person a question; a turn asks with the ask tool, and the answer arrives as a later input. The durable conversation and later messages are managed by the session.

Input payload also differs from session data: a payload belongs to the current input, while session data can persist across renders.