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
32 changes: 22 additions & 10 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,10 @@
# on the package (repo responsibleai/agent-hooks, workflow
# release.yml, environment release) at
# npmjs.com > package > Settings > Trusted Publisher.
# No token; all five packages (loader + four platform
# packages) have trusted publishers configured.
# No token; every package (the loader + each platform
# package) needs its own trusted publisher. A brand-new
# platform package cannot be configured before its first
# publish, so its leg fails soft (see the npm publish step).
# NuGet — trusted publishing (OIDC): configure a Trusted Publishing
# policy on nuget.org (repo responsibleai/agent-hooks,
# workflow release.yml, environment release); the NuGet/login
Expand Down Expand Up @@ -211,10 +213,11 @@ jobs:
fail-fast: false
matrix:
include:
- { os: ubuntu-latest, target: x86_64-unknown-linux-gnu }
- { os: macos-latest, target: aarch64-apple-darwin }
- { os: macos-latest, target: x86_64-apple-darwin }
- { os: windows-latest, target: x86_64-pc-windows-msvc }
- { os: ubuntu-latest, target: x86_64-unknown-linux-gnu }
- { os: ubuntu-24.04-arm, target: aarch64-unknown-linux-gnu }
- { os: macos-latest, target: aarch64-apple-darwin }
- { os: macos-latest, target: x86_64-apple-darwin }
- { os: windows-latest, target: x86_64-pc-windows-msvc }
runs-on: ${{ matrix.os }}
permissions:
contents: read
Expand Down Expand Up @@ -278,7 +281,7 @@ jobs:
sdk/typescript/npm/**/*.node
- name: Publish to npm (platform packages, then the loader package)
if: env.DRY_RUN != 'true'
# OIDC trusted publishing: no token. Each package (the four
# OIDC trusted publishing: no token. Each package (the
# platform packages and the loader) has its own trusted
# publisher on npmjs.com (repo responsibleai/agent-hooks,
# workflow release.yml, environment release). The repo is
Expand All @@ -299,20 +302,29 @@ jobs:
node -e '
const fs = require("fs");
const p = JSON.parse(fs.readFileSync("package.json", "utf8"));
const targets = ["linux-x64-gnu","darwin-x64","darwin-arm64","win32-x64-msvc"];
const targets = ["linux-x64-gnu","linux-arm64-gnu","darwin-x64","darwin-arm64","win32-x64-msvc"];
p.optionalDependencies = Object.fromEntries(
targets.map(t => ["@responsibleai/agent-hooks-" + t, p.version]));
fs.writeFileSync("package.json", JSON.stringify(p, null, 2) + "\n");
'
for dir in npm/linux-x64-gnu npm/darwin-x64 npm/darwin-arm64 npm/win32-x64-msvc .; do
for dir in npm/linux-x64-gnu npm/linux-arm64-gnu npm/darwin-x64 npm/darwin-arm64 npm/win32-x64-msvc .; do
name=$(node -p "require('./$dir/package.json').name")
if [ "$dir" != "." ] && ! ls "$dir"/*.node >/dev/null 2>&1; then
echo "::error::$name has no native artifact"; exit 1
fi
if npm view "$name@$V" version >/dev/null 2>&1; then
echo "$name@$V already on npm — skipping"
elif (cd "$dir" && npm publish --access public --tag "$T"); then
:
elif ! npm view "$name" name >/dev/null 2>&1; then
# npm trusted publishing cannot be configured for a package
# that has never been published, so a brand-new platform
# package fails its first OIDC publish. Fail soft: consumers
# tolerate a missing optionalDependency; bootstrap the
# package manually, then rerun this workflow (idempotent).
echo "::warning::$name@$V first publish failed — bootstrap the new package on npmjs.com (one-time token publish), configure its trusted publisher, then rerun"
else
(cd "$dir" && npm publish --access public --tag "$T")
exit 1
fi
done
working-directory: sdk/typescript
Expand Down
15 changes: 14 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,21 @@
User-visible changes to the spec and SDKs. Versioning rules:
[VERSIONING.md](VERSIONING.md).

## Unreleased
## 0.1.0-alpha.5 — tag `v0.1.0-alpha.5`

- **Python: `Verdict.allow()` constructor sugar,** completing the
cross-SDK vocabulary: Rust `Verdict::allow`, TypeScript
`Verdict.allow()`, .NET `Verdict.Allow` and Go `AllowVerdict`
already existed; Python exposed only the module-level `ALLOW`
constant. The Python README interceptor example now uses the sugar
throughout.
- **npm: prebuilt `linux-arm64-gnu` binary.** The TypeScript loader
gains a fifth platform package
(`@responsibleai/agent-hooks-linux-arm64-gnu`,
`aarch64-unknown-linux-gnu`), built natively on the arm64 hosted
runner. musl (Alpine) targets remain unshipped — the pipeline
builds on native runners only, and `linux-x64-musl` does not ship
either; a musl pair can land together later.
- **§12.1 admits incremental mediation.** Previously a host had to
assemble the complete response before `post_model_call` with no
exception, so a host mediating a stream incrementally (e.g. per ACS
Expand Down
4 changes: 2 additions & 2 deletions docs-site/docs/quickstart/typescript.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ npm install @responsibleai/agent-hooks
```

The package resolves a prebuilt native binary for your platform through
`optionalDependencies` (linux-x64-gnu, darwin-x64, darwin-arm64,
win32-x64-msvc today).
`optionalDependencies` (linux-x64-gnu, linux-arm64-gnu, darwin-x64,
darwin-arm64, win32-x64-msvc today).

A minimal host with one control interceptor:

Expand Down
2 changes: 1 addition & 1 deletion sdk/dotnet/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<Authors>Responsible AI</Authors>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<RepositoryUrl>https://github.com/responsibleai/agent-hooks</RepositoryUrl>
<Version>0.1.0-alpha.4</Version>
<Version>0.1.0-alpha.5</Version>
<!-- Supply chain: lock NuGet resolution; CI restores in locked mode. -->
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
</PropertyGroup>
Expand Down
4 changes: 2 additions & 2 deletions sdk/python/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion sdk/python/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "agent-hooks-python"
version = "0.1.0-alpha.4"
version = "0.1.0-alpha.5"
edition = "2021"
license = "MIT"
description = "PyO3 bindings for the agent-hooks Rust core."
Expand Down
13 changes: 7 additions & 6 deletions sdk/python/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,18 +40,19 @@ result = invoke_tool(ctx["tool_call"]["args"]) # post-transform args
## Interceptor usage

```python
from agent_hooks import AgentContext, Decision, Verdict
from agent_hooks import AgentContext, Verdict


class MyPolicy:
def intercept(self, ctx: AgentContext) -> Verdict:
if ctx["interception_point"] == "pre_tool_call" and ctx["tool_call"]["name"] == "rm":
return Verdict(decision=Decision.DENY, reason="dangerous")
return Verdict(decision=Decision.ALLOW)
return Verdict.deny(reason="dangerous")
return Verdict.allow()
```

`Verdict.warn(...)` (allow + recorded warning) and `Verdict.escalate(...)`
(liftable deny for the approval seam, §9) are the constructor shortcuts for
the other two §5 shapes.
`Verdict.allow()`, `Verdict.deny(...)`, `Verdict.warn(...)` (allow + recorded
warning) and `Verdict.escalate(...)` (liftable deny for the approval seam, §9)
are the constructor shortcuts for the §5 shapes.

## Running the CTK against your framework

Expand Down
2 changes: 1 addition & 1 deletion sdk/python/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ build-backend = "maturin"
# similarity check blocks all separator variants (incl. agenthooks).
# Import name stays agent_hooks.
name = "agent-hooks-sdk"
version = "0.1.0a4"
version = "0.1.0a5"
description = "Framework-neutral agent control contract: interception points, agent context, verdict, and conformance test kit. Python wrapper over the canonical Rust core. Cooperative contract, not a security boundary: the host is fully trusted (see SECURITY.md)."
readme = "README.md"
requires-python = ">=3.10"
Expand Down
5 changes: 5 additions & 0 deletions sdk/python/python/agent_hooks/_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,11 @@ def is_liftable(self) -> bool:
"""A deny carrying an ``approval`` block (§5.1)."""
return self.decision is Decision.DENY and self.approval is not None

@classmethod
def allow(cls) -> Verdict:
"""The trivial permit verdict."""
return cls(decision=Decision.ALLOW)

@classmethod
def warn(cls, *, reason: str | None = None, message: str | None = None) -> Verdict:
"""Constructor sugar for what earlier drafts called ``warn``: an
Expand Down
10 changes: 10 additions & 0 deletions sdk/python/tests/test_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,16 @@ def test_approval_only_on_deny(self) -> None:
assert v.is_liftable
assert not Verdict(decision=Decision.DENY).is_liftable

def test_allow_sugar_is_trivial_permit(self) -> None:
from agent_hooks import ALLOW

v = Verdict.allow()
assert v.decision is Decision.ALLOW
assert v == ALLOW
assert v.warnings == ()
assert v.approval is None
assert not v.is_liftable

def test_warn_sugar_is_allow_with_warning(self) -> None:
v = Verdict.warn(reason="pii", message="found ssn")
assert v.decision is Decision.ALLOW
Expand Down
4 changes: 2 additions & 2 deletions sdk/rust/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion sdk/rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ resolver = "2"
members = ["core", "ffi"]

[workspace.package]
version = "0.1.0-alpha.4"
version = "0.1.0-alpha.5"
edition = "2021"
rust-version = "1.75"
license = "MIT"
Expand Down
4 changes: 2 additions & 2 deletions sdk/typescript/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion sdk/typescript/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "agent-hooks-node"
version = "0.1.0-alpha.4"
version = "0.1.0-alpha.5"
edition = "2021"
license = "MIT"
description = "napi-rs bindings for the agent-hooks Rust core."
Expand Down
12 changes: 6 additions & 6 deletions sdk/typescript/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,10 @@ rejected fail-closed by a pre-serialization scan.

## Native module deployment

The npm package bundles the napi-rs native module (`*.node`) **for the
platform it was built on** — the published alpha ships `linux-x64-gnu`
only. On other platforms, install from source (needs a Rust toolchain):
`npm run build` produces the module for your host platform. Per-platform
The napi-rs native module (`*.node`) ships as per-platform
`optionalDependencies` packages (the standard napi-rs multi-platform
layout) are planned. A platform mismatch fails at `require` time with a
module-load error naming the missing `.node` binary.
layout): `linux-x64-gnu`, `linux-arm64-gnu`, `darwin-x64`,
`darwin-arm64` and `win32-x64-msvc`. On other platforms, install from
source (needs a Rust toolchain): `npm run build` produces the module
for your host platform. A platform mismatch fails at `require` time
with a module-load error naming the missing `.node` binary.
Loading
Loading