@zof-ai/agent-evals 0.0.0-stage → 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 +5 -0
- package/CONTRIBUTING.md +5 -0
- package/LICENSE +202 -0
- package/README.md +46 -2
- package/SECURITY.md +9 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +26 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.js +178 -0
- package/docs/RELEASE.md +13 -0
- package/docs/SANDBOX.md +17 -0
- package/docs/TASKS.md +15 -0
- package/examples/correct.mjs +1 -0
- package/examples/incorrect.mjs +1 -0
- package/examples/profile.mjs +1 -0
- package/package.json +61 -4
package/CHANGELOG.md
ADDED
package/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Use Node.js 22 or 24, run `npm ci --ignore-scripts`, `npm run build`, and `npm test`. Add a regression test for functional or security changes. Keep changes independently authored and free of proprietary code, credentials and customer data. Discuss incompatible API changes before implementation. Contributions use the repository license and require maintainer review; dependency updates are never automatically merged.
|
|
4
|
+
|
|
5
|
+
Maintainers review issue reproductions, security boundaries, tests, compatibility and package contents. Use semantic versioning; announce breaking interfaces in the changelog. Deprecations require a documented replacement and at least one minor release before removal.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
|
|
2
|
+
Apache License
|
|
3
|
+
Version 2.0, January 2004
|
|
4
|
+
http://www.apache.org/licenses/
|
|
5
|
+
|
|
6
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
7
|
+
|
|
8
|
+
1. Definitions.
|
|
9
|
+
|
|
10
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
11
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
12
|
+
|
|
13
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
14
|
+
the copyright owner that is granting the License.
|
|
15
|
+
|
|
16
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
17
|
+
other entities that control, are controlled by, or are under common
|
|
18
|
+
control with that entity. For the purposes of this definition,
|
|
19
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
20
|
+
direction or management of such entity, whether by contract or
|
|
21
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
22
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
23
|
+
|
|
24
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
25
|
+
exercising permissions granted by this License.
|
|
26
|
+
|
|
27
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
28
|
+
including but not limited to software source code, documentation
|
|
29
|
+
source, and configuration files.
|
|
30
|
+
|
|
31
|
+
"Object" form shall mean any form resulting from mechanical
|
|
32
|
+
transformation or translation of a Source form, including but
|
|
33
|
+
not limited to compiled object code, generated documentation,
|
|
34
|
+
and conversions to other media types.
|
|
35
|
+
|
|
36
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
37
|
+
Object form, made available under the License, as indicated by a
|
|
38
|
+
copyright notice that is included in or attached to the work
|
|
39
|
+
(an example is provided in the Appendix below).
|
|
40
|
+
|
|
41
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
42
|
+
form, that is based on (or derived from) the Work and for which the
|
|
43
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
44
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
45
|
+
of this License, Derivative Works shall not include works that remain
|
|
46
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
47
|
+
the Work and Derivative Works thereof.
|
|
48
|
+
|
|
49
|
+
"Contribution" shall mean any work of authorship, including
|
|
50
|
+
the original version of the Work and any modifications or additions
|
|
51
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
52
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
53
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
54
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
55
|
+
means any form of electronic, verbal, or written communication sent
|
|
56
|
+
to the Licensor or its representatives, including but not limited to
|
|
57
|
+
communication on electronic mailing lists, source code control systems,
|
|
58
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
59
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
60
|
+
excluding communication that is conspicuously marked or otherwise
|
|
61
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
62
|
+
|
|
63
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
64
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
65
|
+
subsequently incorporated within the Work.
|
|
66
|
+
|
|
67
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
68
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
69
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
70
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
71
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
72
|
+
Work and such Derivative Works in Source or Object form.
|
|
73
|
+
|
|
74
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
75
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
76
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
77
|
+
(except as stated in this section) patent license to make, have made,
|
|
78
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
79
|
+
where such license applies only to those patent claims licensable
|
|
80
|
+
by such Contributor that are necessarily infringed by their
|
|
81
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
82
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
83
|
+
institute patent litigation against any entity (including a
|
|
84
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
85
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
86
|
+
or contributory patent infringement, then any patent licenses
|
|
87
|
+
granted to You under this License for that Work shall terminate
|
|
88
|
+
as of the date such litigation is filed.
|
|
89
|
+
|
|
90
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
91
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
92
|
+
modifications, and in Source or Object form, provided that You
|
|
93
|
+
meet the following conditions:
|
|
94
|
+
|
|
95
|
+
(a) You must give any other recipients of the Work or
|
|
96
|
+
Derivative Works a copy of this License; and
|
|
97
|
+
|
|
98
|
+
(b) You must cause any modified files to carry prominent notices
|
|
99
|
+
stating that You changed the files; and
|
|
100
|
+
|
|
101
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
102
|
+
that You distribute, all copyright, patent, trademark, and
|
|
103
|
+
attribution notices from the Source form of the Work,
|
|
104
|
+
excluding those notices that do not pertain to any part of
|
|
105
|
+
the Derivative Works; and
|
|
106
|
+
|
|
107
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
108
|
+
distribution, then any Derivative Works that You distribute must
|
|
109
|
+
include a readable copy of the attribution notices contained
|
|
110
|
+
within such NOTICE file, excluding those notices that do not
|
|
111
|
+
pertain to any part of the Derivative Works, in at least one
|
|
112
|
+
of the following places: within a NOTICE text file distributed
|
|
113
|
+
as part of the Derivative Works; within the Source form or
|
|
114
|
+
documentation, if provided along with the Derivative Works; or,
|
|
115
|
+
within a display generated by the Derivative Works, if and
|
|
116
|
+
wherever such third-party notices normally appear. The contents
|
|
117
|
+
of the NOTICE file are for informational purposes only and
|
|
118
|
+
do not modify the License. You may add Your own attribution
|
|
119
|
+
notices within Derivative Works that You distribute, alongside
|
|
120
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
121
|
+
that such additional attribution notices cannot be construed
|
|
122
|
+
as modifying the License.
|
|
123
|
+
|
|
124
|
+
You may add Your own copyright statement to Your modifications and
|
|
125
|
+
may provide additional or different license terms and conditions
|
|
126
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
127
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
128
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
129
|
+
the conditions stated in this License.
|
|
130
|
+
|
|
131
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
132
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
133
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
134
|
+
this License, without any additional terms or conditions.
|
|
135
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
136
|
+
the terms of any separate license agreement you may have executed
|
|
137
|
+
with Licensor regarding such Contributions.
|
|
138
|
+
|
|
139
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
140
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
141
|
+
except as required for reasonable and customary use in describing the
|
|
142
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
143
|
+
|
|
144
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
145
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
146
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
147
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
148
|
+
implied, including, without limitation, any warranties or conditions
|
|
149
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
150
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
151
|
+
appropriateness of using or redistributing the Work and assume any
|
|
152
|
+
risks associated with Your exercise of permissions under this License.
|
|
153
|
+
|
|
154
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
155
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
156
|
+
unless required by applicable law (such as deliberate and grossly
|
|
157
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
158
|
+
liable to You for damages, including any direct, indirect, special,
|
|
159
|
+
incidental, or consequential damages of any character arising as a
|
|
160
|
+
result of this License or out of the use or inability to use the
|
|
161
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
162
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
163
|
+
other commercial damages or losses), even if such Contributor
|
|
164
|
+
has been advised of the possibility of such damages.
|
|
165
|
+
|
|
166
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
167
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
168
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
169
|
+
or other liability obligations and/or rights consistent with this
|
|
170
|
+
License. However, in accepting such obligations, You may act only
|
|
171
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
172
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
173
|
+
defend, and hold each Contributor harmless for any liability
|
|
174
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
175
|
+
of your accepting any such warranty or additional liability.
|
|
176
|
+
|
|
177
|
+
END OF TERMS AND CONDITIONS
|
|
178
|
+
|
|
179
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
180
|
+
|
|
181
|
+
To apply the Apache License to your work, attach the following
|
|
182
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
183
|
+
replaced with your own identifying information. (Don't include
|
|
184
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
185
|
+
comment syntax for the file format. We also recommend that a
|
|
186
|
+
file or class name and description of purpose be included on the
|
|
187
|
+
same "printed page" as the copyright notice for easier
|
|
188
|
+
identification within third-party archives.
|
|
189
|
+
|
|
190
|
+
Copyright [yyyy] [name of copyright owner]
|
|
191
|
+
|
|
192
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
193
|
+
you may not use this file except in compliance with the License.
|
|
194
|
+
You may obtain a copy of the License at
|
|
195
|
+
|
|
196
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
197
|
+
|
|
198
|
+
Unless required by applicable law or agreed to in writing, software
|
|
199
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
200
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
201
|
+
See the License for the specific language governing permissions and
|
|
202
|
+
limitations under the License.
|
package/README.md
CHANGED
|
@@ -1,3 +1,47 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Zof Agent Evaluations
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Evaluate a JavaScript candidate snapshot against explicit input/output tests in a constrained **local Docker container**. Candidate output is compared in the parent process; reports include test outcomes and fixed reason codes, never raw output or logs. Three public task definitions come from `@zof-ai/reliability-bench` and results use Evidence 1.0.0.
|
|
4
|
+
|
|
5
|
+
Version 0.1.0 is prepared under the Apache-2.0 license. Use the GitHub source installation below; npm registry publication is not claimed.
|
|
6
|
+
|
|
7
|
+
Requires Node.js 22 or newer, a local Docker engine and the pinned Node image documented in [sandbox controls](docs/SANDBOX.md). The evaluator never pulls images, installs candidate dependencies, or uses a remote Docker endpoint.
|
|
8
|
+
|
|
9
|
+
<!-- ZOF_RELEASE_INSTALL_START -->
|
|
10
|
+
## Install from GitHub source
|
|
11
|
+
|
|
12
|
+
Requires Node.js 22 or 24, npm, and Git. Filesystem operations require Linux or macOS with Python 3.10 or newer; Windows file operations fail closed. Clone this repository and explicitly prepare its reviewed release dependencies:
|
|
13
|
+
|
|
14
|
+
```sh
|
|
15
|
+
git clone https://github.com/Zof-AI/zof-agent-evals.git
|
|
16
|
+
cd zof-agent-evals
|
|
17
|
+
node scripts/bootstrap.mjs --allow-network
|
|
18
|
+
npm ci --ignore-scripts --prefer-offline
|
|
19
|
+
npm run build
|
|
20
|
+
npm test
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
The bootstrap downloads only the exact `v0.1.0` dependency assets from [GitHub Releases](https://github.com/Zof-AI/zof-agent-evals/releases), as listed in `scripts/release-dependencies.json`, verifies SHA256 and byte size, and adds them to the npm cache. It does not extract archives, run package scripts, collect application data, or send credentials. Downloads require the explicit flag and follow only HTTPS GitHub asset redirects. Installation then fetches ordinary third-party dependencies from npm. Use a writable npm cache; set `npm_config_cache` consistently for both commands if needed. An unavailable asset, changed hash, or oversized response stops installation.
|
|
24
|
+
|
|
25
|
+
GitHub source and release artifacts are the initial distribution channel. These instructions do not claim that `@zof-ai` packages are published on npm or that the VS Code extension is listed in Marketplace.
|
|
26
|
+
<!-- ZOF_RELEASE_INSTALL_END -->
|
|
27
|
+
|
|
28
|
+
## Three examples
|
|
29
|
+
|
|
30
|
+
```sh
|
|
31
|
+
npm run build
|
|
32
|
+
node dist/cli.js evaluate correct.mjs --root examples --task deduplicate-payments
|
|
33
|
+
node dist/cli.js evaluate incorrect.mjs --root examples --task deduplicate-payments
|
|
34
|
+
node dist/cli.js evaluate profile.mjs --root examples --task compatible-profile
|
|
35
|
+
npm test
|
|
36
|
+
npm run test:container
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Correct and profile exit 0; incorrect exits 1. Missing Docker, rejected input, timeouts or incomplete execution exit 2. Known candidate failures remain failed evidence. The explicit evaluate command authorizes running only the selected bounded `.mjs` file against the selected data-only task. It never executes the source on the host.
|
|
40
|
+
|
|
41
|
+
Candidates export `solve(input)` returning a JSON-compatible value, synchronously or asynchronously. Each case runs in a fresh container. Only one candidate file, one case input, and a fixed harness are copied into a fresh protected staging directory; the original directory is not mounted. Expected values remain outside the container.
|
|
42
|
+
|
|
43
|
+
`node dist/cli.js tasks` lists the actual public tasks. `--task-file task.json` loads an explicitly selected custom data-only task under the same root. `--timeout 500..10000` controls total per-case time including container startup. `--agent name` records a bounded, redacted user-provided label. No environment variables are copied into evidence.
|
|
44
|
+
|
|
45
|
+
Library: `evaluateCandidate({root, candidate, task: idOrTask, timeoutMs?, agent?: {name,version?}})` returns `{taskId,outcomes,evidence}`. `validateTask(value)` validates and copies the data-only task. `SANDBOX_IMAGE` exports the pinned image identifier. `sandboxArguments` exposes the constrained argument builder for auditing, without executing a command.
|
|
46
|
+
|
|
47
|
+
Read [task format](docs/TASKS.md), [sandbox and limitations](docs/SANDBOX.md), and [security reporting](SECURITY.md).
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Security policy
|
|
2
|
+
|
|
3
|
+
The 0.1.x line is the initial supported release line. Local analyses are bounded and offline unless an operation is explicitly documented otherwise. Do not provide production secrets or execute untrusted generated code.
|
|
4
|
+
|
|
5
|
+
Use GitHub private vulnerability reporting when enabled for this repository. If it is unavailable, contact an organization owner through an already verified private channel; do not file credentials, exploit details, or customer data in a public issue. Reports will be triaged, reproduced using synthetic data, fixed and reviewed before coordinated disclosure. No response SLA is promised for this initial release.
|
|
6
|
+
|
|
7
|
+
Untrusted candidates are never imported or executed on the host. They are copied as a bounded module into a protected temporary staging parent and run in a non-root, network-disabled, read-only Docker container with CPU/memory/PID/time/output limits. Raw logs are discarded. No host credential or source-directory mount is allowed. See docs/SANDBOX.md for cleanup verification, local-only engine enforcement, and kernel/runtime limitations. The tool is not a public multi-tenant execution service. Missing isolation produces missing evidence rather than a passing result.
|
|
8
|
+
|
|
9
|
+
Filesystem operations use bounded, directory-relative descriptors on Linux/macOS with Python 3.10 or newer. Symlinks, hard-linked inputs, special files and traversal are rejected. Open descriptors remain attached to the original directory when names change. File contents can still change during a read or snapshot; this is not an atomic filesystem snapshot. Windows filesystem operations fail closed; in-memory JSON validation and analysis do not require the file helper.
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { parseArgs } from 'node:util';
|
|
3
|
+
import { safeReadJson, serializeEvidence } from '@zof-ai/evidence-spec';
|
|
4
|
+
import { listEvaluationTasks } from '@zof-ai/reliability-bench';
|
|
5
|
+
import { evaluateCandidate, validateTask } from './index.js';
|
|
6
|
+
try {
|
|
7
|
+
const { values, positionals } = parseArgs({ options: { root: { type: 'string', default: '.' }, task: { type: 'string', default: 'deduplicate-payments' }, 'task-file': { type: 'string' }, timeout: { type: 'string', default: '2000' }, agent: { type: 'string' }, help: { type: 'boolean' }, version: { type: 'boolean' } }, allowPositionals: true });
|
|
8
|
+
if (values.help)
|
|
9
|
+
console.log('Usage: zof-agent-evals evaluate <candidate.mjs> --root directory [--task id | --task-file relative.json] [--timeout 500..10000] [--agent name]\nCandidates run only in constrained local Docker; no automatic image pull. Exit0 pass,1 candidate failure,2 incomplete/missing/input.\nUse zof-agent-evals tasks to list public data-only tasks.');
|
|
10
|
+
else if (values.version)
|
|
11
|
+
console.log('0.1.0');
|
|
12
|
+
else if (positionals.length === 1 && positionals[0] === 'tasks')
|
|
13
|
+
console.log(JSON.stringify(listEvaluationTasks(), null, 2));
|
|
14
|
+
else if (positionals.length === 2 && positionals[0] === 'evaluate') {
|
|
15
|
+
const task = values['task-file'] ? validateTask(await safeReadJson(values.root, values['task-file'])) : values.task;
|
|
16
|
+
const result = await evaluateCandidate({ root: values.root, candidate: positionals[1], task, timeoutMs: Number(values.timeout), ...(values.agent ? { agent: { name: values.agent } } : {}) });
|
|
17
|
+
process.stdout.write(serializeEvidence(result.evidence));
|
|
18
|
+
process.exitCode = result.evidence.execution.status === 'completed' ? (result.evidence.execution.result === 'pass' ? 0 : 1) : 2;
|
|
19
|
+
}
|
|
20
|
+
else
|
|
21
|
+
throw new Error('usage');
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
console.error(JSON.stringify({ error: 'Invalid candidate, task or evaluation options.' }));
|
|
25
|
+
process.exitCode = 2;
|
|
26
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Evidence } from '@zof-ai/evidence-spec';
|
|
2
|
+
import type { EvaluationTask } from '@zof-ai/reliability-bench';
|
|
3
|
+
export declare const SANDBOX_IMAGE = "node@sha256:43ac6c60b8f89723f746e8a92ce91abd5017e627ce1ddfe4238355d3a30b772c";
|
|
4
|
+
export interface EvaluationOptions {
|
|
5
|
+
root: string;
|
|
6
|
+
candidate: string;
|
|
7
|
+
task: string | EvaluationTask;
|
|
8
|
+
timeoutMs?: number;
|
|
9
|
+
agent?: {
|
|
10
|
+
name: string;
|
|
11
|
+
version?: string;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
export interface EvaluationResult {
|
|
15
|
+
taskId: string;
|
|
16
|
+
outcomes: Array<{
|
|
17
|
+
name: string;
|
|
18
|
+
status: 'passed' | 'failed' | 'error';
|
|
19
|
+
reason: 'MATCH' | 'MISMATCH' | 'CANDIDATE_ERROR' | 'TIMEOUT' | 'OUTPUT_LIMIT' | 'SANDBOX_UNAVAILABLE' | 'CLEANUP_UNCONFIRMED';
|
|
20
|
+
}>;
|
|
21
|
+
evidence: Evidence;
|
|
22
|
+
}
|
|
23
|
+
/** Bounded public data-only task format. No shell commands, harness code, imports or endpoint fields. */
|
|
24
|
+
export declare function validateTask(value: unknown): EvaluationTask;
|
|
25
|
+
export declare function sandboxArguments(directory: string, name: string): string[];
|
|
26
|
+
export declare function evaluateCandidate(options: EvaluationOptions): Promise<EvaluationResult>;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { execFile, spawn } from 'node:child_process';
|
|
2
|
+
import { promisify, isDeepStrictEqual } from 'node:util';
|
|
3
|
+
import { mkdtemp, writeFile, rm, mkdir } from 'node:fs/promises';
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { randomUUID } from 'node:crypto';
|
|
7
|
+
import { createEvidence, safeReadFile, assertBounded, parseJson, redactText } from '@zof-ai/evidence-spec';
|
|
8
|
+
import { getEvaluationTask } from '@zof-ai/reliability-bench';
|
|
9
|
+
export const SANDBOX_IMAGE = 'node@sha256:43ac6c60b8f89723f746e8a92ce91abd5017e627ce1ddfe4238355d3a30b772c';
|
|
10
|
+
const exec = promisify(execFile);
|
|
11
|
+
const MAX_SOURCE_BYTES = 128 * 1024;
|
|
12
|
+
const MAX_OUTPUT_BYTES = 64 * 1024;
|
|
13
|
+
/** Bounded public data-only task format. No shell commands, harness code, imports or endpoint fields. */
|
|
14
|
+
export function validateTask(value) {
|
|
15
|
+
assertBounded(value);
|
|
16
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
17
|
+
throw new Error('Invalid evaluation task.');
|
|
18
|
+
const t = value;
|
|
19
|
+
if (t.version !== '1.0.0' || typeof t.id !== 'string' || !(/^[a-z0-9][a-z0-9-]{0,99}$/).test(t.id) || typeof t.description !== 'string' || t.description.length > 1000 || !Array.isArray(t.cases) || !t.cases.length || t.cases.length > 32 || Object.keys(t).some(k => !['version', 'id', 'description', 'cases'].includes(k)))
|
|
20
|
+
throw new Error('Invalid evaluation task.');
|
|
21
|
+
for (const item of t.cases) {
|
|
22
|
+
if (!item || typeof item !== 'object' || Array.isArray(item))
|
|
23
|
+
throw new Error('Invalid test case.');
|
|
24
|
+
const c = item;
|
|
25
|
+
if (typeof c.name !== 'string' || !c.name || c.name.length > 200 || !Object.hasOwn(c, 'input') || !Object.hasOwn(c, 'expected') || Object.keys(c).some(k => !['name', 'input', 'expected'].includes(k)))
|
|
26
|
+
throw new Error('Invalid test case.');
|
|
27
|
+
if (Buffer.byteLength(JSON.stringify(c)) > 32 * 1024)
|
|
28
|
+
throw new Error('Test case exceeds limit.');
|
|
29
|
+
}
|
|
30
|
+
return structuredClone(value);
|
|
31
|
+
}
|
|
32
|
+
export function sandboxArguments(directory, name) {
|
|
33
|
+
if (!path.isAbsolute(directory) || /[\x00-\x1f,]/.test(directory) || !/^zof-eval-[a-z0-9-]+$/.test(name))
|
|
34
|
+
throw new Error('Invalid sandbox path or name.');
|
|
35
|
+
return ['run', '--rm', '--pull=never', '--name', name, '--network=none', '--read-only', '--cap-drop=ALL', '--security-opt=no-new-privileges', '--pids-limit=32', '--memory=128m', '--memory-swap=128m', '--cpus=0.5', '--user=65534:65534', '--tmpfs=/tmp:rw,noexec,nosuid,size=16m', '--workdir=/task', '--mount', `type=bind,src=${directory},dst=/task,readonly`, SANDBOX_IMAGE, 'node', '--max-old-space-size=64', '/task/runner.mjs'];
|
|
36
|
+
}
|
|
37
|
+
const harness = "import fs from 'node:fs'; const write=process.stdout.write.bind(process.stdout);const stringify=JSON.stringify; const input=JSON.parse(fs.readFileSync('/task/input.json','utf8'));try{const module=await import('/task/candidate.mjs');if(typeof module.solve!=='function')process.exit(41);const output=await module.solve(input);const text=stringify(output);if(typeof text!=='string')process.exit(42);write(text);}catch{process.exit(43);}";
|
|
38
|
+
async function ready() {
|
|
39
|
+
try {
|
|
40
|
+
const context = await exec('docker', ['context', 'inspect', '--format', '{{(index .Endpoints "docker").Host}}'], { timeout: 5000, maxBuffer: 4096 });
|
|
41
|
+
// Only local Unix/named-pipe engines are supported. Never redirect evaluation to a remote endpoint.
|
|
42
|
+
const endpoint = process.env.DOCKER_HOST ?? context.stdout.trim();
|
|
43
|
+
if (!endpoint.startsWith('unix://') && !endpoint.startsWith('npipe://'))
|
|
44
|
+
return;
|
|
45
|
+
await exec('docker', ['--host', endpoint, 'image', 'inspect', SANDBOX_IMAGE, '--format', '{{.Id}}'], { timeout: 5000, maxBuffer: 4096 });
|
|
46
|
+
return endpoint;
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
async function runOne(directory, timeoutMs, endpoint) {
|
|
53
|
+
const name = `zof-eval-${randomUUID()}`;
|
|
54
|
+
let result = { reason: 'CANDIDATE_ERROR' };
|
|
55
|
+
try {
|
|
56
|
+
result = await new Promise(resolve => {
|
|
57
|
+
const child = spawn('docker', ['--host', endpoint, ...sandboxArguments(directory, name)], { stdio: ['ignore', 'pipe', 'pipe'] });
|
|
58
|
+
let stdout = Buffer.alloc(0), count = 0, stopped = false, reason;
|
|
59
|
+
const stop = (why) => { if (!stopped) {
|
|
60
|
+
stopped = true;
|
|
61
|
+
reason = why;
|
|
62
|
+
child.kill('SIGKILL');
|
|
63
|
+
} };
|
|
64
|
+
const timer = setTimeout(() => stop('TIMEOUT'), timeoutMs);
|
|
65
|
+
child.stdout.on('data', (chunk) => { count += chunk.length; if (count > MAX_OUTPUT_BYTES)
|
|
66
|
+
stop('OUTPUT_LIMIT');
|
|
67
|
+
else
|
|
68
|
+
stdout = Buffer.concat([stdout, chunk]); });
|
|
69
|
+
// Raw stderr is counted and discarded; it is never recorded or returned.
|
|
70
|
+
child.stderr.on('data', (chunk) => { count += chunk.length; if (count > MAX_OUTPUT_BYTES)
|
|
71
|
+
stop('OUTPUT_LIMIT'); });
|
|
72
|
+
child.on('error', () => { clearTimeout(timer); resolve({ reason: 'SANDBOX_UNAVAILABLE' }); });
|
|
73
|
+
child.on('close', code => { clearTimeout(timer); if (reason) {
|
|
74
|
+
resolve({ reason });
|
|
75
|
+
return;
|
|
76
|
+
} if (code !== 0) {
|
|
77
|
+
resolve({ reason: code === 125 ? 'SANDBOX_UNAVAILABLE' : 'CANDIDATE_ERROR' });
|
|
78
|
+
return;
|
|
79
|
+
} try {
|
|
80
|
+
resolve({ reason: 'MATCH', value: parseJson(stdout.toString('utf8'), MAX_OUTPUT_BYTES) });
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
resolve({ reason: 'CANDIDATE_ERROR' });
|
|
84
|
+
} });
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
finally {
|
|
88
|
+
// Killing a Docker client alone does not stop its container. Always remove the unique job container.
|
|
89
|
+
let confirmed = false;
|
|
90
|
+
for (let attempt = 0; attempt < 5 && !confirmed; attempt++) {
|
|
91
|
+
try {
|
|
92
|
+
await exec('docker', ['--host', endpoint, 'rm', '-f', name], { timeout: 5000, maxBuffer: 4096 });
|
|
93
|
+
confirmed = true;
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
try {
|
|
97
|
+
const remaining = await exec('docker', ['--host', endpoint, 'ps', '-a', '--filter', `name=^/${name}$`, '--format', '{{.ID}}'], { timeout: 5000, maxBuffer: 4096 });
|
|
98
|
+
confirmed = !remaining.stdout.trim();
|
|
99
|
+
}
|
|
100
|
+
catch { }
|
|
101
|
+
}
|
|
102
|
+
if (!confirmed)
|
|
103
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
104
|
+
}
|
|
105
|
+
if (!confirmed)
|
|
106
|
+
result = { reason: 'CLEANUP_UNCONFIRMED' };
|
|
107
|
+
}
|
|
108
|
+
return result;
|
|
109
|
+
}
|
|
110
|
+
/** Explicit invocation authorizes only this bounded source file and data-only task in local Docker. */
|
|
111
|
+
let activeEvaluations = 0;
|
|
112
|
+
export async function evaluateCandidate(options) {
|
|
113
|
+
if (activeEvaluations >= 2)
|
|
114
|
+
throw new Error('Concurrent evaluation limit reached.');
|
|
115
|
+
activeEvaluations++;
|
|
116
|
+
try {
|
|
117
|
+
return await evaluateInternal(options);
|
|
118
|
+
}
|
|
119
|
+
finally {
|
|
120
|
+
activeEvaluations--;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
async function evaluateInternal(options) {
|
|
124
|
+
if (typeof options.candidate !== 'string' || !options.candidate.endsWith('.mjs'))
|
|
125
|
+
throw new Error('Candidate must be an mjs module.');
|
|
126
|
+
const task = validateTask(typeof options.task === 'string' ? getEvaluationTask(options.task) : options.task);
|
|
127
|
+
const timeout = options.timeoutMs ?? 2000;
|
|
128
|
+
if (!Number.isInteger(timeout) || timeout < 500 || timeout > 10000)
|
|
129
|
+
throw new Error('Timeout must be between 500 and 10000 milliseconds.');
|
|
130
|
+
const source = await safeReadFile(options.root, options.candidate, MAX_SOURCE_BYTES);
|
|
131
|
+
if (options.agent !== undefined) {
|
|
132
|
+
assertBounded(options.agent);
|
|
133
|
+
if (typeof options.agent.name !== 'string' || options.agent.name.length > 100 || (options.agent.version !== undefined && (typeof options.agent.version !== 'string' || options.agent.version.length > 100)))
|
|
134
|
+
throw new Error('Invalid agent metadata.');
|
|
135
|
+
}
|
|
136
|
+
const outcomes = [];
|
|
137
|
+
let status = 'completed';
|
|
138
|
+
const endpoint = await ready();
|
|
139
|
+
if (!endpoint) {
|
|
140
|
+
outcomes.push(...task.cases.map(c => ({ name: redactText(c.name), status: 'error', reason: 'SANDBOX_UNAVAILABLE' })));
|
|
141
|
+
status = 'missing';
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
const stagingRoot = await mkdtemp(path.join(tmpdir(), 'zof-eval-'));
|
|
145
|
+
const directory = path.join(stagingRoot, 'task');
|
|
146
|
+
try {
|
|
147
|
+
await mkdir(directory, { mode: 0o755 });
|
|
148
|
+
await writeFile(path.join(directory, 'candidate.mjs'), source, { mode: 0o444 });
|
|
149
|
+
await writeFile(path.join(directory, 'runner.mjs'), harness, { mode: 0o444 });
|
|
150
|
+
for (const item of task.cases) {
|
|
151
|
+
await rm(path.join(directory, 'input.json'), { force: true });
|
|
152
|
+
await writeFile(path.join(directory, 'input.json'), JSON.stringify(item.input), { mode: 0o444 });
|
|
153
|
+
const result = await runOne(directory, timeout, endpoint);
|
|
154
|
+
const matched = result.reason === 'MATCH' && isDeepStrictEqual(result.value, item.expected);
|
|
155
|
+
const reason = result.reason === 'MATCH' ? (matched ? 'MATCH' : 'MISMATCH') : result.reason;
|
|
156
|
+
outcomes.push({ name: redactText(item.name), status: reason === 'MATCH' ? 'passed' : reason === 'MISMATCH' ? 'failed' : 'error', reason });
|
|
157
|
+
if (reason === 'TIMEOUT' || reason === 'OUTPUT_LIMIT')
|
|
158
|
+
status = 'partial';
|
|
159
|
+
if (reason === 'SANDBOX_UNAVAILABLE' || reason === 'CLEANUP_UNCONFIRMED') {
|
|
160
|
+
status = 'error';
|
|
161
|
+
break;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
finally {
|
|
166
|
+
await rm(stagingRoot, { recursive: true, force: true });
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
const failed = outcomes.some(o => o.status !== 'passed');
|
|
170
|
+
const agent = {};
|
|
171
|
+
if (options.agent) {
|
|
172
|
+
agent.name = redactText(options.agent.name);
|
|
173
|
+
if (options.agent.version)
|
|
174
|
+
agent.version = redactText(options.agent.version);
|
|
175
|
+
}
|
|
176
|
+
const evidence = createEvidence({ tool: { name: 'zof-agent-evals', version: '0.1.0' }, type: 'agent-evaluation', execution: { status, result: status === 'missing' || status === 'error' ? 'unknown' : failed ? 'fail' : 'pass' }, tests: outcomes.map(o => ({ name: o.name, status: o.status })), findings: outcomes.filter(o => o.reason !== 'MATCH').map((o, i) => ({ id: `case-${i + 1}`, severity: 'info', message: o.reason })), environment: { os: process.platform, architecture: process.arch, runtime: process.version }, extensions: { 'org.zof.evaluation': { taskId: task.id, taskVersion: task.version, engine: 'docker', image: SANDBOX_IMAGE, timeoutMs: timeout, agent } } });
|
|
177
|
+
return { taskId: task.id, outcomes, evidence };
|
|
178
|
+
}
|
package/docs/RELEASE.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Release process
|
|
2
|
+
|
|
3
|
+
Version 0.1.0 is prepared under [Apache-2.0](../LICENSE). [GitHub Releases](https://github.com/Zof-AI/zof-agent-evals/releases) is the initial distribution channel. Refer to the release's actual assets and commit SHA; these instructions are not evidence that an upload or installation succeeded.
|
|
4
|
+
|
|
5
|
+
Start with the [source installation](../README.md#install-from-github-source). Run `node scripts/bootstrap.mjs --allow-network`, `npm ci --ignore-scripts --prefer-offline`, `npm run build`, `npm test`, and `npm pack --ignore-scripts`. The bootstrap checks the exact dependency release SHA256 and byte sizes. Do not replace failed checks with an unverified dependency or a `file:` reference in published metadata.
|
|
6
|
+
|
|
7
|
+
Before releasing a new version, inspect source and Git history, dependency licenses, build outputs and every package entry. Resolve security findings; run independent negative and installation tests. Record artifact SHA256 and release commits. Maintain branch protection, private vulnerability reporting, least-privilege workflows and a protected release environment. The manual release-candidate workflow builds artifacts and does not publish automatically. Material release changes require a new review.
|
|
8
|
+
|
|
9
|
+
Publish the reviewed package tarball as a GitHub Release asset. npm registry publication requires independently verified `@zof-ai` scope ownership and an authenticated publisher. Registry publication and registry install acceptance are not claimed by a GitHub release.
|
|
10
|
+
|
|
11
|
+
After publication, retrieve assets anonymously, verify their checksums, install in a clean project, and execute documented examples. Check every intended distribution channel separately. Missing or unrun acceptance is not a pass. Hosted Zof integration remains unavailable until an approved public contract and real service acceptance exist.
|
|
12
|
+
|
|
13
|
+
CI targets Node 22/24 on Linux/macOS. File operations require Python 3.10 or newer and fail closed on Windows; in-memory JSON operations remain portable. Only completed jobs establish platform verification. Docker acceptance runs on Linux; editor acceptance exercises a real VS Code host.
|
package/docs/SANDBOX.md
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Sandbox controls and privacy
|
|
2
|
+
|
|
3
|
+
Candidate code runs only inside the locally available pinned image:
|
|
4
|
+
|
|
5
|
+
`node@sha256:43ac6c60b8f89723f746e8a92ce91abd5017e627ce1ddfe4238355d3a30b772c`
|
|
6
|
+
|
|
7
|
+
The image is the Node 22 slim reference used for this release candidate. Image availability must be arranged explicitly by an authorized operator. No automatic pull or remote Docker endpoint is accepted.
|
|
8
|
+
|
|
9
|
+
Controls: network none, read-only root filesystem, UID/GID 65534, all capabilities dropped, no-new-privileges, Docker's default seccomp profile, PID limit 32, memory/swap 128 MiB, CPU limit 0.5, Node heap 64 MiB, and a 16 MiB noexec/nosuid temporary filesystem. There are no published ports, privileged mode, host PID namespaces, production directories, credential mounts or Docker socket mounts. The only bind mount is the copied task staging directory, read-only.
|
|
10
|
+
|
|
11
|
+
The host staging parent has mode 0700; its mounted child permits the non-root container to read only the copied candidate, current input and fixed harness. The original source directory and expected values are not mounted. At most two evaluations may be active per process; additional calls are rejected. Each case gets a unique container name. A wall-clock timeout kills the Docker client and triggers forced container removal; bounded follow-up checks confirm absence. Cleanup uncertainty is reported as an error and stops further cases. Staging files are removed in finally blocks.
|
|
12
|
+
|
|
13
|
+
Candidate stdout/stderr share a 64 KiB limit. Stderr is discarded immediately. Stdout is parsed only as a JSON result, compared privately with expected output, and discarded. No raw outputs, logs, source, expected values, arbitrary environment or Docker diagnostics are returned. Only explicit agent metadata, outcomes and safe fixed codes are recorded. Known secret patterns are redacted in supplied labels; do not use production credentials in task data.
|
|
14
|
+
|
|
15
|
+
Docker shares a kernel with its Linux host and is not a complete defense against container-runtime/kernel vulnerabilities. This release demonstrates bounded local isolation for synthetic tasks, not multi-tenant hostile-code hosting. Do not expose it as a public compute service or run it with privileged options. There is no unsafe execution mode. Resource limits apply per process/container; an external service would additionally need server-side global quota/authentication/isolation controls and is not implemented.
|
|
16
|
+
|
|
17
|
+
Input file reads require a stable caller-controlled source root, reject symlinks/traversal and never fetch URLs. Local Docker daemon access is a prerequisite and is trusted. Windows engine behavior and hosted runner matrices have not been accepted in this local release candidate; measured container acceptance uses local Docker Desktop on macOS with Linux containers.
|
package/docs/TASKS.md
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Task format 1.0.0
|
|
2
|
+
|
|
3
|
+
```json
|
|
4
|
+
{"version":"1.0.0","id":"double","description":"Double the supplied number","cases":[{"name":"positive","input":2,"expected":4},{"name":"zero","input":0,"expected":0}]}
|
|
5
|
+
```
|
|
6
|
+
|
|
7
|
+
Tasks contain only id, version, description and 1–32 cases. Each case contains a bounded name, JSON input and JSON expected value. Unknown properties are rejected. Cases cannot contain commands, imports, harness source, environment mappings or endpoint settings. A case is at most 32 KiB, with the shared evidence JSON depth and node bounds.
|
|
8
|
+
|
|
9
|
+
Candidate source is a single `.mjs` file of at most 128 KiB. It may use Node built-ins within the container, but has no installed third-party dependencies and cannot fetch them. Multi-file changes, arbitrary repository build scripts, package installation and non-JavaScript candidates are currently unsupported. Save the resulting self-contained module snapshot for evaluation; this tool does not apply patches to a repository.
|
|
10
|
+
|
|
11
|
+
The parent compares actual JSON output with the expected value using strict deep equality. It does not trust candidate-supplied pass labels. Extra logging causes invalid protocol output and cannot be used to forge a summary report. Public tasks can be overfit and are not hidden security tests. Test passing is a measured observation for these cases, not proof of general correctness.
|
|
12
|
+
|
|
13
|
+
Reasons are MATCH, MISMATCH, CANDIDATE_ERROR, TIMEOUT, OUTPUT_LIMIT, SANDBOX_UNAVAILABLE, or CLEANUP_UNCONFIRMED. No caught candidate exception text or output is included. Missing sandbox produces missing/unknown evidence. Candidate mismatches/errors produce failed tests. Timeout/output limit yields partial/fail; unconfirmed cleanup or infrastructure loss yields error/unknown and stops remaining cases.
|
|
14
|
+
|
|
15
|
+
The optional agent name/version is an explicit assertion supplied by the user. No agent identity is authenticated and no model ranking is claimed. Core evidence source repository/commit are unknown because only a selected file snapshot is loaded. Evaluation metadata records task version, pinned image and time limit. The outer environment field identifies the host runner runtime, not the container's untrusted self-report.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function solve(payments){const seen=new Set();return payments.reduce((sum,p)=>{if(seen.has(p.id))return sum;seen.add(p.id);return sum+p.amount;},0);}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function solve(payments){return payments.reduce((sum,p)=>sum+p.amount,0);}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function solve(profile){return {name:profile.name,displayName:profile.name};}
|
package/package.json
CHANGED
|
@@ -1,6 +1,63 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zof-ai/agent-evals",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Constrained local Docker evaluation of untrusted JavaScript candidates",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"bin": {
|
|
15
|
+
"zof-agent-evals": "./dist/cli.js"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist",
|
|
19
|
+
"README.md",
|
|
20
|
+
"LICENSE",
|
|
21
|
+
"CHANGELOG.md",
|
|
22
|
+
"docs",
|
|
23
|
+
"examples",
|
|
24
|
+
"SECURITY.md",
|
|
25
|
+
"CONTRIBUTING.md"
|
|
26
|
+
],
|
|
27
|
+
"engines": {
|
|
28
|
+
"node": ">=22"
|
|
29
|
+
},
|
|
30
|
+
"license": "Apache-2.0",
|
|
31
|
+
"repository": {
|
|
32
|
+
"type": "git",
|
|
33
|
+
"url": "https://github.com/Zof-AI/zof-agent-evals.git"
|
|
34
|
+
},
|
|
35
|
+
"homepage": "https://github.com/Zof-AI/zof-agent-evals#readme",
|
|
36
|
+
"bugs": {
|
|
37
|
+
"url": "https://github.com/Zof-AI/zof-agent-evals/issues"
|
|
38
|
+
},
|
|
39
|
+
"keywords": [
|
|
40
|
+
"verification",
|
|
41
|
+
"testing",
|
|
42
|
+
"zof"
|
|
43
|
+
],
|
|
44
|
+
"publishConfig": {
|
|
45
|
+
"access": "public",
|
|
46
|
+
"provenance": true
|
|
47
|
+
},
|
|
48
|
+
"scripts": {
|
|
49
|
+
"build": "tsc -p tsconfig.json",
|
|
50
|
+
"test": "node --test tests/*.test.mjs",
|
|
51
|
+
"check": "npm run build && npm test",
|
|
52
|
+
"test:container": "node tests/container.mjs",
|
|
53
|
+
"test:package": "node tests/package.mjs"
|
|
54
|
+
},
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"typescript": "5.9.3",
|
|
57
|
+
"@types/node": "22.19.11"
|
|
58
|
+
},
|
|
59
|
+
"dependencies": {
|
|
60
|
+
"@zof-ai/evidence-spec": "0.1.0",
|
|
61
|
+
"@zof-ai/reliability-bench": "0.1.0"
|
|
62
|
+
}
|
|
63
|
+
}
|