Extensions / Guides
Publish lifecycle events
Your provider translates its CLI’s private journal format into a small, validated lifecycle vocabulary that Terminay can show consistently across agents.
Map records at the edge
Keep parsing specific to your agent. Publish only safe titles, prompts, summaries, and IDs; omit a field when it is not present instead of inventing it.
function mapRecord(record: unknown, session: AgentRecordContext) {
if (!isRecord(record)) return;
switch (record.type) {
case "session":
session.publish.sessionStarted({ title: text(record.title) });
break;
case "prompt":
if (id(record.turnId)) {
session.publish.turnStarted({ turnId: id(record.turnId)!, promptText: text(record.text) });
}
break;
case "tool_started":
if (id(record.toolId)) {
session.publish.toolStarted({ toolId: id(record.toolId)!, name: text(record.name) ?? "Tool" });
}
break;
case "approval_needed":
if (id(record.requestId)) {
session.publish.waitStarted({ waitId: id(record.requestId)!, state: "waiting", reason: "Approval requested" });
}
break;
case "approval_resolved":
if (id(record.requestId)) session.publish.waitFinished({ waitId: id(record.requestId)! });
break;
case "complete":
session.publish.done({ outcome: record.ok === false ? "error" : "success", summary: text(record.summary) });
break;
}
}Event vocabulary
| Event | Convenience publisher | Use for |
|---|---|---|
session.started | sessionStarted() | The bound session becomes known. |
agent.metadata | metadataChanged() | Title, prompt, agent ID, or model changes. |
turn.started | turnStarted() | A user request begins. |
tool.started / tool.finished | toolStarted() / toolFinished() | A tool invocation and its outcome. |
wait.started / wait.finished | waitStarted() / waitFinished() | Approval, input, or other blocked work. |
agent.done | done() | The agent completed, failed, or was cancelled. |
agent.exited | exited() | The process ended, with an optional exit code or signal. |
subagent.started / subagent.done | subagentStarted() / subagentDone() | A child agent’s work. |
session.stopped | publish() | The session observation ends; this event has no shorthand. |
Model waiting and completion accurately
A wait has a stable waitId and must finish with the same ID. Use
state: "waiting" when an agent awaits input or approval and
state: "blocked" when it cannot continue for another reason. Completion
outcomes are success, error, or cancelled.
Report subagents from their own evidence
Use the provider’s native child ID, and only emit child events when the journal or record establishes the parent relationship. Do not create a second root binding for a subagent.
session.publish.subagentStarted({
subagentId: child.id,
parentAgentId: rootAgentId,
title: text(child.title),
});
session.publish.subagentDone({
subagentId: child.id,
outcome: child.failed ? "error" : "success",
summary: text(child.summary),
});
// Use publish() for the event that has no convenience helper.
session.publish.publish({ kind: "session.stopped", reason: "terminal closed" });Good event hygiene
- Use stable IDs from the agent’s records for turns, tools, waits, and subagents.
- Include
occurredAtonly when the agent provides a trustworthy timestamp. - Never publish credentials, raw journal lines, absolute paths, or unbounded prompt text.
- Use
publish()for a valid event without a helper; the host validates every event.
Next: observe JSONL journals or return to the Extensions API overview.