harness-everything 0.23.0 → 0.25.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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +23 -0
- package/benchmarks/results/system-one/labeler-holdout-check-2026-09-23/README.md +55 -0
- package/benchmarks/results/system-one/labeler-holdout-check-2026-09-23/report.json +1104 -0
- package/ci/fixtures/system-one-resident-stub.py +3 -0
- package/ci/mechanism-system-one-collect.test.js +158 -0
- package/ci/mechanism-system-one-label.test.js +105 -0
- package/ci/mechanism-system-one-resident.test.js +36 -2
- package/ci/mechanism-system-one-router.test.js +13 -0
- package/ci/mechanism-system-one-train.test.js +82 -0
- package/ci/system-one-train-test.py +55 -0
- package/docs/system-one-corpus.md +4 -0
- package/docs/system-one-routing.md +8 -1
- package/docs/system-one-training.md +131 -0
- package/harness-everything/SKILL.md +1 -1
- package/harness-everything/scripts/system-one/provider.js +12 -5
- package/harness-everything/scripts/system-one/router.js +1 -1
- package/opencode-plugin/plugin.json +1 -1
- package/package.json +1 -1
- package/plugins/harness-everything/.codex-plugin/plugin.json +1 -1
- package/plugins/harness-everything/plugin.json +1 -1
- package/plugins/harness-everything/skills/harness-everything/SKILL.md +1 -1
- package/plugins/harness-everything/skills/harness-everything/scripts/system-one/provider.js +12 -5
- package/plugins/harness-everything/skills/harness-everything/scripts/system-one/router.js +1 -1
- package/sbom.cdx.json +7 -7
- package/scripts/evaluate-system-one.js +1 -1
- package/scripts/system-one-calibrate.js +44 -0
- package/scripts/system-one-collect.js +200 -0
- package/scripts/system-one-label.js +145 -0
- package/scripts/system-one-train.py +168 -0
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# System One training data and weights
|
|
2
|
+
|
|
3
|
+
This document defines how the `harness-routing-v1` tier checkpoint for
|
|
4
|
+
[System One](system-one-routing.md) is trained, calibrated and released
|
|
5
|
+
(issue [#233](https://github.com/dyphn1/Harness-everything/issues/233), Phase 4).
|
|
6
|
+
Gold labels follow [system-one-corpus.md](system-one-corpus.md). The owner-reviewed
|
|
7
|
+
holdout in that document is used only for evaluation. It is never used for training,
|
|
8
|
+
threshold selection or labeler tuning.
|
|
9
|
+
|
|
10
|
+
## Pipeline
|
|
11
|
+
|
|
12
|
+
1. **Collect**: copy the owner's own prompts from local assistant histories into a local
|
|
13
|
+
dataset (`scripts/system-one-collect.js`).
|
|
14
|
+
2. **Label**: an LLM labels each prompt against the owner's rules. The labeler is checked
|
|
15
|
+
once against the holdout, and the owner spot-checks a sample
|
|
16
|
+
(`scripts/system-one-label.js`).
|
|
17
|
+
3. **Train**: train a fresh `cua_s1` `tinyx` scorer on the train split and select the epoch
|
|
18
|
+
on the validation split.
|
|
19
|
+
4. **Calibrate**: choose the confidence and margin thresholds on validation.
|
|
20
|
+
5. **Evaluate**: produce one paired report on the holdout.
|
|
21
|
+
6. **Release**: publish the weights as a GitHub release asset. The installer pins them by
|
|
22
|
+
SHA-256.
|
|
23
|
+
|
|
24
|
+
## Sources
|
|
25
|
+
|
|
26
|
+
Only text the owner typed is collected. Injected context, tool output and assistant text are
|
|
27
|
+
never collected.
|
|
28
|
+
|
|
29
|
+
| Source | Location | User text |
|
|
30
|
+
| --- | --- | --- |
|
|
31
|
+
| Claude Code | `~/.claude/projects/*/*.jsonl` | `type: "user"` records that are not meta or sidechain, string content or text parts |
|
|
32
|
+
| Codex | `~/.codex/sessions/**/*.jsonl` | `event_msg` `user_message.message`. Older sessions without it fall back to `response_item` user text |
|
|
33
|
+
| Copilot CLI | `~/.copilot/session-state/*/events.jsonl` | `user.message` `data.content` |
|
|
34
|
+
| VS Code Copilot Chat | `%APPDATA%/Code/User/workspaceStorage/*/chatSessions/*.json(l)` and `globalStorage/emptyWindowChatSessions` | `requests[].message.text` in snapshots (`kind: 0`) and request pushes (`kind: 2`, key `["requests"]`) |
|
|
35
|
+
|
|
36
|
+
The following are dropped:
|
|
37
|
+
|
|
38
|
+
- Text that starts with an injected wrapper tag, such as `<environment_context>`,
|
|
39
|
+
`<system-reminder>`, `<command-…>`, `<task-notification>` or `<ide_…>`.
|
|
40
|
+
- Tool results.
|
|
41
|
+
- Slash commands.
|
|
42
|
+
- Session-continuation summaries ("This session is being continued…").
|
|
43
|
+
- Empty text.
|
|
44
|
+
|
|
45
|
+
Codex sessions are kept only when they are interactive, meaning `session_meta` has a
|
|
46
|
+
`source` of `vscode` or `cli` and an originator that is neither `Claude Code` nor
|
|
47
|
+
`codex_exec`. Exec sessions, sub-agent threads and sessions another agent started contain
|
|
48
|
+
prompts written by an agent, not by the owner. VS Code terminal notifications
|
|
49
|
+
(`[Terminal … notification …`) are dropped as well.
|
|
50
|
+
|
|
51
|
+
## Filters, families and splits
|
|
52
|
+
|
|
53
|
+
- **Length**: at most 1024 UTF-8 bytes, which matches the checkpoint's `context_tokens`. A
|
|
54
|
+
longer prompt is still refused at inference and falls back to lexical routing. It is never
|
|
55
|
+
truncated.
|
|
56
|
+
- **Deduplication**: exact duplicates after whitespace normalization are dropped. The first
|
|
57
|
+
occurrence is kept.
|
|
58
|
+
- **Holdout leakage**: a prompt is dropped if its character-trigram Jaccard similarity to any
|
|
59
|
+
holdout prompt is at least 0.5. The same applies if either prompt contains the other after
|
|
60
|
+
normalization and the shorter one is more than 12 characters long. This matters because
|
|
61
|
+
the holdout includes rewrites of local prompt shapes.
|
|
62
|
+
- **Family**: the source session. Prompts from one conversation never span splits.
|
|
63
|
+
- **Split**: 15% of families go to `validation`, chosen by a stable SHA-256 of the family ID.
|
|
64
|
+
The rest go to `train`.
|
|
65
|
+
|
|
66
|
+
## Privacy
|
|
67
|
+
|
|
68
|
+
- Collected prompts and labels stay on the local machine under
|
|
69
|
+
`~/.agents/harness-everything/system-one/training/` and are **never committed**. The
|
|
70
|
+
repository holds only the scripts, aggregate statistics and the SHA-256 of each dataset
|
|
71
|
+
file.
|
|
72
|
+
- Email addresses and user home paths are replaced with placeholders before storage. Names
|
|
73
|
+
and product terms are not reliably detectable and remain.
|
|
74
|
+
- The released weights come from a 4-way byte-level classifier with about 0.7M parameters. It
|
|
75
|
+
has no text-generation head, but a trained model can still encode fragments of its data.
|
|
76
|
+
The owner accepted this for their own prompts.
|
|
77
|
+
- Labeling sends the prompts to the Claude API. The owner authorized this.
|
|
78
|
+
|
|
79
|
+
## Labeling
|
|
80
|
+
|
|
81
|
+
- **Labeler**: `claude -p` with Sonnet.
|
|
82
|
+
- Flags: `--setting-sources ""` (no hooks or plugins), `--no-session-persistence` (labeling
|
|
83
|
+
sessions never become collectable history), `--tools ""` and a JSON schema.
|
|
84
|
+
- The instructions contain the gold table and rules from
|
|
85
|
+
[system-one-corpus.md](system-one-corpus.md).
|
|
86
|
+
- Prompts go in batches of 100. `--max-batches N` stops after N batches so a
|
|
87
|
+
memory-constrained host can label in short foreground runs that resume from the
|
|
88
|
+
labels already written. Every returned index must be labeled exactly once with
|
|
89
|
+
`tier1`, `tier2`, `tier3` or `null`. A malformed batch is retried once and then
|
|
90
|
+
recorded as failed. It is never partially accepted.
|
|
91
|
+
- **Labeler check**: the labeler labels the 215 holdout prompts once, before any training
|
|
92
|
+
data is labeled. The report gives agreement with the owner's gold and the per-class recall.
|
|
93
|
+
- Labeling proceeds only when agreement is at least 80%.
|
|
94
|
+
- The instructions are never tuned on the holdout. If the check fails, the owner reviews
|
|
95
|
+
the disagreements and decides how to proceed.
|
|
96
|
+
- **Owner spot-check**: a deterministic sample of the labeled training data is reviewed on
|
|
97
|
+
the review page, and the disagreement rate is reported.
|
|
98
|
+
- Each case shows the preceding prompts from the same session as read-only context.
|
|
99
|
+
Without it, short replies read as confirmations: the first spot-check (103 cases,
|
|
100
|
+
no context) disagreed 51% and was not used to override labels.
|
|
101
|
+
- A checkpoint may be trained before the spot-check, but it is released only when
|
|
102
|
+
disagreement is below 20%.
|
|
103
|
+
- Decisions made with context replace the LLM labels for those cases.
|
|
104
|
+
|
|
105
|
+
## Training and calibration
|
|
106
|
+
|
|
107
|
+
- **Model**: a new `cua_s1` `tinyx` scorer built with `make_system`.
|
|
108
|
+
- Config: `width` 128, `rank` 128, `layers` 2, `heads` 4, `context_tokens` 1024 and
|
|
109
|
+
`option_tokens` 96.
|
|
110
|
+
- Weights are initialized fresh, not from the forms checkpoint, whose domain and
|
|
111
|
+
positional range differ.
|
|
112
|
+
- **Target**: each example is the fixed tier catalog with the index of the gold option.
|
|
113
|
+
`null` maps to `unclassified`.
|
|
114
|
+
- **Optimization**:
|
|
115
|
+
- Cross-entropy, AdamW, a fixed seed and CPU.
|
|
116
|
+
- The epoch with the lowest validation negative log-likelihood is kept.
|
|
117
|
+
- The checkpoint metadata records the dataset hashes, the config and the validation metrics.
|
|
118
|
+
- **Calibration**: `minConfidence` and `minMargin` are chosen on validation to maximize
|
|
119
|
+
coverage, subject to an accepted precision of at least 98%. The router currently uses the
|
|
120
|
+
Phase 0 defaults (0.9 and 0.2). Carrying calibrated thresholds into the manifest is a
|
|
121
|
+
separate contract change.
|
|
122
|
+
|
|
123
|
+
## Evaluation and release
|
|
124
|
+
|
|
125
|
+
- **Evaluation**: `scripts/evaluate-system-one.js` runs once on
|
|
126
|
+
`benchmarks/fixtures/system-one-holdout.json` with the new manifest. The rollout gates and
|
|
127
|
+
the lexical baseline (macro-F1 0.254) decide the result. The report goes under
|
|
128
|
+
`benchmarks/results/system-one/`.
|
|
129
|
+
- **Release**: the `.safetensors` and `.json` files are attached to a GitHub release. The
|
|
130
|
+
installer's pins gain the new model ID, revision, `domain: "harness-routing-v1"` and both
|
|
131
|
+
SHA-256 values. Until every rollout gate passes, the router default stays `off`.
|
|
@@ -10,10 +10,17 @@ const unavailable = reason => ({ status: 'unavailable', reason });
|
|
|
10
10
|
const exact = (value, keys) => value && typeof value === 'object' && !Array.isArray(value)
|
|
11
11
|
&& Object.keys(value).length === keys.length && keys.every(k => Object.hasOwn(value, k));
|
|
12
12
|
const label = value => typeof value === 'string' && value.length > 0 && value.length <= 128 && !/[\u0000-\u001f]/.test(value);
|
|
13
|
+
// Thresholds calibrated on validation for this checkpoint (docs/system-one-training.md).
|
|
14
|
+
const inRange = (v, lo, hi) => typeof v === 'number' && Number.isFinite(v) && v >= lo && v <= hi;
|
|
15
|
+
const validAcceptance = a => exact(a, ['minConfidence', 'minMargin']) && inRange(a.minConfidence, 0.5, 0.99) && inRange(a.minMargin, 0, 0.9);
|
|
16
|
+
// A scored result carries the manifest's thresholds so every caller decides with them.
|
|
17
|
+
const withAcceptance = (result, manifest) => (result && result.status === 'scored' && manifest.acceptance
|
|
18
|
+
? { ...result, acceptance: { ...manifest.acceptance } } : result);
|
|
13
19
|
function validateManifest(m) {
|
|
14
20
|
const required = ['schemaVersion', 'python', 'checkpoint', 'weightsSha256', 'configSha256', 'modelId', 'revision', 'domain'];
|
|
15
21
|
if (!m || typeof m !== 'object' || Array.isArray(m) || required.some(k => !Object.hasOwn(m, k))
|
|
16
|
-
|| Object.keys(m).some(k => ![...required, 'timeoutMs', 'transport', 'idleTimeoutMs'].includes(k)) || m.schemaVersion !== 1
|
|
22
|
+
|| Object.keys(m).some(k => ![...required, 'timeoutMs', 'transport', 'idleTimeoutMs', 'acceptance'].includes(k)) || m.schemaVersion !== 1
|
|
23
|
+
|| (m.acceptance !== undefined && !validAcceptance(m.acceptance))
|
|
17
24
|
|| ['python', 'modelId', 'revision', 'domain'].some(k => typeof m[k] !== 'string' || !m[k].trim() || m[k].length > 4096 || m[k].includes('\0'))
|
|
18
25
|
|| typeof m.checkpoint !== 'string' || !path.isAbsolute(m.checkpoint) || path.extname(m.checkpoint) !== '.safetensors'
|
|
19
26
|
|| ['weightsSha256', 'configSha256'].some(k => typeof m[k] !== 'string' || !/^[a-f0-9]{64}$/.test(m[k]))
|
|
@@ -50,8 +57,8 @@ function score(request, manifestPath) {
|
|
|
50
57
|
validateRequest(request);
|
|
51
58
|
let manifest;
|
|
52
59
|
try { manifest = readManifest(manifestPath); } catch (_) { return unavailable('provider-config'); }
|
|
53
|
-
if (manifest.transport === 'resident') return require('./resident').score({ ...request }, manifest, manifestPath);
|
|
54
|
-
return oneShot(request, manifest);
|
|
60
|
+
if (manifest.transport === 'resident') return withAcceptance(require('./resident').score({ ...request }, manifest, manifestPath), manifest);
|
|
61
|
+
return withAcceptance(oneShot(request, manifest), manifest);
|
|
55
62
|
}
|
|
56
63
|
// Async callers (the hook entry) reach a resident server without the sync worker bridge.
|
|
57
64
|
// One-shot has no server to overlap with, so it runs the same child synchronously.
|
|
@@ -59,8 +66,8 @@ async function scoreAsync(request, manifestPath) {
|
|
|
59
66
|
validateRequest(request);
|
|
60
67
|
let manifest;
|
|
61
68
|
try { manifest = readManifest(manifestPath); } catch (_) { return unavailable('provider-config'); }
|
|
62
|
-
if (manifest.transport === 'resident') return require('./resident').scoreAsync({ ...request }, manifest, manifestPath);
|
|
63
|
-
return oneShot(request, manifest);
|
|
69
|
+
if (manifest.transport === 'resident') return withAcceptance(await require('./resident').scoreAsync({ ...request }, manifest, manifestPath), manifest);
|
|
70
|
+
return withAcceptance(oneShot(request, manifest), manifest);
|
|
64
71
|
}
|
|
65
72
|
// Send the exact validated manifest to the child on stdin: no config reread race.
|
|
66
73
|
function oneShot(request, manifest) {
|
|
@@ -26,7 +26,7 @@ function selectTier(input, env = process.env, scorer = provider.score) {
|
|
|
26
26
|
const allowed = ['provider-timeout', 'provider-output-limit', 'provider-exit', 'provider-json', 'provider-config', 'provider-unavailable', 'provider-starting'];
|
|
27
27
|
return fallback(allowed.includes(result?.reason) ? result.reason : 'provider-unavailable');
|
|
28
28
|
}
|
|
29
|
-
const decision = decide(request, result.response);
|
|
29
|
+
const decision = decide(request, result.response, result.acceptance ? { ...result.acceptance } : {});
|
|
30
30
|
if (decision.selectedId === 'unclassified') Object.assign(decision, { selectedId: null, status: 'abstain', reason: 'unclassified' });
|
|
31
31
|
const accepted = decision.status === 'accepted';
|
|
32
32
|
const belowFloor = accepted && input.floor != null && RANK[decision.selectedId] < RANK[input.floor];
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
|
|
3
3
|
"name": "harness-everything",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.25.0",
|
|
5
5
|
"description": "Invariant-first engineering harness for routing, verification, and failure recovery while preserving agent autonomy.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Miya Daniel",
|
|
@@ -10,10 +10,17 @@ const unavailable = reason => ({ status: 'unavailable', reason });
|
|
|
10
10
|
const exact = (value, keys) => value && typeof value === 'object' && !Array.isArray(value)
|
|
11
11
|
&& Object.keys(value).length === keys.length && keys.every(k => Object.hasOwn(value, k));
|
|
12
12
|
const label = value => typeof value === 'string' && value.length > 0 && value.length <= 128 && !/[\u0000-\u001f]/.test(value);
|
|
13
|
+
// Thresholds calibrated on validation for this checkpoint (docs/system-one-training.md).
|
|
14
|
+
const inRange = (v, lo, hi) => typeof v === 'number' && Number.isFinite(v) && v >= lo && v <= hi;
|
|
15
|
+
const validAcceptance = a => exact(a, ['minConfidence', 'minMargin']) && inRange(a.minConfidence, 0.5, 0.99) && inRange(a.minMargin, 0, 0.9);
|
|
16
|
+
// A scored result carries the manifest's thresholds so every caller decides with them.
|
|
17
|
+
const withAcceptance = (result, manifest) => (result && result.status === 'scored' && manifest.acceptance
|
|
18
|
+
? { ...result, acceptance: { ...manifest.acceptance } } : result);
|
|
13
19
|
function validateManifest(m) {
|
|
14
20
|
const required = ['schemaVersion', 'python', 'checkpoint', 'weightsSha256', 'configSha256', 'modelId', 'revision', 'domain'];
|
|
15
21
|
if (!m || typeof m !== 'object' || Array.isArray(m) || required.some(k => !Object.hasOwn(m, k))
|
|
16
|
-
|| Object.keys(m).some(k => ![...required, 'timeoutMs', 'transport', 'idleTimeoutMs'].includes(k)) || m.schemaVersion !== 1
|
|
22
|
+
|| Object.keys(m).some(k => ![...required, 'timeoutMs', 'transport', 'idleTimeoutMs', 'acceptance'].includes(k)) || m.schemaVersion !== 1
|
|
23
|
+
|| (m.acceptance !== undefined && !validAcceptance(m.acceptance))
|
|
17
24
|
|| ['python', 'modelId', 'revision', 'domain'].some(k => typeof m[k] !== 'string' || !m[k].trim() || m[k].length > 4096 || m[k].includes('\0'))
|
|
18
25
|
|| typeof m.checkpoint !== 'string' || !path.isAbsolute(m.checkpoint) || path.extname(m.checkpoint) !== '.safetensors'
|
|
19
26
|
|| ['weightsSha256', 'configSha256'].some(k => typeof m[k] !== 'string' || !/^[a-f0-9]{64}$/.test(m[k]))
|
|
@@ -50,8 +57,8 @@ function score(request, manifestPath) {
|
|
|
50
57
|
validateRequest(request);
|
|
51
58
|
let manifest;
|
|
52
59
|
try { manifest = readManifest(manifestPath); } catch (_) { return unavailable('provider-config'); }
|
|
53
|
-
if (manifest.transport === 'resident') return require('./resident').score({ ...request }, manifest, manifestPath);
|
|
54
|
-
return oneShot(request, manifest);
|
|
60
|
+
if (manifest.transport === 'resident') return withAcceptance(require('./resident').score({ ...request }, manifest, manifestPath), manifest);
|
|
61
|
+
return withAcceptance(oneShot(request, manifest), manifest);
|
|
55
62
|
}
|
|
56
63
|
// Async callers (the hook entry) reach a resident server without the sync worker bridge.
|
|
57
64
|
// One-shot has no server to overlap with, so it runs the same child synchronously.
|
|
@@ -59,8 +66,8 @@ async function scoreAsync(request, manifestPath) {
|
|
|
59
66
|
validateRequest(request);
|
|
60
67
|
let manifest;
|
|
61
68
|
try { manifest = readManifest(manifestPath); } catch (_) { return unavailable('provider-config'); }
|
|
62
|
-
if (manifest.transport === 'resident') return require('./resident').scoreAsync({ ...request }, manifest, manifestPath);
|
|
63
|
-
return oneShot(request, manifest);
|
|
69
|
+
if (manifest.transport === 'resident') return withAcceptance(await require('./resident').scoreAsync({ ...request }, manifest, manifestPath), manifest);
|
|
70
|
+
return withAcceptance(oneShot(request, manifest), manifest);
|
|
64
71
|
}
|
|
65
72
|
// Send the exact validated manifest to the child on stdin: no config reread race.
|
|
66
73
|
function oneShot(request, manifest) {
|
|
@@ -26,7 +26,7 @@ function selectTier(input, env = process.env, scorer = provider.score) {
|
|
|
26
26
|
const allowed = ['provider-timeout', 'provider-output-limit', 'provider-exit', 'provider-json', 'provider-config', 'provider-unavailable', 'provider-starting'];
|
|
27
27
|
return fallback(allowed.includes(result?.reason) ? result.reason : 'provider-unavailable');
|
|
28
28
|
}
|
|
29
|
-
const decision = decide(request, result.response);
|
|
29
|
+
const decision = decide(request, result.response, result.acceptance ? { ...result.acceptance } : {});
|
|
30
30
|
if (decision.selectedId === 'unclassified') Object.assign(decision, { selectedId: null, status: 'abstain', reason: 'unclassified' });
|
|
31
31
|
const accepted = decision.status === 'accepted';
|
|
32
32
|
const belowFloor = accepted && input.floor != null && RANK[decision.selectedId] < RANK[input.floor];
|
package/sbom.cdx.json
CHANGED
|
@@ -2,23 +2,23 @@
|
|
|
2
2
|
"$schema": "https://cyclonedx.org/schema/bom-1.7.schema.json",
|
|
3
3
|
"bomFormat": "CycloneDX",
|
|
4
4
|
"specVersion": "1.7",
|
|
5
|
-
"serialNumber": "urn:uuid:
|
|
5
|
+
"serialNumber": "urn:uuid:a66915ec-6b2f-51ca-a5d4-4a642ce3a914",
|
|
6
6
|
"version": 1,
|
|
7
7
|
"metadata": {
|
|
8
|
-
"timestamp": "2026-09-
|
|
8
|
+
"timestamp": "2026-09-23T15:52:59.593Z",
|
|
9
9
|
"tools": [
|
|
10
10
|
{
|
|
11
11
|
"vendor": "Harness Everything",
|
|
12
12
|
"name": "generate-sbom.js",
|
|
13
|
-
"version": "0.
|
|
13
|
+
"version": "0.25.0"
|
|
14
14
|
}
|
|
15
15
|
],
|
|
16
16
|
"component": {
|
|
17
17
|
"type": "application",
|
|
18
|
-
"bom-ref": "pkg:npm/harness-everything@0.
|
|
18
|
+
"bom-ref": "pkg:npm/harness-everything@0.25.0",
|
|
19
19
|
"name": "harness-everything",
|
|
20
|
-
"version": "0.
|
|
21
|
-
"purl": "pkg:npm/harness-everything@0.
|
|
20
|
+
"version": "0.25.0",
|
|
21
|
+
"purl": "pkg:npm/harness-everything@0.25.0"
|
|
22
22
|
}
|
|
23
23
|
},
|
|
24
24
|
"components": [
|
|
@@ -1999,7 +1999,7 @@
|
|
|
1999
1999
|
]
|
|
2000
2000
|
},
|
|
2001
2001
|
{
|
|
2002
|
-
"ref": "pkg:npm/harness-everything@0.
|
|
2002
|
+
"ref": "pkg:npm/harness-everything@0.25.0",
|
|
2003
2003
|
"dependsOn": [
|
|
2004
2004
|
"pkg:npm/%40commitlint/cli@19.8.1",
|
|
2005
2005
|
"pkg:npm/%40commitlint/config-conventional@19.8.1",
|
|
@@ -29,7 +29,7 @@ async function run(corpus, manifest) {
|
|
|
29
29
|
for (let i = 0; i < 2; i++) {
|
|
30
30
|
const start = performance.now();
|
|
31
31
|
const result = await scoreAsync(c.request, manifest);
|
|
32
|
-
const decision = result.status === 'scored' ? decide(c.request, result.response) : result;
|
|
32
|
+
const decision = result.status === 'scored' ? decide(c.request, result.response, result.acceptance ? { ...result.acceptance } : {}) : result;
|
|
33
33
|
if (decision.selectedId === 'unclassified') Object.assign(decision, { status: 'abstain', reason: 'unclassified', selectedId: null });
|
|
34
34
|
const scores = ['accepted', 'abstain'].includes(decision.status) ? result.response.scores.map(s => s.probability) : null;
|
|
35
35
|
runs.push({ decision, scores, latencyMs: performance.now() - start, coldStart: !warm });
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
// Chooses System One acceptance thresholds on validation scores (docs/system-one-training.md).
|
|
4
|
+
// The holdout is never used here.
|
|
5
|
+
const fs = require('node:fs');
|
|
6
|
+
|
|
7
|
+
const IDS = ['tier1', 'tier2', 'tier3', 'unclassified'];
|
|
8
|
+
// Same acceptance rule as contract.decide: top probability and top-minus-second margin, unclassified abstains.
|
|
9
|
+
function decideRow(probs, { minConfidence, minMargin }) {
|
|
10
|
+
const order = probs.map((p, i) => [p, i]).sort((a, b) => b[0] - a[0] || a[1] - b[1]);
|
|
11
|
+
const [top, second] = order;
|
|
12
|
+
const margin = top[0] - second[0];
|
|
13
|
+
if (margin === 0 || top[0] < minConfidence || margin < minMargin || IDS[top[1]] === 'unclassified') return null;
|
|
14
|
+
return IDS[top[1]];
|
|
15
|
+
}
|
|
16
|
+
function calibrate(rows, { minPrecision = 0.98 } = {}) {
|
|
17
|
+
let best = null;
|
|
18
|
+
for (let c = 50; c <= 99; c++) {
|
|
19
|
+
for (let m = 0; m <= 90; m += 5) {
|
|
20
|
+
const t = { minConfidence: c / 100, minMargin: m / 100 };
|
|
21
|
+
let accepted = 0; let correct = 0;
|
|
22
|
+
for (const r of rows) { const d = decideRow(r.probs, t); if (d) { accepted++; if (d === (r.gold === null ? 'unclassified' : r.gold)) correct++; } }
|
|
23
|
+
const precision = accepted ? correct / accepted : 0;
|
|
24
|
+
if (!accepted || precision < minPrecision) continue;
|
|
25
|
+
const coverage = accepted / rows.length;
|
|
26
|
+
// Highest coverage wins; ties keep the first (lowest) thresholds scanned.
|
|
27
|
+
if (!best || coverage > best.coverage) best = { ...t, coverage, precision, accepted };
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return best ? { feasible: true, rows: rows.length, minPrecision, ...best }
|
|
31
|
+
: { feasible: false, rows: rows.length, minPrecision, minConfidence: null, minMargin: null, coverage: 0, precision: null, accepted: 0 };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (require.main === module) {
|
|
35
|
+
try {
|
|
36
|
+
const [input, output, extra] = process.argv.slice(2);
|
|
37
|
+
if (!input || !output || extra) throw new Error('usage: system-one-calibrate.js <val-scores.jsonl> <calibration.json>');
|
|
38
|
+
const rows = fs.readFileSync(input, 'utf8').split('\n').filter(Boolean).map(l => JSON.parse(l));
|
|
39
|
+
const result = calibrate(rows);
|
|
40
|
+
fs.writeFileSync(output, `${JSON.stringify(result, null, 2)}\n`);
|
|
41
|
+
console.log(JSON.stringify(result));
|
|
42
|
+
} catch (err) { console.error(`system-one-calibrate: ${err.message}`); process.exitCode = 1; }
|
|
43
|
+
}
|
|
44
|
+
module.exports = { decideRow, calibrate };
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
// Collects the owner's own prompts from local assistant histories into a local training dataset.
|
|
4
|
+
// See docs/system-one-training.md. The dataset is never written inside the repository.
|
|
5
|
+
const fs = require('node:fs');
|
|
6
|
+
const os = require('node:os');
|
|
7
|
+
const path = require('node:path');
|
|
8
|
+
const { createHash } = require('node:crypto');
|
|
9
|
+
const { StringDecoder } = require('node:string_decoder');
|
|
10
|
+
|
|
11
|
+
const REPO = path.resolve(__dirname, '..');
|
|
12
|
+
const MAX_BYTES = 1024;
|
|
13
|
+
const VALIDATION_PERCENT = 15;
|
|
14
|
+
// Whole-text wrappers injected by hosts or hooks; text that starts with one is not typed by the owner.
|
|
15
|
+
const WRAPPER = /^(<(environment_context|user_instructions|recommended_plugins|send_user_message_question_reply|command-|local-command|task-notification|bash-|turn_aborted|user_shell_command|skill)|\[Terminal [0-9a-f-]+ notification)/i;
|
|
16
|
+
// Codex sessions typed by the owner: interactive IDE/desktop/terminal ones, not exec lanes, sub-agents or agent-started sessions.
|
|
17
|
+
const interactiveCodex = meta => !!meta && ['vscode', 'cli'].includes(meta.source) && !['Claude Code', 'codex_exec'].includes(meta.originator);
|
|
18
|
+
// Blocks a host inserts inside typed text; they are removed and the rest is kept.
|
|
19
|
+
const INSERTED = /<(system-reminder|ide_[a-z_]+)>[\s\S]*?<\/\1>/gi;
|
|
20
|
+
const sha = text => createHash('sha256').update(text, 'utf8').digest('hex');
|
|
21
|
+
|
|
22
|
+
function* readLines(file) {
|
|
23
|
+
let fd;
|
|
24
|
+
try { fd = fs.openSync(file, 'r'); } catch (_) { return; }
|
|
25
|
+
const decoder = new StringDecoder('utf8'); const buf = Buffer.alloc(4 * 1024 * 1024); let rest = '';
|
|
26
|
+
try {
|
|
27
|
+
for (let n; (n = fs.readSync(fd, buf, 0, buf.length, null)) > 0;) {
|
|
28
|
+
const parts = (rest + decoder.write(buf.subarray(0, n))).split('\n');
|
|
29
|
+
rest = parts.pop();
|
|
30
|
+
yield* parts;
|
|
31
|
+
}
|
|
32
|
+
rest += decoder.end();
|
|
33
|
+
if (rest) yield rest;
|
|
34
|
+
} finally { fs.closeSync(fd); }
|
|
35
|
+
}
|
|
36
|
+
const parse = line => { try { return JSON.parse(line); } catch (_) { return null; } };
|
|
37
|
+
function walk(dir, pred, out = [], depth = 0) {
|
|
38
|
+
if (depth > 8) return out;
|
|
39
|
+
let entries = [];
|
|
40
|
+
try { entries = fs.readdirSync(dir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name)); } catch (_) { return out; }
|
|
41
|
+
for (const e of entries) {
|
|
42
|
+
const p = path.join(dir, e.name);
|
|
43
|
+
if (e.isDirectory()) walk(p, pred, out, depth + 1); else if (pred(p)) out.push(p);
|
|
44
|
+
}
|
|
45
|
+
return out;
|
|
46
|
+
}
|
|
47
|
+
// Typed text after removing inserted blocks; null when the text is a wrapper or nothing is left.
|
|
48
|
+
function typed(text) {
|
|
49
|
+
if (typeof text !== 'string') return null;
|
|
50
|
+
const t = text.replace(INSERTED, '').trim();
|
|
51
|
+
return !t || WRAPPER.test(t) ? null : t;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function fromClaude(file) {
|
|
55
|
+
const rows = [];
|
|
56
|
+
for (const line of readLines(file)) {
|
|
57
|
+
const e = parse(line);
|
|
58
|
+
if (!e || e.type !== 'user' || e.isMeta || e.isSidechain) continue;
|
|
59
|
+
const c = e.message && e.message.content;
|
|
60
|
+
const raw = typeof c === 'string' ? c : Array.isArray(c) ? c.filter(p => p && p.type === 'text').map(p => p.text).join('\n') : null;
|
|
61
|
+
const t = typed(raw); if (t) rows.push(t);
|
|
62
|
+
}
|
|
63
|
+
return rows;
|
|
64
|
+
}
|
|
65
|
+
function fromCodex(file) {
|
|
66
|
+
const events = []; const items = []; let meta = null;
|
|
67
|
+
for (const line of readLines(file)) {
|
|
68
|
+
const e = parse(line); if (!e || !e.payload) continue;
|
|
69
|
+
if (e.type === 'session_meta' && !meta) { meta = e.payload; if (!interactiveCodex(meta)) return []; continue; }
|
|
70
|
+
if (e.type === 'event_msg' && e.payload.type === 'user_message') { const t = typed(e.payload.message); if (t) events.push(t); }
|
|
71
|
+
if (e.type === 'response_item' && e.payload.role === 'user') for (const c of e.payload.content || []) { const t = typed(c && c.text); if (t) items.push(t); }
|
|
72
|
+
}
|
|
73
|
+
if (!interactiveCodex(meta)) return [];
|
|
74
|
+
// Event messages are the typed text; response items repeat it with injected context, so they are a fallback only.
|
|
75
|
+
return events.length ? events : items;
|
|
76
|
+
}
|
|
77
|
+
function fromCopilotCli(file) {
|
|
78
|
+
const rows = [];
|
|
79
|
+
for (const line of readLines(file)) {
|
|
80
|
+
const e = parse(line);
|
|
81
|
+
if (e && e.type === 'user.message' && e.data) { const t = typed(e.data.content); if (t) rows.push(t); }
|
|
82
|
+
}
|
|
83
|
+
return rows;
|
|
84
|
+
}
|
|
85
|
+
function fromVscode(file) {
|
|
86
|
+
const rows = [];
|
|
87
|
+
const take = requests => { for (const r of Array.isArray(requests) ? requests : [requests]) { const t = typed(r && r.message && r.message.text); if (t) rows.push(t); } };
|
|
88
|
+
if (file.endsWith('.json')) {
|
|
89
|
+
let j = null; try { j = JSON.parse(fs.readFileSync(file, 'utf8')); } catch (_) { /* unreadable snapshot */ }
|
|
90
|
+
if (j) take(j.requests || []);
|
|
91
|
+
return rows;
|
|
92
|
+
}
|
|
93
|
+
for (const line of readLines(file)) {
|
|
94
|
+
const e = parse(line); if (!e) continue;
|
|
95
|
+
if (e.kind === 0 && e.v) take(e.v.requests || []);
|
|
96
|
+
else if (e.kind === 2 && Array.isArray(e.k) && e.k.length === 1 && e.k[0] === 'requests') take(e.v);
|
|
97
|
+
}
|
|
98
|
+
return rows;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Every prompt with its source and a family derived from its session file (no path is stored).
|
|
102
|
+
function readSources({ home, appdata }) {
|
|
103
|
+
const sources = [
|
|
104
|
+
['claude', walk(path.join(home, '.claude', 'projects'), p => p.endsWith('.jsonl')), fromClaude],
|
|
105
|
+
['codex', walk(path.join(home, '.codex', 'sessions'), p => p.endsWith('.jsonl')), fromCodex],
|
|
106
|
+
['copilot-cli', walk(path.join(home, '.copilot', 'session-state'), p => path.basename(p) === 'events.jsonl'), fromCopilotCli],
|
|
107
|
+
['vscode', [
|
|
108
|
+
...walk(path.join(appdata, 'Code', 'User', 'workspaceStorage'), p => /[\\/]chatSessions[\\/][^\\/]+\.jsonl?$/.test(p)),
|
|
109
|
+
...walk(path.join(appdata, 'Code', 'User', 'globalStorage', 'emptyWindowChatSessions'), p => /\.jsonl?$/.test(p)),
|
|
110
|
+
], fromVscode],
|
|
111
|
+
];
|
|
112
|
+
const rows = [];
|
|
113
|
+
for (const [source, files, read] of sources) {
|
|
114
|
+
for (const file of files) {
|
|
115
|
+
const family = `${source}:${sha(path.relative(home, file)).slice(0, 16)}`;
|
|
116
|
+
for (const text of read(file)) rows.push({ source, family, text });
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return rows;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function keep(text) {
|
|
123
|
+
const t = typeof text === 'string' ? text.trim() : '';
|
|
124
|
+
return t.length > 0 && Buffer.byteLength(t, 'utf8') <= MAX_BYTES && !/^\/\S/.test(t) && !t.startsWith('This session is being continued');
|
|
125
|
+
}
|
|
126
|
+
function redact(text) {
|
|
127
|
+
return text
|
|
128
|
+
.replace(/[\w.+-]+@[\w-]+\.[\w.-]+/g, '<email>')
|
|
129
|
+
.replace(/[A-Za-z]:\\Users\\[^\\\s]+/gi, '<home>')
|
|
130
|
+
.replace(/[A-Za-z]:\/Users\/[^/\s]+/gi, '<home>')
|
|
131
|
+
.replace(/\/(home|Users)\/[^/\s]+/g, '<home>');
|
|
132
|
+
}
|
|
133
|
+
const squash = text => text.replace(/\s+/g, ' ').trim();
|
|
134
|
+
function dedupe(rows) {
|
|
135
|
+
const seen = new Set();
|
|
136
|
+
return rows.filter(r => { const k = squash(r.text); if (seen.has(k)) return false; seen.add(k); return true; });
|
|
137
|
+
}
|
|
138
|
+
const normalize = text => text.toLowerCase().replace(/[\p{P}\p{S}]+/gu, ' ').replace(/\s+/g, ' ').trim();
|
|
139
|
+
function trigrams(text) {
|
|
140
|
+
const s = new Set();
|
|
141
|
+
if (text.length < 3) { if (text) s.add(text); return s; }
|
|
142
|
+
for (let i = 0; i <= text.length - 3; i++) s.add(text.slice(i, i + 3));
|
|
143
|
+
return s;
|
|
144
|
+
}
|
|
145
|
+
function jaccard(a, b) {
|
|
146
|
+
const x = trigrams(a); const y = trigrams(b);
|
|
147
|
+
if (!x.size && !y.size) return 1;
|
|
148
|
+
let inter = 0; for (const g of x) if (y.has(g)) inter++;
|
|
149
|
+
return inter / (x.size + y.size - inter);
|
|
150
|
+
}
|
|
151
|
+
// True when a prompt near-duplicates any holdout prompt (evaluation data must not leak into training).
|
|
152
|
+
function leakGuard(holdoutTexts) {
|
|
153
|
+
const held = holdoutTexts.map(t => { const n = normalize(t); return { n, g: trigrams(n) }; });
|
|
154
|
+
return text => {
|
|
155
|
+
const n = normalize(text); const g = trigrams(n);
|
|
156
|
+
return held.some(h => {
|
|
157
|
+
const [short, long] = n.length <= h.n.length ? [n, h.n] : [h.n, n];
|
|
158
|
+
if (short.length > 12 && long.includes(short)) return true;
|
|
159
|
+
let inter = 0; for (const x of g) if (h.g.has(x)) inter++;
|
|
160
|
+
return inter / (g.size + h.g.size - inter) >= 0.5;
|
|
161
|
+
});
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
const splitFor = family => (parseInt(sha(family).slice(0, 8), 16) % 100 < VALIDATION_PERCENT ? 'validation' : 'train');
|
|
165
|
+
|
|
166
|
+
function build({ home, appdata, holdoutTexts }) {
|
|
167
|
+
const raw = readSources({ home, appdata });
|
|
168
|
+
const dropped = { filtered: 0, duplicate: 0, leak: 0 };
|
|
169
|
+
const kept = raw.filter(r => keep(r.text) || (dropped.filtered++, false)).map(r => ({ ...r, text: redact(r.text.trim()) }));
|
|
170
|
+
const unique = dedupe(kept); dropped.duplicate = kept.length - unique.length;
|
|
171
|
+
const leaks = leakGuard(holdoutTexts);
|
|
172
|
+
const rows = unique.filter(r => !leaks(r.text) || (dropped.leak++, false))
|
|
173
|
+
.map(r => ({ id: sha(squash(r.text)).slice(0, 16), family: r.family, source: r.source, split: splitFor(r.family), text: r.text }));
|
|
174
|
+
const count = key => rows.reduce((a, r) => ((a[r[key]] = (a[r[key]] || 0) + 1), a), {});
|
|
175
|
+
return { rows, stats: { extracted: raw.length, kept: rows.length, dropped, bySource: count('source'), bySplit: count('split'),
|
|
176
|
+
families: new Set(rows.map(r => r.family)).size, zhShare: rows.length ? +(rows.filter(r => /[一-鿿]/.test(r.text)).length / rows.length).toFixed(3) : 0 } };
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (require.main === module) {
|
|
180
|
+
const args = process.argv.slice(2);
|
|
181
|
+
const opt = name => { const i = args.indexOf(name); return i >= 0 ? args[i + 1] : undefined; };
|
|
182
|
+
try {
|
|
183
|
+
const out = path.resolve(opt('--out') || path.join(os.homedir(), '.agents', 'harness-everything', 'system-one', 'training'));
|
|
184
|
+
const rel = path.relative(REPO, out);
|
|
185
|
+
if (!rel.startsWith('..') && !path.isAbsolute(rel)) throw new Error('refusing to write the dataset inside the repository');
|
|
186
|
+
const holdoutFile = opt('--holdout') || path.join(REPO, 'benchmarks', 'fixtures', 'system-one-holdout.json');
|
|
187
|
+
const holdoutTexts = JSON.parse(fs.readFileSync(holdoutFile, 'utf8')).cases.map(c => c.request.context);
|
|
188
|
+
const { rows, stats } = build({ home: opt('--home') || os.homedir(), appdata: opt('--appdata') || process.env.APPDATA || '', holdoutTexts });
|
|
189
|
+
fs.mkdirSync(out, { recursive: true });
|
|
190
|
+
const body = rows.map(r => JSON.stringify(r)).join('\n') + '\n';
|
|
191
|
+
fs.writeFileSync(path.join(out, 'prompts.jsonl'), body, { mode: 0o600 });
|
|
192
|
+
const report = { ...stats, sha256: sha(body) };
|
|
193
|
+
fs.writeFileSync(path.join(out, 'collect-stats.json'), `${JSON.stringify(report, null, 2)}\n`);
|
|
194
|
+
console.log(JSON.stringify(report));
|
|
195
|
+
} catch (err) {
|
|
196
|
+
console.error(`system-one-collect: ${err.message}`);
|
|
197
|
+
process.exitCode = 1;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
module.exports = { readSources, keep, redact, dedupe, leakGuard, jaccard, splitFor, build };
|