src/protocol defines the machine-facing contract of agent-tty: the CLI JSON envelopes, the error codes, the Zod schemas for session manifests and event records, and the RPC request, params and result shapes between the CLI and the per-session host. sessionStatusPolicy.ts maps each session status to the operations it allows. The files have no I/O. A change to any CLI JSON shape must update these schemas and their tests in the same change (see AGENTS.md).
CommandError,CommandSuccessEnvelope,CommandErrorEnvelopeandCommandEnvelopeare the CLI output envelope types. Success is{ ok: true, command, timestamp, result }. Error is{ ok: false, command, timestamp, error: { code, message, retryable, details } }, plusresultwhen the error carries one (a timed-outwait, a fail-fastbatch).CommandErrorEnvelope<TResult>types thatresult, andCommandEnvelope<TResult, TErrorResult>defaultsTErrorResulttonever. [envelope.ts:CommandEnvelope]createSuccessEnvelopeandcreateErrorEnvelopebuild the envelopes withtimestamp = new Date().toISOString(). [envelope.ts:createSuccessEnvelope] [envelope.ts:createErrorEnvelope]ERROR_CODESand its value typeProtocolErrorCodelist the error codes:SESSION_NOT_FOUND,SESSION_NOT_RUNNING,SESSION_ALREADY_DESTROYED,HOST_UNREACHABLE,HOST_TIMEOUT,INVALID_SESSION_ID,INVALID_DIMENSIONS,INVALID_SIGNAL,INVALID_KEYS,INVALID_DURATION,INVALID_INPUT,CAPABILITY_UNAVAILABLE,STORAGE_READ_ERROR,STORAGE_WRITE_ERROR,MANIFEST_VALIDATION_ERROR,RPC_ERROR,PROTOCOL_ERROR,EXPORT_ERROR,REPLAY_ERROR,SKILL_NOT_FOUND,WAIT_TIMEOUTandINTERNAL_ERROR. [errors.ts:ERROR_CODES] [errors.ts:ProtocolErrorCode]DEFAULT_ERROR_MESSAGESholds one default message per code. [errors.ts:DEFAULT_ERROR_MESSAGES]makeCliError(code, options?)returns a newCliError(class fromsrc/cli/errors.ts).MakeCliErrorOptionshasmessage,retryable,details,resultandcause.resultbecomes the error envelope'sresult. [errors.ts:makeCliError] [errors.ts:MakeCliErrorOptions]parseValidatedResult(schema, rawValue, message, errorCode?)runsschema.safeParseand throws aCliErrorwith the Zod issues on failure. The default code isPROTOCOL_ERROR. [validation.ts:parseValidatedResult]deriveTerminationCategory(record)maps aSessionRecordto aTerminationCategory. [terminationCategory.ts:deriveTerminationCategory]SessionStatusPolicyandgetSessionStatusPolicy(status)give the per-status flagsactive,terminal,commandable,liveHostEligible,offlineReplayEligible,collectableanddestroyed. The predicatesisActiveSessionStatus,isTerminalSessionStatus,isCommandableSessionStatus,isLiveHostEligibleSessionStatus,isOfflineReplayEligibleSessionStatus,isCollectableSessionStatusandisDestroyedSessionStatusread the same table. [sessionStatusPolicy.ts:getSessionStatusPolicy] [sessionStatusPolicy.ts:isCommandableSessionStatus]- Session manifest schemas:
SessionStatusSchema(running,exiting,exited,failed,destroying,destroyed),FailureOriginSchema(host-death,renderer-failure,storage-corruption,unknown),SessionRecordSchemaandSha256HexSchema, with their inferred types. [schemas.ts:SessionStatusSchema] [schemas.ts:FailureOriginSchema] [schemas.ts:SessionRecordSchema] [schemas.ts:Sha256HexSchema] - Event log schemas:
EventTypeSchema(output,input_text,input_paste,input_keys,input_mouse,input_run,run_complete,resize,signal,exit,marker), one*EventPayloadSchemaand one*EventRecordSchemaper type, andEventRecordSchemawith typeEventRecord, a union discriminated ontype. [schemas.ts:EventTypeSchema] [schemas.ts:EventRecordSchema] - Snapshot, screenshot, wait and record schemas:
SnapshotParamsSchema,SnapshotResultSchema(discriminated onformat:structuredortext),ScreenshotParamsSchema,ScreenshotResultSchema(with optionalmissingGlyphsentries fromMissingGlyphSchema: aU+XXXXcodePointand at least one{ row, col }cell, and an optionalmissingGlyphsTruncated: true),RenderWaitScopeSchema,WaitForRenderParamsSchema,WaitForRenderResultSchema(a timed-out result has an optionaleventLogStop),EventLogStopSchema({ reason, seq }, strict) withEventLogStopReasonSchema(bytes,eventsorunknown),WaitResultSchema,RecordExportResultSchema(discriminated onformat: onlywebmallows optionalmissingGlyphsentries fromRecordedMissingGlyphSchema, aU+XXXXcodePointand no cells, and an optionalmissingGlyphsTruncated: true),RecordDiffResultSchemaandReplayTimingModeSchema(recorded,accelerated,max-speed), with related schemas and types. [schemas.ts:SnapshotResultSchema] [schemas.ts:WaitForRenderParamsSchema] [schemas.ts:RecordDiffResultSchema] [schemas.ts:ReplayTimingModeSchema] schemas.tsandmessages.tsre-export the capability and renderer-runtime schemas (CapabilityEntrySchema,RendererRuntimeSummarySchemaand others) fromsrc/renderer/capabilities.ts. [src/renderer/capabilities.ts:CapabilityEntrySchema]messages.tsalso re-exports theseschemas.tsschemas:MissingGlyphSchema,RecordedMissingGlyphSchema,RecordDiffLineSchema,RecordDiffResultSchema,RecordDiffSideSchema,RecordExportResultSchema,ReplayTimingModeSchema,RichSnapshotLineSchema,SnapshotCellSchema,SnapshotParamsSchema,SnapshotResultSchema,ScreenshotParamsSchema,ScreenshotResultSchema,WaitForRenderParamsSchema,WaitForRenderResultSchema,WaitResultSchema,EventLogStopSchemaandEventLogStopReasonSchema. [schemas.ts:RecordDiffResultSchema] [schemas.ts:WaitResultSchema]- RPC envelope schemas:
RpcRequestSchema,RpcErrorSchema,RpcSuccessResponseSchema,RpcErrorResponseSchemaandRpcResponseSchema(discriminated onok), with their types. [messages.ts:RpcRequestSchema] [messages.ts:RpcResponseSchema] RpcMethodSchemaandRpcMethodlist the host RPC methods:inspect,snapshot,screenshot,type,paste,run,mark,sendKeys,mouse,resize,signal,wait,waitForRenderanddestroy.RpcMethodSchemasmaps each method to its{ params, result }schemas. [messages.ts:RpcMethodSchema] [messages.ts:RpcMethodSchemas]- Per-method schemas:
InspectParamsSchema,HostInspectResultSchema,HostInfoSchema,InspectResultSchema,ArtifactHealthSummarySchema,TerminationCategorySchema, and theType,Paste,Run,Mark,SendKeys,Mouse,Resize,SignalandDestroyparams and result schemas, plusWaitParamsSchema. [messages.ts:InspectResultSchema] [messages.ts:TerminationCategorySchema] [messages.ts:RunParamsSchema] - Mouse schemas:
MouseActionSchema(press,release,move),MouseButtonSchema(left,middle,right,wheel-up,wheel-down,wheel-left,wheel-right) andMouseModifiersSchemainschemas.ts.MouseInputSchemainmessages.tsis one cell-addressed action, as a batchmousestep takes it.MouseParamsSchemaadds an optionalrendererNamefor the RPC.MouseResultSchemais{ reported, bytesWritten, seq }. [schemas.ts:MouseButtonSchema] [messages.ts:MouseInputSchema] [messages.ts:MouseParamsSchema] [messages.ts:MouseResultSchema]
- Every
z.object(...)inschemas.tsandmessages.tsends in.strict(), so unknown keys fail validation. [schemas.ts:SessionRecordSchema] [messages.ts:RpcRequestSchema] SessionRecordSchemarequiresversionto be the literal1, at least onecommandentry, positive integercolsandrows, and ISO datetimes forcreatedAtandupdatedAt. [schemas.ts:SessionRecordSchema]Sha256HexSchemarequires 64 lowercase hex characters. [schemas.ts:Sha256HexSchema]- An event record has a non-negative integer
seqand an ISO datetimets. [schemas.ts:EventRecordSchema] ReplaySeqSchemais an integer of at least -1: the Event Log seq a replay reached, or -1 before the first event. The snapshot, screenshot, render-wait and record export results,afterSeq, and record diff sides use it. [schemas.ts:ReplaySeqSchema]InputRunEventPayloadSchemarequiresmarkerwhennoWaitis false and rejects it whennoWaitis true. [schemas.ts:InputRunEventPayloadSchema]WaitForRenderParamsSchemarequires at least one oftext,regex,screenStableMs,cursorRoworcursorCol.textandregexare mutually exclusive, and scopecursor-lineneedstextorregex. [schemas.ts:WaitForRenderParamsSchema]RecordDiffResultSchemachecks thatidenticalmatches the equality of the two screen hashes. An identical result must have an empty diff and equal side row counts. A side withcapturedAtSeq-1 must hash to a blank screen. For a non-identical result, the rows rebuilt from the diff must hash to each side'sscreenHash. [schemas.ts:RecordDiffResultSchema]RpcRequestSchemarequires non-emptyidandmethod. Theidtakes at mostMAX_RPC_REQUEST_ID_CHARS(128) characters, so the id that the server echoes in a response stays small.MAX_RPC_RESPONSE_CHARS(67,108,864) is the largest response line, newline included, thatsendRpcreads andRpcServersends. The spaces that a host writes ahead of the line as keep-alives are not part of it. It does not restrictmethodtoRpcMethodSchema. [messages.ts:RpcRequestSchema]RpcErrorSchemarequires non-emptycodeandmessage, and allows an optionaldetailsrecord. A CLI from beforedetailswas added rejects an error response that has it, so that CLI reportsRPC_ERROR(retryable: true) for such an error from a newer host, and retrying does not help. A newer CLI reads an older host's error responses. [messages.ts:RpcErrorSchema]RpcMethodSchemasusessatisfies Record<RpcMethod, ...>, so TypeScript requires one entry per method. [messages.ts:RpcMethodSchemas]RunParamsSchemadefaultsnoWaittofalse. [messages.ts:RunParamsSchema]MouseInputSchemaandMouseParamsSchemarequirebuttonforpressandrelease, reject it formove, and rejectreleaseof a wheel button.InputMouseEventPayloadSchemaapplies the first two rules and requiresdataBase64, which is empty for a suppressed report. [messages.ts:MouseInputSchema] [schemas.ts:InputMouseEventPayloadSchema]makeCliErrorsetsretryableto true by default only forHOST_UNREACHABLE,HOST_TIMEOUTandRPC_ERROR. An explicitretryableoption wins. [errors.ts:makeCliError]- When the module loads, a top-level loop over
SESSION_STATUS_POLICIESinsessionStatusPolicy.tsasserts these rules for each status:activediffers fromterminal,collectableimpliesterminal,commandableimpliesactive, andliveHostEligiblediffers fromofflineReplayEligible. [sessionStatusPolicy.ts:SESSION_STATUS_POLICIES] - Only
runningis commandable. Onlyrunningandexitingare live-host eligible.exited,failedanddestroyedare collectable.getSessionStatusPolicythrows for an unknown status. [sessionStatusPolicy.ts:getSessionStatusPolicy] deriveTerminationCategorymapsrunningandexitingtorunning, anddestroyinganddestroyedtodestroyed. It mapsfailedto the record'sfailureOrigin(orunknown). It mapsexitedtosignal-exitwhenexitSignalis set,clean-exitwhenexitCodeis 0 or null, andnonzero-exitotherwise. [terminationCategory.ts:deriveTerminationCategory]
- None found in code beyond reading the clock:
createSuccessEnvelopeandcreateErrorEnvelopecallnew Date(). ThesessionStatusPolicy.tschecks throw at import time if the policy table breaks a rule. [envelope.ts:createSuccessEnvelope] [sessionStatusPolicy.ts:SESSION_STATUS_POLICIES]
- Run:
npx vitest run test/unit/protocol - Run:
npx vitest run --maxWorkers=1 test/integration/lifecycle.test.ts test/integration/run.test.ts test/integration/host-renderer-rpc.test.ts - Run:
npm run typecheck