JSON Contracts
September 21, 2026 ยท View on GitHub
ctx JSON is for local agents and scripts. It can include prompts, command arguments, typed result identifiers, and local paths. Treat it as private until a user reviews it.
Command result JSON uses schema_version: 1 except for
ctx setup --format json, ctx stats --format json,
ctx import --format json, ctx search --format json,
ctx status --format json (including usage controls and errors), all ctx index
JSON modes, ctx daemon status, ctx daemon enable, ctx daemon disable, and
MCP status, and the Blame result/diagnostic contract described in
Blame. Generic daemon command JSON uses version 2; uninstall-quiescence
receipts retain their separate schema.
Progress-event JSON is stderr progress output and does not include
schema_version.
All machine timestamp fields remain exact UTC RFC 3339 values with their
contracted millisecond precision. The OS-local, no-millisecond presentation
used by selected ordinary human search, show, and locate fields
does not apply to JSON, JSONL, MCP structured content, filters, storage,
other durable processing, or text/Markdown transcript artifacts.
Setup
ctx setup --format json
ctx setup --format json --no-daemon
Writes local storage and returns schema version 3:
schema_version;initialized;data_root;config_path;mode, one ofready,pending,stale, orunavailable;history_epoch;lexical;catalog;refresh;refresh_request;semantic;attribution(see below);daemon;daemon_autostart;indexed_items,indexed_sessions,indexed_events, andindexed_sourceswhen their counts are available;network_required: falseandrepo_writes: false.
The deprecated_catalog_only_ignored field remains present for schema-2
compatibility and is always false, because the ignored --catalog-only option
has been removed.
In automatic indexing mode, human and machine-readable setup both health-check
and recover the persistent daemon before returning. In manual mode, setup does
not start a persistent or finite worker.
daemon_autostart.status: "verified" includes the live PID. A one-run
--no-daemon opt-out reports status: "not_requested" and reason
explicit_opt_out; a durable manual configuration uses reason
daemon_disabled. refresh_request separately reports whether setup queued
or waited for daemon-owned Core publication. A completed --wait request
also includes its request-bound terminal receipt; callers should use that
receipt rather than a later periodic daemon job when reporting the setup run.
If the platform's native current-user service manager is not operational,
automatic setup starts the same coordinator as a persistent detached process without
forcing an initial Core-refresh wait. daemon_autostart.status is then
"degraded", persistent is true, and reason is
"native_supervisor_unavailable"; the nested supervisor report uses status
"manager_unavailable" and reports that native automatic restart after
failure, login, or reboot is unavailable. Existing native registration
artifacts are preserved when the unavailable manager cannot verify or remove
them. Ownership, identity, integrity, fencing, and security failures remain
errors rather than degraded limitations.
An unmanaged install or custom data root instead uses the persistent
CLI-self-healing fallback process. Its autostart status is "degraded" because
automatic restart registration is unavailable, but persistent is true and
it does not report a process-lifetime limitation. The nested supervisor report
uses status "fallback".
Setup does not perform a foreground provider import. --wait waits for the
daemon-owned Core refresh; without it, setup requests a background Core
refresh. When that first request finds zero sources and no prior publication,
setup attaches long enough to certify a verified empty Core generation instead
of returning an uncertified pending state.
Use ctx status --format json for complete process, supervisor, and applied
configuration health.
Status
ctx status --format json
Reads local storage state and returns:
schema_version: 3;initialized;data_root;config_path;indexing, with effectivemode(autoormanual);history_epoch;lexical;refresh;semantic;attribution(see below);daemon;indexed_items;indexed_sessions;indexed_events;indexed_sources;upgrade;- compact
local_usagehealth (enabled, state, and a content-free error when unavailable), without aggregates, estimates, or operation details; read_only: true.
For status, read_only: true means the command does not mutate canonical
history or Core search generations. Usage control modes return a separate
action-focused JSON shape with schema_version: 2 and read_only: false, and
do not read Core status. Generic status configuration errors also use version 2.
Status and setup version 3, and usage/error, index, and daemon command version 2,
remove the generic local_only property. Callers must update version checks and
stop requiring or reading that property. Semantic executor diagnostics,
standalone stats, and uninstall receipts retain their separate locality fields.
history_epoch and lexical identify the verified searchable Core generation.
refresh reports the latest observed daemon-owned refresh request and its exact
generation binding. A generation-bound published refresh with deterministic
record rejections but zero source failures has refresh.status: "ready"; its
current.current_rejected_records remains present for diagnostics. Source
failures, retryable failures, and publication/generation mismatches do not
become ready merely because an older verified generation remains searchable.
When automatic refresh is confirming whether an internal failure repeats,
refresh.status is pending with reason automatic_retry_confirming. When
the current terminal internal failure has no retryable route left after the
same failure occurs twice, status is paused with reason
automatic_retry_paused. A mix of confirming and paused routes, or a paused
route alongside other active refresh work, is partial with reason
automatic_retry_partially_paused; it does not claim that all refresh work
has stopped.
The refresh object preserves the terminal structured_outcome, including its
closed code, class, and retryable fields, and may add:
"automatic_retry": {
"state": "confirming | paused | mixed",
"reason": "internal_failure_confirmation | repeated_internal_failure",
"confirmation_limit": 2,
"routes": {
"<route sha256>": {
"state": "confirming | paused",
"matching_failures": 1,
"source_observation": "<sha256>",
"failure_fingerprint": "<sha256>",
"build_version": "<version>"
}
},
"resume_on": ["source_change", "ctx_upgrade", "manual_import"]
}
matching_failures is 1 while confirming and 2 when paused. Route keys,
source observations, and failure fingerprints are SHA-256 identities, not
source paths or raw debug chains. manual_import means an explicit
ctx import --all retry after fixing the source or updating ctx.
semantic reports the current source-backed semantic
projection, including exact flat_f32 document/event/chunk coverage when it is
available. daemon reports process and relevant job state. These diagnostic
objects can contain local paths and should not be persisted or forwarded outside
local diagnostics.
Blame attribution readiness
Setup and status keep schema version 3 and add attribution. Doctor includes
the same observation in its schema-3 source_epoch; its outer schema remains 1.
Status omits catalog and has read_only: true. Setup retains catalog, omits
read_only, and keeps its existing setup-specific fields. Core health remains
independent from attribution readiness.
The attribution object is the runtime's serialized CoreProjectionStatus:
| Field | Meaning |
|---|---|
currentness | not_materialized, partial, stale, needs_rebuild, or current. |
requested_core_generation_id | Current Core generation requested for the observation, or null. |
receipt | Canonical completed Core materialization receipt, or null. |
materialized_coverage | not_materialized, partial, complete, empty, or abstained. |
coverage | Six repository evidence counts listed below. |
local_repository_access | Whether the projection has available local repository access. |
availability | Booleans file_blame, commit_blame, and pull_request_blame. |
diagnostic | Canonical Blame diagnostic for noncurrent state; omitted when absent. |
The six coverage fields are repository_candidate_events,
logical_binding_events, certified_live_root_access_events,
file_evidence_events, exact_commit_evidence_events, and
exact_pull_request_evidence_events.
currentness: "current" with materialized_coverage: "empty" or "abstained"
is terminal and has no import/rebuild instruction. Availability may still be
false because there is no usable evidence. Noncurrent diagnostics carry the
ordinary completion action under diagnostic.next_action:
{ "kind": "import_all", "argv": ["ctx", "import", "--all"] }
An actual observation failure returns a canonical diagnostic at
attribution.error while preserving the Core status report. A stale committed
index may still answer a query with explicit freshness; readiness does not
silently deny that result. Reading status, doctor, or index progress does not
import, schedule work, create an attribution root, or open legacy product data.
Semantic Lifecycle
ctx semantic enable --format json
ctx semantic enable --executor https://embeddings.example.test/ctx --format json
ctx semantic status --format json
ctx semantic disable --format json
These commands return schema_version: 1, operation (enable, status, or
disable), the effective enabled, status, reason, and config_source,
indexing.mode, optional projection and catch_up diagnostics, reduced
daemon state, executor, local_only, and read_only. executor.kind is
builtin or http; an HTTP executor also reports its configured endpoint.
HTTP executors report protocol_schema_version. V2 URL selections also report
the persisted opaque space_id and dimensions; retained fixed-E5 V1
selections omit those two fields.
executor.scope is builtin, loopback, or remote, and
executor.content_leaves_machine makes the content boundary explicit.
executor.authentication names the canonical token environment and may report
whether the current process environment is usable for the selected endpoint;
it never reports the token or claims that shell state is daemon-effective.
local_only is true only for the in-process built-in executor. A loopback
executor keeps ctx's first hop on the machine, but the receiving process can
retain or forward the content. Status is credential-free and read-only: it
reads local configuration and observed state without probing the endpoint or
making a network request.
builtin_throttling.configured is boolean and includes the default value
true when the config key is absent. builtin_throttling.effective is boolean
for the selected built-in executor and null for an HTTP executor.
config_source is default or config; optional reason is
external_executor when the setting is not applicable. An explicitly configured
HTTP executor cannot also set the built-in-only config key.
Enable and disable persist policy and report read_only: false. Disablement
retains downloaded model/runtime assets and derived semantic indexes.
In auto mode, enablement starts or recovers the persistent daemon and returns
after semantic work is accepted. ctx semantic enable --wait --format json
uses the Index Readiness wait result below and waits for the current Core
generation's semantic projection. Plain enablement in manual mode records the
opt-in without changing mode or the current executor selection; wait requires
auto mode.
Index Readiness
ctx index --format json
ctx index mode --format json
ctx index mode auto --format json
ctx index mode manual --format json
ctx index watch --format jsonl
ctx index wait --format json
ctx index --format json returns the one-shot readiness snapshot. Each watch
line uses the same read-only shape: schema_version: 2, initialized, indexing,
lexical, refresh, semantic, daemon, and read_only.
indexing.mode is auto or manual. Lexical counts and certified source bytes
describe the currently verified generation. Refresh progress contains only
values reported by the active refresh job; no synthetic work units, failure
counts, rates, or remaining time are added. The one-shot and watch snapshots
preserve refresh.structured_outcome and refresh.automatic_retry with the
same shapes and status meanings as ctx status --format json.
ctx index mode --format json returns schema_version: 2, indexing.mode,
config_path, and read_only: true. Supplying auto or manual
persists the mode and returns read_only: false plus
indexing.requested_mode, indexing.overridden, daemon.running, optional
daemon.pid, daemon.persistent, and daemon.supervisor. overridden is true
when a process-level control keeps a different effective mode. Mode changes
reconcile supervision to that effective mode. When auto remains effective, ctx
installs or repairs supervision and starts the persistent daemon; manual mode
stops it and removes persistent supervision. Explicit import and search
--refresh wait can still use finite workers.
Wait returns one object with schema_version: 2, status (ready, blocked, or
timeout), selection, the final readiness snapshot, and
read_only. Use ctx status --format json for the complete health contract,
including daemon and supervisor diagnostics.
Index snapshots expose the reduced
semantic.{status,reason,enabled,builtin_throttling.{configured,effective,config_source,reason},coverage.{candidate_items,searchable_items,embedded_items,filtered_items,embedded_chunks}}
shape and daemon.{status,running,jobs.semantic_index}. The complete semantic
and daemon fields below describe ctx status --format json, not index
snapshots.
semantic.status is disabled, pending, ready, or unavailable.
When enabled, a failed background semantic job makes this status unavailable
with the job's reason and optional semantic.last_error. A pending configuration
reload takes precedence over a previous job failure; resource-pressure deferral
without an error remains pending. The retained semantic.flat_f32 inventory
below describes projection state separately from the background failure, and
lexical readiness is unchanged.
semantic.flat_f32 reports the source-backed projection and can include its
status, reason, path, Core and flat generation identity, semantic document
count, projected and intentionally filtered document counts, active
event/chunk/vector-byte counts, and last_error. For a ready generation,
semantic_documents = projected_documents + filtered_documents and
projected_documents = active_events. These document counters, and the index
snapshot candidate/searchable/embedded/filtered counters derived from them,
are integers in the exact inclusive range 0..9007199254740991; a larger
internal count makes semantic status unavailable instead of emitting an unsafe
JSON number. Optional semantic.catch_up retains the latest semantic-index job
receipt, including its source_contract_fingerprint when produced by current
daemon maintenance. Live worker state and coverage are reported under
daemon.jobs.semantic_index below.
daemon reports the ctx-owned background coordinator state. Fields listed as
nullable may be omitted when unavailable:
enabled, retained as a compatibility-shaped boolean and true when indexing mode isauto;status, one ofunknown,disabled,running,stopped,completed,failed, orstale_lock;completedremains readable for legacy finite-run receipts, while current persistent and finite workers usestoppedafter graceful shutdown. A stopped finite worker may renderdisabledbecause the durable indexing mode is manual;running;pid, nullable/omitted;started_at_ms,heartbeat_at_ms, andfinished_at_ms, nullable/omitted;heartbeat_age_msandheartbeat_stale, omitted unless a non-future heartbeat belongs to the live daemon PID. Stale means older than 30 seconds; it is a progress warning, not proof of process death or permission to replace a live owner;last_error, nullable/omitted;start_mode, nullable/omitted, currentlyautofor setup/import/search/semantic process starts ormanualfor explicit daemon runs;trigger_command, nullable/omitted, currentlysetup,import,search, orsemanticfor automatic or finite starts;semantic_runtime_active, true only while the running daemon owns its semantic query service;config_reload, withstatus,out_of_sync,requested,applied,last_attempt_at_ms,last_applied_at_ms, and optionallast_error;lock_path;status_path;jobs.
daemon.jobs.core_refresh preserves structured_outcome and
automatic_retry from the daemon-owned refresh job. A running daemon can
therefore report history refresh as confirming, paused, or partially paused
without reporting the daemon process itself as stopped.
Its schema-1 request/status projection still emits
coalesced_into_request_id: null and coalesced_logical_demands: 0 after
cross-request-ID coalescing was removed. Those keys may disappear only in an
explicit successor schema after schema 1 is outside the supported client floor.
request_fingerprint is not a compatibility placeholder: explicit stable
request IDs still use it to reject a conflicting payload. No successor schema
or calendar sunset is scheduled.
config_reload.status is applied, pending, failed,
activation_failed, or unknown. requested reflects the current effective
daemon/semantic configuration read by the status command. applied is the last
configuration acknowledged by the running daemon. Both include
semantic_builtin_throttling_configured and
semantic_builtin_throttling_effective; the effective value is null when the
selected executor is HTTP. A changed config remains
pending with out_of_sync: true until the daemon reloads it. Parse/read
failures report failed, deactivate semantic runtime ownership, stop the
semantic query service, clear the applied semantic executor, and surface
daemon_config_reload_failed; Core refresh continues independently and
last_run_* remains historical. A later valid configuration can reactivate
semantic work. Inability to establish newly requested semantic runtime
ownership reports activation_failed. ctx daemon status --format json
remains a hidden compatibility diagnostic when malformed configuration caused
the reload failure; ordinary commands reject malformed configuration.
daemon.jobs.semantic_index mirrors live semantic coverage and includes
status, enabled, runtime_active, semantic_enabled,
daemon_configured, semantic_configured, config_reload_status,
configuration_pending, optional current reason, optional
last_run_at_ms, optional last_run_status, optional last_run_reason,
optional last_error, optional indexed_chunks, model_cache_available,
worker_status, and coverage with searchable_items, completed_items,
embedded_items, embedded_chunks, dirty_items, and
queued_items_estimate. Current
status/reason are derived from live coverage and daemon runtime ownership;
last_run_* fields preserve the persisted result from the last daemon
iteration. enabled retains its released meaning: daemon and semantic
configuration are enabled and the platform supports semantic service.
runtime_active separately reports observed query-service ownership. Before
reload they therefore may differ. A pending opt-in reports enabled: true,
runtime_active: false, status: "pending", and
reason: "daemon_config_reload_pending"; failed query-service activation
reports status: "failed" and reason: "semantic_activation_failed". A config
parse/read failure reports enabled: false, runtime_active: false,
status: "failed", and reason: "daemon_config_reload_failed"; its
last_error comes from the reload while last_run_* remains historical. When
the daemon is disabled for ordinary status reporting, the semantic job reports
enabled: false, status: "disabled", and reason: "daemon_disabled".
daemon.jobs.history_refresh.rejection_diagnostics preserves aggregate
rejected_records and sources_completed_with_rejections from the latest
completed observation of each currently discovered history source. These
diagnostics survive later healthy source cycles and daemon restarts, clear when
that source is observed without rejections, and do not make the daemon fail.
Source-level failures remain terminal and are reported separately.
ctx daemon run --format json returns the daemon object directly. The legacy hidden
__ctx-daemon entry point follows the same run output for compatibility.
ctx doctor --format json returns schema_version, ok, findings, and the
same top-level daemon object used by status so callers can inspect daemon
lifecycle and job state without parsing human findings.
Stats
ctx stats --format json
ctx stats --detail --format json
Stats is local, offline, read-only, and excluded from its own counts. It does
not create usage.sqlite on a pristine root. Its top-level
schema_version is 3. The top-level object contains:
schema_version;local_only: true;read_only: true;enabled;state, one ofdisabled,empty,ready, orerror;retention_days;- optional
definitions[]; - optional
estimates; - optional content-free
error, withcodeandmessage.
definitions is absent when measurement is disabled or unavailable, an empty
array when no rows exist, and otherwise contains one object per retained
measurement definition. Each object contains:
definition_version;ctx_versions[];first_day_utc;last_day_utc;active_days;summary;- optional nonempty
by_operation[]; - optional nonempty
duration_buckets[].
summary contains exactly calls, successful_calls, failed_calls,
result_bearing_calls, empty_calls, not_applicable_calls, result_count,
delivered_output_bytes, delivered_context_bytes,
matched_normalized_session_bytes, complete_context_eligible_calls, and
unavailable_context_eligible_calls.
Each by_operation row contains exactly ctx_version, surface, operation,
calls, successful_calls, failed_calls, result_bearing_calls,
empty_calls, not_applicable_calls, result_count,
delivered_output_bytes, delivered_context_bytes,
matched_normalized_session_bytes, complete_context_eligible_calls, and
unavailable_context_eligible_calls. Each duration_buckets row contains
duration_bucket and calls.
The report reads the Core-owned usage.sqlite sidecar. Its current SQLite
schema version is 5, whose daily aggregate rows contain only day_utc,
definition_version, ctx_version, surface, operation, outcome,
value_class, duration_bucket, context_coverage, calls, result_count,
delivered_output_bytes, delivered_context_bytes, and
matched_normalized_session_bytes. SQLite schema versions 1 through 4 are
accepted only as migration inputs; new stores and current writes use version 5.
The SQLite schema version and JSON report schema version are independent.
CLI delivered_output_bytes counts the actual final stdout and stderr bytes
accepted for delivery, including the selected terminal wrapping and ANSI mode.
MCP output bytes count the serialized response transport. These are delivery
measurements, not context measurements. Definition-3 CLI Blame rows have no output-byte measurement;
their zero byte aggregate means unavailable and human detail renders N/A.
Blame rows report technical outcome and duration with not_applicable value
classification and zero results, without storing transcript or target content.
When complete search-context measurements are available, estimates contains
approximate_context_tokens with coefficient_version,
delivered_context_bytes, low, central, and high, plus
estimated_context_reduction with estimate_model_version,
coefficient_version, covered_calls, unavailable_calls,
comparison_baseline_bytes, observed_delivered_context_bytes,
estimated_avoided_context_bytes, low, central, and high. Estimates are
absent when the required complete measurements are unavailable.
Transport response bytes are never used as context bytes or as the basis for
context-reduction estimates. Migrated rows without byte samples never become
zero-byte measurements. An unavailable store returns the stable content-free
error object rather than fabricated aggregates. All report fields remain
aggregate-only and content-free.
Enable, disable, and reset remain explicit ctx status --usage controls.
Successful enable/disable controls report persisted_enabled,
effective_enabled, and environment_override; reset reports
store_state: "cleared"|"missing". A failed JSON control exits nonzero with a
parseable, content-free usage_control_failed or usage_reset_failed error.
Reset is logical deletion, not forensic secure erasure.
Sources
ctx sources --format json
Returns:
schema_version;scope, eitherdefaultorall;automatic_discovery, whether inferred provider history roots are enabled;hidden_missing_sources;sources[];issues[];issues_truncated.
Each built-in provider source includes:
provider;path;exists;source_format;status;import_support;native_import;importable;unsupported_reason;selection, withkind(automaticorconfigured), configuredroot, and configuredgroup.
For a configured row, selection.root is the exact case-sensitive configured
name and selection.group is its configured group or null. For an automatic
row, both values are null. History-source plugin rows retain their plugin
identity fields and do not have configured history root selection metadata.
status is available, empty, unknown, missing, or unsupported.
import_support is native, explicit, or unsupported. native_import
is derived from import_support == "native"; explicit sources therefore report
native_import: false. importable is true when a source is available and
either native or explicitly importable. Explicit sources require a targeted
provider import and are excluded from setup, --all, daemon refresh, and
search refresh. unknown means the bounded
provider-specific transcript probe hit its scan budget before proving the
source available or empty. unsupported_reason is a string for unsupported,
empty, or unknown rows and otherwise null.
issues[] reports provider discovery configurations that could not safely
produce a source row. It is additive to sources[], contains at most 64 rows,
and each row includes provider, nullable path, stable code, message,
and message_truncated. Messages are capped at 512 UTF-8 bytes. Stable issue
codes are no_disk_history, selector_unreconstructible,
insufficient_official_evidence, configured_root_conflict, and
configured_root_missing.
A configured_root_conflict row additionally contains nullable
conflict_kind (configured_configured or automatic_configured) and
configured_roots, a possibly empty array of the recoverable configured
name and path pairs involved. The existing top-level nullable path remains
the path reported by discovery. issues_truncated is true when additional
issue rows were omitted. Invalid history-source plugin manifests remain
non-importable rows in sources[]; they are not provider discovery issues.
A configured_root_missing row represents a durable configured root that
cannot safely produce a concrete source route while absent. Its
configured_root member is always present: when the persisted definition is
recoverable it is an object containing name, path, and nullable group;
otherwise it is null. The member is absent, rather than null, on every other
issue code. Recoverable missing-root rows are ordered before automatic issues
so all 64 valid configured roots remain represented at the issue limit; the
root remains configured until restored, replaced, or removed.
Named provider history root mutations have a separate schema-version-1 JSON result:
ctx sources add personal --provider claude --root /path/to/claude --source-group work --format json
ctx sources add personal --provider claude --root /path/to/moved-claude --replace --format json
ctx sources add openhands-cli --provider openhands --root /path/to/conversations --kind current-conversations --format json
ctx sources remove personal --format json
Both successful shapes contain exactly schema_version, operation,
changed, and root. operation is "add" or "remove"; root contains
name, provider, canonical absolute path, and nullable group. For an
OpenHands root only, root additionally contains kind with the exact value
"current-conversations" or "legacy-persistence"; the member is omitted,
not null, for every other provider. Repeating an add with the same name and
identical canonical settings is idempotent and returns changed: false.
Reusing the name with different settings fails unless the add includes
--replace. A same-provider replacement atomically writes the new canonical
path, kind, and complete group state while retaining operation: "add";
supplying --source-group sets the group and omitting it clears the group. It
does not expose an intermediate removed definition. A provider mismatch under
the stable name is rejected. With an absent name, --replace performs an
ordinary add. A successful remove returns changed: true and the removed root;
removing an absent name is an error, not a successful no-op. Root names and
non-null groups use 1 to 64 ASCII letters, digits, hyphens, or underscores and
remain case-sensitive.
Import
ctx import --format json
ctx import --format json --no-daemon
Requests Core generation publication and returns:
schema_version;outcome;failure_scope;failure_type;resume;resume_mode;totals;sources[].
Import result schema version 2 uses source statuses success,
completed_with_rejections, and failure. Run-level outcome uses success,
completed_with_rejections, completed_with_source_failures,
completed_with_rejections_and_source_failures, or failure.
failure_scope in an import result is none, record, source, or
record_and_source; failure_type is a coarse, non-sensitive classification.
Ctx-owned storage, index, worker, and operational-I/O failures abort before an
import-result JSON object is emitted and exit nonzero. Provider database
corruption, locks, unreadable source files, and malformed source data are
source failures and do not stop independent sources. Failed sources can include
up to five rejection details when every content record was rejected.
totals and each source row include change, whose value is changed or
no_op, plus file, byte, session, event, edge, skipped, and
rejected_records counts. change reports whether certified source state
changed; it is independent of insert counters. A deterministic source
replacement can therefore report change: "changed", zero newly imported
events, and skipped existing events while reconciling those rows in place.
Run-level totals also includes current_indexed_sessions. On a manual import
with a safely retained preceding Core generation, it includes an additive
index_delta object whose signed sessions and searchable_events members
report the net cardinality change in that exact Core publication. The object is
omitted on the first import and whenever the preceding generation is no longer
available; omission means unknown, not zero. A no-op reports zero deltas, while
a same-cardinality rewrite may report change: "changed" with zero deltas.
These are generation-wide index facts, not imported_* work counters, and are
not repeated on individual source rows.
Rejection details are exposed as rejections
(bounded to five entries); failed_sources remains the count of source-level
failures. sources_completed_with_rejections counts sources that committed
accepted content while rejecting other records. resume_mode is currently idempotent_rescan when
--resume is passed and normal_scan otherwise.
In automatic indexing mode, imports may start the persistent ctx-owned daemon.
In manual mode, an explicit import may start a finite Core worker. Both use the
same source-refresh endpoint and publication engine, including for custom JSONL
and history-source imports, and both wait for authoritative Core publication.
Set ctx import --no-daemon to prevent any start or restart; explicit routes
then require an already-running endpoint. Output format does not change this
authority. A process started for import reports start_mode: "auto" and
trigger_command: "import" through live status surfaces.
Import result schema version 2 does not embed daemon process state. Use
ctx status --format json to inspect daemon and supervisor health.
When opted-in semantic completion fails after Import has durably published Core, JSON mode exits nonzero, writes no import result to stdout, and writes one structured error object to stderr. For example:
{
"error": "daemon semantic job failed for Core generation core-gen-42: embedding executor unavailable",
"error_code": "semantic_completion_failed",
"reason": "semantic_completion_job_failed",
"generation_id": "core-gen-42",
"core_published": true,
"retryable": true,
"detail": "daemon semantic job failed for Core generation core-gen-42: embedding executor unavailable",
"failure_class": "resource_pressure"
}
error_code is the stable top-level classification. reason retains the
granular SemanticCompletionError code, generation_id identifies the
already-published Core generation, and core_published: true makes clear that
semantic failure did not roll Core back. detail and error contain the same
human-readable typed diagnostic. retryable comes from the typed completion
failure. A superseded-generation failure additionally includes
active_generation_id. A daemon-job failure includes failure_class only for
the four recognized values: retryable, permanent, corrupt_sidecar, and
resource_pressure. Missing, non-string, and unknown durable legacy values are
omitted rather than echoed. The job remains a terminal typed failure, and its
detail and retryable fields are preserved. Those variant-specific members
are otherwise omitted.
Clients must ignore unknown additive fields and should branch first on
error_code, then on reason when finer recovery behavior is needed. Human
output is unchanged.
Progress
ctx setup --progress json
ctx import --progress json
ctx import --format json --progress json
--progress json writes newline-delimited progress objects to stderr for
setup and import. It does not change command result stdout. This means
ctx setup --format json --progress json and ctx import --format json --progress json
write the command result object to stdout and zero or more progress objects to
stderr.
Each progress object includes:
type: "ctx_progress";operation, currentlysetuporimport;phase;message;completed_bytes;total_bytes;percent;elapsed_seconds;eta_seconds, nullable when no estimate is available or the operation is complete;completed_files, nullable;total_files, nullable;imported_events, nullable;done.
Daemon-owned source refresh events additionally include:
completed_sources,total_sources, andtotal_sources_known;source_completed_records, the nullable accepted Core-record count for the current source, andsource_completed_bytes, its nullable authoritative logical-byte progress;current_source, nullable bounded and control-safe presentation text for the active source (not an exact route identifier), andcurrent_source_progress, nullable typed substep detail;request_id,request_state,logical_request_id,logical_phase,physical_attempt_id,physical_attempt_state,progress_owner_request_id,progress_owner_attempt_state, andmaintenance_wakewhen the corresponding status authority supplies them;structured_outcomewhen a terminal refresh outcome is available.whole_run_stage, the current stage on the path to a verified, durable, active generation, andestimated_remaining_millis, a whole-run numeric estimate ornull. The estimate is intentionally unavailable unless the active cold setup attempt has complete exact accounting and has passed its credibility gate.eta_secondsretains its legacy meaning.
A failed structured_outcome uses closed code, class, retryability, route
disposition, and retry-advice values. source_unclaimed means an existing Core
source was not claimed by its expected successful provider route during this
refresh. It has class coverage and always blocks the culpable route. With no
other retryable route it is non-retryable and advises inspect_sources; when
independent unpublished routes remain eligible, only those routes are
retryable and the advice is
retry_retryable_routes_and_inspect_blocked.
Source record and byte counters reset or clear at source and finalization
boundaries. A scan with no authoritative total does not fabricate a total or
ETA; the common transfer fields use total_bytes: 0 and percent: 0.0 as
unknown-denominator sentinels. The completed_bytes / total_bytes pair is
used when the active transfer has a real denominator; source scans expose their
authoritative completed count through source_completed_bytes.
Progress events are operational status events, not durable result records.
Consumers should key on type and operation, ignore unknown fields, and read
the final command result from stdout when --format json is present.
Show
ctx show session <ctx-session-id> --format json
ctx show session <ctx-session-id> --format jsonl
ctx show event <ctx-event-id> --format json
Show resolves identities and complete policy-selected normalized records from
the active verified Core/Tantivy generation. Session presentation writes each
selected stored event as it is rendered; it does not retain the complete
session. The CLI has no public session cursor or page limit. Without
--max-events, ctx show session streams every event selected by the requested
mode in deterministic order, so large transcripts are complete rather than
silently capped.
Session JSON is one session_transcript object containing:
schema_version;target: "session";payload_type: "session_transcript";ctx_session_id,provider, andprovider_session_idwhen known;provider_keyandsource_idfor custom history-source sessions;modeandformat;sessionfor session output;events[].
--max-events <N> is an explicit terminal truncation control, not pagination.
When it stops selection, JSON adds
truncated: {"events": true, "max_events": N} and does not return a
continuation cursor. Without that option, session JSON has no pagination,
has_more, or next_cursor fields.
Session JSONL emits zero or more event records followed by exactly one terminal
completion record. An event record has
payload_type: "session_transcript_event", the session identity and mode, and
one rendered event. The terminal record has
payload_type: "session_transcript_completion", the same session identity and
mode, events_returned, complete, and, only after explicit --max-events
truncation, truncated: {"events": true, "max_events": N}. A complete empty
session therefore emits only a completion record with events_returned: 0 and
complete: true. JSONL completion metadata is terminal stream metadata; it is
not an MCP pagination envelope.
Event JSON remains one event_window object with target: "event", format,
event, events[], and copied_lineage.
Event-range JSON from ctx list events --format json is one
event_range_page with schema_version: 1, the pinned generation_id, the
normalized request domain/filters/direction, content, bounded
events[], the requested limit and page usage, terminal, truncated, next_cursor, and
explicit freshness/frontier metadata. The cursor is present exactly when the
page can continue. It is opaque and bound to the complete selection and pinned
generation.
Event-range JSONL emits zero or more event_range_event records followed by
exactly one event_range_completion. Each event record includes
schema_version, record_type, generation_id, a contiguous zero-based
ordinal, and one normalized event. The completion echoes the normalized
selection, content projection and requested limit, reports aggregate usage,
and records terminal/truncated/cursor and freshness/frontier state. A consumer
must reject EOF, a second completion, an event after completion, mixed
generations, or noncontiguous ordinals. Diagnostics and typed failures are on
stderr; stdout has only the selected JSON or JSONL.
Event-range event rows expose exact ctx event/source/session and parent/root
session identities, provider and provider-session identity when present,
source format, native identity, sequence and chronology, role/event/agent
fields, content-policy state, selected text/structured content, citations, and
normalized repository evidence already in Core. Event type is an open string;
unknown normalized values are preserved. See
event-queries.md for selection details and jq examples.
session includes the ctx-owned item_id, record_type, provider,
provider_session_id when known, and provider_key plus source_id for a
custom history source. For Codex, provider_session_id is the resume UUID.
event and events[] rows include ctx_event_id, record_type,
ctx_session_id, provider, custom-source provider_key and source_id,
provider_session_id, source_format,
sequence, event_type, role, occurred_at, and exact normalized text
or structured_content when policy permits. Each rendered event also includes
content.complete, content.policy_status, and an optional
content.policy_reason.
A selected full-content event can additionally include optional activity.
The revision-1 object can contain exact typed provider_call_id, invocation
and/or result channels, and ordered provider-declared facts. Invocation
arguments and structured results use explicit
present/absent/unavailable/omitted capture states and remain decoded JSON
when present; result text additionally supports normalized_body. CLI/Core
fields use snake_case.
An exact MCP invocation has protocol: "mcp" plus nonempty source server
and advertised tool strings. Machine output preserves admitted activity
exactly. Absence means only that the event has no retained provider activity;
it does not mean that the event was not MCP. ctx list events --content text
and --content none omit activity; full show-event and log-mode show-session
rows can include it. See
mcp-tool-call-attribution.md and
mcp-exchange-capture.md.
Human rendering escapes terminal controls and may bound a rendered event.
JSON, JSONL, and MCP structuredContent retain the exact admitted activity
value.
Show reads the active Core/Tantivy generation without reopening provider
history. It does not return provider source paths, existence checks, or source
cursors. With --out, a
session transcript is staged and atomically installed only after the complete
stream succeeds; a failed stream does not replace an existing destination.
ShowSessionOptions::default() has
no limit or cursor and uses the complete CLI stream. Supplying either option
uses the existing local MCP show_session page contract; its returned
pagination object is preserved in the SDK session result's additive fields.
Current Core activity is preserved as an additive event field; keys inside
captured JSON values are not camelized.
If the active generation changes while show or search is opening its
verified reader, JSON mode exits nonzero and writes one error object to stderr
with error: "generation_changed/active_generation_race",
error_code: "generation_changed",
failure_kind: "active_generation_race", and retryable: true. Clients may
retry the same command; this race is not returned as a successful result.
JSON search, show, and locate also fail before query or rendering when an
existing Core generation lacks valid publication authority. The one stderr
object has equal error and detail text plus a stable error_code and
boolean retryable. An uncertified empty generation reports
source_unavailable and is retryable; malformed or unknown publication
metadata reports publication_authority_invalid and is not retryable. These
states are distinct from a genuinely missing Core generation.
Locate
ctx locate session <ctx-session-id> --format json
ctx locate session --provider codex --provider-session <provider-session-id> --format json
ctx locate session --provider-session <provider-session-id> --provider-key <provider-key> --source-id <source-id> --format json
ctx locate event <ctx-event-id> --format json
Locate reads only the active verified Core/Tantivy generation. It does not reopen provider history or return a provider path.
Session JSON is one session_location object containing:
schema_version: 1,target: "session", andpayload_type: "session_location";ctx_session_id,provider, andprovider_session_idwhen known;provider_keyandsource_idfor custom history-source sessions;- nullable
parent_ctx_session_id,root_ctx_session_id, andstarted_at; sourcewithctx_source_id,source_format,schema_variant, andprovider_identity_version.
Event JSON is one event_location object containing:
schema_version: 1,target: "event", andpayload_type: "event_location";ctx_event_id,ctx_session_id,provider,provider_session_id, andprovider_event_idwhen known;provider_keyandsource_idfor custom history-source events;sequence,event_type,role, andoccurred_at;- the same bounded
sourceidentity object as session locate.
Transcript Artifacts
ctx show session <ctx-session-id> --mode full --format json --out transcript.json
With --out, writes the requested transcript artifact to that path and prints
nothing on success. Without --out, stdout is the requested transcript
artifact. JSON and JSONL artifact rows use the same ctx-owned ID fields as
show; JSONL uses the event-plus-terminal-completion stream described above.
Artifacts inherit the same complete-by-default behavior and explicit,
non-resumable --max-events truncation contract.
Search
ctx search <query>|--term <term>|--file <path> --format json
Returns:
schema_version: 2;payload_type: "search_results";query;filters;freshness;retrieval;generated_at;diversification;results[];result_window;truncation.
filters always includes the resolved content_scope, one of all,
transcript, calls, or outputs. Omission of the CLI option or MCP input
resolves to "content_scope": "all". The field reports query-time event-class
selection; it does not describe a different retained body, Core schema, or
index generation. content_scope and the exact event_type filter cannot
appear in the same successful request because those inputs conflict
unconditionally.
When supplied, repeatable CLI root selectors are echoed as source_root and
group selectors as source_groups, each an array of normalized names. MCP accepts the
equivalent request arrays source_roots and source_groups. Both selector families
resolve against the pinned Core generation. Every root and group value forms
one OR selection set before that set intersects with independent filters using
AND semantics. Names are case-sensitive, and unknown selectors are typed
request errors; they are never ignored or resolved from live config against an
older generation. Each selector is 1 to 64 ASCII letters, digits, hyphens, or
underscores, and each MCP array contains at most 64 entries. Selector
validation diagnostics remain generic and do not echo rejected contents.
result_window has exactly limit, returned, and more_available.
returned is at most limit. more_available is true only when the same
bounded search pass finds one additional fully shaped result: an event for
event-scoped search, or an additional session champion for the default
session-scoped search. A false value does not assert exhaustive backend
availability; readers must inspect truncation. Search does not expose a
cursor, run a second count scan, or claim an exact omitted-result total. Text
output ends with exactly
More results available. only when more_available is true.
truncation independently describes backend candidate-pool limits with
candidate_pool and candidate_pool_truncated. For lexical participation it
also includes lexical.work_complete, lexical.candidate_set_exhaustive, and,
only after bounded work exhaustion, lexical.exhaustion with the operation
counter, used, and limit. It does not expose segment identifiers,
document identifiers, query text, content, scores, or candidate identities.
candidate_set_exhaustive is false when a completed retained heap discarded
lower-relevance matches and whenever bounded work did not complete.
candidate_pool_truncated is true for that lexical non-exhaustiveness or when
a backend reaches its fixed candidate cap; it does not claim an exact omitted
count. Backends without an explicit completeness signal remain conservative in
diversification.status even when no known cap was reached.
Candidate-pool truncation does not by itself make more_available true; that
flag requires an additional shaped result, except that completed dense heap
truncation at the maximum retained horizon proves one additional event.
diversification has status, top_n, and conditionally
changed_final_top_n. status is applied, not_applicable, or
indeterminate; top_n is the requested limit. Dense --events, any explicit
--session, and limit zero are not_applicable and omit
changed_final_top_n. Default session search selects one champion per exact
session, orders families by their strongest champion, and emits one remaining
champion per family per relevance-stable round. Ordinary lexical session search
is applied only when
bounded lexical work completed and either the candidate set is exhaustive or
at least top_n distinct coalesced families were observed. Work exhaustion or
insufficient family coverage is indeterminate. Semantic and hybrid candidate
completeness is not yet explicit, so those backends apply the same coalesced
family policy but remain indeterminate. changed_final_top_n is present only
for applied and compares the full event-identity sequence before and after
family shaping.
query is the normalized display of the positional query plus repeatable
--term alternatives: surrounding whitespace is removed and nonempty
alternatives are joined with OR in argument order. Suggested scoped-search
commands preserve the positional/--term argument shape and shell-quote every
user-provided value. When search uses a non-default data root, each command also
preserves it as a shell-quoted ctx --data-root <path> prefix.
generated_at is the RFC 3339 UTC time at which the result envelope was
rendered.
Each result can include:
ctx_event_idfor event hits;ctx_session_idwhen known;provider_session_id;event_seq;title;snippet;rank, the one-based position in the final shaped result window;retrieval_score, the backend-provided diagnostic score; this score is not an ordering contract and need not be monotonic after query-coverage and family shaping;result_type, the concrete hit kind such asevent,session,session_result, orindexed_item;result_scope, eithersessionfor a session-level result oreventfor an event-level result;session_importancefor default session results, retained as a compatibility alias of the diagnosticretrieval_scorerather than an ordering contract;more_matches_in_sessionfor default session results;provider;provider_keyandsource_idfor custom history-source results;timestamp;cwd;citations[];suggested_next_commands[];visibility.
Ordinary search results do not carry copied_lineage and do not run one
reverse lookup per hit. Their event-local event_copy, when present, is the
selected occurrence's positive direct provider evidence only.
Search schema v2 removes the schema-v1 per-result copied_lineage; explicit
show-event/event-window reads remain the public direct-lineage surface.
copied_lineage is a schema-v2 object on the explicit show-event/event-window
envelope. It is a bounded, direct one-hop query-time view; its resolution does
not affect publication, ranking, grouping, recall, or semantic eligibility.
Its current required fields are listed below;
schema-v2 readers must ignore additional fields:
schema_version: 2;resolution, withstateequal toresolvedorunresolved, the direct targetctx_event_id, and a nullablectx_session_idwhen the target session is not known;selected_depth, zero when the selected event is the anchor and one when its one direct copied-from target is the anchor;observed_count, exact whentruncatedis false and otherwise a lower bound;- numeric
returned, the number of retained occurrence rows; occurrences[], each with fullctx_event_id,ctx_session_id, directcopied_from_ctx_event_idandcopied_from_ctx_session_id, parent and child-claimed-root session IDs,session_relationship, and direct-edgedepth(currently always one);relationship_counts, keyed by relationship kind and subject to the same exact-versus-lower-bound rule;truncated.
Missing copy targets are unresolved references. Reverse lookup can return direct child claims even when the selected target event is absent. A direct self-copy claim is rejected at Core admission, and this surface neither detects nor reports transitive cycles. Missing targets do not invalidate the immutable generation or hide admitted occurrences.
Show event retains at most 20 direct occurrences after at most 4,096 reverse
posting visits. Selected-event and its one direct copied-from target resolution
share at most 2,048 exact event-and-session identity posting visits, counting
live and deleted rows. There is no parent, root, copy-component, or transitive
lineage traversal. The query fails with a typed bound error if more work would
be required. The complete object is limited to 64 KiB. Preview retention alone
does not make an otherwise complete count truncated. The current writer does
not emit more_available,
compact-ID fields, or an exhaustive cursor. CLI JSON and MCP structured content
always use full UUIDs; compact aliases exist only in the separately rendered
human/MCP text projection.
citations[] can cite sessions, events, or files depending on
which indexed item produced the match.
Search JSON is local/private by default.
freshness describes the maintenance request and committed generation observed
by search. It never means that the query process became a foreground writer:
mode, one ofbackground,off, orwait;status, such ascompleted,skipped,no_sources,read_only,budget_exhausted, orfailed.read_onlymeans search queried an existing committed generation without an accepted maintenance wake;budget_exhaustedmeans daemon-owned bounded maintenance left backlog while search served the latest committed generation;reason, present for explanatory read-only or skipped states;budget_reasons, present whenstatusisbudget_exhausted; stable machine-readable reasons includecodex_session_limit,codex_discovery_file_limit,manifest_file_limit,single_file_bytes, andtotal_bytes;source_count;daemon_last_run_at_ms, present when search relies on a recent daemon refresh;totals, when a daemon receipt supplies the same bounded total fields asctx import --format json;error, present when a background maintenance request failed but committed results were still served.
Background mode health-checks and may recover the default-enabled persistent daemon, then returns the latest committed generation without waiting for semantic catch-up. Wait mode waits for the requested source frontier and lexical receipt or fails; it never falls back to a foreground importer. Off mode sends no maintenance wake.
retrieval describes the requested and effective search path:
requested_mode, one ofhybrid,semantic, orlexical;effective_mode, one oflexical,semantic, orhybrid;semantic_weight, the effective semantic contribution used for ranking. It is0.0when the effective mode is lexical, even if a semantic weight was requested. A zero-weight hybrid request performs no model, query-service, vector-open, or vector-scan work;semantic_status;semantic_fallback_code, nullable/omitted stable reason code for clients;semantic_fallback, nullable/omitted;semantic_fallback_retryable, nullable/omitted boolean retaining whether the typed semantic failure may succeed after external state changes;embedding_model, nullable/omitted;coverage;worker, using the same shape asstatus.semantic, nullable/omitted;semantic_diagnostics, nullable/omitted and present when semantic vector retrieval runs.
retrieval.semantic_status is one of:
skipped, lexical retrieval was used and no semantic lookup ran;unsupported, a hybrid request used lexical retrieval because its content scope or exact event type has no semantic projection;unavailable, the semantic sidecar is missing, empty, unreadable, or otherwise not usable for the request;partial, some but not all searchable items have embeddings;ready, sidecar coverage is complete for the current searchable item count and dirty work is drained.
retrieval.semantic_fallback_code, when present, is the stable machine-readable
reason why a hybrid request used lexical fallback.
retrieval.semantic_fallback, when present, is the human-readable explanation.
retrieval.semantic_fallback_retryable, when present, is copied from the same
typed semantic failure rather than inferred from its display text.
Semantic-only unavailability is a typed command error, not a successful
search_results object with effective_mode: "lexical".
retrieval.coverage includes embedded_items, embedded_chunks,
searchable_items, indexed_now, and dirty_items when known. Coverage counts
are numbers when present; null count fields are pruned from public SDK fixtures
and typed SDK shapes.
Index readiness coverage uses candidate_items for the pre-content-filter Core
population, filtered_items for intentional semantic content filtering, and
searchable_items/embedded_items for acknowledged active flat-F32 events.
Ready coverage therefore satisfies
candidate_items = searchable_items + filtered_items and
searchable_items = embedded_items.
The SDK agent-history-v2 contract keeps schema version 1 and normalizes the
resolved filter as search.filters.contentScope, with the same exact four
values. The filters object remains extensible, so SDK consumers must continue
to tolerate additive filter fields. The contract camel-cases the same
retrieval fields (requestedMode, effectiveMode, semanticWeight, and so
on). SDK contract
search results expose retrieval at the top level of search; TypeScript and
Python type the core retrieval/coverage fields, while Go, .NET, JVM, and Swift
preserve retrieval as camel-cased JSON values. Per-hit retrieval details are not
part of the SDK contract unless a future CLI JSON shape emits them. Local diagnostic path
fields such as vector_path/vectorPath can still appear as additive JSON from
the local CLI adapter, but they are intentionally not stable SDK fields.
retrieval.semantic_diagnostics can include query_embed_ms, vector_backend,
vector_scan_ms, chunks_scanned, vector_bytes_read, events_scored, and
semantic_candidates. These fields
are local performance diagnostics and can reveal corpus size/timing; treat them
as private like the rest of search JSON.
suggested_next_commands can include ctx show event, ctx show session, and
ctx search "<query>" --session <ctx-session-id> command strings when the
required ctx IDs are known.
For direct CLI searches, when ctx can identify the current session
unambiguously for Codex, DeepSeek Harness, Grok Build, Pi, Claude Code, Goose,
Hermes, Shelley, Qwen Code, or Mux, it applies the automatic
current-session-tree exclusion. Unsupported or ambiguous detection fails open
and applies no automatic exclusion. Passing
--include-current-session removes the automatic exclusion. Repeatable
--exclude-session accepts a ctx UUID or unambiguous prefix and excludes the
exact named session; it conflicts with --session. JSON search filters echo
those explicit selectors in the repeatable exclude_session field.
MCP Tool Results
ctx mcp serve exposes MCP tools over stdio for status, sources, search,
and showing sessions and events. Startup health-checks may recover the
default-enabled persistent daemon. Search can send a bounded, content-free
maintenance wake; the MCP process never becomes an importer or derived-state
writer and never writes provider history. Tool results include
structuredContent JSON carrying the same typed data as CLI JSON, with
contract-owned event keys in camelCase. MCP output may include absolute paths,
source metadata, snippets, transcript text, MCP arguments, and response
payloads, and the MCP host may log or forward it.
MCP search follows the same committed-generation and lexical/semantic/hybrid
retrieval contract as CLI search. Hybrid may report lexical fallback when
semantic is disabled or unavailable; semantic-only unavailability is a typed
error, and zero semantic weight performs no vector work. MCP search does not
itself import provider history and does not automatically exclude the caller's
session. Direct CLI current-session detection and
--include-current-session do not apply to MCP calls.
Its optional source_roots and source_groups arrays use the same generation-pinned
source-key filter as CLI search. If both arrays are absent, all indexed roots
are searched.
MCP show_event, show_session, and full-content query_events structured
event rows reuse the same snake_case activity value. Text fallback is
display-safe rather than the exact machine authority. query_events with
content: "text" or content: "none" omits activity. Discovery-eligible
retained protocol/server/tool/present arguments, result status/present text/structured
content, and facts enter the shared Core projection used by CLI and MCP search
matching, ranking, snippets, and semantic source text. Activity adds no
dedicated selector or SQL field. Paginated MCP callers filter each returned page
client-side and continue with the existing opaque cursor; show_session
requires mode: "log" for ordinary tool events.
The MCP sources tool returns schema_version, automatic_discovery,
sources, issues, issues_truncated, and read_only: true. Its built-in
provider rows use the same selection objects as CLI JSON, so configured
root values and non-null configured group values enumerate candidates for
MCP search source_roots and source_groups. Automatic rows have null root and
group; plugin rows do not participate in configured history root selection.
The bounded issues and issues_truncated fields retain the CLI contract.
Tool-level argument validation failures set isError: true, preserve the
diagnostic error, and add stable error_code: "invalid_request" in
structuredContent. JSON-RPC framing and envelope failures retain the
protocol-level parse-error or invalid-params responses.
Integrations
ctx integrations install mcp --format json
ctx integrations remove mcp --format json
ctx integrations status mcp --format json
ctx integrations install skill --format json
ctx integrations status skill --format json
ctx integrations remove skill --format json
ctx integrations install slash-command --format json
ctx integrations status slash-command --format json
ctx integrations remove slash-command --format json
ctx integrations install plugin --format json
ctx integrations status plugin --format json
ctx integrations remove plugin --format json
MCP integration JSON returns:
integration, currentlymcp;server.name,server.command, andserver.args;scope, eitherglobalorproject;results[].
Each install result includes:
agent;agent_display_name;scope;path, or null for unsupported targets;detected;supported;success;previous_status;status;already_installed;modified;error.
Each status result uses the same target fields and includes status and
error. Status values are current, missing, conflict, invalid_config,
and unsupported.
Each remove result uses the install target fields and includes success,
previous_status, status, already_absent, modified, and error.
Successful results have status: "missing"; modified distinguishes an
entry removed by this invocation from an idempotent already-absent result.
Skill integration JSON keeps the top-level skill: "ctx", scope, and
results[] fields. Install and status retain their existing target fields and
status values: current, stale, modified, and missing. Remove results
include agent, agent_display_name, scope, path, success,
previous_status, status, already_absent, removed, removed_current,
removed_legacy, and error. Successful removal reports status: "missing".
Slash-command integration JSON keeps the released top-level discriminator
integration: "slash-commands", plus command: "ctx", scope, and
results[]. Status results include target identity, nullable scope, path,
and legacy_path, plus success, status, error, and note. Remove adds
previous_status, already_absent, modified, current_removed,
legacy_removed, and metadata_removed. Informational skill_only and
manual_only targets have null paths and are non-mutating.
Plugin integration JSON uses integration: "plugin", scope, and
results[]. Each result includes agent, agent_display_name, scope,
capability, detected, supported, marketplace_status, previous_status,
status, action, installed_version, success, modified, instructions,
and error. Automatic status values are installed, legacy_installed, and
missing; the native manager owns release selection, so installed_version is
nullable and informational. capability: "manual_required" means the host must
be managed through its UI; ctx does not claim an installed or missing state.
Status invokes manager list operations only. Plugin removal does not remove the
retained marketplace, direct skills, MCP configuration, the ctx CLI, or
history.
The canonical CLI targets are mcp, skill, slash-command, and plugin.
The released plural spellings skills and slash-commands remain accepted as
hidden aliases. Alias use never changes JSON discriminators or field shapes.
Docs
ctx docs list --format json
ctx docs search <query> --format json
ctx docs show <topic> --format json
ctx docs list --format json returns:
schema_version;topics[].
Each topic includes id, title, audience, summary, tags, and
source_path.
ctx docs search <query> --format json returns:
schema_version;query;results[].
Each result uses the topic fields above and adds score.
ctx docs show <topic> --format json returns one topic object plus:
schema_version;body, containing the embedded markdown source.
Docs JSON is generated from embedded static docs and does not read provider history or ctx data-root state.
Upgrade
ctx upgrade --format json
ctx upgrade --dry-run --format json
ctx upgrade check --format json
ctx upgrade status --format json
ctx upgrade and ctx upgrade check return:
schema_version;command, eitherupgradeorupgrade_check;ok;status, such asavailable,up_to_date,dry_run,applied, orscheduled;message;current_version;latest_version;update_available;channel;platform;metadata_url;artifact_url;install_path;managed;applied;dry_run;warnings[].
ctx upgrade status --format json returns:
schema_version;command: "upgrade_status";auto_upgrade, includingmodeandenabled;state;install.
state is the last local upgrade-state object when present, or
status: "never_checked". install.managed is true only when the running
binary has a matching official installer sidecar. Unmanaged installs report
managed: false and a reason.
Automatic upgrade does not write JSON to foreground stdout. Auto indexing with
the full daemon profile uses the enabled persistent daemon as the sole automatic
check and apply driver. Manual indexing, source-refresh-only mode, ordinary
foreground commands, MCP, and finite workers perform no automatic upgrade work.
Daemon and explicit upgrades share one scheduler state and replacement journal
beside the managed executable. Windows self-upgrade can
report scheduled with applied: false while a helper waits for the running
ctx.exe to exit and then replaces the binary and sidecar.
Citation Fields
Citations can include:
item_id;target_type;ctx_event_id;ctx_session_id;label;time;provider;session_id;event_seq.
Doctor
ctx doctor --format json
Reads local storage and returns findings. The outer schema_version remains 1.
Its source_epoch contains the status report at schema version 3, without the
removed generic local_only property.
schema_version;ok;progress;findings.
Doctor checks Core/Tantivy generation health, read-only attribution and semantic sidecar health, source/daemon state, and compact local-usage health. Its JSON includes daemon status. It does not initialize embedding executors or write sidecar data. Semantic or hybrid search may ask the daemon query service to use the selected executor for the query; search does not download models or write sidecar data from the search path.
Provider Smoke
Provider smoke tests call normal ctx commands with temporary local storage and
static fixtures. Their output is ordinary command JSON covered by the command
schemas above; there is no separate provider artifact schema in the public CLI.
Compatibility Limits
Compatibility item_id, id, session_id, and event_id fields can remain
in some outputs. New integrations should prefer ctx-owned ctx_session_id and
ctx_event_id where present, and should treat provider-owned IDs as metadata
unless an explicit provider lookup flag is present.