{
  "schemaVersion": 1,
  "updatedAt": "2026-10-27",
  "dimensions": ["harnessMarker", "agentOnlyMarker", "sessionLabel", "nonInteractiveHints"],
  "contracts": [
    {
      "harness": "codex",
      "variant": "0.164.0-source",
      "default": true,
      "verifiedAt": "2026-10-27",
      "evidence": [
        "environment-codex-shell-environment",
        "environment-codex-unified-exec-env",
        "environment-codex-user-shell",
        "environment-codex-sdk-resume",
        "environment-codex-exec-server-metadata"
      ],
      "references": [
        "https://github.com/openai/codex/blob/6aaac694803004a63c6f393d153c96d22521a6b9/codex-rs/protocol/src/shell_environment.rs#L105-L172",
        "https://github.com/openai/codex/blob/6aaac694803004a63c6f393d153c96d22521a6b9/codex-rs/core/src/unified_exec/process_manager.rs#L94-L134",
        "https://github.com/openai/codex/blob/6aaac694803004a63c6f393d153c96d22521a6b9/codex-rs/core/src/tasks/user_shell.rs#L169-L181",
        "https://github.com/openai/codex/blob/6aaac694803004a63c6f393d153c96d22521a6b9/sdk/typescript/README.md#L98-L105",
        "https://github.com/openai/codex/blob/6aaac694803004a63c6f393d153c96d22521a6b9/codex-rs/exec-server/README.md#L17-L35"
      ],
      "method": "source-inspection",
      "sourceRevision": "6aaac694803004a63c6f393d153c96d22521a6b9",
      "clientVersion": "0.164.0",
      "observedVersion": "0.164.0",
      "surface": "cli-and-app-server",
      "conditions": [
        "Remote executors only carry the thread ID when they honour command metadata; older executors may ignore it or restore a stale value from a shell snapshot."
      ],
      "unknowns": [
        "Whether the desktop app's own terminal, hosted Codex cloud tasks or custom executors set the same variables.",
        "Whether CODEX_CI is meant as a stable public interface; it is only an implementation default."
      ],
      "details": {},
      "support": {
        "harnessMarker": "native",
        "agentOnlyMarker": "partial",
        "sessionLabel": "native",
        "nonInteractiveHints": "native"
      },
      "answers": {
        "marker": {
          "status": "supported",
          "answer": "`CODEX_THREAD_ID` holds the current thread's ID (a UUID). Commands the model runs also get `CODEX_CI=1`. Check either to know you're inside Codex.",
          "reference": "https://github.com/openai/codex/blob/6aaac694803004a63c6f393d153c96d22521a6b9/codex-rs/protocol/src/shell_environment.rs#L105-L172",
          "evidence": ["environment-codex-shell-environment", "environment-codex-unified-exec-env"],
          "notes": [
            "`CODEX_CI` doesn't mean a hosted CI job; Codex sets it on its own command runs."
          ]
        },
        "reaches": {
          "status": "supported",
          "answer": "Every command the model runs through its shell tool, including commands sent to a remote executor, and every command you run yourself with `!`.",
          "reference": "https://github.com/openai/codex/blob/6aaac694803004a63c6f393d153c96d22521a6b9/codex-rs/core/src/tasks/user_shell.rs#L169-L181",
          "evidence": [
            "environment-codex-user-shell",
            "environment-codex-shell-environment",
            "environment-codex-exec-server-metadata"
          ],
          "notes": ["Child processes inherit it, so anything those commands start sees it too."]
        },
        "person": {
          "status": "partial",
          "answer": "Use `CODEX_CI`. Your own `!` commands get `CODEX_THREAD_ID` but not `CODEX_CI`, which is only set on commands the model runs.",
          "reference": "https://github.com/openai/codex/blob/6aaac694803004a63c6f393d153c96d22521a6b9/codex-rs/core/src/unified_exec/process_manager.rs#L94-L134",
          "evidence": ["environment-codex-unified-exec-env", "environment-codex-user-shell"]
        },
        "session": {
          "status": "supported",
          "answer": "`CODEX_THREAD_ID` is the thread ID; the SDK resumes it with `codex.resumeThread(process.env.CODEX_THREAD_ID)`. Each command also gets `CODEX_TOOL_CALL_ID`, the call that started it.",
          "reference": "https://github.com/openai/codex/blob/6aaac694803004a63c6f393d153c96d22521a6b9/sdk/typescript/README.md#L98-L105",
          "evidence": ["environment-codex-sdk-resume", "environment-codex-exec-server-metadata"],
          "notes": [
            "Codex says these labels are for correlation, not authorization: shell startup files and child processes can change or remove them."
          ]
        },
        "nonInteractive": {
          "status": "supported",
          "answer": "Commands the model runs get `CODEX_CI=1`, `TERM=dumb`, `NO_COLOR=1`, `COLORTERM` empty, UTF-8 locale and `cat` as `PAGER`, `GIT_PAGER` and `GH_PAGER`.",
          "reference": "https://github.com/openai/codex/blob/6aaac694803004a63c6f393d153c96d22521a6b9/codex-rs/core/src/unified_exec/process_manager.rs#L94-L134",
          "evidence": ["environment-codex-unified-exec-env"]
        },
        "control": {
          "status": "unsupported",
          "answer": "No. `shell_environment_policy` (`inherit`, `exclude`, `include_only`, `set`) is applied first and `CODEX_THREAD_ID` is added afterwards, so no setting removes or replaces it. The `CODEX_CI` defaults are also applied after your policy.",
          "reference": "https://github.com/openai/codex/blob/6aaac694803004a63c6f393d153c96d22521a6b9/codex-rs/protocol/src/shell_environment.rs#L105-L172",
          "evidence": ["environment-codex-shell-environment", "environment-codex-unified-exec-env"]
        }
      }
    },
    {
      "harness": "claude-code",
      "variant": "2.1.310",
      "default": true,
      "verifiedAt": "2026-10-27",
      "evidence": [
        "environment-claude-env-vars",
        "environment-claude-2.1.310-tool-env",
        "environment-claude-2.1.310-binary"
      ],
      "references": ["https://code.claude.com/docs/en/env-vars#variables"],
      "method": "documentation",
      "surface": "cli",
      "observedVersion": "2.1.310",
      "conditions": [
        "Publisher documentation plus the installed 2.1.310 binary and its Bash tool; the AI_AGENT, CLAUDE_CODE_SESSION_ATTENDED, CLAUDE_PID and CLAUDE_EFFORT values are not in the documentation and may change."
      ],
      "unknowns": [
        "What CLAUDE_CODE_SESSION_ATTENDED reports for each non-interactive entry point (claude -p, SDK, GitHub Action)."
      ],
      "details": {},
      "support": {
        "harnessMarker": "native",
        "agentOnlyMarker": "native",
        "sessionLabel": "native",
        "nonInteractiveHints": "partial"
      },
      "answers": {
        "marker": {
          "status": "supported",
          "answer": "`CLAUDECODE=1`. Since v2.1.172 commands Claude Code itself starts also get `CLAUDE_CODE_CHILD_SESSION=1`. It also sets `AI_AGENT`: `claude-code_2-1-310_agent` in commands its tools run, and `claude-code_2-1-310_harness` everywhere else it starts (unless another agent already set `AI_AGENT`).",
          "reference": "https://code.claude.com/docs/en/env-vars#variables",
          "evidence": [
            "environment-claude-env-vars",
            "environment-claude-2.1.310-tool-env",
            "environment-claude-2.1.310-binary"
          ],
          "notes": [
            "`AI_AGENT` isn't in the environment reference, and the value carries the version, so match the `claude-code_` prefix."
          ]
        },
        "reaches": {
          "status": "supported",
          "answer": "`CLAUDECODE`: Bash and PowerShell tool commands, tmux sessions, hooks, status line commands and stdio MCP servers. `CLAUDE_CODE_CHILD_SESSION`: Bash, PowerShell and Monitor tool commands, hooks and status line commands, but not MCP servers.",
          "reference": "https://code.claude.com/docs/en/env-vars#variables",
          "evidence": ["environment-claude-env-vars"]
        },
        "person": {
          "status": "supported",
          "answer": "Use `CLAUDE_CODE_CHILD_SESSION`. The IDE extensions also set `CLAUDECODE` in their integrated terminals, so a person typing there looks like Claude Code; `CLAUDE_CODE_CHILD_SESSION` is only set when Claude Code launches the process.",
          "reference": "https://code.claude.com/docs/en/env-vars#variables",
          "evidence": ["environment-claude-env-vars"]
        },
        "session": {
          "status": "supported",
          "answer": "`CLAUDE_CODE_SESSION_ID` in tool commands, hooks and stdio MCP servers; it matches the hooks' `session_id` and changes on `/clear` (an MCP server keeps the ID it started with). Cloud sessions add `CLAUDE_CODE_REMOTE_SESSION_ID`, and Remote Control adds `CLAUDE_CODE_BRIDGE_SESSION_ID` (v2.1.199+).",
          "reference": "https://code.claude.com/docs/en/env-vars#variables",
          "evidence": ["environment-claude-env-vars"]
        },
        "nonInteractive": {
          "status": "partial",
          "answer": "Undocumented, but commands get `CLAUDE_CODE_SESSION_ATTENDED`: `1` in an interactive session, `0` in background and daemon sessions. Bash tool commands also get `GIT_EDITOR=true`, so git never opens an editor.",
          "reference": "https://code.claude.com/docs/en/env-vars#variables",
          "evidence": [
            "environment-claude-env-vars",
            "environment-claude-2.1.310-tool-env",
            "environment-claude-2.1.310-binary"
          ],
          "notes": [
            "Commands also get `CLAUDE_PID` (the Claude Code process) and `CLAUDE_EFFORT` (the current effort level)."
          ]
        },
        "control": {
          "status": "unknown",
          "answer": "Not documented. The reference describes a scrub that strips credentials from subprocesses, not a way to remove these markers.",
          "reference": "https://code.claude.com/docs/en/env-vars#variables",
          "evidence": ["environment-claude-env-vars"]
        }
      }
    },
    {
      "harness": "cursor-agent",
      "variant": "2026.10.12",
      "default": true,
      "verifiedAt": "2026-10-27",
      "evidence": ["environment-cursor-terminal", "environment-cursor-agent-2026.10.12-shell-env"],
      "references": [
        "https://cursor.com/docs/agent/tools/terminal#disable-heavy-prompts-for-cursor-sessions"
      ],
      "method": "documentation",
      "surface": "cli",
      "observedVersion": "2026.10.12-d47ca18",
      "conditions": [
        "The documentation names the variable but not its value or which processes get it; those come from the installed 2026.10.12 build and may change."
      ],
      "unknowns": [
        "Whether hooks, MCP servers or Cursor's cloud agents get CURSOR_AGENT.",
        "Whether a person's own terminal in the Cursor editor also gets it."
      ],
      "details": {},
      "support": {
        "harnessMarker": "native",
        "agentOnlyMarker": "unknown",
        "sessionLabel": "partial",
        "nonInteractiveHints": "unknown"
      },
      "answers": {
        "marker": {
          "status": "supported",
          "answer": "`CURSOR_AGENT`. Cursor suggests checking it in `~/.zshrc` or `~/.bashrc` to skip heavy prompts and themes; the CLI sets it to `1`.",
          "reference": "https://cursor.com/docs/agent/tools/terminal#disable-heavy-prompts-for-cursor-sessions",
          "evidence": [
            "environment-cursor-terminal",
            "environment-cursor-agent-2026.10.12-shell-env"
          ]
        },
        "reaches": {
          "status": "partial",
          "answer": "Commands the agent runs in its terminal. Hooks and MCP servers aren't documented.",
          "reference": "https://cursor.com/docs/agent/tools/terminal#disable-heavy-prompts-for-cursor-sessions",
          "evidence": [
            "environment-cursor-terminal",
            "environment-cursor-agent-2026.10.12-shell-env"
          ]
        },
        "person": {
          "status": "unknown",
          "answer": "Not documented. Cursor doesn't say whether a person's own terminal in the editor also gets `CURSOR_AGENT`.",
          "reference": "https://cursor.com/docs/agent/tools/terminal#disable-heavy-prompts-for-cursor-sessions",
          "evidence": ["environment-cursor-terminal"]
        },
        "session": {
          "status": "partial",
          "answer": "Undocumented, but agent commands in 2026.10.12 get `CURSOR_CONVERSATION_ID`, `CURSOR_REQUEST_ID` and `AGENT_TRANSCRIPTS` (the project's transcript folder).",
          "reference": "https://cursor.com/docs/agent/tools/terminal#disable-heavy-prompts-for-cursor-sessions",
          "evidence": [
            "environment-cursor-terminal",
            "environment-cursor-agent-2026.10.12-shell-env"
          ]
        },
        "nonInteractive": {
          "status": "unknown",
          "answer": "Not documented. Cursor doesn't say whether agent commands get a dumb terminal, pager overrides or a CI flag.",
          "reference": "https://cursor.com/docs/agent/tools/terminal#disable-heavy-prompts-for-cursor-sessions",
          "evidence": ["environment-cursor-terminal"]
        },
        "control": {
          "status": "unknown",
          "answer": "Not documented. Cursor doesn't say whether `CURSOR_AGENT` can be removed or changed.",
          "reference": "https://cursor.com/docs/agent/tools/terminal#disable-heavy-prompts-for-cursor-sessions",
          "evidence": ["environment-cursor-terminal"]
        }
      }
    },
    {
      "harness": "opencode",
      "variant": "1.x",
      "default": false,
      "verifiedAt": "2026-10-27",
      "evidence": ["environment-opencode-1x-shell-tool"],
      "references": [
        "https://github.com/anomalyco/opencode/blob/148842ef7cb1bc34192b3fff57d0f9721ab9c801/packages/opencode/src/tool/shell.ts#L416-L425"
      ],
      "method": "source-inspection",
      "sourceRevision": "148842ef7cb1bc34192b3fff57d0f9721ab9c801",
      "clientVersion": "1.19.4",
      "surface": "cli",
      "conditions": [
        "The 1.x shell tool merges process.env with output from shell.env plugins; the 2.x markers were not traced in 1.x."
      ],
      "unknowns": ["Whether any 1.x entry point sets an automatic agent marker."],
      "details": {},
      "support": {
        "harnessMarker": "unknown",
        "agentOnlyMarker": "unknown",
        "sessionLabel": "unknown",
        "nonInteractiveHints": "unknown"
      }
    },
    {
      "harness": "opencode",
      "variant": "2.x",
      "default": true,
      "verifiedAt": "2026-10-27",
      "evidence": [
        "environment-opencode-shell-tool",
        "environment-opencode-shell-create",
        "environment-opencode-pty",
        "environment-opencode-plugin-registration"
      ],
      "references": [
        "https://github.com/anomalyco/opencode/blob/68bc26b7da1990ce10ffecfbc1e09eb13aa5d824/packages/core/src/tool/plugin/shell.ts#L191-L218",
        "https://github.com/anomalyco/opencode/blob/68bc26b7da1990ce10ffecfbc1e09eb13aa5d824/packages/core/src/shell.ts#L264-L315",
        "https://github.com/anomalyco/opencode/blob/68bc26b7da1990ce10ffecfbc1e09eb13aa5d824/packages/core/src/pty.ts#L166-L184",
        "https://github.com/anomalyco/opencode/blob/68bc26b7da1990ce10ffecfbc1e09eb13aa5d824/packages/core/src/plugin/internal.ts#L247"
      ],
      "method": "source-inspection",
      "sourceRevision": "68bc26b7da1990ce10ffecfbc1e09eb13aa5d824",
      "clientVersion": "2.1.4",
      "observedVersion": "2.1.4",
      "surface": "cli",
      "conditions": [
        "Read from source, not a published contract; the names may change between releases."
      ],
      "unknowns": [
        "Whether hooks and MCP servers get these variables.",
        "Whether `!` commands typed by a person go through the same shell tool."
      ],
      "details": {},
      "support": {
        "harnessMarker": "native",
        "agentOnlyMarker": "partial",
        "sessionLabel": "native",
        "nonInteractiveHints": "partial"
      },
      "answers": {
        "marker": {
          "status": "supported",
          "answer": "`OPENCODE=1` and `AGENT=1`, plus `AI_AGENT=opencode` unless `AI_AGENT` is already set.",
          "reference": "https://github.com/anomalyco/opencode/blob/68bc26b7da1990ce10ffecfbc1e09eb13aa5d824/packages/core/src/tool/plugin/shell.ts#L191-L218",
          "evidence": [
            "environment-opencode-shell-tool",
            "environment-opencode-plugin-registration"
          ],
          "notes": [
            "Because an existing `AI_AGENT` is kept, OpenCode running inside another agent passes the outer agent's name through."
          ]
        },
        "reaches": {
          "status": "partial",
          "answer": "Commands the agent runs with its shell tool. Terminals OpenCode opens get only `OPENCODE_TERMINAL=1`, which agent commands also carry.",
          "reference": "https://github.com/anomalyco/opencode/blob/68bc26b7da1990ce10ffecfbc1e09eb13aa5d824/packages/core/src/tool/plugin/shell.ts#L191-L218",
          "evidence": [
            "environment-opencode-shell-tool",
            "environment-opencode-pty",
            "environment-opencode-shell-create"
          ]
        },
        "person": {
          "status": "partial",
          "answer": "Use `OPENCODE` or `AGENT`: OpenCode's terminals don't get them. Don't use `OPENCODE_TERMINAL`, which both get.",
          "reference": "https://github.com/anomalyco/opencode/blob/68bc26b7da1990ce10ffecfbc1e09eb13aa5d824/packages/core/src/pty.ts#L166-L184",
          "evidence": ["environment-opencode-pty", "environment-opencode-shell-tool"]
        },
        "session": {
          "status": "supported",
          "answer": "`OPENCODE_SESSION_ID` in agent shell commands.",
          "reference": "https://github.com/anomalyco/opencode/blob/68bc26b7da1990ce10ffecfbc1e09eb13aa5d824/packages/core/src/tool/plugin/shell.ts#L191-L218",
          "evidence": ["environment-opencode-shell-tool"]
        },
        "nonInteractive": {
          "status": "partial",
          "answer": "Agent commands run with standard input closed, so a prompt reads end-of-file. `TERM` is `xterm-256color`, not a dumb terminal.",
          "reference": "https://github.com/anomalyco/opencode/blob/68bc26b7da1990ce10ffecfbc1e09eb13aa5d824/packages/core/src/shell.ts#L264-L315",
          "evidence": ["environment-opencode-shell-create"]
        },
        "control": {
          "status": "partial",
          "answer": "A plugin's `shell` `create.before` hook can edit the environment, but the shell tool sets `OPENCODE`, `AGENT` and `OPENCODE_SESSION_ID` after the hooks run, so only `AI_AGENT` can be changed that way.",
          "reference": "https://github.com/anomalyco/opencode/blob/68bc26b7da1990ce10ffecfbc1e09eb13aa5d824/packages/core/src/shell.ts#L264-L315",
          "evidence": ["environment-opencode-shell-create", "environment-opencode-shell-tool"]
        }
      }
    },
    {
      "harness": "pi",
      "variant": "1.4.0",
      "default": true,
      "verifiedAt": "2026-10-27",
      "evidence": [
        "environment-pi-env-docs",
        "environment-pi-session-env-docs",
        "environment-pi-cli-setup",
        "environment-pi-changelog-ai-agent",
        "environment-pi-changelog-pi-coding-agent"
      ],
      "references": [
        "https://github.com/earendil-works/pi/blob/4a080252f06caf63a525d7873c5d94d3c23887e0/packages/coding-agent/docs/environment-variables.md#L11-L18",
        "https://github.com/earendil-works/pi/blob/4a080252f06caf63a525d7873c5d94d3c23887e0/packages/coding-agent/docs/environment-variables.md#L20-L68",
        "https://github.com/earendil-works/pi/blob/4a080252f06caf63a525d7873c5d94d3c23887e0/packages/coding-agent/src/cli/setup.ts#L4-L7",
        "https://github.com/earendil-works/pi/blob/4a080252f06caf63a525d7873c5d94d3c23887e0/packages/coding-agent/CHANGELOG.md#L1063",
        "https://github.com/earendil-works/pi/blob/4a080252f06caf63a525d7873c5d94d3c23887e0/packages/coding-agent/CHANGELOG.md#L2806"
      ],
      "method": "source-inspection",
      "sourceRevision": "4a080252f06caf63a525d7873c5d94d3c23887e0",
      "clientVersion": "1.4.0",
      "observedVersion": "1.4.0",
      "surface": "cli-and-rpc",
      "conditions": [
        "Applies to the CLI and RPC entry points; Pi embedded through the SDK sets neither marker."
      ],
      "unknowns": ["No runtime probe of a running Pi session."],
      "details": {},
      "support": {
        "harnessMarker": "native",
        "agentOnlyMarker": "partial",
        "sessionLabel": "native",
        "nonInteractiveHints": "unknown"
      },
      "answers": {
        "marker": {
          "status": "supported",
          "answer": "`AI_AGENT=pi` (since 0.84.0) and `PI_CODING_AGENT=true` (since 0.67.1).",
          "reference": "https://github.com/earendil-works/pi/blob/4a080252f06caf63a525d7873c5d94d3c23887e0/packages/coding-agent/docs/environment-variables.md#L11-L18",
          "evidence": [
            "environment-pi-env-docs",
            "environment-pi-cli-setup",
            "environment-pi-changelog-ai-agent",
            "environment-pi-changelog-pi-coding-agent"
          ]
        },
        "reaches": {
          "status": "supported",
          "answer": "Everything Pi starts. Pi sets both on its own process at startup, so every child inherits them. Not set when Pi is embedded through the SDK.",
          "reference": "https://github.com/earendil-works/pi/blob/4a080252f06caf63a525d7873c5d94d3c23887e0/packages/coding-agent/docs/environment-variables.md#L11-L18",
          "evidence": ["environment-pi-env-docs", "environment-pi-cli-setup"]
        },
        "person": {
          "status": "partial",
          "answer": "Use `PI_SESSION_ID`. The two markers are inherited by everything, including your own `!` and `!!` commands, but the session variables are only given to the agent's `bash` and `powershell` tools.",
          "reference": "https://github.com/earendil-works/pi/blob/4a080252f06caf63a525d7873c5d94d3c23887e0/packages/coding-agent/docs/environment-variables.md#L20-L68",
          "evidence": ["environment-pi-session-env-docs", "environment-pi-env-docs"]
        },
        "session": {
          "status": "supported",
          "answer": "`PI_SESSION_ID`, `PI_SESSION_FILE` (the session's JSONL file, unset for ephemeral sessions), `PI_PROVIDER`, `PI_MODEL` and `PI_REASONING_LEVEL` in the agent's `bash` and `powershell` commands.",
          "reference": "https://github.com/earendil-works/pi/blob/4a080252f06caf63a525d7873c5d94d3c23887e0/packages/coding-agent/docs/environment-variables.md#L20-L68",
          "evidence": ["environment-pi-session-env-docs"]
        },
        "nonInteractive": {
          "status": "unknown",
          "answer": "Not documented. Pi's environment reference lists no terminal, pager or CI settings for agent commands.",
          "reference": "https://github.com/earendil-works/pi/blob/4a080252f06caf63a525d7873c5d94d3c23887e0/packages/coding-agent/docs/environment-variables.md#L20-L68",
          "evidence": ["environment-pi-session-env-docs"]
        },
        "control": {
          "status": "supported",
          "answer": "A custom shell tool's `spawnHook` can rewrite the environment, and `exposeSessionEnvironment: false` drops the session variables (and strips inherited ones). The two process markers can't be turned off.",
          "reference": "https://github.com/earendil-works/pi/blob/4a080252f06caf63a525d7873c5d94d3c23887e0/packages/coding-agent/docs/environment-variables.md#L20-L68",
          "evidence": ["environment-pi-session-env-docs"]
        }
      }
    }
  ]
}
