Skip to content
Back to skills

Claude Pty Agents

ASecurity

Launch and safely retire one bounded Claude Code stage per owned session, with a Sonnet 5 parent by default, role-routed Haiku 4.5/Sonnet 5/Opus 5.5/Fable 5.1 subagents, and GPT-5.6 native Codex fallback. Use when Claude is requested or a bounded repository stage benefits from context isolation. Do not use for routine known-file work, user-launched standalone Claude, or environments without an interactive PTY.

  • 5 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added September 24, 2026
ai-agentsjavascriptrustgojavabashtestinggitsecurity

Works with

  • claude code
  • terminal
  • mcp

Security analysis

A100/100

Pro scans all 20 files and shows the line behind each finding

Scanned September 26, 2026

npx -y skills add coredo-eu/codex-claude-orchestrator --skill claude-pty-agents --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Claude Pty Agents?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Claude Pty Agents
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/coredo-eu-claude-pty-agents/badge)](https://www.skillsdirectory.com/skills/coredo-eu-claude-pty-agents)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: claude-pty-agents
description: Launch and safely retire one bounded Claude Code stage per owned session, with a Sonnet 5 parent by default, role-routed Haiku 4.5/Sonnet 5/Opus 5.5/Fable 5.1 subagents, and GPT-5.6 native Codex fallback. Use when Claude is requested or a bounded repository stage benefits from context isolation. Do not use for routine known-file work, user-launched standalone Claude, or environments without an interactive PTY.
---

# Claude PTY agents

Use persistent Claude Code processes owned by this Codex thread. Keep Codex as the owner of intent, material
architecture or product tradeoffs, authority, conflicts, independent
verification, and the final verdict. Treat this skill as transport and custody
policy, never as additional authority.

## Contract and boundaries

Give an edit-capable worker one compact contract:

- `Outcome`: state that must become true.
- `Done when`: observable acceptance criteria.
- `Boundaries`: exact root, side effects, prohibited actions, and ownership.
- `Authoritative context`: applicable source-of-truth material and unknowns.
- `Non-goals`: adjacent work not to absorb.
- `Known evidence`: concise observed facts and material uncertainty.
- `Required handoff`: material evidence, risk, uncertainty, missing authority,
  deliberate non-actions, and custody needed for Codex to decide.

The worker is permanently local-only. It cannot commit, push, publish, deploy,
control services, send external messages, administer the host, operate on
credentials, modify Claude/Codex configuration, or perform destructive
remediation. These restrictions bind the worker, not the owning Codex session.
After custody returns, Codex may perform shared or external actions already
authorized by the active goal and its unambiguous scope. Codex asks the user only
for a material scope expansion or when the target or intended end state cannot
be determined safely. Maintain one edit owner per canonical worktree.

The generated settings, deny rules, prompt, and hook are cooperative controls,
not an OS sandbox. Bash or a malicious repository instruction can bypass path
rules. Use Codex sandbox/approval policy, source review, and least privilege as
the actual containment boundary.

## Start or resume

Resolve this skill's directory, then check prerequisites and status:

```text
<skill-dir>/scripts/toggle-agents.zsh status
command -v claude jq zsh git
```

Read status literally: `busy` is live active plus live reserved capacity;
`active` and `reserved` are durable open records; `orphaned` is a proven-dead
active writer; `stale_reserved` is a proven-dead reservation that ordinary
admission may safely tombstone; and `blocked_roots` includes every open record.

The file `$HOME/.codex/claude-pty-agents.disabled` is the sole ON/OFF state.
Check it before every launch, resume, assignment, and PTY poll. Do not remove it
unless the user explicitly asks to enable workers.

Launch only a narrow absolute project root, with an interactive PTY:

```javascript
const worker = await tools.exec_command({
  cmd: "exec <skill-dir>/scripts/launch-worker.zsh /absolute/project/root",
  workdir: "/absolute/project/root",
  yield_time_ms: 1000,
  max_output_tokens: 8000,
  tty: true,
});
```

A normal launch atomically reserves the canonical root and one shared busy slot
before it emits `CODEX_PTY_WORKER_READY`; assignment upgrades that durable
record from `access:none` to `access:write`. A root conflict, active orphan, or
full two-worker capacity is therefore rejected before Claude is started. Never
use `--idle` to bypass this admission path. That explicit flag exists only for
operator diagnostics, compatibility testing, or deliberate prewarming; it
creates an unreserved process and is not part of this skill's routine workflow.

The launcher requires `CODEX_THREAD_ID` and defaults the parent to
`claude-sonnet-5` at `high` effort. Override only the parent with non-secret
process variables. An Opus override must record a route class and reason:

```text
CODEX_CLAUDE_PARENT_MODEL=claude-opus-5-5
CODEX_CLAUDE_PARENT_EFFORT=high
CODEX_CLAUDE_PARENT_ROUTE_CLASS=judgment
CODEX_CLAUDE_PARENT_ROUTE_REASON=independent_review
```

The launcher passes a private session-scoped `--agents` roster. Explorer,
log-analyzer, test-triager, and scout use `claude-haiku-4-5-20251001`; scout is
for bounded read-only local operational reconnaissance. Implementer and debugger
use `claude-sonnet-5`; reviewer and security-reviewer use
`claude-opus-5-5`. Long-horizon uses `claude-fable-5-1`. The ordinary Sonnet parent
starts at `high` effort. Haiku roles use the model's fixed behavior because Haiku 4.5 has
no configurable effort; implementer uses `high`, reviewer uses `medium`, and
debugger, security-reviewer, and long-horizon use `xhigh`. When Fable is outside
the account's allowed model set, Claude Code inherits the current parent; for
other availability failures, the parent retains
the outcome. Built-in agents are denied, and a pre-spawn hook rejects unlisted
roles or mismatched model overrides. Read-only roles receive Bash in `plan`
mode when the parent permission mode permits that override. The default parent
may use the read-only Haiku roles proactively when independent evidence,
context isolation, or safe parallelism has expected net value after transfer
and integration costs. Sonnet and Opus roles are selected only when their
specific descriptions justify that cost. Long-horizon is explicit-route-only
and requires explicit sole edit custody. Roles are optional routes, not a
mandatory pipeline, and no fixed task-size threshold replaces outcome judgment.
The default parent
starts in Claude Code Auto Mode to avoid manual approval queues; current Claude
Code versions make subagents inherit parent Auto Mode, so their remaining
read-only boundary is the role contract and absence of Edit/Write tools, not an
OS-enforced Bash sandbox. The launcher explicitly removes inherited
`CLAUDE_CODE_SUBAGENT_MODEL`, `CLAUDE_CODE_EFFORT_LEVEL`, and the legacy
`CODEX_CLAUDE_SUBAGENT_MODEL` convention because those global overrides would
collapse role-specific routing.

The launcher loads no user/project/local settings sources, enables no MCP
servers, adds a generated private overlay, and does not edit standalone Claude
configuration. Claude Code may show a repository trust dialog on the first
launch. Codex owns the repository trust decision and does not ask the user.
This does not expand any other authority.

Keep the returned PTY `session_id` together with the exact UUID, name, root, and
lease from the JSON object after `CODEX_PTY_WORKER_READY`. Reuse only that
current-thread mapping.
Never use bare `claude -c`, an unqualified `--resume`, or another session.

## Ownership and admission boundaries

The launcher permits at most two live admitted workers per HOME by default
(`CODEX_CLAUDE_MAX_BUSY_WORKERS` may be only `1` or `2`) and one normal
reservation or active write assignment per canonical root. A live reservation
counts against capacity before a task receives write access. If its exact
registered process dies first, the runtime may preserve a
`cancelled_unassigned` tombstone and release it automatically because it never
held write custody. An active record for a dead named worker remains an orphaned
root block and is never released by ordinary launch or assignment.

The boundary is control of another principal's session. Resume, assignment,
successor lineage, rotation, and native-fallback retirement all require the
exact current `CODEX_THREAD_ID`, canonical root, and UUID registration, and fail
closed otherwise. Liveness and retirement checks target only the named session:
another live worker in the same root never blocks its lifecycle, while resume
refuses a UUID that is still live by its own lease *or* its registered process
group, so deleting a lease directory cannot let a second process attach to a
live session. A foreign or standalone Claude session is never adopted, resumed,
signalled, or discovered by process name. Lease or registration state that is
malformed, contradictory, or too incomplete to prove death fails closed rather
than reading as a dead worker.

An explicitly unreserved `--idle` worker can coexist with another worker and
does not count against capacity until legacy-compatible assignment admission.
Do not assign it to evade a launch rejection. Any such same-root process still
shares one worktree, so one edit-capable owner remains mandatory.

Resume a dead, registered worker only after validating the same thread/root and
confirming no native transfer:

```text
<skill-dir>/scripts/launch-worker.zsh /absolute/project/root --resume <exact-uuid>
```

## Assign and observe

Wait for the interactive prompt, then run the assignment gate:

```text
<skill-dir>/scripts/assign-worker.zsh <root> <uuid> <task-id>
```

If it exits `76`, decide whether the current parent remains useful. To continue,
rerun once with `--continue-current-context`; that decision remains valid until
the next completed compaction. Otherwise rotate only after terminal handoff,
custody return, and process-group death:

```text
<skill-dir>/scripts/rotate-worker.zsh <root> <uuid> <task-id> \
  --handoff <ready_for_verification|blocked> --custody-returned
<skill-dir>/scripts/launch-worker.zsh <root> --successor-of <uuid>
```

If the gate exits `70`, the observer state is not trustworthy. Do not delete
its pending marker or continue the parent; use the same handoff, shutdown, and
rotation boundary.

The old UUID is then non-resumable and each registered successor attempt records
its lineage without preventing a retry after a failed launch.
One successful assignment owns exactly one bounded stage/outcome. After accepting
its handoff, Codex sends `/exit`, proves named process-group death, then calls
the matching rotate or retire script. Only that terminal lifecycle step releases
the assignment. A resumed active assignment is recovery only: do not rerun
`assign-worker.zsh` or resend its full prompt.
Claude Code still owns compaction; the runtime counts completed `PostCompact`
events without retaining their summaries.

New stages also receive a parent-only `PreToolUse` checkpoint. Its private
registration state contains only counters, request identifiers, usage numbers,
timestamps, reason codes, and the task identifier; it never stores transcript
content. Subagent-internal tool calls are excluded. The default warning/checkpoint
envelope is 32/64 observed parent requests, 128/256 parent tool calls,
131072/262144 maximum observed `cache_read_input_tokens`, and 600/1200 elapsed
seconds. The parent-tool bound remains active when transcript usage is delayed
or unavailable. Override these only at
new launch with `CODEX_CLAUDE_STAGE_WARN_REQUESTS`,
`CODEX_CLAUDE_STAGE_MAX_REQUESTS`,
`CODEX_CLAUDE_STAGE_WARN_PARENT_TOOL_CALLS`,
`CODEX_CLAUDE_STAGE_MAX_PARENT_TOOL_CALLS`,
`CODEX_CLAUDE_STAGE_WARN_CACHE_READ_TOKENS`,
`CODEX_CLAUDE_STAGE_MAX_CACHE_READ_TOKENS`,
`CODEX_CLAUDE_STAGE_WARN_SECONDS`, and `CODEX_CLAUDE_STAGE_MAX_SECONDS`; the
validated policy is then immutable in that session snapshot. Transcript usage
can lag, so request/cache observations are best-effort; elapsed time remains a
separate bound.

A warning adds context without approving or denying the requested tool. A
checkpoint denies the next parent tool and asks Claude to return the required
handoff. It does not kill or retire the process, claim `Done when`, complete the
outer goal, or transfer custody. Codex still judges the handoff and performs the
normal terminal lifecycle.

After a successful gate, immediately recheck the kill switch and retirement
marker, then send one task body without putting it in a process argument.
Submit multiline content and the final carriage return separately.

```text
TASK_ID: <unique-id>

Outcome:
Done when:
Boundaries:
Authoritative context:
Non-goals:
Known evidence:
Required handoff:

Work autonomously inside this contract. Preserve unrelated changes. Stop only
when ready for independent verification, genuinely blocked, or missing a
material decision or authority. Return one terminal marker after the handoff
and custody return:
CODEX_HANDOFF_READY <TASK_ID> <ready_for_verification|blocked>
```

The seven headings are verbatim and ordered; do not replace them with routing
metadata. The persistent outer goal and completion authority remain with Codex.
Each Claude worker owns one bounded stage/outcome, and its handoff is evidence,
never goal completion.

Before every poll or other `write_stdin`, including an empty poll, recheck the
kill switch and confirm the registration
has no `retirement.json`. This is a cooperative
preflight, not an atomic lock around the external PTY call: a call already in
flight may finish after `off` returns. A retired UUID receives no new input.
Accept a handoff only when the task/state marker matches, the full evidence
precedes it, the prompt returns, all edit/card/phase custody returns, and no
delegated writer or background child remains. Independently inspect the
artifacts and choose evidence that resolves material uncertainty around `Done
when`.

## Native fallback

Fallback transfers ownership; it never duplicates execution. Use it when the
kill switch is active, Claude/PTY is unavailable, capacity prevents useful work,
or the exact worker cannot be recovered.

1. Require a clean terminal handoff and prove edit custody has returned. The
   process-group checks catch same-group descendants, but cooperative policy is
   not proof against a deliberately detached daemon. After a crash or ambiguous
   PTY loss, keep native work read-only or move it to an isolated root.
2. Retire the current registration; the script refuses while this exact
   session's lease, registered process group, or worker process is still live.
   Other Codex-owned workers in the same root are a separate lifecycle and do
   not block it:

   ```text
   <skill-dir>/scripts/retire-native-fallback.zsh <absolute-root> <uuid> <task-id>
   ```

3. Transfer the unchanged outcome and boundaries to one bounded native owner.
   Add read-only explorer/reviewer roles or a post-custody test runner only when
   they materially improve confidence.

Native task identity and native role selection are separate. `task_name` only
names the task and its canonical path; it never selects a custom agent. Every
role-routed native launch must pass `agent_type` with the exact custom-agent
`name`. A full-history `fork_turns: "all"` (including that default) inherits the
parent role, model, and effort, so use `fork_turns: "none"` or a bounded numeric
fork with an explicit `agent_type`, and put required context in the task body.

```json
{
  "task_name": "gateway_minimal_reuse_audit",
  "agent_type": "source_explorer",
  "fork_turns": "none",
  "message": "<bounded outcome, context, evidence, and handoff>"
}
```

Before transferring edit/card custody or relying on the result, verify exposed
launch metadata: `agent_role` must equal the requested `agent_type`, and the
model/effort must match the intended role profile below. If `agent_type` is
rejected, `agent_role` is null or mismatched, or the expected profile is not
applied, stop the child and fail closed. Never retry by substituting the role
name into `task_name`.

The custom-agent `sandbox_mode` is a role default, not proof that a child was
narrowed below the parent turn. A built-in child inherits the parent turn's live
sandbox policy. Use it only when the observed child policy is no broader than
the selected role profile.

When the parent policy is broader, or the current native tool surface rejects
`agent_type`, run the role as a separate non-interactive Codex process instead:

```text
print -r -- "<bounded task>" | \
  <skill-dir>/scripts/run-native-agent.zsh source_explorer <absolute-root>
```

The launcher reads only a regular, non-symlink user-level role profile and
requires its role contract to match the bundled template; a repository-owned
profile is not a trusted isolation authority. Install the trusted copy with
`setup-native-agents.zsh --target user`. The selected model remains the model
explicitly installed in that profile. The launcher passes the task only on stdin and uses
`codex exec --ignore-user-config` with an explicit `--sandbox`, disables hooks,
apps, web search, inherited MCP servers, and nested agents, and refuses
`danger-full-access`. Its output is evidence awaiting Codex verification; it is
not a child thread or a custody transfer receipt. Never run this isolated path
and a built-in child for the same outcome.

Optional native role templates are installed separately and never by plugin
activation. Preview first:

```text
<skill-dir>/scripts/setup-native-agents.zsh --target project --root <absolute-root>
```

Use `--apply` for interactive confirmation or `--apply --yes` after reviewing
the dry run. Existing role files are never overwritten. For an older
installation, `--add-missing --apply` keeps existing regular profiles and adds
only missing bundled roles (including `scout`); symlink and non-regular
collisions are rejected. Defaults are
role-specific:

```text
source_explorer=gpt-5.6-luna   test_runner=gpt-5.6-luna
mech_executor=gpt-5.6-terra    reviewer=gpt-5.6-terra
security_reviewer=gpt-5.6-sol
```

Use `--model <model>` or `CODEX_NATIVE_AGENT_MODEL` only for an intentional
uniform override. Use repeatable `--role-model <role=model>` for targeted
overrides; role overrides take precedence over a uniform override. The Codex
orchestrator always inherits the main session model and is never pinned here.

To make Claude-first selection durable, review and manually adopt the opt-in
policy in [references/codex-policy-snippet.md](references/codex-policy-snippet.md).
The plugin never edits `AGENTS.md`.

## Disable and recover

`toggle-agents.zsh off` creates the kill switch without terminating processes;
conforming Codex transport refuses calls after its next preflight, while an
already-started PTY call may complete. `off --stop` additionally sends `TERM` to
isolated process groups whose live parent identity is verified through this
runtime's leases, then fails closed if a registered group remains. `on` requires
an explicit user action. These operations never discover or target standalone
Claude by process name.

If a PTY handle is lost, do not guess one. Recover only from the exact durable
current-thread registration after the prior process is proven dead. If identity
cannot be proven, keep native agents read-only or use an isolated worktree until
the ambiguity is resolved.

An orphaned active assignment is a separate operator decision, not normal
recovery and not an automatic response to capacity pressure. Never run the
following flow unless the user explicitly authorizes abandoning the exact
`root + UUID + task-id` after seeing its bounded effects. First preview without
mutation:

```text
<skill-dir>/scripts/reconcile-orphan.zsh <root> <uuid> <task-id>
```

The preview succeeds only when the durable tuple and registration match and the
named worker is proven dead. It returns a confirmation token and states that the
flow writes a retirement tombstone, terminalizes the assignment, and preserves
the worktree and registration; it sends no signal, reads no transcript, adopts
no session, and deletes no worktree file. Apply only the unchanged preview and
explicit authorization:

```text
<skill-dir>/scripts/reconcile-orphan.zsh <root> <uuid> <task-id> \
  --apply <confirmation-token>
```

After `CODEX_PTY_ORPHAN_RECONCILED`, inspect the preserved worktree and resolve
any unknown partial edits before transferring write custody. A live or
ambiguously identified worker, mismatched tuple, changed token, conflicting
retirement, or malformed shared state fails closed.

Files in this skill

  • SKILL.md19.3 KB
  • agents/openai.yaml233 B
  • assets/native-agents/mech_executor.toml.in659 B
  • assets/native-agents/reviewer.toml.in472 B
  • assets/native-agents/scout.toml.in897 B
  • assets/native-agents/security_reviewer.toml.in494 B
  • assets/native-agents/source_explorer.toml.in496 B
  • assets/native-agents/test_runner.toml.in526 B
  • assets/worker-agents.json5 KB
  • assets/worker-system-prompt.txt2 KB
  • references/codex-policy-snippet.md5.4 KB
  • scripts/assign-worker.zsh12.2 KB
  • scripts/launch-worker.zsh39.5 KB
  • scripts/reconcile-orphan.zsh5.7 KB
  • scripts/retire-native-fallback.zsh3.7 KB
  • scripts/rotate-worker.zsh4 KB
  • scripts/run-native-agent.zsh4.4 KB
  • scripts/runtime-lib.zsh25.9 KB
  • scripts/setup-native-agents.zsh6.7 KB
  • scripts/toggle-agents.zsh7 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…