Extensions / Guides
Observe JSONL journals
Use the terminal observation broker to find one bounded journal, prove it belongs to the
foreground agent session, and hand its stream to jsonlSession().
The JSONL path
- Resolve a known journal below the selected terminal’s environment or home directory.
- Read and validate a bounded header record.
- Bind the provider’s stable session ID with an opaque file or process fingerprint.
- Follow the resulting opaque file handle and map each record to a lifecycle event.
async function observe(terminal: AgentTerminalContext) {
const file = await terminal.observation.files.resolveHomeRelative(
".acme-agent/sessions/current.jsonl",
{
beneath: { homeRelative: ".acme-agent/sessions" },
extension: ".jsonl",
signal: terminal.signal,
},
);
if (!file) return { state: "not-bound" };
// Check one bounded record before treating a file as a session.
const first = await terminal.observation.files.readJsonLine(file, {
position: "first", maxBytes: 64 * 1024, signal: terminal.signal,
});
if (!isSessionHeader(first)) return { state: "not-bound" };
const binding = await terminal.bindSession({
providerSessionId: first.id,
mappingVersion: "1",
journal: file,
fingerprint: { kind: "acme-root-journal", file },
});
return jsonlSession({
binding,
source: terminal.observation.files.follow(file, { signal: terminal.signal }),
mapRecord(record, session) {
if (isRecord(record) && record.type === "completed") {
session.publish.done({ outcome: "success" });
}
},
});
}Use handles, not paths
Every resolver returns an opaque handle valid only for this terminal context. A path string from a process or journal is descriptive; it is never permission to read a local file.
| Need | Broker method |
|---|---|
| A known file under agent home | files.resolveHomeRelative() |
| A file below a declared CLI environment variable | files.resolveRelativeToEnvironment() |
| One record for session proof | files.readJsonLine() |
| Bounded stream of journal changes | files.follow() |
| Discover bounded child journals | resolveHomeDirectory(), listDirectory(), or watchDirectory() |
Pass terminal.signal to each operation. Terminay cancels it when the terminal
observation is no longer valid.
Bind only a proven root
A binding ties a provider session ID and mapping version to the exact terminal. Use a file or process handle in its fingerprint—not a guessed newest file, a local absolute path, or a working directory. A child journal extends an existing root; it never creates another root binding.
Add subagent journals
If the CLI writes separate child journals, provide stable child IDs. The host owns replay, flow control, and de-duplication; your mapping only supplies verified terminal-scoped sources.
const directory = await terminal.observation.files.resolveHomeDirectory(
".acme-agent/subagents",
{ beneath: { homeRelative: ".acme-agent/subagents" }, signal: terminal.signal },
);
const listing = directory && await terminal.observation.files.listDirectory(directory, {
extensions: [".jsonl"], maxDepth: 2, maxEntries: 32, maxBytes: 4 * 1024 * 1024,
signal: terminal.signal,
});
const childSources = await Promise.all((listing?.entries ?? []).map(async (entry) => ({
childId: entry.relativePath,
journal: entry.handle,
source: terminal.observation.files.follow(entry.handle, { signal: terminal.signal }),
})));
return jsonlSession({ binding, source, childSources, mapRecord });Keep record mapping defensive
mapRecord() receives unknown. Validate record shape, bound text
that you publish, and ignore malformed or unsupported records. JSONL replacement and
truncation are handled as stream changes; do not assume a journal is append-only.
Next: publish lifecycle events.