Scope
Current authority:
AgentExecutionis the canonical in-memory Entity instance and process owner. AgentExecution logs are owned audit/recovery material.AgentExecutionRegistryis active lookup and process-handle plumbing.
This spec implements the requirements in Agent Execution Interaction Journal PRD and the decision in ADR-0006.08.
The implementation creates one durable semantic interaction path for AgentExecution without replacing Terminal recordings, Mission workflow events, AgentExecution protocol descriptors, or Entity commands.
Authoritative Inputs
CONTEXT.md: canonical Mission vocabulary.- ADR-0006.03: runtime-defined AgentExecution messages.
- ADR-0006.07: AgentExecution logs as daemon audit material.
- ADR-0006.04: prompt-scoped Agent execution signals.
- ADR-0006.01: Agent, AgentAdapter, AgentExecution, AgentExecutionRegistry, and Terminal vocabulary.
- ADR-0006.05: AgentExecution structured interaction vocabulary.
- ADR-0006.06: Mission MCP server Agent signal transport.
- ADR-0006.08: AgentExecution interaction journal persistence.
Ownership
AgentExecution
AgentExecution owns:
- journal record schemas and inferred types.
- append-only semantic interaction state.
- replay from journal records into AgentExecution state.
- idempotency for message ids and observation ids.
- projection from journal records into chat/timeline data.
- lifecycle, attention, semantic activity, and input-request state.
- journaled process activity and telemetry records when live process observations are accepted into durable audit material.
- the storage contract for append/read/replay semantics, independent of owning Entity scope.
AgentExecution Process And Observation Path
AgentExecution owns:
- writing process-observed records through its journal path, not directly to ad hoc filesystem locations.
- routing stdout markers, MCP tool calls, provider output, terminal heuristics, and daemon-observed semantic operation records into observations.
- delivering AgentExecutionMessages to the selected AgentAdapter or terminal transport.
- recording delivery attempts and failures.
AgentExecutionRegistry resolves active AgentExecution instances and live process handles. Journal writes, delivery policy, and runtime state shape stay inside AgentExecution.
Mission Workflow Runtime
Mission workflow runtime owns:
- task and AgentExecution orchestration state.
- launch, completion, failure, cancellation, termination, and task-completion workflow events.
- references to AgentExecution journal and terminal recording paths for Mission-backed executions.
Mission workflow runtime does not own message transcripts, chat projection, observation policy, or terminal I/O truth.
AgentExecution Journal Store
The AgentExecution journal store is owner-independent. System, Repository, Mission, Task, and Artifact ownership may cause a storage adapter to choose a different filesystem path or database key, but every owner uses the same journal record schemas, append semantics, read semantics, replay semantics, and idempotency rules.
The storage contract is shaped around an AgentExecution journal reference rather than a filesystem backend location:
type AgentExecutionJournalReference = {
journalId: string;
ownerEntity: 'System' | 'Repository' | 'Mission' | 'Task' | 'Artifact';
ownerId: string;
agentExecutionId: string;
recordCount: number;
lastSequence: number;
};
type AgentExecutionJournalStore = {
ensureJournal(reference: AgentExecutionJournalReference): Promise<void>;
appendRecord(reference: AgentExecutionJournalReference, record: AgentExecutionJournalRecord): Promise<void>;
readRecords(reference: AgentExecutionJournalReference): Promise<AgentExecutionJournalRecord[]>;
};
Storage adapters may inspect the journal reference and runtime context to choose where bytes live. The journal shape and append/read/replay behavior remain shared across owner types.
Phase-one file-backed path policy is explicit:
- System-owned journals live under the configured System config/state folder.
- Repository-owned journals live under the main Repository
.open-missioncontrol state. - Mission-owned journals live under
.open-mission/missions/<missionId>. - Task-owned journals live under the owning Mission record.
- Artifact-owned journals are deferred until Artifact-scoped AgentExecution persistence is implemented.
If durable storage cannot be resolved, launch fails before the journal header is written. Phase one starts new AgentExecutions only after durable journal storage is available.
Terminal
Terminal and TerminalRegistry own raw PTY input, output, resize, screen, exit, and terminal recording updates.
Live Process State
Replay reconstructs semantic AgentExecution truth. It does not reconstruct every live process detail.
AgentExecution and Terminal may expose live process state for active executions. That state is used for current UI and process control; it is not the durable semantic source of truth.
Suggested shape:
type AgentExecutionProcessState = {
agentExecutionId: string;
capturedAt: string;
attachedTerminalAgentExecutionId?: string;
activeTransportConnections: string[];
currentPtyState?: {
terminalName: string;
connected: boolean;
dead: boolean;
cols?: number;
rows?: number;
};
currentTerminalSnapshotRef?: string;
activeToolCalls?: Array<{
toolCallId: string;
toolName: string;
startedAt: string;
status: 'running' | 'cancelling';
}>;
inFlightDeliveries?: Array<{
messageId: string;
transport: 'agent-message' | 'pty-terminal' | 'adapter';
attemptedAt: string;
}>;
lastHeartbeatAt?: string;
};
If live process observations should survive restart, explain durable audit, or affect deterministic replay, they must be accepted into journal records. Otherwise they remain live process data.
Storage Layout
The first implementation uses the same AgentExecution journal store for every supported owner. The file-backed storage engine writes newline-delimited AgentExecutionJournalRecord values to an adapter-resolved path for an AgentExecutionJournalReference.
For Mission-backed AgentExecutions, the file-backed store may write under the Mission record in an Agent journal location distinct from terminal recordings:
agent-journals/<encoded-agent-execution-id>.interaction.jsonl
terminal-recordings/<encoded-agent-execution-id>.terminal.jsonl
agent-executions/<encoded-agent-execution-id>.metadata.json
Mission record modules may expose validated path construction and reference helpers for Mission-backed Agent journal locations:
getMissionAgentJournalRelativePath(agentExecutionId)resolveMissionAgentJournalPath(missionDir, agentJournalPath)
The Agent journal path pattern is:
agent-journals/<agentExecutionId>.interaction.jsonl
Those Mission helpers are path helpers only. ensure, append, and read belong to the shared AgentExecution journal store after the file-backed store has resolved a path for the journal reference.
Repository and System file-backed relative paths should use the same filename convention:
agent-journals/<encoded-agent-execution-id>.interaction.jsonl
The root path differs by owner context; the relative interaction journal filename pattern stays the same. This path policy is separate from AgentExecution journal identity, leaving room for a future database-backed store to write all journal records to one table.
Schema Model
Add the journal schemas in the AgentExecution Entity module, preferably in a dedicated file imported by AgentExecutionSchema.ts if the schema file becomes too broad.
Phase one should keep two explicit registries rather than one over-generalized event table.
Journal Record Registry
The journal record registry is the canonical list of appendable journal record kinds. It owns record discrimination, schema validation, and replay routing for top-level journal entries.
| Record type | Purpose | Canonical owner |
|---|---|---|
journal.header | freezes launch contract and journal identity | AgentExecution journal record registry |
message.accepted | records accepted owner/operator/daemon input | AgentExecution journal record registry |
message.delivery | records best-effort delivery attempts and outcomes | AgentExecution journal record registry |
observation.recorded | records normalized runtime observations | AgentExecution journal record registry |
decision.recorded | records durable policy outcomes | AgentExecution journal record registry |
state.changed | records semantic lifecycle, attention, activity, and input-request transitions | AgentExecution journal record registry |
activity.updated | records compressible runtime activity and telemetry | AgentExecution journal record registry |
owner-effect.recorded | records accepted owner-facing effects | AgentExecution journal record registry |
projection.recorded | reserves durable projection materialization for later phases | AgentExecution journal record registry |
The journal record registry stays explicit even when some record payloads derive from narrower registries. Headers, decisions, state changes, and owner effects keep their own record shapes rather than passing through a signal-shaped registry.
Signal Registry
The signal registry is narrower. It owns the canonical AgentExecution signal vocabulary used inside observation.recorded.signal and drives the descriptor list, the journal signal payload variants, and replay projection behavior.
| Signal type | Payload schema key | Descriptor surfaced | Replay chat projection |
|---|---|---|---|
progress | agent-signal.progress.v1 | yes | progress message |
status | agent-signal.status.v1 | yes | status message |
needs_input | agent-signal.needs-input.v1 | yes | needs-input message |
blocked | agent-signal.blocked.v1 | yes | blocked message |
ready_for_verification | agent-signal.ready-for-verification.v1 | yes | claim message |
completed_claim | agent-signal.completed-claim.v1 | yes | claim message |
failed_claim | agent-signal.failed-claim.v1 | yes | failure message |
message | agent-signal.message.v1 | yes | chat message |
usage | none | no | none |
diagnostic | none | no | none |
Journal replay should project observation.recorded.signal through the signal registry rather than duplicating one switch per signal family in multiple modules.
Suggested baseline:
type AgentExecutionJournalRecord =
| AgentExecutionJournalHeaderRecord
| AgentExecutionMessageAcceptedRecord
| AgentExecutionMessageDeliveryRecord
| AgentExecutionObservationRecord
| AgentExecutionDecisionRecord
| AgentExecutionStateChangedRecord
| AgentExecutionActivityUpdatedRecord
| AgentExecutionOwnerEffectRecord
| AgentExecutionProjectionRecord;
Every record has:
type AgentExecutionJournalRecordBase = {
recordId: string;
sequence: number;
type: string;
schemaVersion: 1;
agentExecutionId: string;
ownerId: string;
scope: AgentExecutionScopeType;
occurredAt: string;
};
Header Record
The first record identifies the journal and frozen launch contract:
type AgentExecutionJournalHeaderRecord = AgentExecutionJournalRecordBase & {
type: 'journal.header';
kind: 'agent-execution-interaction-journal';
agentId: string;
protocolDescriptor: AgentExecutionProtocolDescriptorType;
transportState?: AgentExecutionTransportStateType;
workingDirectory?: string;
};
Header creation is mandatory before the Agent process receives the initial prompt or launch instructions. AgentExecution launch resolves durable journal storage for the journal reference, ensures the journal, appends journal.header, and only then starts delivery to the process. If header append fails, launch fails and no Agent process should be started.
Message Records
Owner/operator/daemon-to-AgentExecution input is recorded as a message before delivery:
type AgentExecutionMessageAcceptedRecord = AgentExecutionJournalRecordBase & {
type: 'message.accepted';
messageId: string;
source: 'operator' | 'daemon' | 'system' | 'owner';
messageType: string;
payload: unknown;
mutatesContext: boolean;
};
Phase-one message mapping is fixed:
| Current input | Journal record | Source | Delivery record |
|---|---|---|---|
submitPrompt with source: operator | message.accepted | operator | yes |
submitPrompt with source: system | message.accepted | system | yes |
submitPrompt with source: engine | message.accepted | daemon | yes |
submitCommand runtime command | message.accepted | operator or daemon from caller context | yes |
| raw terminal input | none | n/a | no |
Raw terminal input remains Terminal transport input. It can be recorded in terminal recordings, and it becomes an AgentExecutionMessage only when it enters through an AgentExecution message descriptor.
Delivery is a separate best-effort record:
type AgentExecutionMessageDeliveryRecord = AgentExecutionJournalRecordBase & {
type: 'message.delivery';
messageId: string;
status: 'attempted' | 'delivered' | 'failed' | 'skipped';
transport: 'agent-message' | 'pty-terminal' | 'adapter' | 'none';
reason?: string;
};
Observation Records
Observations are transport-neutral:
type AgentExecutionObservationRecord = AgentExecutionJournalRecordBase & {
type: 'observation.recorded';
observationId: string;
source: 'pty' | 'mcp' | 'sdk' | 'provider-output' | 'terminal-heuristic' | 'filesystem' | 'git' | 'daemon';
confidence: 'authoritative' | 'high' | 'medium' | 'low' | 'diagnostic';
signal?: AgentExecutionSignal;
rawText?: string;
payload?: Record<string, unknown>;
};
An AgentSignal remains a structured Agent-authored signal payload. It is one kind of observation payload, not the umbrella term for all runtime observations.
The signal field should derive from the canonical AgentExecution signal registry. The registry owns signal payload variants, descriptor metadata, and replay projection rules. observation.recorded still belongs to the journal record registry; only its signal payload family derives from the signal registry.
Decision Records
Policy decisions are durable so replay can preserve idempotency and explain accepted/rejected effects:
type AgentExecutionDecisionRecord = AgentExecutionJournalRecordBase & {
type: 'decision.recorded';
decisionId: string;
observationId?: string;
messageId?: string;
action: 'reject' | 'record-only' | 'emit-message' | 'update-state' | 'route-owner-effect';
reason?: string;
};
Phase one maps existing AgentExecutionObservationPolicy decisions to journal decisions exactly:
| Existing decision action | Journal action |
|---|---|
reject | reject |
record-observation-only | record-only |
emit-message | emit-message |
update-execution | update-state |
route-owner-effect is reserved for the first owner effect that actually emits an Entity event or Mission workflow event from an accepted observation. Owner-effect records appear only when there is a concrete owner effect.
State Records
State records contain durable semantic transitions. They should stay low-frequency and meaningful for replay:
type AgentExecutionStateChangedRecord = AgentExecutionJournalRecordBase & {
type: 'state.changed';
lifecycle?: AgentExecutionLifecycleStateType;
attention?: 'none' | 'autonomous' | 'awaiting-operator' | 'awaiting-system' | 'blocked';
activity?: 'idle' | 'planning' | 'reasoning' | 'communicating' | 'editing' | 'executing' | 'testing' | 'reviewing';
currentInputRequestId?: string | null;
};
Noisy progress percentages, token counts, streaming summaries, active file paths, and transient tool metadata belong in runtime activity and telemetry records rather than state.changed semantic transitions.
awaiting-input is not a valid lifecycle state. Input requests are represented as lifecycle: running plus attention: awaiting-operator and currentInputRequestId.
Runtime Activity Records
Runtime activity records contain high-frequency, compressible, or telemetry-shaped updates. They can be retained, compacted, sampled, or summarized without changing semantic lifecycle replay:
type AgentExecutionActivityUpdatedRecord = AgentExecutionJournalRecordBase & {
type: 'activity.updated';
activity?: 'idle' | 'planning' | 'reasoning' | 'communicating' | 'editing' | 'executing' | 'testing' | 'reviewing';
progress?: {
summary?: string;
detail?: string;
units?: { completed?: number; total?: number; unit?: string };
};
telemetry?: {
inputTokens?: number;
outputTokens?: number;
totalTokens?: number;
activeToolName?: string;
};
capabilities?: {
terminalAttached?: boolean;
streaming?: boolean;
toolCallActive?: boolean;
filesystemMutating?: boolean;
};
currentTarget?: {
kind: 'file' | 'command' | 'tool' | 'artifact' | 'unknown';
label?: string;
path?: string;
};
};
The latest activity update may inform projections and operator status. Replay must not require every historical activity update to recover semantic lifecycle, attention, or input-request state.
Owner Effect Records
Owner effects bridge accepted interaction to Entity events or workflow events:
type AgentExecutionOwnerEffectRecord = AgentExecutionJournalRecordBase & {
type: 'owner-effect.recorded';
effectId: string;
observationId?: string;
ownerEntity: 'System' | 'Repository' | 'Mission' | 'Task' | 'Artifact';
effectType: string;
workflowEventId?: string;
entityEventId?: string;
payload?: Record<string, unknown>;
};
Projection Records
Projection records are optional durable material for efficient UI reconstruction. They are not the source of domain truth if they conflict with earlier records.
type AgentExecutionProjectionRecord = AgentExecutionJournalRecordBase & {
type: 'projection.recorded';
projection: 'timeline-item';
payload: Record<string, unknown>;
};
Phase one derives timelineItems directly during replay from message, observation, decision, state, and activity records. It should not write projection.recorded records until a measured read-performance need appears. Keep the record type in the schema so the format has an explicit future compaction/materialization path.
Replay
Add an AgentExecutionJournalReplayer module owned by AgentExecution.
Replay input:
- header record.
- ordered journal records.
Replay output:
- AgentExecution data shape.
- processed message id set.
- processed observation id set.
- current lifecycle, attention, semantic activity, and current input request.
- latest retained runtime activity and telemetry snapshot when present.
- projected timeline items.
Replay is deterministic for the same ordered records. Invalid records fail clean-slate validation; fallback parsing or compatibility aliases require a Mission runtime migration ADR.
Replay should route observation signal projection through the signal registry so descriptor metadata, journal signal variants, and timeline projection behavior stay aligned.
Live runtime snapshots are composed after replay for active read models. Replay output, idempotency hydration, and semantic state reconstruction remain journal-derived.
Write Path
Owner/Operator Message
Entity command or AgentExecution command
-> validate AgentExecutionMessageDescriptor
-> append message.accepted
-> mutate AgentExecution context if needed
-> attempt adapter/terminal delivery
-> append message.delivery
-> append state/projection records if applicable
Runtime Observation
transport/provider/runtime input
-> normalize AgentExecutionObservation
-> append observation.recorded if not duplicate
-> evaluate AgentExecutionObservationPolicy
-> append decision.recorded
-> append state.changed, activity.updated, or owner-effect.recorded records
-> publish Entity events from accepted records
Duplicate observations must return an acknowledgement based on the durable journal state and must not append repeated effects.
Read Model
AgentExecution data should expose projection fields derived from replay, not stored independently as authority:
projection.timelineItemsas recent or bounded projection data.journalmetadata with path, cursor, record count, and last sequence.statusdimensions for lifecycle, attention, and semantic activity.- latest journaled runtime activity, telemetry, and capabilities.
- live runtime snapshot overlay when the execution is active.
protocolDescriptorand message/signal descriptors.- terminal recording reference when available.
The phase-one persisted AgentExecution data fields are:
type AgentExecutionJournalReference = {
journalId: string;
ownerEntity: 'System' | 'Repository' | 'Mission' | 'Task' | 'Artifact';
ownerId: string;
agentExecutionId: string;
recordCount: number;
lastSequence: number;
};
Mission workflow runtime should store only the Mission-backed Agent journal relative path needed for workflow participation and audit references:
agentJournalPath?: string;
The richer journal reference belongs on AgentExecution read data, not Mission workflow runtime data.
Open Mission should render from these projections and request older journal windows by cursor when needed.
Migration Strategy
This is a Mission runtime data change because Mission workflow runtime needs an Agent journal location reference. Follow ADR-0003.01: no tolerant readers or hidden fallback parsers.
The first implementation may initialize an interaction journal for newly launched AgentExecutions only. Existing disposable local Mission state can be regenerated or migrated through an explicit runtime migration if preservation is required.
Repository and System journal roots are file-store path policy, not Mission runtime data migrations. They still use clean-slate validation for every journal record.
Test Plan
- Schema tests for every journal record kind.
- Filesystem tests for shared journal store append/read behavior and Mission-backed location validation.
- Replay tests that reconstruct state from journal records.
- Replay tests proving semantic state reconstruction does not depend on retaining every
activity.updatedrecord. - Replay tests proving deterministic replay output stays independent of live runtime snapshots.
- Policy tests proving progress, token usage, streaming summaries, and transient targets append
activity.updatedrather thanstate.changed. - Duplicate observation replay tests across fresh
AgentExecutionObservationPolicyinstances. needs_inputtests proving request and response are separate records.- MCP and stdout-marker tests proving both transports produce equivalent observation/decision records.
- Terminal recording tests proving raw terminal logs remain separate from interaction journals.
- Mission workflow tests proving runtime AgentExecution records store journal references while workflow state remains orchestration-only.
- Journal reference tests for owner-scoped identity and file-store path resolution.
- Header write tests proving launch fails before runtime start when the journal header cannot be appended.
Implementation Sequence
- Add AgentExecution journal schemas and types.
- Add shared AgentExecution journal reference and store contracts.
- Add owner-scoped AgentExecution journal references.
- Add shared AgentExecution journal writer and reader over journal references, with filesystem path resolution kept inside the file-backed store.
- Append
journal.headerbefore Agent runtime start and fail launch if it cannot be written. - Add journal replay and idempotency hydration.
- Add live runtime snapshot overlay for active executions.
- Route owner/operator messages through journal writes.
- Route observations and decisions through journal writes.
- Route noisy progress, telemetry, and transient target updates into
activity.updatedrather thanstate.changed. - Add Agent journal location references to Mission workflow runtime AgentExecution records through an explicit runtime data change.
- Keep
timelineItemsas a replay-derived projection without writingprojection.recordedrecords in phase one. - Keep input-request semantics out of lifecycle by representing them as
runningplusattentionandcurrentInputRequestIdin AgentExecution data. - Update Open Mission readers to consume projection fields rather than inventing local transcript truth.