Agent session sources
A session source reports every live coding-agent session of its harnesses on the server's machine, however it was started. Terminay decides which project and terminal each session belongs to.
Manifest contribution
"permissions": ["agent-observation"],
"contributes": {
"agentSessionSources": [{
"id": "com.example.agent/sessions",
"displayName": "Example Agent",
"harnesses": [{ "id": "example-agent", "displayName": "Example Agent" }],
"environmentVariables": ["EXAMPLE_AGENT_HOME"]
}]
}A harness is one agent the source can report, such as Claude Code or Codex. Each declared harness gets its own switch under the extension in Settings → Extensions. Field rules are in the manifest reference.
Runtime contract
Register with context.agents.registerSessionSource(sourceId, runtime).
defineSessionSource() is an identity helper that types the runtime.
interface AgentSessionSourceRuntime {
start(start: AgentSessionSourceStart): void | Promise<void>;
}
interface AgentSessionSourceStart {
enabledHarnesses: readonly string[];
publisher: AgentSessionPublisher;
signal: AbortSignal;
onEnabledHarnessesChanged(
listener: (enabledHarnesses: readonly string[]) => void,
): Disposable;
}
interface AgentSessionPublisher {
reset(sessions: readonly AgentSessionSnapshot[]): void;
upsert(session: AgentSessionSnapshot): void;
remove(sessionId: string): void;
diagnostic(diagnostic: AgentSessionSourceDiagnostic): void;
}| Member | Meaning |
|---|---|
start() | Begins watching. Resolve once the source is running; keep publishing until signal aborts. |
enabledHarnesses | Declared harness IDs currently switched on. |
onEnabledHarnessesChanged | Delivers the new enabled set when a user switches a harness, without restarting the extension. |
signal | Aborts when the source is disposed, the extension is disabled, or agent status is switched off. |
Publication rules
- Ordering. Calls take effect in the order made.
resetreplaces everything the source reported before,upsertreplaces one session by ID, andremoveforgets one. - Resets. Send a reset after starting and after every enabled-harness change. A reset holds at most 1,024 sessions and never names one session twice; report more with upserts.
- Harness switches. A snapshot naming an undeclared or switched-off harness is rejected. Stop reporting a harness when it is switched off and report its live sessions when it is switched back on.
- Cancellation. When
signalaborts, release every watch. Later publisher calls are ignored. - Watching. Watch files and processes; do not poll.
Session snapshot
publisher.upsert({
id: 'sess-1',
harness: 'example-agent',
pid: 101,
cwd: '/work/app',
title: 'Fix tests',
model: 'example-large',
status: 'waiting',
waitingFor: 'Approve shell command',
lastTurn: 'completed',
lastTurnEndedAt: Date.now(),
subagents: [{ id: 'sub-1', type: 'explore', status: 'running' }],
});AgentSessionSnapshot is a closed object. Unknown fields are rejected. Times are epoch milliseconds.
| Field | Required | Meaning and bound |
|---|---|---|
id | Yes | Source-scoped and stable for the conversation. At most 256 characters. |
harness | Yes | A declared harness ID that is switched on. |
pid | Yes | The live process holding the session. A positive integer. |
cwd | Yes | Absolute working directory, starting with /. At most 4,096 characters. |
title | No | At most 512 characters. |
model | No | At most 256 characters. |
status | No | running, waiting, blocked, or idle. |
waitingFor | No | What a waiting or blocked session waits for, such as a tool approval. At most 1,024 characters. |
tool | No | Name of the tool currently running. At most 256 characters. |
lastTurn | No | completed, failed, or interrupted. |
lastTurnEndedAt | No | When the last turn ended. |
error | No | Bounded, redacted message for a failed turn. At most 1,024 characters. |
subagents | No | Up to 64 entries with unique IDs: id, optional parentId, type (at most 128 characters), optional title, and status of running, completed, failed, or cancelled. |
The bounds are exported as EXTENSION_LIMITS. Use
boundedAgentText(value, maximum) to shorten display text such as titles and
messages. Never shorten identity values; an over-long ID is rejected.
Transcripts, prompts beyond the title, tool arguments, and raw records are never accepted.
Diagnostics
publisher.diagnostic({ code, message }) records a typed problem. code
is lowercase kebab-case, at most 64 characters, such as watch-failed.
message is at most 512 characters and must carry no paths or conversation content.
What Terminay owns
- Project. A session appears in a project's Agents panel when its
cwdis the project root or below it, or inside a linked Git worktree of the project's repository. - Terminal. A session whose process descends from a Terminay terminal is bound to that terminal. Any other session is shown as External. An extension cannot claim a terminal.
- Everything else. Acknowledgement, ordering, and presentation.
Validation errors
validateAgentSessionSnapshot, validateAgentSessionReset, and
validateAgentSessionSourceDiagnostic apply the host's rules and return issues
with codes such as invalid_string, invalid_array,
invalid_enum, invalid_path, invalid_integer,
invalid_id, unknown_field, duplicate, and
harness_not_enabled.
See the session source example for a complete package.