Repository navigation
Agent Guard 1.0.0
Agent Guard 1.0.0
The first stable release of @shieldaai/agent-guard, -mcp, -scenarios and
-executor. @shieldaai/agent-map is published at the same version but stays
experimental. The last published version was 0.1.0-alpha.3; everything since then is
in this release. The full list is in the changelog, grouped by
package.
What 1.0 promises
| Package | Promise |
|---|---|
@shieldaai/agent-guard, -mcp, -scenarios |
Root exports follow semantic versioning. |
@shieldaai/agent-guard-executor |
Root, /sandbox, /pilot and /ops follow semantic versioning. Every 1.x executor opens ledgers written by earlier 1.x versions and by 0.1.0-alpha.3; downgrade is not supported. /inventory is experimental; /internal has no promise. |
@shieldaai/agent-map |
Experimental. Its API and graph model may change in a minor release. |
Agent Guard limits what a misled agent can do through the calls routed to it. It does not
detect prompt injection and it is not a sandbox. Risks and how Agent Guard handles
them and Support and limits say what is and is
not covered.
What is in 1.0
If you did not use the alphas, this is what the release contains:
- The guard (
@shieldaai/agent-guard): tools you register and approve, tasks with
grants on exact resources, tenants and recipients, approvals tied to one exact action,
budgets, delegation to sub-tasks, and revocation. In memory, in one process, with no
third-party runtime dependencies. - MCP adapter: the tools, resources and prompts of a Model Context Protocol server
behind the same checks, with a digest for noticing a changed tool definition. - Durable executor: the same checks with an encrypted SQLite ledger, a token per task
over HTTP, governed memory and an optional Docker profile for untrusted code. - Scenarios: thirteen hostile and ordinary scenarios (twenty-six cases) to run against
your own integration. - Experimental: Agent Map, an inventory of declared agents and what they can reach.
New since the alphas
- Outside-content labels (
readsUntrusted,untrustedContext,sendsExternal) in core,
executor, MCP and governed memory, off by default. executeDetailed(),retireTask(),unapproveTool(),unregisterTool(),onDecision,
limits,monotonicClock,GuardErrorCatalogandisGuardError()in core.- A restart-stable
descriptorDigestfor pinning reviewed MCP tool definitions. - A trusted read-back of prepared actions for review screens
(executor.preparedAction(),POST /v1/admin/action). - An explicit executor ledger format version with migrations.
- Agent Map boundary flags, outside-guard reach, discovery leads and host manifest v3.
- Scenario catalog 1.3.0 (thirteen scenarios, twenty-six cases).
Known limits
- The inventory observer is experimental; one logical CPU with 250 ms polling is not
supported. - Agent Map full-graph reads are estimated to work up to roughly 150 agents with default
limits and roughly 800 at the maximum. Limits above the defaults have not been run against
a Neo4j database, and the 5-second read deadline does not grow with them: test your
largest reads against your own database first. - Agent Ops needs a per-client limit at the load balancer or firewall when clients share an
address or an attacker controls many addresses. - The core keeps state in memory in one process; the executor is single-owner. There are
no distributed quotas, no distributed revocation and no high availability.
Verification
All 19 checks passed on the release candidate 424810a
(pull request 56): npm run check on
Node 22, 24 and 26 on Linux and on macOS and Windows; the browser checks in Chromium,
Firefox and WebKit; the Neo4j integration tests; the Docker isolation and production-image
checks; CodeQL with no open alerts. Main CI run:
43310105088.
Upgrading from 0.1.0-alpha.3
Core and executor
- Root aliases are gone. Import the internal building blocks from
@shieldaai/agent-guard/internal, and the executor's sandbox, inventory, pilot and
operator names from/sandbox,/inventory,/pilotand/ops. - Handler errors (executor). Whatever a host tool handler throws,
execute()now fails
withTOOL_FAILED, orEXECUTION_ABORTEDif the call was canceled, revoked or past its
deadline. In the alphas aGuardErrororExecutorErrorthrown by a handler reached the
caller with its own code. That code is now inerror.detail; match ondetailinstead of
code. The built-in memory, model, HTTP, private-file and sandbox tools keep their codes. - Callbacks inside built-in tools. An error thrown by a host callback inside a built-in
tool (modelcomplete, outboundcredential, sandboxonResult, artifact brokerclock)
is alwaysTOOL_FAILEDwith the error's code asdetail. - Aborted handlers. A handler that is aborted before it performs its effect must throw
(for examplesignal.throwIfAborted()). A handler that returns normally is taken to have
performed the effect and is reported asEXECUTION_ABORTEDwith detailEFFECT_COMMITTED. - Executor task server statuses. Denials use 401, 403, 404, 409, 429 and 503 instead
of 400, and the body addsretryable. UpgradeExecutorClientand the server together.
requestsPerMinutecounts only task requests that authenticated. - Failed revocation.
revokeTask()whose ledger write fails throwsOPERATION_FAILED
withdetail: 'REVOCATION_NOT_DURABLE'(the store error is thecause), and
POST /v1/admin/revokeanswers 503 with"retryable": trueand thatdetail. The task
is denied in the running process and the revocation is recorded by the next ledger write
that succeeds. Treat it as "not revoked yet" and repeat the call: if the process exits
before any ledger write succeeds, the revocation is lost. - Tool registration at startup. Registering an unchanged tool after a start no longer
writes aTOOL_REGISTEREDevent or raises the tool's revision. Approvals are still
withdrawn at every start. ADMITTEDstatus.execution()no longer returnsADMITTED; an admitted action is
RUNNINGat once. Remove code that waits forADMITTED. The value stays in
ExecutionStatusbecause alpha ledgers can contain it; they still open and recover. The
ACTION_ADMITTEDaudit event is unchanged.- Receipt retention. Settled receipts are now kept per task family (
maxReceiptsPerFamily,
default 64) and per tenant (256); the alphas kept 256 for the whole ledger. An evicted
receipt reads asstatus: 'PRUNED'with itsoutcomeandcharged. Once its stub is
evicted too (256 per family, 1,024 per tenant),execution()returnsstatus: 'PRUNED'
with nooutcome, and later fails withINVALID_PREPARED. Do not assumeoutcomeis
present.audit()keeps 256 events per tenant by default (maxRecentEventsPerTenant,
up to 1,000) instead of one shared ring of 1,000. - Ledger soft limit. Calls that add state (
createTask,delegateTask,prepare,
approve,execute, registering a new or changed tool, memory writes) fail with
STORE_CAPACITY_EXCEEDEDanddetail: 'SOFT_LIMIT'once the ledger would pass
maxLedgerBytesminus a reserve of one eighth, at most 1 MiB: 15 MiB with the default
16 MiB. Revoke, cancel, settlement andreconcileuse the reserve; if that is full they
drop the oldest half of the audit events. Archive or raisemaxLedgerBytesearlier than
you did with the alphas. A ledger past its soft limit still starts and can register its
existing tools, revoke, reconcile and archive. - Path rule. Paths in artifact receipts and build-artifact policy must be at most 240
UTF-8 bytes in Unicode NFC (they could be 1,024 characters). Sandbox and private-file
paths are measured in bytes, not characters, so a non-ASCII path under 240 characters can
now be refused. A receipt signed by an alpha with a longer path failsARTIFACT_INVALID
and a policy entry failsBUILD_PROVENANCE_INVALID: shorten the path and issue them again. - Ledger format. The executor writes
formatVersion1 when it opens an alpha ledger.
An alpha build still opens that ledger only while it stays at schema 1; retiring or
archiving a task, key custody and the recovery mirror raise the schema, and the alphas
refuse it then. Downgrade is not supported. - Delegation errors.
DELEGATION_DENIEDnow has adetailnaming the exceeded limit.
MCP adapter
- Error names. A non-object argument value fails with
ARGUMENTS_INVALID;
INVALID_ARGUMENTSno longer exists. failureReason. A malformed name advertised by the server is reported as
REGISTRY_REJECTED(orSCHEMA_INVALIDinskipped[].reason), not
INVALID_CONFIGURATION. After the transport closes unexpectedly,failureReasonis
CONNECTION_UNAVAILABLE, notundefined.VerifiedMcpHttpSession.connect()rejects bad options withINVALID_CONFIGURATIONor
REGISTRY_LIMITbefore any network call; handle these separately from
AUTHORIZATION_DENIED.- Reviewed digests.
descriptorDigestdoes not cover the server. If you store reviewed
digests keyed by remote name only and connect to more than one server, key them by server
ID and remote name.
Agent Map
- Neo4j 5.23 or later is required.
- Sort order. Listings sort by name in Unicode code-point order, field by field. Do not
keep page offsets across the upgrade. - Policy evidence that was not evaluated. Read
policyCoveragebeforepolicyEvidence:
an empty list with statusunknownwas not evaluated.get_agent_inventoryreports
counts.policiesand thepoliciessectiontotalasnullin that case. - Degraded answers instead of failures. Check
data.graphStatusbefore usinggraph
fromget_agent_access(too_largemeans it was left out), andtruncatedReasonon
find_agents_with_accessresults. - Owners.
agentCountandresourceCountare no longer on the owners in
get_agent_inventory; take them fromlist_owners. - Errors. Invalid store options throw
MAP_CONFIG_INVALID(wasMAP_REQUEST_INVALID).
An unmetExpectheader answers 417, not 403.MAP_TIMEOUTanswers 504, not 408, when
the request had arrived in full and the read ran out of time; read theerrorcode, not
the status, to recognise a timeout. - Viewer files.
viewerDirectorymust hold four files:index.html,app.js,
demo.jsandapp.css. Rebuild the viewer; a directory withoutdemo.jsis refused at
startup withMAP_CONFIG_INVALID. - Custom stores. A custom
MapStorereceivesownersandpolicyin the read request;
it may ignore them but must not reject unknown keys.
Collector
- Commands. Recovery and the difference report are installed commands:
agent-guard-recovery(waspython -m agent_guard_cartography.pilot_recovery) and
agent-guard-map-report(waspython -m agent_guard_cartography.reference_report). The
python -mforms still work.agent-guard-source-lifecycleno longer creates a missing
--archive-dir; pass--create. - Lock. Every command that names an archive locks
<archive directory>.lockbeside it,
so commands on one archive exclude each other from any working directory. Stop every
collector, lifecycle and recovery command before installing: an old command and a new one
do not share a lock. The collector's user must be able to create that file in the
archive's parent directory, or create it once with mode 0600; otherwise commands fail
withPROCESS_LOCK_PATH_INVALID. A--lock-fileyou already pass is held as well. - Errors. All four commands report a failure as one JSON line on standard error,
{"status":"failed","code":"…"}. Lifecycle and recovery printed{"error":"…"}; the
collector and the report printed a bare code. Neo4j and path failures have specific codes
and exit 2;agent-guard-map-reportexits 2 for a specific code (it always exited 1). - Refused captures. A newly collected snapshot refused as older than the published view
(GENERATED_AT_ROLLBACK,REVISION_ROLLBACK) is reported and not kept; its failure line
has noreceipt_id. - Snapshots.
ledgerRevisionandpolicyEpochmust be true integers;1.0is rejected
withINVENTORY_SCHEMA_INVALID.
Upgrading Agent Ops from an unpublished source build
Agent Ops was never in a published version; this applies to builds from earlier source.
- A client that drives sign-in itself must send the cookie set by the
POST /auth/login
response with the callback; the earlier cookie is refused with 401.POST /auth/login
from a signed-in session answers409 ALREADY_SIGNED_IN. GET /api/maintenance: eacharchiveCandidatesentry is
{ taskId, requestId, ticketId, state, requesterId, createdAt };labelis removed.
POST /api/maintenance/archive/preview:previewgainsrequestIdand loses
retainedCount.- Request rows add
operationId,requesterKind, and, when present,denialReasonand
cause; a request can have the statewithdrawn.GET /api/sessionadds
policy.forbidSelfApprovalwhen signed in. - An unknown key under
requestsin the configuration fails withREQUEST_POLICY_INVALID
(wasINVALID_INPUT). New keys:requests.shareDenialReason,signInFlowsPerClient,
sessionTtlMsandrateLimitPerMinute. - Approve and deny can answer
409 REVIEW_STALE. An agent proposal for a ticket outside
its allowlist answers 403 whether or not the ticket exists. - An approval whose outcome is not known ends
unconfirmed, neverfailed. An operator
resolves it with "Check whether it was applied" or the close-out. Close-out evidence
shows the ticket as reviewed beside the ticket as it is now; a changed ticket no longer
answersCLOSE_OUT_UNPROVEN. LOGIN_CAPACITY_REACHEDis gone: when the sign-in table is full, the oldest pending
sign-in gives way. A client that is not signed in can get503 BUSYwhile four other
such requests from its address are in progress. Put a per-client limit in front of the
service when clients share an address.- Automatic archiving records
system:retentionas the operator (wasretention), and
enrolled operator or agent ids may not start withsystem:.