Skip to content

Releases: pedromvgomes/agentic-driver

v0.10.0

Choose a tag to compare

@github-actions github-actions released this 28 Sep 13:32
fa17df9

A run can deny tools outright, closing a gap AllowedTools alone can't reach,
and a prompt embedding a full diff no longer fails to exec on Linux.

Repository:

  • A new Disallower interface, separate from Permitter: a provider can
    grant an allowlist without being able to remove anything from it, or the
    reverse, and folding removal into PermissionArgs would force every
    existing implementer to change its signature for a capability some of
    them don't have.
  • Request.DisallowedTools names tools a scripted run must never use,
    regardless of what AllowedTools or PermissionMode otherwise grant. A
    tool that needs no permission is invisible to an allowlist; this is the
    field that closes it off directly. Set on a provider that isn't a
    Disallower, it is refused with ErrDisallowUnsupported before any
    process starts, the same way an unsupported turn limit or schema already
    is.
  • Driver.Provider() returns the wrapped agentic.Provider, so a caller
    can type-assert any capability — Disallower or one not yet invented —
    without holding onto its own concrete reference against the grain of the
    README's own usage example, which discards it after agentic.New, or
    submitting a Request just to read back the error.
  • Invocation gains Stdin []byte, piped to the child's standard input
    when non-nil; nil leaves it on the null device exactly as before, so
    this is a no-op for any provider that doesn't set it. Linux's execve(2)
    caps a single argv element at 128 KiB, and a prompt embedding a full diff
    routinely exceeds that on its own — the run used to fail at spawn with
    fork/exec ...: argument list too long before a single byte of the
    request was read. See ADR 0008.
  • Invocation.Args no longer carries the prompt for either dialect — an
    exported-behavior change. A caller that inspected StreamCommand's
    returned argv for the prompt text sees a different shape now. The known
    downstream consumer is agentic-toolkit, currently pinning this
    repository at v0.9.0.
  • Governed by gt 2.1.1 (up from 2.0.0): .github/workflows/ci-orchestration.yml
    gains a lydite coverage-baseline job, and the managed lydite-clearance
    workflow is renamed to gt-lydite-clearance.yml.

claudecode:

  • Implements Disallower, emitting --disallowedTools as one
    comma-joined argv element, the same shape --allowedTools already uses.
  • A bare * is accepted on the deny-list, unlike the allowlist: denying
    every tool is a coherent thing to ask for, where granting every tool
    under the guise of a restriction is refused.
  • --disallowedTools takes precedence over both the allowlist and the
    permission mode — a denied tool stays denied even under
    bypassPermissions.
  • The prompt travels on Invocation.Stdin instead of as the value of -p;
    -p itself stays in argv so claude still runs non-interactively.

codex:

  • Does not implement Disallower. Its nearest analogue,
    features.multi_agent, is a single switch over five tools at once, not
    a per-tool control, so a Disallower built on it could only honor a
    couple of hardcoded names and silently refuse the rest. A non-empty
    DisallowedTools is refused outright with ErrDisallowUnsupported,
    exactly like AllowedTools today.
  • The prompt travels on Invocation.Stdin instead of as the trailing
    positional argument to codex exec. It is dropped from argv entirely
    rather than replaced with -, since codex exec appends piped stdin as
    an extra <stdin> block rather than treating it as the prompt when a
    positional is also given.

agentictest:

  • Fake captures what was piped to its standard input, read back with the
    new Fake.Stdin(t), kept in its own file separate from the existing
    argv/env record so a prompt containing newlines or arg:/ENV-prefixed
    lines can't corrupt either.
  • A new IgnoreStdin field makes the fake never read its stdin, for
    testing a child that exits or stalls while the driver still has a
    payload left to write.

Full changelog: v0.9.1...v0.10.0

v0.9.1

Choose a tag to compare

@github-actions github-actions released this 23 Sep 05:41
747d3ed

PinnedVersion catches up to the Claude Code build v0.9.0's Opus 5.5 alias
already depends on.

claudecode:

  • PinnedVersion moves from 2.1.258 to 2.1.280. v0.9.0 resolved opus
    to claude-opus-5-5 without also pinning a CLI build known to run it;
    this release closes that gap.
  • No flag, envelope, or permission-mode change accompanies the bump. The
    dialect is unchanged; only the vendored build a New provider installs
    and verifies moves.

Full changelog: v0.9.0...v0.9.1

v0.9.0

Choose a tag to compare

@github-actions github-actions released this 23 Sep 03:36
2f91629

opus resolves to Claude Opus 5.5.

claudecode:

  • The opus family alias resolves to claude-opus-5-5. A driver built with
    WithModel("opus"), or a Request naming "opus", now runs Opus 5.5, and
    Driver.Model() reports claude-opus-5-5.
  • Opus 5 stays available: name claude-opus-5 and it reaches the CLI
    unchanged. A caller that has to stay on Opus 5 pins that name rather than
    the alias.
  • Nothing else in the dialect changes. It sets no thinking, effort or
    tool-choice options, so Opus 5.5's API differences from Opus 5 (thinking
    cannot be disabled, effort defaults to medium) are the CLI's to apply and
    need no change from a caller.

Repository:

  • Governed by gt 2.0.0. The security gate is lydite, configured under
    .lydite/, and a pull request also needs the lydite/referral status to
    merge.
  • AGENTS.md is rendered by agentic-toolkit from
    agentic/agentic-driver-repo.md, alongside CLAUDE.md, for both Claude Code
    and Codex.

Full changelog: v0.8.0...v0.9.0

v0.8.0

Choose a tag to compare

@github-actions github-actions released this 13 Sep 18:07
55d58ea

feat(codex): read a Block from turn.failed's error message
feat(codex): claim BlockReporter for exhausted and rejected reasons
test(codex): add derived exhausted-block fixtures and testdata provenance
test(codex): assert BlockReporter behavior and its rejection boundary
test(codex): guard the installed binary against a stale usage-limit phrase
docs: record ADR 0007 and bring 0006, CONTEXT.md, README.md into agreement
feat(codex): classify a spent usage limit as a block, not an ordinary failure (#13)

Full changelog: v0.7.0...v0.8.0

v0.6.0

Choose a tag to compare

@github-actions github-actions released this 07 Sep 19:25
e3a64f8

feat(codex): every exec skips the git repository check
feat: a provider declares whether its runs can be concurrent
feat(codex): resolve family aliases to concrete models

Full changelog: v0.5.0...v0.6.0

v0.5.0

Choose a tag to compare

@github-actions github-actions released this 07 Sep 16:48
caf3ffd

Release the stream decoder's tolerance for lines it does not read, and let a
codex run say which profile it authenticates as.

The decoder reads a line's type on its own before decoding anything else, so a
bookkeeping event whose message is a bare string is skipped rather than
failing the run. A modelled line whose shape is unreadable is surfaced as an
event instead, and only the terminal line stays fatal. This shipped to main
before v0.4.0 was cut against the older decoder, which turned a working stream
into ErrProviderUnavailable for any consumer pinned there.

codex.WithConfigDir exports CODEX_HOME, matching the option claudecode already
had. CODEX_HOME redirects the whole Codex profile rather than settings alone,
so it carries the credential too: a profile holding a session outranks the
token Isolated injects, and HOME is left to Driver.WithHome because codex needs
no second variable to find its cache.

Full changelog: v0.4.0...v0.5.0

v0.4.0

Choose a tag to compare

@github-actions github-actions released this 07 Sep 07:55
7574adf

Vendor the Codex CLI at a pinned version, and split the capability that says
which build runs from the one that says who signed it.

codex installs its own copy from npm at an exact version, refusing any tarball
but the one whose SHA-512 this repository commits, and executes it by absolute
path. That is what keeps the decoder and the fixtures it was written against
describing the same CLI: an agent that updates itself moves its event schema
silently, and the break then lands on a user mid-run instead of on a red test at
the bump.

Pinner is that guarantee and nothing more. Installer is Pinner plus provenance —
a named publisher signed the artifact, verified against a trust anchor embedded
here — which claudecode does and codex cannot. OpenAI publishes sigstore bundles
for its linux-musl release assets alone; npm attests every platform, darwin
included, but verifying a Sigstore bundle in Go costs an order of magnitude more
dependencies than this library carries, so that attestation is checked by hand
when the pin moves. docs/adr/0004 records the alternatives and the procedure.

A message's content is a string or a list of blocks, and claudecode modelled
only the list. A bare string is a shape the CLI emits, so a stream that was
working failed the whole run, and it surfaced as the provider being unavailable
rather than as anything a caller could act on.

Breaking:

  • codex.New takes a providers root and returns an error. Running whichever
    codex is on PATH is codex.NewOnPath().
  • agentic.Installer gains SigningIdentity(). A provider that vendors a binary
    without verifying a publisher's signature implements agentic.Pinner instead,
    and the driver asserts on Pinner wherever it means "can install".
  • Driver.SigningIdentity() answers ErrProvenanceUnsupported, wrapping
    ErrInstallUnsupported for a provider that vendors nothing at all.

Full changelog: v0.3.0...v0.4.0

v0.3.0

Choose a tag to compare

@pedromvgomes pedromvgomes released this 06 Sep 23:12
430b2d2

Drive Codex for non-interactive single-turn runs, and bind a run's final answer to a JSON Schema.

Codex, and a stream-first interface

The codex provider is written against captured output from the real CLI, and what it found reshaped the interface. codex exec --json is JSONL from its first line to its last and has no envelope to parse — -o/--output-last-message writes bare text to a file rather than a document to stdout — so a Result exists only as a fold over a whole run. Streaming is therefore mandatory on Provider, and Run is Stream folded to its terminal Result.

A terminal result outranks both the exit code and the timeout. Codex reports a rejected credential and an unsupported model by finishing its stream properly and then exiting non-zero; judging the exit code first turns those verdicts into spurious outages.

A turn limit is a capability, not a field every provider honours. Codex has no turn bound at any spelling — -c max_turns=N is accepted and silently ignored without --strict-config — and no per-tool allowlist of any kind, so it refuses MaxTurns and AllowedTools rather than accepting and discarding them.

Schema-constrained structured output

Request.Schema binds a run's final answer to a JSON Schema and Result.Structured carries it, so a caller can fan out N reviewer runs over a diff and unmarshal what comes back.

Both CLIs genuinely constrain rather than suggest — given a schema requiring count between 1000 and 2000 and a prompt insisting on 4, each returns a conforming document. They constrain by different mechanisms that fail differently: codex constrains the decoder, so an unsatisfiable schema is a grammar it cannot finish and the run burns to its output ceiling and reports turn.failed; Claude Code validates each StructuredOutput call, feeds the rejection back, and when the model gives up answers in prose on exit 0 with is_error: false and subtype: "success".

A run that was given a schema and produced no payload is reported as an unmet constraint — IsError set, Structured nil, Text carrying whatever account exists. It is the one verdict the library reaches on its own, and it outranks a sandbox refusal on the same run: refusing is a successful verdict about authority, but the caller still did not get the shape it asked for.

The schema reaches each CLI differently — inline for Claude Code, a file path for codex. The codex file is content-addressed so one request always renders one argv, lives in a per-account directory that is checked before use, and is read back and compared rather than trusted for having the right name.

Breaking changes

For anyone implementing Provider:

  • Command and Parse are replaced by StreamCommand and NewDecoder(Request); the Streamer interface and ParseEvent are gone.
  • MaxTurns now requires a TurnLimiter, and Request.Schema a SchemaConstrainer. Both are refused before a process starts rather than silently dropped.
  • Result carries a json.RawMessage and is no longer comparable with ==; use reflect.DeepEqual.

Notes

Every fixture under testdata is real output from the two CLIs, sanitised. The decisions above are recorded in docs/adr/, and CONTEXT.md fixes the vocabulary — verdict, outage, refusal, unmet constraint — that means one thing regardless of which CLI is behind it.

The API is not yet stable.