Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions .github/workflows/release-sdk.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
name: Release TypeScript SDK

# Publishes sdk/typescript to npm as @agentfox/sdk when a maintainer pushes a tag:
#
# git tag sdk-v0.1.0 && git push origin sdk-v0.1.0
#
# Nothing else triggers it. The tag's version must equal sdk/typescript/package.json's,
# or it stops before publishing. Authentication: npm Trusted Publishing (OIDC) when the
# package is configured for it on npmjs.com for architsharm/agentfox + this workflow;
# otherwise an NPM_TOKEN repository secret (an automation token for the @agentfox
# scope). With neither, `npm publish` fails closed.

on:
push:
tags:
- "sdk-v*"

permissions:
contents: read
id-token: write # npm provenance and Trusted Publishing

jobs:
publish:
runs-on: ubuntu-latest
environment: npm
defaults:
run:
working-directory: sdk/typescript
steps:
- uses: actions/checkout@v4

- uses: actions/setup-node@v4
with:
node-version: "22"
registry-url: "https://registry.npmjs.org"

- name: Refuse to publish unless package.json matches the tag
run: |
set -euo pipefail
TAG="${GITHUB_REF_NAME#sdk-v}"
DECLARED="$(node -p "require('./package.json').version")"
if [ "$TAG" != "$DECLARED" ]; then
echo "error: tag says $TAG, package.json says $DECLARED" >&2
exit 1
fi

- run: npm ci
- run: npm run typecheck
- run: npm test
- run: npm run build

- name: Publish
run: npm publish --provenance --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
154 changes: 97 additions & 57 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,26 @@ the file it came from.

## [Unreleased]

## [0.4.0] - 2026-10-12

### Added

- **New tool-call paths.** Every agent's tool-call sequences in production, the ones
never seen before, and one click to add a path to the regression tests; a daily scan
raises a `new_tool_path` issue whose fix adds the path (Test → Tool paths).
- **Agent circuit breaker.** An agent whose calls start getting blocked in a burst is
flagged (default) or paused for a cool-down, then probed and closed after clean
calls; Resume now from the agent's settings or the issue.
- **Your own access control.** A workspace registers its RBAC service (or an OPA or
Cedar server); matching tool calls are posted to it with the end user before they
run (`principal` on `/v1/guard/tool_call`, `fox.session(principal=...)`).
- **`govern(fox, *agents)`** for the OpenAI Agents SDK: one call guards every agent,
hand-off and tool.
- Playground sandboxes come from a spare pool (about 2.5 s to start), and due jobs run
off ordinary traffic as well as the daily cron.
- Policy import from agent-governance YAML, Codex CLI hooks, industry and regional
packs, Rego and Cedar libraries, a coding-agent blocklist.

- **Key rotation** for the two keys that protect stored data.
`AGENTFOX_TOKEN_ENCRYPTION_KEY_PREVIOUS` and `AGENTFOX_AUDIT_SIGNING_KEY_PREVIOUS`
(comma-separated) hold retired keys: secrets still decrypt and checkpoints still
Expand Down Expand Up @@ -54,65 +72,20 @@ the file it came from.
variable still set (in use, or set but not read) and any `nometria.toml` / `[nometria]`
config in use.

### Removed

- The `nometria` Python package shim and the `nometria` console script. Import from `agentfox` (e.g. `agentfox.frameworks.langgraph`) and run `agentfox`.
- **The last of the Nometria names (rename stage B). Breaking.** The `NOMETRIA_*`
environment fallback (settings, `NOMETRIA_CONFIG`, the CLI's `NOMETRIA_API_URL` /
`NOMETRIA_API_TOKEN` / `NOMETRIA_USER`, `NOMETRIA_AGENT`, `NOMETRIA_MCP_LOG_LEVEL`, the
evidence package's `NOMETRIA_AUDIT_KEY`, the dashboard's `NOMETRIA_*`, the demos'
`NOMETRIA_DEMO_MODEL` and `NOMETRIA_DATABASE_*`), the `nometria.toml` / `[nometria]`
config file, acceptance of `x-nometria-*` request headers, the `legacy names` doctor
line and the one-time `NOMETRIA_` key bridge are gone. Only `AGENTFOX_*`,
`agentfox.toml` / `[agentfox]` and `X-AgentFox-*` are read; an old name is ignored
without a warning. Key rotation (`*_PREVIOUS`) stays.
- **Breaking:** the dashboard session cookie is `agentfox_session` (was
`nometria_session`). Everyone is signed out once and signs in again.
- **Breaking:** the LangGraph governance state key is `__agentfox__` (was
`__nometria__`). Declare `__agentfox__` in your state schema. Graph state checkpointed
before the upgrade keeps its governance state under the old key, where it is no
longer read: an in-flight thread resumed across the upgrade starts a new trace.
- **Breaking:** OPA policy documents are pushed as `agentfox_<policy>` (were
`nometria_<policy>`). An OPA sidecar that kept documents pushed by an older version
holds both under the same package; restart it (pushed policies are in memory) or
`DELETE /v1/policies/nometria_<policy>` for each.
- The secrets detector's entity for AgentFox API and agent keys (`nom_api_…`,
`nom_agt_…`) is `SECRET.AGENTFOX_KEY` (was `SECRET.NOMETRIA_KEY`). The `SECRET.*`
policy rules match it unchanged; a rule naming the old entity must be updated. The
key prefixes themselves are unchanged, so issued keys keep working and keep being
detected.

- The hidden pre-consolidation CLI names. `agentfox --help` shows thirteen verbs, and
the old top-level names had kept running, hidden, with a "now called" hint. They no
longer resolve. `agentfox hooks run` and `agentfox mcp serve` stay,
because installed hook configs and MCP client configs call them. Where each old name
lives now:

| Old | New |
|---|---|
| `check` · `quickscan` | `scan` · `scan --sessions` |
| `quickstart` · `version` · `seed` | `init` · `--version` / `admin version` · `admin seed` |
| `analyse-action` | `test action` |
| `eval run/gate/baseline/suites/online` · `eval drift` | `test …` · `report drift` |
| `redteam run` · `redteam probes` | `test redteam` · `test probes` |
| `audit verify` · `audit checkpoint` | `report verify` · `admin checkpoint` |
| `evidence export` | `report evidence` |
| `compliance status/risk/obligations/frameworks/board/review-packet` · `compliance review` | `report …` · `report signoff` |
| `compliance sync/compute/validate` | `admin catalog …` |
| `tools declare` · `tools set-triggers` · `tools list` | `declare tool` · `declare triggers` · `declare list tools` |
| `access declare-scope` · `access declare-reference` | `declare scope` · `declare reference` |
| `boundary set` · `boundary check` | `declare boundary` · `test boundary` |
| `sources add` · `sources import` · `sources list` | `declare source` · `declare import-sources` · `declare list sources` |
| `escalation set` · `escalation scan` | `declare escalation` · `report escalations` |
| `entitlement principal` · `entitlement grant` · `entitlement report` | `declare principal` · `permit user` · `report entitlement` |
| `capability grant/list/revoke` | `permit grant/list/revoke` |
| `guardrails …` · `guardrails test` | `policy rules …` · `test rule` |
| `proposals …` · `approvals …` | `policy proposals …` · `permit approvals …` |
| `auth …` · `db …` · `users …` · `hooks …` | `admin auth …` · `admin db …` · `admin users …` · `admin hooks …` |
| `mcp tools` | `admin mcp tools` |

### Changed

- **Get started's last step enforces everything that protects the agent** (baseline,
its own protections, blocked words and topics), with a preview of what it would have
stopped. Previews now count calls that went through and would be stopped.
- **Fewer, plainer issues.** One medium issue per tool an agent has not been given
(granting it closes the issue); calls held for the approval their grant asks for
are not issues; titles and refusal reasons are plain words.
- Loop detection counts within a turn, so a conversation that repeats itself across
turns is not refused; a stopped loop is a recorded, blocked run.
- Runs are grouped by conversation; the SDK reads `AGENTFOX_API_TOKEN`.
- Reports show controls working out of every control that applies, not out of those
measured; rates over nothing show a dash.

- **A cascade that only sends a message escalates instead of blocking.** A tool can be
declared `effect: communication` (`agentfox declare tool --effect communication`, or
`effect:` in a pack's `world.yaml`): its irreversible effect is a message leaving, not
Expand Down Expand Up @@ -170,6 +143,73 @@ the file it came from.
- `McpCallBlocked`, raised by the MCP governor, is now an `agentfox.AgentFoxError` like
every other refusal. It is still a `RuntimeError`.

### Removed

- The `nometria` Python package shim and the `nometria` console script. Import from `agentfox` (e.g. `agentfox.frameworks.langgraph`) and run `agentfox`.
- **The last of the Nometria names (rename stage B). Breaking.** The `NOMETRIA_*`
environment fallback (settings, `NOMETRIA_CONFIG`, the CLI's `NOMETRIA_API_URL` /
`NOMETRIA_API_TOKEN` / `NOMETRIA_USER`, `NOMETRIA_AGENT`, `NOMETRIA_MCP_LOG_LEVEL`, the
evidence package's `NOMETRIA_AUDIT_KEY`, the dashboard's `NOMETRIA_*`, the demos'
`NOMETRIA_DEMO_MODEL` and `NOMETRIA_DATABASE_*`), the `nometria.toml` / `[nometria]`
config file, acceptance of `x-nometria-*` request headers, the `legacy names` doctor
line and the one-time `NOMETRIA_` key bridge are gone. Only `AGENTFOX_*`,
`agentfox.toml` / `[agentfox]` and `X-AgentFox-*` are read; an old name is ignored
without a warning. Key rotation (`*_PREVIOUS`) stays.
- **Breaking:** the dashboard session cookie is `agentfox_session` (was
`nometria_session`). Everyone is signed out once and signs in again.
- **Breaking:** the LangGraph governance state key is `__agentfox__` (was
`__nometria__`). Declare `__agentfox__` in your state schema. Graph state checkpointed
before the upgrade keeps its governance state under the old key, where it is no
longer read: an in-flight thread resumed across the upgrade starts a new trace.
- **Breaking:** OPA policy documents are pushed as `agentfox_<policy>` (were
`nometria_<policy>`). An OPA sidecar that kept documents pushed by an older version
holds both under the same package; restart it (pushed policies are in memory) or
`DELETE /v1/policies/nometria_<policy>` for each.
- The secrets detector's entity for AgentFox API and agent keys (`nom_api_…`,
`nom_agt_…`) is `SECRET.AGENTFOX_KEY` (was `SECRET.NOMETRIA_KEY`). The `SECRET.*`
policy rules match it unchanged; a rule naming the old entity must be updated. The
key prefixes themselves are unchanged, so issued keys keep working and keep being
detected.

- The hidden pre-consolidation CLI names. `agentfox --help` shows thirteen verbs, and
the old top-level names had kept running, hidden, with a "now called" hint. They no
longer resolve. `agentfox hooks run` and `agentfox mcp serve` stay,
because installed hook configs and MCP client configs call them. Where each old name
lives now:

| Old | New |
|---|---|
| `check` · `quickscan` | `scan` · `scan --sessions` |
| `quickstart` · `version` · `seed` | `init` · `--version` / `admin version` · `admin seed` |
| `analyse-action` | `test action` |
| `eval run/gate/baseline/suites/online` · `eval drift` | `test …` · `report drift` |
| `redteam run` · `redteam probes` | `test redteam` · `test probes` |
| `audit verify` · `audit checkpoint` | `report verify` · `admin checkpoint` |
| `evidence export` | `report evidence` |
| `compliance status/risk/obligations/frameworks/board/review-packet` · `compliance review` | `report …` · `report signoff` |
| `compliance sync/compute/validate` | `admin catalog …` |
| `tools declare` · `tools set-triggers` · `tools list` | `declare tool` · `declare triggers` · `declare list tools` |
| `access declare-scope` · `access declare-reference` | `declare scope` · `declare reference` |
| `boundary set` · `boundary check` | `declare boundary` · `test boundary` |
| `sources add` · `sources import` · `sources list` | `declare source` · `declare import-sources` · `declare list sources` |
| `escalation set` · `escalation scan` | `declare escalation` · `report escalations` |
| `entitlement principal` · `entitlement grant` · `entitlement report` | `declare principal` · `permit user` · `report entitlement` |
| `capability grant/list/revoke` | `permit grant/list/revoke` |
| `guardrails …` · `guardrails test` | `policy rules …` · `test rule` |
| `proposals …` · `approvals …` | `policy proposals …` · `permit approvals …` |
| `auth …` · `db …` · `users …` · `hooks …` | `admin auth …` · `admin db …` · `admin users …` · `admin hooks …` |
| `mcp tools` | `admin mcp tools` |

### Fixed

- **Benchmark honesty.** Detector patterns and similarity-corpus entries that had been
written from the misses of the datasets they are scored on, and the AgentDojo
grader's tool list in the repository scanner, are removed; every published number
was re-measured. Default held-out injection recall is 16.7% (had been reported as
26.7%). The adaptive benchmark's "unmapped" operators now test characters the
product does not map, and show the open gap that had been hidden.
- A due-jobs pass now runs on a freshly booted instance.

## [0.3.1] - 2026-09-26

The first release published to PyPI, which is the whole point of it: 0.3.0 was
Expand Down
2 changes: 1 addition & 1 deletion api/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,4 @@ cryptography>=43
# actual build error: a bare "vendor/..." resolved to ".../api/api/vendor/...").
# Rebuild after changing src/agentfox: `uv build --wheel --out-dir api/vendor` from the
# repo root, then delete the old wheel file.
../vendor/agentfox-0.3.1-py3-none-any.whl
../vendor/agentfox-0.4.0-py3-none-any.whl
Binary file not shown.
2 changes: 1 addition & 1 deletion benchmarks/claims.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,6 @@ claims:
- {file: dashboard/app/(marketing)/benchmark/page.tsx, text: "only {n} of {d} benign tasks ran without escalating to a human"}
- {file: dashboard/app/(marketing)/benchmark/page.tsx, text: '["Session-level taint (shipped default)", "{n}/{d} ({pct}% {ci})", "588/588", "702/702"]'}
- {file: dashboard/app/docs/benchmarks/page.tsx, text: "only {n} of {d} benign tasks ({pct}% {ci}) ran without escalating to a human"}
- {file: dashboard/app/(product)/login/page.tsx, text: '["{n} of {d}", "benign tasks ran without escalating to a human"]'}
- {file: docs/design/architecture-differentiation.md, text: "{n}/{d} benign tasks ({pct}% {ci}) run without escalating to a human"}
# The blog.
- {file: dashboard/components/marketing/blog/posts/prompt-injection-containment-not-detection.tsx, text: 'only {n} of {d} benign tasks ran without escalating to a human'}
Expand Down Expand Up @@ -203,6 +202,7 @@ claims:
# The blog.
- {file: dashboard/components/marketing/blog/posts/prompt-injection-containment-not-detection.tsx, text: '{n}/{d} of those bypasses still contained at the action'}
- {file: dashboard/components/marketing/blog/posts/prompt-injection-containment-not-detection.tsx, text: 'value="{n}/{d}" label="adaptive bypasses of the detector, still contained at the action"'}
- {file: dashboard/app/(product)/login/page.tsx, text: '["{n} of {d}", "attacks that got past the detector, still stopped before they acted"]'}

- id: crescendo.trajectory_detection
source: benchmarks/crescendo/results/crescendo_results.json
Expand Down
15 changes: 15 additions & 0 deletions dashboard/app/(product)/app/compliance/controls/[key]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ export const dynamic = "force-dynamic";
* evidence packages), where it is mapped, and the attestations people have made
* against it. Recording a review freezes the evidence shown here onto the review.
*/
/** Where the thing a control measures is set, for the controls a new workspace fails first. */
const FIX: Record<string, { label: string; href: string }> = {
"NOM-DSC-03": { label: "Assign owners on Agents", href: "/app/agents" },
"NOM-GOV-03": { label: "Assess risk in the risk register", href: "/app/compliance?tab=risk" },
"NOM-AUD-05": { label: "Set retention periods", href: "/app/compliance?tab=retention" },
"NOM-RTG-05": { label: "Turn on content checks", href: "/app/policies?tab=checks" },
};

export default async function ControlPage({
params,
searchParams,
Expand Down Expand Up @@ -66,6 +74,13 @@ export default async function ControlPage({
]}
/>
<p className="small muted" style={{ marginTop: 10 }}>{ev.rationale || "No status computed yet."}</p>
{FIX[c.key] && ev.status !== "effective" && (
<p style={{ marginTop: 8 }}>
<Link href={FIX[c.key].href} className="k-btn">
{FIX[c.key].label}
</Link>
</p>
)}
</Card>

<Card title="Rules that implement it" flush>
Expand Down
7 changes: 4 additions & 3 deletions dashboard/app/(product)/app/glossary/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,10 @@ export default function Glossary() {
<td className="mono small">Policies</td>
<td className="small">
The rules that decide what a detector's result actually does — allow,
block, or escalate. A policy is in <span className="mono">observe</span>{" "}
mode (records only) or <span className="mono">enforce</span> mode
(actually blocks) — see <a href="/app/policies">Policies</a>.
block, or escalate. A policy is <strong>watching</strong> (it records what it
would do, and stops nothing) or <strong>enforcing</strong> (it actually blocks).
In the API and CLI these are the <span className="mono">observe</span> and{" "}
<span className="mono">enforce</span> modes — see <a href="/app/policies">Policies</a>.
</td>
</tr>
<tr id="controls" data-term="controls">
Expand Down
2 changes: 1 addition & 1 deletion dashboard/app/(product)/app/start/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ async function ConnectTab({

<ConnCard
title="SDK"
status={<Pill tone="neutral">Python, TypeScript</Pill>}
status={<Pill tone="neutral">Python</Pill>}
text="Check each message and tool call from your agent's own code."
expandLabel="Show setup"
>
Expand Down
Loading
Loading