@agentskit/harness 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +77 -0
- package/CODE_OF_CONDUCT.md +5 -0
- package/CONTRIBUTING.md +26 -0
- package/LICENSE +21 -0
- package/README.md +473 -0
- package/SECURITY.md +11 -0
- package/dist/cli.js +1308 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +968 -0
- package/dist/index.js +1828 -0
- package/dist/index.js.map +1 -0
- package/docs/ADR-0001-extensible-kernel.md +41 -0
- package/docs/ADR-0002-profiles-and-context.md +22 -0
- package/docs/ADR-0003-doc-bridge-context-binding.md +36 -0
- package/docs/ADR-0004-run-metrics.md +29 -0
- package/docs/ADR-0005-benchmark-manifest.md +27 -0
- package/docs/ADR-0006-agent-session-protocol.md +36 -0
- package/docs/ADR-0007-policy-gate.md +33 -0
- package/docs/ADR-0008-runtime-executor.md +33 -0
- package/docs/ADR-0009-process-runtime-boundary.md +34 -0
- package/docs/ADR-0010-docker-sandbox-runtime.md +32 -0
- package/docs/ADR-0011-runtime-attestation.md +30 -0
- package/docs/ADR-0012-controlled-baseline-observations.md +27 -0
- package/docs/ADR-0013-honest-benchmark-comparability.md +27 -0
- package/docs/ADR-0014-criterion-level-benchmark-evidence.md +24 -0
- package/docs/ADR-0015-directional-benchmark-outcomes.md +23 -0
- package/docs/ADR-0016-baseline-evidence-digests.md +21 -0
- package/docs/ADR-0017-event-log-integrity.md +25 -0
- package/docs/ADR-0018-verification-projection-attestation.md +23 -0
- package/docs/ADR-0019-human-decision-attestation.md +27 -0
- package/docs/ADR-0020-terminal-reconciliation.md +26 -0
- package/docs/ADR-0021-event-lock-recovery.md +25 -0
- package/docs/ADR-0022-signed-evidence-bundle.md +27 -0
- package/docs/ADR-0023-safe-action-recovery.md +30 -0
- package/docs/ADR-0024-controlled-completion-metrics.md +27 -0
- package/docs/ADR-0025-ci-dogfood.md +22 -0
- package/docs/ADR-0026-ci-evidence-artifact.md +22 -0
- package/docs/ADR-0027-portable-evidence.md +19 -0
- package/docs/ADR-0028-effective-metrics.md +20 -0
- package/docs/ADR-0029-honest-ci-preparation.md +20 -0
- package/docs/ADR-0030-agentskit-os-benchmark-bridge.md +20 -0
- package/docs/ADR-0031-real-provider-baseline.md +18 -0
- package/docs/ADR-0032-harness-equivalent-benchmark.md +25 -0
- package/docs/ADR-0033-portable-agent-gate.md +25 -0
- package/docs/ADR-0034-measurement-quality-gates.md +25 -0
- package/docs/ADR-0035-reproducible-benchmark-samples.md +20 -0
- package/docs/ADR-0036-comparable-baseline-samples.md +20 -0
- package/docs/ADR-0037-replicated-baseline-collection.md +27 -0
- package/docs/ADR-0038-end-to-end-benchmark-boundary.md +28 -0
- package/docs/ADR-0039-artifact-and-protocol-metrics.md +39 -0
- package/docs/ADR-0040-benchmark-corpus-surfaces.md +32 -0
- package/package.json +68 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `@agentskit/harness` are documented here.
|
|
4
|
+
|
|
5
|
+
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the package follows Semantic Versioning.
|
|
6
|
+
|
|
7
|
+
## [0.1.0] - 2026-08-31
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- Replicated baseline collection with independent disposable samples, median
|
|
12
|
+
duration recording, and atomic complete-manifest replacement.
|
|
13
|
+
- Baseline samples use the same end-to-end measurement boundary as the harness
|
|
14
|
+
check while retaining provider-only duration for diagnosis.
|
|
15
|
+
- Incomplete baselines no longer qualify for duration or resource comparisons;
|
|
16
|
+
escape reduction remains visible as a separate outcome metric.
|
|
17
|
+
- Baseline reports expose artifact acceptance separately from protocol
|
|
18
|
+
completion, preserving useful outcome data without weakening fail-closed
|
|
19
|
+
comparison gates.
|
|
20
|
+
- Benchmark execution supports collision-safe task-specific replacement
|
|
21
|
+
samples, preserving blocked and timed-out evidence.
|
|
22
|
+
- Benchmark comparisons expose artifact acceptance rates and protocol
|
|
23
|
+
completion rates separately from performance comparability.
|
|
24
|
+
- Protocol and artifact acceptance comparisons expose absolute deltas when
|
|
25
|
+
relative improvement is undefined because the baseline was zero.
|
|
26
|
+
- Benchmark tasks declare typed surface coverage; the AgentsKit OS corpus
|
|
27
|
+
includes executable CLI and documentation tasks with real acceptance
|
|
28
|
+
validation.
|
|
29
|
+
- Fail-closed baseline sample policy and median baseline duration support for fair replicated comparisons.
|
|
30
|
+
- Independent benchmark samples with median-duration comparisons and retry metrics that do not confuse replication with retries.
|
|
31
|
+
- Configurable benchmark quality gates, confidence labels, and explicit retry metrics.
|
|
32
|
+
- Portable `ak-verify` fixture gate for real coding-agent benchmark runs; provider delivery remains blocked until its result is complete and human approval is recorded.
|
|
33
|
+
- Harness-equivalent benchmark preparation and collection for paired real-provider runs, with explicit human approval and honest non-comparability.
|
|
34
|
+
- Source snapshots now ignore untracked directories while retaining hashes for untracked files.
|
|
35
|
+
- Reproducible disposable-fixture runner and validator for the first real-provider AgentsKit OS baseline; improvement remains unavailable until equivalent harness runs exist.
|
|
36
|
+
- Provenance-bearing benchmark tasks and a portable validator for AgentsKit OS coding benchmark reports; missing controlled observations remain explicitly non-comparable.
|
|
37
|
+
- Playbook CI dogfooding for the package test suite, built CLI, and clean packed consumer.
|
|
38
|
+
- Playbook CI now preserves the official harness run as an auditable artifact for human review.
|
|
39
|
+
- Signed evidence flow now verifies a copied bundle from an isolated directory.
|
|
40
|
+
- Benchmark reports now separate effective non-superseded delivery metrics from attempt history.
|
|
41
|
+
- CI evidence now records automated preparation separately from human contract approval.
|
|
42
|
+
- Strict TypeScript modular core with generated declarations and source maps.
|
|
43
|
+
- Contract-frozen lifecycle, structured evidence, stale detection, human approval, retry, and cleanup.
|
|
44
|
+
- Explicit human cancellation and superseded retry history.
|
|
45
|
+
- Typed dependency-aware plugin lifecycle with deterministic cleanup.
|
|
46
|
+
- Append-only, source- and contract-bound lifecycle event log per run.
|
|
47
|
+
- Declarative profile inheritance with validated check overrides.
|
|
48
|
+
- Optional provenance-bearing context provider slot for Doc Bridge and Playbook adapters.
|
|
49
|
+
- Dependency-free Doc Bridge index adapter with deterministic references and frozen context snapshots.
|
|
50
|
+
- Context lifecycle events and stable context hashes that ignore resolution timestamps.
|
|
51
|
+
- Portable CLI snapshot resolution and `plan --context-file` binding for shell-based agents.
|
|
52
|
+
- Tamper-evident validation for imported context snapshots.
|
|
53
|
+
- Direct API callers now receive the same tamper-evident context validation as CLI callers.
|
|
54
|
+
- `benchmarkRuns` and `ak-harness benchmark` for reproducible historical run metrics.
|
|
55
|
+
- Phase 0 benchmark manifests, task identity bindings, and explicit baseline comparisons.
|
|
56
|
+
- Typed agent session recorder with correlated turn/tool events and guarded ordering.
|
|
57
|
+
- Adapter metadata and session event protocol that persists hashes instead of raw agent content.
|
|
58
|
+
- Required deny-by-default Policy Gate with ordered rules and correlated blocked-tool events.
|
|
59
|
+
- Bounded in-process tool runtime with timeout, abort signal, hashed results, and structured failures.
|
|
60
|
+
- Shell-free child-process runtime with timeout, output limits, and structured process failures.
|
|
61
|
+
- Optional Docker runtime with a no-network, read-only, unprivileged, resource-limited sandbox profile.
|
|
62
|
+
- Typed runtime attestation with Docker image digest and effective profile hash in terminal tool events.
|
|
63
|
+
- Controlled baseline observation recording through the typed API and `ak-harness benchmark baseline`, with duplicate, unknown-task, and atomic-write protections.
|
|
64
|
+
- Benchmark comparisons now require completed harness evidence and report honest non-comparability reasons plus check, outcome, evidence, and review metrics.
|
|
65
|
+
- Benchmark baselines now require explicit, unique criterion-level evidence; comparisons report baseline evidence coverage and reject incomplete evidence.
|
|
66
|
+
- Comparable benchmark reports now expose directional duration, attempt, and human-review outcomes, with `unavailable` for non-comparable tasks.
|
|
67
|
+
- CLI-recorded baseline evidence now preserves a SHA-256 digest of the evidence file and validates digest format on manifest load.
|
|
68
|
+
- New lifecycle event logs carry a chained SHA-256 digest and expose explicit integrity verification; legacy logs remain readable but are not reported as verified.
|
|
69
|
+
- Event-log locks now carry owner metadata and expose explicit human-authorized stale-lock inspection and recovery through the API and `events lock|unlock` CLI commands.
|
|
70
|
+
- Signed evidence verification now supports stable key identities and explicit active/revoked trust stores for controlled key rotation.
|
|
71
|
+
- Policy rules can require explicit human approval before sensitive tool actions enter the runtime; unresolved and rejected approvals remain fail-closed and auditable.
|
|
72
|
+
- Agent sessions can be resumed from their hash-chained event log, preserving pending approvals without replaying completed tools.
|
|
73
|
+
- Ambiguous resumed tool actions now require an explicit human retry or abandonment decision after `tool.execution.started`.
|
|
74
|
+
- Benchmark comparisons now expose controlled `escapedIncomplete` deltas for measuring incomplete deliveries that escaped validation.
|
|
75
|
+
- Verification results now carry a projection digest in `run.json` and a matching `verification.completed` event; approval rejects projection tampering.
|
|
76
|
+
- `ak-harness` CLI and `ak-verify` common-protocol alias.
|
|
77
|
+
- Public package documentation and community policy files.
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
# Code of Conduct
|
|
2
|
+
|
|
3
|
+
Contributors are expected to communicate respectfully, assume good faith, and keep technical review focused on behavior, evidence, and maintainability.
|
|
4
|
+
|
|
5
|
+
Harassment, discrimination, threats, and deliberate exposure of private information are not acceptable. Maintainers may remove contributions or restrict participation when this standard is violated.
|
package/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Contributing to `@agentskit/harness`
|
|
2
|
+
|
|
3
|
+
## Before opening a change
|
|
4
|
+
|
|
5
|
+
1. State the behavior and failure mode the change addresses.
|
|
6
|
+
2. Keep the public API in `src/index.ts` intentionally small.
|
|
7
|
+
3. Add or update a typed test for every behavior change.
|
|
8
|
+
4. Update the README, changelog, and contract when the user-facing protocol changes.
|
|
9
|
+
|
|
10
|
+
## Local checks
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
pnpm --filter @agentskit/harness typecheck
|
|
14
|
+
pnpm --filter @agentskit/harness test
|
|
15
|
+
pnpm --filter @agentskit/harness build
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Do not claim completion when a required check is unavailable. Preserve criterion-level evidence and report blocked work honestly.
|
|
19
|
+
|
|
20
|
+
## Pull requests
|
|
21
|
+
|
|
22
|
+
Use a focused branch and commit. Describe the contract change, affected states, evidence produced, and exact commands executed. Breaking public API or CLI changes require a changelog entry and a maintainer review.
|
|
23
|
+
|
|
24
|
+
## License
|
|
25
|
+
|
|
26
|
+
Contributions are accepted under the repository's MIT license.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 AgentsKit Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,473 @@
|
|
|
1
|
+
# @agentskit/harness
|
|
2
|
+
|
|
3
|
+
Portable, evidence-backed development protocol for coding agents. The harness freezes a human-approved task contract, executes every configured check, binds evidence to the current source revision, detects stale results, and refuses completion without human approval.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add -D @agentskit/harness
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
The package requires Node.js 22 or newer and exposes both `ak-harness` and the common-protocol alias `ak-verify`.
|
|
12
|
+
|
|
13
|
+
## Workflow
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
ak-harness doctor --json
|
|
17
|
+
ak-harness plan approved --by human
|
|
18
|
+
ak-harness start
|
|
19
|
+
ak-verify run --json
|
|
20
|
+
ak-verify approve <run-id> approved --by human --json
|
|
21
|
+
ak-harness cancel <run-id> --by human --reason "Requirements changed"
|
|
22
|
+
ak-harness benchmark --manifest benchmarks/harness-phase-0.json --json
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
`plan` rejects unresolved ambiguities and unauthorized dirty worktrees. After `start`, the contract is frozen. Any source, configuration, or contract change invalidates evidence and moves the run to `STALE`. A human can cancel an active run; retrying a blocked, stale, or cancelled run marks the previous run `SUPERSEDED`.
|
|
26
|
+
|
|
27
|
+
Automated CI may prepare a run with `ak-harness plan prepared --by ci`. This records a CI preparation, never a human approval; the run remains unable to become `COMPLETE` until a human approves the verified result.
|
|
28
|
+
|
|
29
|
+
## External coding benchmark bridge
|
|
30
|
+
|
|
31
|
+
The harness validates a provenance-bearing task manifest and the JSON report
|
|
32
|
+
emitted by an external coding benchmark, including AgentsKit OS:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
AGENTSKIT_OS_ROOT=/path/to/agentskit-os \
|
|
36
|
+
AGENTSKIT_OS_BENCHMARK_REPORT=/path/to/report.json \
|
|
37
|
+
node scripts/verify-harness-agentskit-os-benchmark.mjs \
|
|
38
|
+
--manifest benchmarks/agentskit-os-phase-28.json \
|
|
39
|
+
--target "$AGENTSKIT_OS_ROOT" \
|
|
40
|
+
--report "$AGENTSKIT_OS_BENCHMARK_REPORT"
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
The bridge checks the pinned source revision, task definition, prompt digests,
|
|
44
|
+
scope, and provider report shape. Provider status and heuristic scores remain
|
|
45
|
+
observations: they do not grant human acceptance. With no controlled baseline,
|
|
46
|
+
improvement is reported as unavailable rather than inferred.
|
|
47
|
+
|
|
48
|
+
The phase-45 corpus adds explicit task surfaces and keeps the task definition
|
|
49
|
+
bound to the real AgentsKit OS fixture. It covers logic, CLI, and documentation
|
|
50
|
+
surfaces; the added CLI task executes compact, pretty, and invalid-input paths,
|
|
51
|
+
while the documentation task checks the API contract and its reproducible test
|
|
52
|
+
command:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
node scripts/verify-harness-agentskit-os-benchmark.mjs \
|
|
56
|
+
--manifest benchmarks/agentskit-os-phase-45.json \
|
|
57
|
+
--target /path/to/agentskit-os \
|
|
58
|
+
--require-surface-coverage logic,cli,docs
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Corpus coverage is a readiness gate, not a performance claim. Live provider
|
|
62
|
+
samples and human approval remain required before the expanded tasks become
|
|
63
|
+
comparable benchmark evidence.
|
|
64
|
+
|
|
65
|
+
Phase 29 adds a reproducible real-provider baseline runner for the AgentsKit OS
|
|
66
|
+
seed corpus. It uses disposable fixture copies, the existing OS benchmark
|
|
67
|
+
runner, and task-specific validation:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
node scripts/run-agentskit-os-baseline.mjs \
|
|
71
|
+
--target /path/to/agentskit-os \
|
|
72
|
+
--provider codex \
|
|
73
|
+
--output benchmarks/agentskit-os-phase-29-baseline
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
The observations are not an improvement claim until equivalent harness runs
|
|
77
|
+
exist.
|
|
78
|
+
|
|
79
|
+
To collect equivalent baseline samples, run the same corpus independently and
|
|
80
|
+
record the aggregate only after every task has been sampled:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
node scripts/run-agentskit-os-baseline.mjs \
|
|
84
|
+
--target /path/to/agentskit-os \
|
|
85
|
+
--provider codex \
|
|
86
|
+
--repeats 3 \
|
|
87
|
+
--output benchmarks/agentskit-os-phase-36-baseline \
|
|
88
|
+
--record-manifest benchmarks/agentskit-os-phase-28.json
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Each repeat uses a fresh disposable fixture. The report stores the end-to-end
|
|
92
|
+
duration of fixture setup, provider execution, validation, and cleanup in
|
|
93
|
+
`durationSamplesMs`; the provider's own duration remains nested in the raw
|
|
94
|
+
report. The manifest is replaced atomically through
|
|
95
|
+
the typed observation recorder. `--record-manifest` requires the complete task
|
|
96
|
+
set; omit it to inspect an uncommitted collection. Replicas are not retries,
|
|
97
|
+
and a failed sample keeps the aggregate failed. Escape reduction may remain
|
|
98
|
+
visible as a separate outcome, but duration, attempt, and review improvements
|
|
99
|
+
remain unavailable until the baseline proves complete delivery.
|
|
100
|
+
|
|
101
|
+
## Contract
|
|
102
|
+
|
|
103
|
+
Every repository supplies `.codex/verification.json` with explicit scope, outcomes, applicable surfaces, and executable checks. Each check must declare `evidence: "structured"`; its final output line must be JSON and map to the outcome IDs it proves:
|
|
104
|
+
|
|
105
|
+
```json
|
|
106
|
+
{"status":"passed","criteria":["api-behavior"]}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Endpoint, database, CLI, MCP, and UI checks must declare `execution: "real"`. UI checks additionally require `real-browser` and `screenshot` capabilities. Screenshot artifacts carry a project-relative path, SHA-256 hash, and viewport.
|
|
110
|
+
|
|
111
|
+
## API
|
|
112
|
+
|
|
113
|
+
The public TypeScript API is exported from `src/index.ts` and includes configuration loading, lifecycle operations, state transitions, evidence verification, approvals, cancellation, retries, and task-owned cleanup. Internal modules are not part of the supported API.
|
|
114
|
+
|
|
115
|
+
## Extensibility
|
|
116
|
+
|
|
117
|
+
The kernel stays responsible for contracts, state transitions, evidence, source
|
|
118
|
+
binding, stale detection, and human decisions. Optional integrations use the
|
|
119
|
+
typed plugin registry instead of changing those guarantees:
|
|
120
|
+
|
|
121
|
+
```ts
|
|
122
|
+
import { createPluginRegistry, createPluginSlot } from '@agentskit/harness'
|
|
123
|
+
|
|
124
|
+
const providers = createPluginSlot<{ readonly resolve: (query: string) => Promise<string> }>('context.provider')
|
|
125
|
+
const registry = createPluginRegistry()
|
|
126
|
+
registry.register({
|
|
127
|
+
id: 'my-context', version: '1.0.0', apiVersion: 1,
|
|
128
|
+
apply: (context) => {
|
|
129
|
+
context.register(providers, 'local', { resolve: async (query) => `context:${query}` })
|
|
130
|
+
},
|
|
131
|
+
})
|
|
132
|
+
registry.mount()
|
|
133
|
+
// registry.contributions(providers) is deterministic and typed.
|
|
134
|
+
registry.dispose()
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Each run also writes an append-only `events.ndjson` containing lifecycle facts
|
|
138
|
+
bound to its source revision and contract hash. New event logs carry a chained
|
|
139
|
+
SHA-256 digest; verify one with `ak-harness events verify [run-id]`. Logs from
|
|
140
|
+
older harness versions remain readable but are reported as `legacy`, not as
|
|
141
|
+
verified. After verification, `run.json` also carries a `verificationDigest`
|
|
142
|
+
that must match the `verification.completed` event before human approval. Human
|
|
143
|
+
approvals, rejections, and tracking authorizations are then recorded as
|
|
144
|
+
hash-chained `approval.recorded` or `authorization.recorded` events bound to
|
|
145
|
+
that digest, source revision, and contract hash. The stable `run.json` remains
|
|
146
|
+
the CLI projection and evidence index.
|
|
147
|
+
|
|
148
|
+
Use `ak-harness audit [run-id]` to reconcile a run projection with its verified
|
|
149
|
+
events. `ak-harness status` performs the same reconciliation before reporting
|
|
150
|
+
the current state, so a post-approval edit cannot appear as `COMPLETE`.
|
|
151
|
+
Concurrent event writers are serialized by an atomic per-run lock and fail
|
|
152
|
+
closed if the log is busy.
|
|
153
|
+
|
|
154
|
+
Export a reconciled `COMPLETE` run for external review with an Ed25519 key:
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
ak-harness events export <run-id> --output evidence.json --private-key private.pem --key-id release-v1
|
|
158
|
+
ak-harness events verify-bundle evidence.json --trusted-key-store trust-store.json
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
The bundle includes the run projection, event log, and referenced check outputs,
|
|
162
|
+
each with a SHA-256 digest. A trust store can mark keys `active` or `revoked` to
|
|
163
|
+
support controlled key rotation. Treat exported outputs as potentially sensitive.
|
|
164
|
+
|
|
165
|
+
The signed bundle is self-contained: it can be copied to an isolated directory
|
|
166
|
+
and verified there with only the bundle and the trusted public key.
|
|
167
|
+
|
|
168
|
+
Profiles are optional declarative overlays in `.codex/verification.json`. They
|
|
169
|
+
inherit in order, override existing checks by ID, and are resolved before the
|
|
170
|
+
contract is frozen:
|
|
171
|
+
|
|
172
|
+
```json
|
|
173
|
+
{
|
|
174
|
+
"profile": "ci",
|
|
175
|
+
"profiles": {
|
|
176
|
+
"ci": {
|
|
177
|
+
"checkOverrides": [{ "id": "unit", "timeoutMs": 120000 }],
|
|
178
|
+
"budget": { "maxDurationMs": 900000 }
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Doc Bridge and Playbook integrations can implement `ContextProvider` and
|
|
185
|
+
register it through `CONTEXT_PROVIDER_SLOT`; the kernel records neither their
|
|
186
|
+
credentials nor their transport and does not depend on either package. The
|
|
187
|
+
portable adapter reads a local Doc Bridge index without adding a dependency:
|
|
188
|
+
|
|
189
|
+
```ts
|
|
190
|
+
import { createDocBridgeContextProvider, planRun } from '@agentskit/harness'
|
|
191
|
+
|
|
192
|
+
const provider = createDocBridgeContextProvider({ root: process.cwd() })
|
|
193
|
+
const context = await provider.resolve({ query: 'harness', scope: ['playbook'] })
|
|
194
|
+
const run = await planRun({
|
|
195
|
+
configPath: '.codex/verification.json',
|
|
196
|
+
decision: 'approved',
|
|
197
|
+
contextSnapshots: [context],
|
|
198
|
+
})
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
The same boundary is available to shell-based agents:
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
ak-harness context resolve harness --scope playbook --json > context.json
|
|
205
|
+
ak-harness plan approved --context-file context.json --json
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
`context.json` may contain one snapshot or an array of snapshots, so providers
|
|
209
|
+
outside this package can participate without a runtime plugin loader.
|
|
210
|
+
The loader rejects a snapshot when its semantic contents no longer match its
|
|
211
|
+
`snapshotHash`.
|
|
212
|
+
|
|
213
|
+
The snapshot stores the Doc Bridge `contentHash`, reference hashes, and a
|
|
214
|
+
stable `contextHash`; resolution time is metadata and does not change the
|
|
215
|
+
reproducibility hash. Context is resolved before planning and is frozen with
|
|
216
|
+
the run, so later index changes cannot silently change its evidence.
|
|
217
|
+
|
|
218
|
+
Agent sessions can record adapter identity, turns, and guarded tool actions
|
|
219
|
+
during `IMPLEMENTING` without persisting prompt, argument, or result contents:
|
|
220
|
+
|
|
221
|
+
```ts
|
|
222
|
+
import { createSessionRecorder } from '@agentskit/harness'
|
|
223
|
+
|
|
224
|
+
const session = createSessionRecorder({
|
|
225
|
+
stateDir: '.codex/verification',
|
|
226
|
+
run: implementingRun,
|
|
227
|
+
adapter: { id: 'my-agent', version: '1.0.0', capabilities: ['tool-calls'] },
|
|
228
|
+
policy,
|
|
229
|
+
runtime,
|
|
230
|
+
})
|
|
231
|
+
const turn = session.startTurn(inputHash)
|
|
232
|
+
const action = session.requestTool({ turnId: turn.payload.turnId, toolId: 'shell', argumentsHash })
|
|
233
|
+
await session.executeTool({ actionId: action.payload.actionId, arguments: { command: 'echo ok' } })
|
|
234
|
+
session.end('completed')
|
|
235
|
+
|
|
236
|
+
// After a process interruption, recover the same session from events.ndjson.
|
|
237
|
+
const resumed = createSessionRecorder({ stateDir, run, adapter, policy, runtime, sessionId: session.sessionId, resume: true })
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
The recorder enforces turn-before-tool, one terminal result per action, no
|
|
241
|
+
pending actions or unresolved approvals at session end, and no calls after
|
|
242
|
+
termination. With `resume: true`, it reconstructs turns, pending actions, and
|
|
243
|
+
approval decisions from the hash-chained event log; completed actions are not
|
|
244
|
+
replayed. It is an observation seam; tool execution and policy decisions remain
|
|
245
|
+
separate kernel phases.
|
|
246
|
+
|
|
247
|
+
Every session also requires a policy gate. The built-in gate is an ordered
|
|
248
|
+
allow/block/approve list with deny-by-default behavior:
|
|
249
|
+
|
|
250
|
+
```ts
|
|
251
|
+
import { createPolicyGate, createSessionRecorder } from '@agentskit/harness'
|
|
252
|
+
|
|
253
|
+
const policy = createPolicyGate({
|
|
254
|
+
rules: [{ id: 'safe-shell', effect: 'allow', toolIds: ['shell'], reason: 'approved local tool' }],
|
|
255
|
+
})
|
|
256
|
+
const session = createSessionRecorder({ stateDir, run, adapter, policy })
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
The first matching rule wins. A blocked attempt writes `policy.evaluated` and
|
|
260
|
+
`tool.blocked` events and raises `POLICY_BLOCKED`; it never becomes a pending
|
|
261
|
+
tool action. An `approve` decision writes `tool.approval.requested` and keeps
|
|
262
|
+
the action out of the runtime until `session.approveTool({ actionId,
|
|
263
|
+
decision: 'approved' })` is called by a human. Rejection writes an auditable
|
|
264
|
+
`tool.approval.recorded` and `tool.blocked` pair. Custom policy gates can
|
|
265
|
+
implement the same typed `PolicyGate` interface without coupling the harness
|
|
266
|
+
to a runtime or provider.
|
|
267
|
+
|
|
268
|
+
When resuming, an action with a persisted `tool.execution.started` event is
|
|
269
|
+
ambiguous: its runtime may have produced an external side effect before the
|
|
270
|
+
process stopped. The harness refuses to execute it until a human calls
|
|
271
|
+
`session.recoverTool({ actionId, decision: 'retry', actor: 'human' })` or
|
|
272
|
+
`session.recoverTool({ actionId, decision: 'abandon', actor: 'human' })`.
|
|
273
|
+
Actions that were requested but never started remain safe to execute after
|
|
274
|
+
recovery. Completed actions are never replayed.
|
|
275
|
+
|
|
276
|
+
The built-in runtime executes registered handlers in memory, passes an
|
|
277
|
+
`AbortSignal`, enforces a timeout, and records only a result hash and duration:
|
|
278
|
+
|
|
279
|
+
```ts
|
|
280
|
+
import { createToolRuntime } from '@agentskit/harness'
|
|
281
|
+
|
|
282
|
+
const runtime = createToolRuntime({
|
|
283
|
+
timeoutMs: 30_000,
|
|
284
|
+
tools: [{ toolId: 'shell', execute: async ({ arguments: input }) => runShell(input) }],
|
|
285
|
+
})
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
Missing tools, handler errors, and timeouts become structured failures. This is
|
|
289
|
+
an execution boundary, not a process/container security sandbox; use a
|
|
290
|
+
provider-specific isolated runtime when hard isolation is required.
|
|
291
|
+
|
|
292
|
+
For a shell-free child-process boundary, register fixed commands with
|
|
293
|
+
`createProcessToolRuntime`. It sends one JSON request over stdin, kills a
|
|
294
|
+
timed-out or oversized process, and hashes stdout without storing it:
|
|
295
|
+
|
|
296
|
+
```ts
|
|
297
|
+
import { createProcessToolRuntime } from '@agentskit/harness'
|
|
298
|
+
|
|
299
|
+
const runtime = createProcessToolRuntime({
|
|
300
|
+
timeoutMs: 30_000,
|
|
301
|
+
maxOutputBytes: 1_048_576,
|
|
302
|
+
tools: [{ toolId: 'worker', command: process.execPath, args: ['worker.mjs'] }],
|
|
303
|
+
})
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
This is a process boundary with bounded I/O, not a container or operating
|
|
307
|
+
system security boundary. Use an isolated provider runtime for untrusted code.
|
|
308
|
+
|
|
309
|
+
For an optional Docker boundary, register fixed image commands with
|
|
310
|
+
`createDockerToolRuntime`. The default is fail-closed for image supply: it
|
|
311
|
+
uses cached images only, disables network access, makes the container root
|
|
312
|
+
filesystem read-only, drops capabilities, runs without privilege escalation,
|
|
313
|
+
and applies resource limits:
|
|
314
|
+
|
|
315
|
+
```ts
|
|
316
|
+
import { createDockerToolRuntime } from '@agentskit/harness'
|
|
317
|
+
|
|
318
|
+
const runtime = createDockerToolRuntime({
|
|
319
|
+
tools: [{
|
|
320
|
+
toolId: 'worker',
|
|
321
|
+
image: 'node:22.13.0-bookworm-slim',
|
|
322
|
+
command: ['node', 'worker.mjs'],
|
|
323
|
+
mounts: [{ source: process.cwd(), target: '/workspace', readOnly: true }],
|
|
324
|
+
cwd: '/workspace',
|
|
325
|
+
}],
|
|
326
|
+
memoryLimit: '512m',
|
|
327
|
+
cpus: 1,
|
|
328
|
+
pidsLimit: 128,
|
|
329
|
+
})
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
The provider does not add Docker as a package dependency and is not a VM or a
|
|
333
|
+
compromised-daemon boundary. Pin images for reproducibility; set `pull` to
|
|
334
|
+
`missing` or `always` only when image acquisition is explicitly authorized.
|
|
335
|
+
Completed and failed tool events carry the resolved image digest and an
|
|
336
|
+
effective profile hash, so reviewers can identify the runtime used for each
|
|
337
|
+
action without storing raw output.
|
|
338
|
+
|
|
339
|
+
`benchmark` aggregates the local run history into a versioned JSON report. It
|
|
340
|
+
includes check/outcome/evidence pass rates, retries, stale runs, human approvals,
|
|
341
|
+
and average/median verification duration. It also reports `effective*` metrics
|
|
342
|
+
over the latest non-superseded run in each retry lineage, so attempt history is
|
|
343
|
+
not confused with the current delivery state. With `--manifest`, it also compares
|
|
344
|
+
bound harness tasks with explicitly recorded baseline observations. A baseline
|
|
345
|
+
must include evidence for every acceptance criterion; missing, duplicate, or
|
|
346
|
+
unknown criterion evidence is rejected. Missing baselines and incomplete
|
|
347
|
+
evidence remain non-comparable; the harness never invents a baseline. Baseline
|
|
348
|
+
collectors also record `baselineArtifactAcceptanceRate`, the fraction of
|
|
349
|
+
repeated samples whose real artifact validation passed. This is independent of
|
|
350
|
+
protocol completion: a correct artifact with missing or incomplete protocol
|
|
351
|
+
evidence is visible as an accepted artifact but remains non-comparable for
|
|
352
|
+
duration and resource claims. These are
|
|
353
|
+
execution metrics, not a claim of productivity improvement; compare reports over
|
|
354
|
+
a controlled task corpus to measure that outcome.
|
|
355
|
+
|
|
356
|
+
Attach a task to a benchmark suite in the verification contract:
|
|
357
|
+
|
|
358
|
+
```json
|
|
359
|
+
{
|
|
360
|
+
"benchmark": {
|
|
361
|
+
"suiteId": "agentskit-harness-phase-9",
|
|
362
|
+
"taskId": "harness-benchmark-evidence",
|
|
363
|
+
"mode": "harness"
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
The manifest format is available at `benchmarks/harness-phase-9.json`. Record a
|
|
369
|
+
controlled baseline through the public CLI instead of editing JSON by hand:
|
|
370
|
+
|
|
371
|
+
```bash
|
|
372
|
+
ak-harness benchmark baseline harness-benchmark-evidence \
|
|
373
|
+
--manifest benchmarks/harness-phase-9.json \
|
|
374
|
+
--status passed \
|
|
375
|
+
--source manual-run-2026-08-29 \
|
|
376
|
+
--evidence-file benchmarks/harness-phase-9-evidence.example.json \
|
|
377
|
+
--attempts 1 --duration-ms 900000 \
|
|
378
|
+
--review-minutes 20 --escaped-incomplete 0
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
The command validates the task and values, rejects duplicate observations, and
|
|
382
|
+
atomically updates the manifest. Baseline observations are explicit records
|
|
383
|
+
with a source, timestamp, and criterion-level evidence. An empty or `not-run` baseline is reported as
|
|
384
|
+
non-comparable rather than treated as success.
|
|
385
|
+
|
|
386
|
+
When evidence is supplied through `--evidence-file`, the manifest also stores
|
|
387
|
+
the file's lowercase SHA-256 digest as `evidenceDigest`. This binds the
|
|
388
|
+
recorded JSON input to the observation; it does not independently validate a
|
|
389
|
+
manual, remote, or external source named by an evidence entry.
|
|
390
|
+
|
|
391
|
+
A comparison is considered comparable only when the task has an explicit
|
|
392
|
+
baseline with complete criterion-level evidence and its latest bound harness
|
|
393
|
+
run is `COMPLETE`. Blocked, incomplete, missing, or `not-run` inputs expose a
|
|
394
|
+
non-comparability reason and mark directional outcomes as `unavailable`.
|
|
395
|
+
Comparable reports include check, outcome, evidence, duration, attempt, and
|
|
396
|
+
human-review metrics plus directional outcomes. Completed runs with every
|
|
397
|
+
check, outcome, and evidence slot passing are projected as
|
|
398
|
+
`escapedIncomplete: 0`; a controlled baseline can record observed escapes and
|
|
399
|
+
the report exposes their delta and direction. A positive improvement rate
|
|
400
|
+
means the harness used less of that measured resource; these metrics do not
|
|
401
|
+
establish causality or productivity improvement alone.
|
|
402
|
+
|
|
403
|
+
When a structured check includes `artifactAcceptanceRate` directly or inside
|
|
404
|
+
its benchmark `reports`, comparisons also expose the harness rate, contributing
|
|
405
|
+
sample count, and acceptance improvement. Missing rates remain uncounted and
|
|
406
|
+
never become a failed artifact implicitly.
|
|
407
|
+
|
|
408
|
+
Baseline and harness comparisons also expose `protocolCompletionRate`, which
|
|
409
|
+
measures whether the verification protocol completed independently of artifact
|
|
410
|
+
acceptance. A protocol rate does not override the fail-closed comparability
|
|
411
|
+
rules.
|
|
412
|
+
|
|
413
|
+
When a baseline rate is zero, relative improvement remains `unavailable`; the
|
|
414
|
+
report still exposes the absolute acceptance or protocol completion delta.
|
|
415
|
+
|
|
416
|
+
For paired real-provider measurements, `scripts/run-agentskit-os-harness-benchmark.mjs`
|
|
417
|
+
prepares one frozen contract per manifest task, binds each run to the suite and
|
|
418
|
+
task ID, and leaves the lifecycle at the human approval gate. Use
|
|
419
|
+
`--repeats 3` for three independent samples per task; each sample has its own
|
|
420
|
+
state directory and must be approved separately. Run `--collect` after
|
|
421
|
+
approvals to aggregate the isolated task state directories. Pending or blocked
|
|
422
|
+
task runs remain non-comparable; only `COMPLETE` runs produce directional
|
|
423
|
+
improvement values. Duration comparisons use the median of completed samples,
|
|
424
|
+
while retry metrics count only superseded retry lineages, so experimental
|
|
425
|
+
replication is not misreported as agent retry cost.
|
|
426
|
+
|
|
427
|
+
When one sample is blocked or times out, prepare a distinct replacement without
|
|
428
|
+
overwriting the original evidence:
|
|
429
|
+
|
|
430
|
+
```bash
|
|
431
|
+
node scripts/run-agentskit-os-harness-benchmark.mjs \
|
|
432
|
+
--execute --task-id feat-formatter --sample 4 \
|
|
433
|
+
--phase-root .codex/verification/phase-39/harness
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
`--sample` requires a positive sample number and refuses an existing config or
|
|
437
|
+
state directory. The original blocked run remains part of the audit trail;
|
|
438
|
+
replacement runs add evidence and do not retroactively turn the blocked sample
|
|
439
|
+
into success.
|
|
440
|
+
|
|
441
|
+
The AgentsKit OS benchmark runner can expose the built `ak-verify` CLI inside
|
|
442
|
+
its disposable fixture. The fixture contract is prepared by CI, the provider
|
|
443
|
+
inherits a fixture-local PATH entry, and human approval is never synthesized.
|
|
444
|
+
This makes provider-side verification available without making the fixture a
|
|
445
|
+
security boundary; the outer harness remains the authoritative evidence gate.
|
|
446
|
+
|
|
447
|
+
Benchmark reports also expose a fail-closed `qualityGate`. It separates
|
|
448
|
+
historical retries from effective delivery, reports duration regressions, and
|
|
449
|
+
labels small samples as `insufficient-data` or `directional` instead of
|
|
450
|
+
claiming enterprise-level improvement. Configure
|
|
451
|
+
`minBaselineSamplesPerTask` alongside `minCompletedRunsPerTask`: a single
|
|
452
|
+
baseline observation is not comparable to replicated harness runs. Baseline
|
|
453
|
+
observations may store `durationSamplesMs`; the report compares medians.
|
|
454
|
+
|
|
455
|
+
## Development
|
|
456
|
+
|
|
457
|
+
```bash
|
|
458
|
+
pnpm install
|
|
459
|
+
pnpm --filter @agentskit/harness typecheck
|
|
460
|
+
pnpm --filter @agentskit/harness test
|
|
461
|
+
pnpm --filter @agentskit/harness build
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
See [CONTRIBUTING.md](./CONTRIBUTING.md) for changes, tests, and release expectations. See [CHANGELOG.md](./CHANGELOG.md) for version history.
|
|
465
|
+
|
|
466
|
+
The Playbook CI dogfoods the package with `harness:test`, `harness:cli`, and a
|
|
467
|
+
clean packed-consumer probe before the broader repository checks run.
|
|
468
|
+
It also uploads the resulting `.codex/verification/harness-phase-24` run
|
|
469
|
+
directory so a human can inspect the exact evidence before approval.
|
|
470
|
+
|
|
471
|
+
## License
|
|
472
|
+
|
|
473
|
+
MIT. See [LICENSE](./LICENSE).
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Security policy
|
|
2
|
+
|
|
3
|
+
## Reporting a vulnerability
|
|
4
|
+
|
|
5
|
+
Do not disclose security issues in public issues. Report a suspected vulnerability privately to the repository maintainers through the security contact configured on GitHub.
|
|
6
|
+
|
|
7
|
+
Include the affected version, reproduction steps, impact, and any proposed mitigation. Do not include secrets or personal data.
|
|
8
|
+
|
|
9
|
+
## Scope
|
|
10
|
+
|
|
11
|
+
The harness executes commands declared by a repository contract. Treat verification contracts as trusted code, review them before execution, and never run an untrusted contract in a sensitive environment.
|