Conversation labels such as conversation-001 are consistent aliases across
these reports, not session IDs. Use actual IDs from your own store when
rerunning commands.
The reader only marks a SQLite sidecar verified when the persisted trajectory, the full raw RPC, and the ordinary RPC's supported projection agree. This prevents a reachable daemon with an old in-memory cache from certifying a partial or same-step stale response.
Profile sqlite-v1-format0-agy-1.3.4-5b93565c5f04-planner50-permission2-prompt23-tools143-permission1-tools144-skill1-history2-ide225-options145-compaction146-skill146-finish147-replay147 supports SQLite
user_version=1, the seven known tables and their exact columns, contiguous
zero-based indices, and step_format=0. All persisted protobuf fields and enum
values must be known, except the two declared source-only fields below.
Decomposed subtrajectories remain unsupported. The decoder checks
redundant step columns against the complete step payload. In particular,
steps.render_info must equal generic.result.step_render_info, including
presence (SQL NULL means absent); malformed and unknown content is rejected.
Unknown versions, fields, formats, tables, and columns return unknown; they
cannot create or refresh completeness evidence.
The tools143 suffix qualifies six modern tool payloads observed with agy
1.4.3. RunCommand retains outer metadata and task details and presents the
stored result text as command output; ReadFile converts one-based ranges to
zero-based VIEW_FILE ranges and keeps the stored result text. FileEdit
maps CREATE and EDIT results into CODE_ACTION, including empty-file diffs.
InvokeSubagent preserves the child specifications and results. ManageTask
and Schedule remain generic, with metadata derived from their payloads.
These are bounded mappings: completed commands, reads, creates and edits; completed single-task status/list/kill results; a canceled timer; and a completed subagent invocation without task mode. Other statuses, argument names, newly populated payload fields, paths requiring URI normalization, and unsupported outcomes still return unknown. Full persisted/RPC equality precedes projection, and every presented field must match. The change does not make a schema fingerprint or equal step counts sufficient evidence.
The combined tools143-permission1 suffix means the modern 1.4.3 tool
mappings initially refused wrappers with permission grants or
completed interactions. Their presentation has not been qualified by a native
1.4.3 capture; the ide225 exception below applies to modern reads only.
Older native-Step permission mappings remain separate.
The tools144 suffix adds mappings observed with agy 1.4.4. Canceled
background run_command steps with render-only results become native command
steps with the stored command and empty output. They retain metadata and task
details, require the observed pending/running/canceled history, background
timestamp and 5000 ms wait argument, and do not invent an exit code. Completed
modern ManageSubagents list calls with two conversation IDs remain generic;
their result metadata gains the action and comma-joined IDs from the payload.
Modern subagent invocations also accept Workspace=inherit when the stored
arguments match the metadata tool call; child specifications and results remain
unchanged. The skill1 suffix derived modern reads' isSkillFile flag from
the exact enabled-skill entry in the step executor's persisted customization
registry. The skill146 suffix replaces that rule; see below. The
expected flag must match the ordinary RPC along with every other presented
field. Other workspace selectors, actions, ID counts, argument names,
permissions, completed interactions
and unsupported fields remain unknown. These mappings use the existing 1.3.4
descriptor; no schema definition or source-only allowance was added.
The 1.4.4 audit records the source/full/default qualification and the broad corpus checks. Old attestations must be reverified under the new profile.
The history2 suffix adds bounded mappings for historical sessions rechecked
with agy 1.4.4: failed list_resources, calls rejected for missing
toolSummary, a command whose bridge failed while formatting its result,
legacy resultless skill reads, and payload-free manage_task send_input.
These rules use persisted arguments, errors, native payloads and executor
registrations. They preserve the full-source comparison and add no descriptor
fields or source-only exceptions. The detailed bounds appear below.
The options145 suffix adds the three configuration fields qualified with
agy 1.4.5: browser auto-invoke, per-tool output limits, and skipping plugin
logos in file views. Two idle conversations persisted enabled and explicit
false flags, plus absent, zero, negative and positive per-tool limits. Their
SQLite sources, full RPC and ordinary RPC agreed. These fields remain part of
the compared payload; they are not source-only exceptions. This qualifies
serialization and retention, not the options' effects on executing tools.
The per-tool map is checked before protobuf decoding, which otherwise discards
unknown entry fields and repeated keys. Unknown, malformed, wrongly encoded or
duplicate entry members and duplicate map keys are refused, including keys
split across repeated singular enclosing messages. Missing key/value members
keep protobuf defaults. The separate new BASH enum remains unsupported.
See the qualification report.
The compaction146 suffix preserves captured checkpoint requests and their
attachments, checkpoint context/media settings, and the customization-kind
map carried by native agy 1.4.6 conversations. Four native conversations with
five manual checkpoints passed source/full/default comparison, offline export,
sync, and saved-attestation checks. The customization map receives the same
strict wire checks as the per-tool map. No projection or source-only allowance
was added. Old attestations require reverification under this profile. See the
feature qualification and its limits;
the separate whole-release 1.4.6 audit failed on one skill-file marker case,
which the skill146 suffix resolves.
The skill146 suffix sets a modern read's isSkillFile flag from its stored
path alone: the flag is set when the path, with backslashes read as slashes,
ends in /SKILL.md or is exactly SKILL.md. Case matters, so skill.md and
SKILL.MD stay unmarked. The executor's skill registry, the filesystem and the
file content play no part. This is what agy 1.4.6 does when it rebuilds a
stored ReadFile result as a VIEW_FILE step for the ordinary RPC; the
installed 1.4.6 binary's rehydrateReadFile calls a path-only isSkillFile
check and stores its answer unconditionally. The earlier registry rule
predicted no marker for a read of an unregistered plugin SKILL.md, while
agy marked it (agy-reader#xpeh). Across the 405-conversation 1.4.6 corpus,
the path rule agrees with all 76 presented modern reads (6 marked, 70
unmarked); the registry rule disagreed on one. Every preserved native capture
from 1.4.4 and 1.4.6 still verifies. Legacy native VIEW_FILE steps keep their
stored flag, and failed legacy reads keep the history2 registry rule below.
Old attestations require reverification under this profile.
The ide225 suffix fixes offline export of the file-tool wrappers observed in
Antigravity IDE 2.25.0. On 2026-10-22, a frozen SQLite source with 76 steps matched
the running IDE's full unrehydrated RPC. The extended projection then matched
all fields of its ordinary response. This qualifies these shapes in that
conversation, not every possible IDE tool or outcome:
- Three completed
read_filewrappers retain their permission grants and completed interactions unchanged. Both fields must be present together. There is no permission record synthesis or removal; full-source equality and presentation equality cover their complete contents. Other modern tools still refuse permission-bearing wrappers. - Ten reads accept
ByteOffset=0, with all arguments repeated in the retained tool call. Nonzero and noncanonical offsets remain unsupported. - Seven artifact creates and six artifact edits retain
isArtifactFile. Creates also retain the payload's summary, update timestamp and user-facing flag. TheirArtifactMetadataargument must match the retained tool call and payload, withRequestFeedback=false. Metadata on edits, missing artifact flags and unqualified metadata fields remain unsupported.
The IDE daemon executable's SHA-256 was
c1deaf112e8d5b634ef5774d063cc763795c5796b98f3cf3151ca553f62339b6.
Synthetic source/presentation pairs in projection_ide225_test.go cover the
mapping and refusals without storing conversation content. The descriptors and
source-only allowances are unchanged. Existing attestations need reverification
under the new profile; offline exports still carry no completeness attestation.
Historical sources (78 retained ones, 72 CLI and 6 IDE) store
exa.cortex_pb.CascadePlannerConfig field 50, wire type 2, inside
generator_metadata and executor_metadatas records. The current descriptor
reserves that number, and the daemon's RPC never returns it, so the full RPC
can never equal these sources.
The field's historical definition is known. The official agy CLI 1.0.4 and
1.0.10 release binaries both declare it as context_engine_config, of type
exa.cortex_pb.ContextEngineConfig
(declarations,
descriptor excerpts,
summary). In 1.0.10 that
message has five fields: enable_prehook (1), enable_tools (2),
rpc_timeout_seconds (3), enable_reflection_agent (4) and
reflection_agent_model (5)
(cortex.proto).
1.0.4 declares only fields 1 to 3, and the IDE 2.0.1 build only field 1;
neither declares field 4, which the accepted shapes below include. Read with
the 1.0.10 definition, each payload the profile accepts is an empty
message or sets enable_tools and/or enable_reflection_agent explicitly to
false.
The profile still identifies the field by number and wire type and checks its payload bytes against the observed shapes below. It does not decode the payload with the recovered type, and the recovered name appears nowhere in the comparison or the attestation.
- Accepted only at the two locations observed in the retained sources:
CortexStepGeneratorMetadata.planner_configandExecutorMetadata.cascade_config.planner_config. It may occur once per message, with no other unknown field beside it.ExecutorMetadata.request_override_config.planner_configwas never observed carrying it and stays unknown. - Its payload must be one of the observed shapes: empty, or varint fields 2 and/or 4, each once, in that order, with value zero.
- Any other field number, wire type, location or nested content is unknown.
That includes another message, trajectory metadata, parent references,
battle-mode infos, a step, an embedded Any, and every
CascadePlannerConfignested deeper inside a record. The descriptor nests planners recursively (an agent definition in a planner's customization settings, or inExecutorMetadata.agent_config_resolution, carries its own cascade and planner configuration), so the allowance names exact paths rather than "any planner in the record". A test walks the embedded descriptor from both record types, entering each message type at most twice on one path, places the field on every planner it reaches, and checks that exactly these two paths accept it. It also checks that no step or trajectory message is reachable from either record outside an embedded Any.
15 retained CLI sources, written between 2026-06-17 and 2026-07-20, store
exa.chat_pb.ChatMessagePrompt field 23, wire type 2, 47 times. It is a
structured copy of the results of the grep_search step that the tool-result
prompt answers. Later builds write the same content to field 25
(prompt_struct, of type genai.Struct), which the reader compares like any
other field. The current descriptor reserves 23, and the daemon's RPC drops it.
The field's historical definition is known. The official agy CLI 1.0.4 and
1.0.10 release binaries both declare it as prompt_struct, of type
exa.codeium_common_pb.Struct
(declarations,
chat.proto).
That type is a custom Struct: a list of Field entries, each a name (1) and
a Value (2), where Value holds one of null, number, string, bool, nested
Struct, list (members 1 to 6) or Media (member 7)
(codeium_common.proto).
It is not google.protobuf.Struct, and it is not the genai.Struct of field 25.
A shared field name does not make the encodings interchangeable, so the reader
does not treat field 23 as an older copy of field 25.
The profile still identifies the field by number and wire type, as for field
50, and parses the payload with google.protobuf.Struct rather than with the
recovered type. That is a strict reader for the narrow shape the profile
accepts, not a claim about the declared type. Within that shape the two encode
the same way: in both, Struct field 1 holds repeated entries of a string at 1
and a Value at 2, and Value members 1 to 6 have the same numbers and wire types
(google/protobuf/struct.proto).
What the custom type allows beyond that is refused: a Media value arrives as
unknown field 7 and fails the no-unknown-field rule, and a repeated name, which
a list allows but a map merges, makes the payload re-encode shorter than
itself.
- Accepted only on an element of
CortexStepGeneratorMetadata.chat_model.message_prompts, at most once per prompt, with no other unknown field beside it. A test walks the descriptor from generator and executor records and from a step, places the field on everyChatMessagePromptit reaches, and checks that only this path accepts it. - The prompt must be a tool result:
sourceisCHAT_MESSAGE_SOURCE_TOOL,tool_call_idandstep_idxare set, andprompt_structis not. - The payload must parse as
google.protobuf.Structwith no unknown field at any depth, and re-encode to its own length (so nothing was dropped or merged, such as a repeated key). Its shape must be the observed one: aresultslist of 1 to 50 objects, either all{path}or all{path, line_content, line_number}, with strings where strings belong and a wholeline_numberof at least 1; and optionally a whole, positivenum_elided_results. Nothing else. - The values must be derived from the step that
step_idxnames, which must be aGREP_SEARCHstep whose tool call ID equals the prompt'stool_call_id. The list has as many entries as the step has results, in the same order. Entry i'spathequals result i's relative or absolute path; itsline_contentequals result i'scontent; itsline_numberequals result i'sline_number+ 1. Path-only entries belong to a search withmatch_per_linefalse, and line entries to one with it true.num_elided_resultsis present exactly whentotal_resultsis larger than the number of results, and equals the difference. - Any other location, wire type, prompt, shape or value is unknown. The step check runs when the whole source has been read, so a field-23 payload that does not match its step refuses the source like an unknown field.
They give each field's authentic name and type in the 1.0.4 and 1.0.10 releases. They do not change any rule above. Both fields stay source-only: the daemon's RPC drops them, so no definition could let full-raw equality cover their bytes. Every location, shape, value, step and byte rule still applies.
The sources carry no build stamp, so which binary wrote any one session is still unproven. The 2026-06-17 field-23 source, for example, was written before 1.0.10 was published.
A separate check decoded the payloads in four retained sessions with the 1.0.10
definitions: all 16 field-50 payloads as ContextEngineConfig and all nine
field-23 payloads as exa.codeium_common_pb.Struct, with no unknown fields
(results).
The four sessions are not a separate sample. They are retained sources
(created 2026-06-02 to 2026-07-16), taken from the older-SQLite compatibility
sample. The nine field-23 payloads are 9 of the 47 occurrences above: six in
conversation-021 and three in
conversation-035. The other 38 were not decoded with the
recovered type. The 16 field-50 payloads come from retained field-50 sources
(conversation-005, conversation-021 and conversation-079), so they are part of the 78-source
population too.
So the check shows that the definitions fit the bytes of a subset of the retained corpus. It is not independent corroboration, it is not a completeness attestation, and it does not show that every historical trajectory decodes under one version.
The source digest keeps the bytes of both fields. The full-raw and presentation comparisons
use a copy with them removed. A verified attestation for such a source carries
the reason verified except declared source-only fields absent from RPC and a
sourceOnly record with one entry per declared field present (field 50
first, then field 23): the declared path, the number of occurrences, the
SHA-256 of their stored encodings in traversal order, and each occurrence's
location and payload. The sidecar therefore keeps the bytes under
agyReader.completeness. A recheck requires that record to match the current
source; a changed record is incomplete, like a changed payload.
Each output that reports a verification result shows such a source apart
from plain verified: audit-qualify prints VERIFIED WITH SOURCE-ONLY FIELDS
with the reason, doctor counts it as verified with source-only fields, an
audit-sweep manifest records the reason and counts it in
verifiedSourceOnly (its verified count holds plain results only), watch
and --sync add the reason to their synced and wrote lines, and the
format-audit helper's summary and record count it. It is otherwise a verified
result: fresh, not re-synced, and qualifying a format-audit record.
Changing the profile identifier makes every attestation minted under the
previous profile read as unknown until the next live sync or watch re-verifies
it. The identifier names the agy release and the first 12 hex digits of the
binary's SHA-256 that the descriptors came from. Suffixes identify additions
or restrictions to the qualified representation. Saved audit manifests from
an earlier profile cannot certify the new rules. For example, the historical
permission1 suffix refuses a native RUN_COMMAND step whose permission
record has no copy on its wrapper, and permission2 refuses a native VIEW_FILE
step in the same case. The newer combined tools143-permission1 suffix
refuses permission grants and completed interactions on modern tool wrappers;
it does not replace or relax those historical native-Step rules. The planner50 and prompt23 suffixes mark the
declared source-only fields; prompt23 also adds a second kind of
sourceOnly entry, so it replaced
sqlite-v1-format0-agy-1.3.0-19be6af38f7b-planner50-permission2. The 1.3.0 profile replaced
sqlite-v1-format0-agy-a759ce7c7a23-planner50 (the 1.2.16 descriptors), under
which the CLI store held 154 attestations on 2026-10-06: 105 standard and 49
with source-only fields. That profile had replaced
sqlite-v1-format0-agy-a759ce7c7a23 (84 standard attestations at the time).
The full RPC uses CLIENT_TRAJECTORY_VERBOSITY_FULL and
disableRehydration=true. It must equal the reconstructed persisted protobuf.
The default response must equal its supported display projection:
-
Ordinary steps pass through unchanged.
-
Generic wrappers containing recognized run-command, code-action, view-file, or invoke-subagent native Steps are replaced with the embedded Step. Outer task details are preserved; conflicting or inner-only task details fail. Wrapper and native status must match except for the qualified command cases. A run-command wrapper
CANCELEDover a nativeERRORwith error details, or a nativeRUNNINGwithout an error, is presented with the outerCANCELEDstatus. Other canceled/native status combinations fail. The separate command formatting failure below presentsERRORover a storedDONEcommand. -
A run-command or view-file step whose call asked for permission and was approved stores the record on the wrapper and on the native Step:
permissions(the grant) andcompleted_interactions(the request and its answer). The ordinary response presents the native Step's copy, with the pre-tool hook results in its metadata, and drops the wrapper's. Both copies must be present and equal, both stepsDONE, and the record must be the shape observed in the agy 1.2.17 audit: one allowingcommandgrant whose target, and resource target, is the command that ran; one permission request for that command, raised by aTYPE_FORCE_ASK_HOOKtrigger and answered withallow, with an emptypersist_grants. Any other field in the grant, request or answer, a rejected request, another trigger, a record on one copy only, or a wrapper record for any other tool or over any other native type fails. A rejected request leaves a different step, with no result and no grant; see the resultless wrappers below. A view-file step (agy 1.2.16, agy-reader#axnr) carries the same two copies: one allowingread_filegrant whose target, and resource target, is theAbsolutePathargument; one permission request for that file with only a resource and an action description (no trigger), answered withallowand an emptypersist_grants. The same equality,DONEand unknown-field rules apply. -
A view-file
ContentOffsetargument is accepted only when it is a canonical decimal and the presented native Step carries the same value, both in its tool-call arguments and inview_file.content_offset. The ordinary response therefore still shows the offset that the dropped argument recorded. -
A
write_to_fileAppendargument is accepted only when it istrueorfalseand the presented native code-action Step records the same value twice: as the same JSON literal in its tool-call arguments, and incode_action.action_spec.create_file.append(set for true, unset for false). Onlytruewas observed, in one retained agy 1.2.16 session. Any other value, a call to another tool, a native Step without acreate_filespec, or a tool call that lacks or changes the value fails withunsupported write_to_file append. -
A failed wrapper with status
ERRORmay carry an outererrorover an embedded native Step only for the types observed with one: run-command, code-action, view-file, generate-image, grep-search, MCP-tool, read-URL-content, search-web and list-resources. Except for the command formatting failure below, that Step must also beERRORand its own error must have the same short error. The ordinary response presents the native Step with its inner error. The outer error may hold only short and full error text; it is dropped from the presentation and still covered by full raw equality. A failedgenerate_imagecall embeds a native image Step instead of aGenerateImageAny and is qualified the same way, with its prompt, image name and aspect ratio arguments. Successful embedded image Steps use a separate mapping that requires both statuses to beDONEand the wrapper's single media item to match the native generated media. An outer error over an invoke-subagent Step, or over any other native type, fails; a type joins this list only with an observed source, full RPC and default RPC triplet. -
A failed
list_resourcescall is qualified only as anERRORwrapper over anERRORnativeLIST_RESOURCESStep with the same short error. TheServerNameargument must equal the nativeserver_name, and all wrapper arguments must equal the native metadata's tool call. The native resource body may contain only that server name; the wrapper result may contain only text, the native payload and an output URI. Task details, permission records, interaction records and successful list calls remain unqualified. -
A
run_commandbridge formatting failure stores a successfulDONEnative command inside anERRORwrapper. The wrapper's short and full errors must match and start withbridged adapter run_command: to string: template execution error:. Its result contains only the native payload. The ordinary response presents that payload with statusERROR, preserving all native command output and metadata while omitting the wrapper's error. Qualification requires exit code zero, a blocking sandboxed Bash command, a 5000 ms wait, and matching command, proposed command, working directory and tool-call arguments. Both steps must record the built-in tool source and oneallowhook; their histories must bePENDING, RUNNING, ERRORoutside andPENDING, RUNNINGinside. Native errors, task details, permission or interaction records, other result fields and other command outcomes fail. Full raw equality still checks the omitted formatting error and wrapper metadata. -
A wrapper that failed before its tool ran stores arguments and an outer
errorbut no result. With statusERROR, the ordinary response presents it unchanged forwrite_to_file,view_filewithout a recorded tool source,find_by_name,list_dirandschedule, with arguments bounded per tool.write_to_fileis also accepted with statusINVALID. Agenerate_imagecall whose arguments the tool rejected has statusINVALIDonly, an outer error startinginvalid arguments:and the argumentsAspectRatio,ImageNameandPrompt; the ordinary response presents it unchanged. Arun_commandcall that aPreToolUsehook denied has statusERROR, an outer error startingtool call denied by pre-tool hook:and the argumentsCommandLine,CwdandWaitMsBeforeAsync; its metadata must record exactly one hook result, adenywhose reason the error repeats. The ordinary response presents it as aRUN_COMMANDstep that keeps the status, metadata and error, with the command line (also as the proposed command line), working directory, a blocking call and empty combined output taken from the arguments. Both outer errors may hold only short and full error text. A resultless wrapper without an outer error (other than the canceled prompt below), with extra wrapper fields, or for any other tool fails. -
A resultless
run_commandorview_filerejected for missingtoolSummarystays generic. Both error strings must be exactlyinvalid arguments:\n- missing property 'toolSummary'(where\nis a newline), the status must beINVALID, the tool sourceBUILTIN, and the historyPENDING, INVALID. The arguments must equal the retained metadata tool call. The command shape requiresCommandLine,Cwd,WaitMsBeforeAsync,IsDaemon=false,RunPersistent=falseandtoolAction; the read shape requiresAbsolutePath,StartLine,EndLineandtoolAction. A result,toolSummary, other arguments, task details, permission or interaction records, or another error fails. This mapping preserves the entire wrapper; it does not synthesize a native failed command or file read. -
A
run_commandcall whose permission request was rejected (agy 1.3.1, agy-reader#xyfe) is also resultless. agy writes it when a user in review mode answers "No, cancel" (or "No, deny" for a subagent's command), and when it soft-denies a request from a subagent of a headless-prun, which nobody can answer; the three observed samples were alike. The wrapper has statusERROR, the argumentsCommandLine,CwdandWaitMsBeforeAsync(the headless child alsoBypassSandbox"false"), no result and nopermissionsgrant. Its metadata records exactly one pre-tool hook result,{decision: allow}, with no reason (agy writes it with no hook configured). Its outer error holds only short and full text, both exactlypermission check failed for command "<command>": user denied permission to run command:, the command, and agy's advice not to circumvent the denial, on three lines. It keeps the completed request incompleted_interactions: one permission request for thatcommandwith only a resource, a suggested persist type and pattern, an action description, the shell name and aTYPE_DEFAULT_FALLBACKtrigger with no detail, answered with noallowfield and an emptypersist_grants. The ordinary response presents it as aRUN_COMMANDstep built like the hook-denied one, keeping the status, metadata, error and completed request, so a transcript shows the command with the rejection as its error. The dropped arguments must repeat the presented tool call. The quoted command in the error is taken to be Go%qquoting; only plain commands were observed, and a different quoting fails closed. An answer that allows, another trigger, a changed error, a missing or second interaction, a grant, a hook result other than the oneallow, another status,BypassSandbox"true", or any other field fails. -
A
run_commandcall canceled while its permission prompt was still open (agy 1.3.1, agy-reader#3cxt) is the one resultless wrapper without an outer error. It was observed once, in conversationconversation-114, when agy was stopped with the prompt unanswered. The wrapper has statusCANCELED, the argumentsCommandLine,CwdandWaitMsBeforeAsync, no result, no error, nopermissionsgrant and no interaction record, pending or completed. Its metadata records exactly one pre-tool hook result,{decision: allow}, as on the rejected request, and a status history whose last three entries areWAITING,RUNNING,CANCELED: the step waited for the answer and was canceled when it resumed, with no answer recorded. The rejected requests endWAITING,RUNNING,ERRORthe same way, and their commands did not run. The ordinary response presents it as aRUN_COMMANDstep built like the hook-denied one, keeping the status and metadata, so a transcript shows a canceled command with no exit code and no output. The dropped arguments must repeat the presented tool call. An outer error, a grant, an interaction record, task details,BypassSandboxor any other argument, a hook result other than the oneallow, a status history that does not end that way, another status or another tool fails. -
A resultless
view_filewith statusERRORthat records theBUILTINtool source is presented instead as aVIEW_FILEstep built from its arguments: the wrapper's status, metadata and error, no generic arguments, and aview_filewithabsolute_path_urifile://plus the path and the one-basedStartLineandEndLineas zero-basedstart_lineandend_line. agy produces it when it rejects a view before running it, for example of a named pipe or/dev/null(path is a device, pipe, or socket, not a standard file). The arguments must equal the presented tool call, which still shows them, and are limited to the path and either both lines or neither, with1 <= StartLine <= EndLine. The path must be absolute, already clean, and made only of letters, digits and/ . _ - @ +, the characters observed copied into the URI unescaped. Another tool source, another status, an outer error with more than short and full text, any other argument, or another path or line shape fails withunsupported resultless view_file.The
history2rule derivesis_skill_filefrom an exact-path enabled skill registration saved by the current executor or a qualified earlier executor. To mark a read, the step's execution ID must select a current executor. A prior registration is usable only when the saved trajectory and cascade IDs and step index identify this exact source step, executor end indices strictly increase, every earlier executor ends before this step, and the current executor ends at or after it. Missing executor binding, no matching registration, or only disabled or non-skill registrations leaves the flag unset, including repeated registrations; registrations after the current executor cannot mark a read. When any enabled skill registration could mark the read, the current execution binding must be unique and the registration must be the only exact-path match in current or earlier executors. An earlier enabled skill registration also requires unique nonempty executor IDs and valid ordering through the current executor. Duplicate IDs do not prevent an unmarked read; unrelated duplicates do not prevent a marker from the current executor. Filenames and the current filesystem do not determine the flag. A different flag in the ordinary response makes the result incomplete.This resolves the earlier refusal of
conversation-098(agy-reader#3193): its failed read at step 62 has a qualifying registration in a prior executor. The 2026-10-21 source/full/default check verifies all 355 steps. The earlier recovery report records the former limitation. ModernReadFilepayloads follow the separateskill146rule instead (see above): the flag comes from the stored path alone, as agy 1.4.6 sets it, and no executor registry is consulted. -
Payload-free
manage_taskandsend_messagesteps remain generic, with bounded arguments and result fields. Task metadata keys are limited to the observedtask_description,task_id, andtool_name, except for the qualifiedsend_inputshape below. A failedmanage_taskstep with statusERRORmay also carry an outererror, which the ordinary response presents unchanged. An outer error on any other payload-free wrapper fails except for the schedule case below. -
A payload-free
manage_taskwithAction=send_inputremains generic when it isDONEand has nonemptyTaskIdandInputarguments matching the retained metadata tool call. Its result metadata must contain exactlyinput,task_id,task_descriptionandtool_name: input and task ID must repeat the arguments, and the target tool must berun_command. Qualification requires the built-in tool source and exactly oneallowhook. Result text, output URI and render information are retained. Empty or mismatched input, other actions or target tools, extra metadata, outer errors, task details, permission or interaction records remain unknown. -
Payload-free
schedule,manage_subagents, anddefine_subagentsteps also remain generic and are presented unchanged. Each is bounded to its observed arguments (DurationSeconds,Prompt,TimerCondition;Action; eitherdefinitionalone ordescription,enable_mcp_tools,enable_subagent_tools,enable_write_tools,name,system_prompt), to the result text, output URI, and render information, and to statusDONE. Onlyschedulemay carry outer task details. As the one exception to the outer-error rule above, aschedulestep may also fail with statusERRORand an outer error that holds only short and full text. Amanage_subagentsresult may carry only thekilled_conversation_idsmetadata key. A payload-free step for any other tool still fails. -
A payload-free
list_permissionsstep also remains generic and is presented unchanged. It is bounded to no arguments, a result of only text and render information (no output URI, metadata, media, or payload), no outer task details or error, and statusDONE. -
A payload-free
ask_permissionstep also remains generic and is presented unchanged. It is bounded to exactly theAction,ReasonandTargetarguments, a result of only text and render information (no output URI, metadata, media, or payload), no outer task details, error or permission records, and statusDONE. -
The
generate_imagetool'sexa.cortex.core.tools.GenerateImageAny becomes a native image step. It preserves outer metadata and maps prompt, image name, aspect ratio, model name, and the single returned media item. The media URI must agree with the payload's image URI. Unqualified payload fields, missing or multiple media items, and other Any types fail closed.
Arguments and wrapper/result fields are bounded separately for each projection. Wrapper result text, media, output URI, arguments, and metadata omitted by the ordinary RPC still have to match the full raw RPC. Image byte count is also checked by raw equality even though the ordinary image step omits it. Unknown RPC-only JSON fields remain in the saved payload and its digest.
The source digest includes the complete reconstructed protobuf and profile.
The payload digest includes all ordinary RPC fields except the reader-owned
agyReader namespace, with exact JSON numbers and canonical object order.
A matching existing attestation can be rechecked offline. Missing evidence is
unknown; changed source or payload is incomplete. Content can change after any
snapshot; the next watch/doctor check detects it without trusting mtime.
internal/completeness/schema.pb.gz contains a transitive descriptor set for
gemini_coder.Trajectory plus third_party/jetski/cortex/core/tools_pb/tools.proto,
based on definitions extracted from the agy 1.3.4 binary with SHA-256
5b93565c5f04ea4f7b79d49d5c3237449bf6ce47b6779e8cf46c1fae97957c98.
The second root supplies exa.cortex.core.tools.GenerateImage, which travels
inside an Any and is absent from the trajectory's ordinary import closure.
The retained base, internal/completeness/schema-base-agy134.pb.gz, contains
31 descriptors (137,394 compressed bytes), with SHA-256
90bd669c32e044ef769763f3e4dc5b27fb187972cc9ff518189ff3933ed8fd92.
All 343 extracted records were checked against bytes at their manifest offsets
in that binary before generation. The bundle contains schema definitions only,
no conversation payloads or credentials.
The bundle adds exactly five declarations from the verified 1.4.5
binary 8af8e7a2a68f72fa7164e00bb59b8902c018fa43e528fc401ad7db8c2972583c:
AntigravityBrowserToolConfig.enable_browser_subagent_auto_invoke,
ViewFileToolConfig.skip_plugin_logo, ToolOutputConfig.per_tool, its nested
PerToolEntry, and ToolOutputOverride. That intermediate bundle has
137,843 compressed bytes with SHA-256
941e3c1eb080db629d2d627b15c7e268d076ed7f43acb9578bac82b0e5ee3aed.
Every original definition remains intact, including historical
PolicyGuardianConfig.use_v2 field 14, which a full 1.4.5 replacement would
remove. BASH and other intervening additions are not enabled.
The compaction146 overlay adds six declarations from the verified 1.4.6
binary 128780f5776959ba6a991bde1f0a8b699eee37a59ed30deb9926147fedfa56c4:
CortexStepCheckpoint.captured_user_requests, CapturedUserRequest,
CheckpointConfig.context_window, CheckpointConfig.max_user_request_media_tokens,
and CustomizationDiscoveryConfig.kind_enablement with its nested map entry.
The customization map is a necessary prerequisite: real 1.4.6 compaction
conversations carry it in planner and executor configuration. It is compared
against both RPC responses, not treated as source-only content. The existing
Media and Blobref definitions match the snapshot and remain unchanged.
The intermediate 1.4.6 bundle contains 31 descriptors, 137,988 compressed bytes
with SHA-256
3d9a1ebb2ae55b316ab35cf19875f50d9f9c568ff16d8a04b95c90703ef026fa.
Every earlier declaration remains intact, including legacy checkpoint
user_requests. The new request text and media participate in exact native,
full-RPC, and default-RPC comparison. Native checkpoint steps need no new
projection rule. Both qualified maps are checked before protobuf decoding can
discard unknown entry fields or duplicate keys; unrelated new fields remain
unsupported.
The finish147 overlay adds only exa.cortex.core.tools.Finish from the
verified 1.4.7 snapshot. The current bundle contains 31 descriptors, 138,024
compressed bytes, with SHA-256
12b303d5dc3cd50630f82a48a3e1616863067b7448a6283b241a55c1434b97b9.
Every earlier declaration remains intact. The new projection supports completed
Finish calls with a result schema, and the same capture qualifies missing-file
read wrappers. The offline export follow-up
records their bounds. The later replay147 projection keeps this descriptor
and every source-only declaration unchanged. It accepts two complete command
presentations justified by same-source 1.4.6 and 1.4.7 captures: modern
RunCommand payloads either omit waits throughout, or copy each stored
WaitMsBeforeAsync argument as an unsigned 64-bit integer. Mixed forms and
altered values fail comparison. Explicit zero still selects the latter form,
so it cannot hide a nonzero source value as a historical omission. Embedded
native command steps and other command mappings are unchanged. Offline export
uses the form that preserves stored waits.
replay147 also qualifies completed image reads with one persisted inline JPEG,
PNG or WebP media item. The source path must match the argument, and the result
text must identify that MIME type. The reader copies the media, byte count,
path, text and outer metadata exactly. Line-range arguments, permission records,
external media URIs, multiple media items and other MIME types remain outside
this rule. Full-source comparison still checks all dropped wrapper content.
Previous attestations become unknown under the new profile.
Rebuild the current bundle from the pinned base and all three snapshots:
python3 internal/completeness/generate_finish147.py \
artifacts/proto-snapshots/agy-1.4.5-8af8e7a2a68f/extracted \
artifacts/proto-snapshots/agy-1.4.6-128780f57769/extracted \
artifacts/proto-snapshots/agy-1.4.7-40349135371a/all-extracted.protoset \
internal/completeness/schema.pb.gzThis authenticates the prior inputs, all 380 records in the 1.4.6 snapshot, and the pinned 1.4.7 descriptor-set hash. It preserves optional-field presence, loads the resulting pool, and writes atomically. Maintenance tests require exactly six additions over the intermediate 1.4.5 bundle, then only the Finish message over that 1.4.6 bundle, with no other changes. The native qualification report records the observed attachment representations and configuration values.
To reproduce only the historical 1.4.5 intermediate bundle, use a separate output file:
python3 internal/completeness/generate_options145.py \
artifacts/proto-snapshots/agy-1.4.5-8af8e7a2a68f/extracted \
/tmp/schema-options145.pb.gzThat generator checks the base and manifest hashes, the manifest's binary identity, and all 379 extracted record hashes, lengths and names. It copies the authentic declarations and their optional-field presence, loads the resulting descriptor pool, and replaces the output atomically. Maintenance tests require an exact five-addition delta and no unrelated changes.
The original generate_schema.py workflow below generates a whole-release
base, not the current augmented bundle. Replacing the bundle with a newer
release requires checking removals and historical decoding as well as additions.
The generator consumes a separate directory of FileDescriptorProto records
and a manifest declaring that hash and the hash of each extracted record.
It verifies those hashes, drops five named annotation-only files whether or not
the extractor found them, removes the imports and extension declarations that
refer to them, and writes deterministic gzip output. Any other missing import
stops it.
The descriptor delta from 1.3.0 adds DefaultAgentConfig.surface (field 5, enum
AgentSurface), RunCommandToolConfig.enable_fast_output_close (field 24, bool),
TaskDetails.moved_to_background_at (field 8, Timestamp),
McpServerSpec.plugin_auth_provider (field 30, string), CortexStepMcpTool.app_view
(field 14, McpAppView), CascadeNUXConfig.session (field 22, enum),
HookArgsCommon.caller_context (11) and root_conversation_id (12), and
StepRenderInfo.TitlePart.agent (field 5, AgentRef).
These schema definitions are extracted directly from the binary descriptors;
not all additive fields are exercised by the native audit scenarios. The
1.3.4 profile includes the extracted subagents and background task schema additions.
To refresh the schema for a new agy release, copy the installed binary so it cannot change underneath you, then run:
python3 scripts/extract_descriptors.py AGY_COPY tmp/descriptors # prints count and binary SHA-256
# set BINARY_SHA256 in internal/completeness/generate_schema.py to that hash
python3 internal/completeness/generate_schema.py tmp/descriptors internal/completeness/schema.pb.gzThe extractor is read-only on the binary and needs google.protobuf;
make maintenance-test runs its tests. Then change Profile in
internal/completeness/schema.go, update this section, and requalify as
described below.
The generator is a maintenance aid, not a runtime dependency. It needs Python
and google.protobuf; ordinary builds need Go 1.25 or later. Runtime decoding
uses the pinned Go protobuf and pure-Go SQLite modules in go.mod, without
Python or a SQLite CLI. Updating descriptors alone does not authorize a new
projection: qualify the source format and both RPC representations, extend
synthetic rejection tests, and update the profile identifier before accepting
new content.
The normal test suite uses synthetic SQLite and HTTP fixtures. The opt-in
TestNativeEvidence accepts explicitly supplied disposable DB/raw/default RPC
files through AGY_READER_SCRATCH_DB, AGY_READER_SCRATCH_RAW, and
AGY_READER_SCRATCH_PRESENTATION; it does not discover live profiles or daemons.
Legacy .pb conversations now use native verification through the same fetch,
cache-write, watcher and doctor paths as SQLite. They no longer rely on sidecar
mtime. When both extensions exist for one conversation, SQLite still wins.
The supported envelope is a 12-byte nonce, AES-256-GCM ciphertext and a 16-byte
tag, with no additional authenticated data. The authenticated plaintext is a
serialized gemini_coder.Trajectory. Authentication precedes protobuf decoding;
invalid tags, truncation, oversized files, unknown fields and unsupported
projections fail closed. The 64 MiB plaintext and existing recursion limits apply.
The reader never decrypts into a file or changes the encrypted source.
legacy_key.bin is the fixed format key distributed in agy, not an account key
or user credential. Static inspection of agy 1.4.4 binary SHA-256
337dcde668441a2828968644d7982829c2d2a5e0c4144f346e1f3fc6abb3cdc7
identified the key copy at virtual address 0x8d77ee6 from file offset
0x5807f90, passed to WithEncryptionKey (0x853ec40), and the corresponding
DiskSaver.loadUnlocked (0x8540940). The compiled reader embeds those 32
format bytes; it does not depend on binary offsets or an installed agy binary
at runtime. Unknown encryption keys/layouts remain unsupported.
LegacyProfile prefixes the unchanged SQLite schema/projection profile with
legacy-aes256gcm-v1-agy-1.4.4-337dcde66844/. Its source digest covers the profile
and exact encrypted file, including nonce and tag. A rewritten ciphertext
invalidates evidence even if the decoded trajectory is unchanged. SQLite
attestations remain valid under their existing profile. Legacy sidecars without
attestations are re-fetched and verified against full/unhydrated and default
responses from one endpoint, bracketed by native source snapshots.
Audit-sweep manifest version 5 counts both formats and records their distinct
profiles. verified and verifiedSourceOnly cover both formats; sqlite and
protobuf count sources. notVerified counts failures across both formats,
and legacyUnverifiable is the legacy subset of those failures. Audit-qualify
requires version 5 and rechecks each source's current profile and digest.
Legacy watcher checks use the same bounded metadata reuse as SQLite, keyed by
both format profiles. Source or sidecar changes, expiry, and a sibling SQLite
file force another native check. Legacy authentication/schema refusals also
reuse unchanged source metadata within that bound. Doctor and one-shot reads
always check native content. No source-only field allowances were added.
agy-reader export-offline [--format=json|md] SESSION.db uses the native SQLite
snapshot reader and the same qualified presentation mapping used when comparing
RPC responses. It does not fetch a response or mint an attestation. A successful
export establishes that this reader can decode and project the selected source;
it does not establish a fresh match with agy's daemon.
JSON stores that distinction under agyReader.offline (version 1), with the
snapshot profile and digest. Declared source-only records retain their bytes,
locations, counts and hashes under sourceOnlyFields. There is no
agyReader.completeness record. Markdown has a visible offline notice. These
exports do not change the sync/cache verification rules or profile identifiers.
See the offline export usage for WAL handling, output protection and scope. An opt-in regression test compares direct exports with an independently captured agy 1.4.4 audit-sweep corpus:
AGY_READER_OFFLINE_MANIFEST=/private/sweep/manifest.json \
AGY_READER_OFFLINE_SOURCES=/private/conversations \
go test ./internal/cli -run '^TestExportOfflineCorpus$' -count=1 -vThe test checks every SQLite source's profile and digest against the manifest,
then compares the entire parsed JSON after removing reader provenance and making
an omitted empty steps array explicit. It compares Markdown rendered from the
saved daemon JSON with the command output, excluding only the generation time
and offline notice. Both sides render only the selected session.
The agy 1.4.4 comparison record records the corpus coverage and representation differences.