@chopin-ai/core 0.2.0 → 0.3.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/dist/evidence/classify.d.ts +31 -0
- package/dist/evidence/classify.js +87 -0
- package/dist/evidence/classify.js.map +1 -0
- package/dist/evidence/corrections.d.ts +14 -0
- package/dist/evidence/corrections.js +61 -0
- package/dist/evidence/corrections.js.map +1 -0
- package/dist/evidence/failures.d.ts +44 -0
- package/dist/evidence/failures.js +91 -0
- package/dist/evidence/failures.js.map +1 -0
- package/dist/evidence/messages-jsonl.d.ts +7 -0
- package/dist/evidence/messages-jsonl.js +97 -0
- package/dist/evidence/messages-jsonl.js.map +1 -0
- package/dist/evidence/points.d.ts +26 -0
- package/dist/evidence/points.js +72 -0
- package/dist/evidence/points.js.map +1 -0
- package/dist/evidence/sanitize.d.ts +16 -0
- package/dist/evidence/sanitize.js +70 -0
- package/dist/evidence/sanitize.js.map +1 -0
- package/dist/evidence/signals.d.ts +25 -0
- package/dist/evidence/signals.js +28 -0
- package/dist/evidence/signals.js.map +1 -0
- package/dist/evidence/usage.d.ts +12 -0
- package/dist/evidence/usage.js +24 -0
- package/dist/evidence/usage.js.map +1 -0
- package/dist/fault.d.ts +3 -0
- package/dist/fault.js +4 -0
- package/dist/fault.js.map +1 -0
- package/dist/gate/admission.d.ts +34 -0
- package/dist/gate/admission.js +2 -0
- package/dist/gate/admission.js.map +1 -0
- package/dist/gate/daily.d.ts +92 -0
- package/dist/gate/daily.js +136 -0
- package/dist/gate/daily.js.map +1 -0
- package/dist/gate/ledger.d.ts +29 -0
- package/dist/gate/ledger.js +88 -0
- package/dist/gate/ledger.js.map +1 -0
- package/dist/gate/sequential.d.ts +39 -0
- package/dist/gate/sequential.js +84 -0
- package/dist/gate/sequential.js.map +1 -0
- package/dist/index.d.ts +38 -1
- package/dist/index.js +21 -1
- package/dist/index.js.map +1 -1
- package/dist/judge/audit.d.ts +45 -0
- package/dist/judge/audit.js +55 -0
- package/dist/judge/audit.js.map +1 -0
- package/dist/judge/calibration.d.ts +70 -0
- package/dist/judge/calibration.js +128 -0
- package/dist/judge/calibration.js.map +1 -0
- package/dist/judge/continuation.d.ts +16 -0
- package/dist/judge/continuation.js +48 -0
- package/dist/judge/continuation.js.map +1 -0
- package/dist/layer/versions.d.ts +71 -0
- package/dist/layer/versions.js +154 -0
- package/dist/layer/versions.js.map +1 -0
- package/dist/meta.d.ts +4 -0
- package/dist/meta.js.map +1 -1
- package/dist/propose/brief.d.ts +31 -0
- package/dist/propose/brief.js +52 -0
- package/dist/propose/brief.js.map +1 -0
- package/dist/propose/candidate.d.ts +15 -0
- package/dist/propose/candidate.js +30 -0
- package/dist/propose/candidate.js.map +1 -0
- package/dist/propose/rejections.d.ts +35 -0
- package/dist/propose/rejections.js +35 -0
- package/dist/propose/rejections.js.map +1 -0
- package/dist/propose/skill.d.ts +4 -0
- package/dist/propose/skill.js +34 -0
- package/dist/propose/skill.js.map +1 -0
- package/package.json +2 -2
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/** Common secret formats: provider keys, access tokens, cloud keys, JWTs and private key blocks. */
|
|
2
|
+
const SECRET_FORMATS = [
|
|
3
|
+
/-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g,
|
|
4
|
+
/\bsk-[A-Za-z0-9_-]{20,}/g,
|
|
5
|
+
/\bgh[pousr]_[A-Za-z0-9]{30,}/g,
|
|
6
|
+
/\bgithub_pat_[A-Za-z0-9_]{30,}/g,
|
|
7
|
+
/\bxox[abprs]-[A-Za-z0-9-]{10,}/g,
|
|
8
|
+
/\bAKIA[0-9A-Z]{16}\b/g,
|
|
9
|
+
/\bAIza[0-9A-Za-z_-]{35}/g,
|
|
10
|
+
/\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}/g,
|
|
11
|
+
/(?<=\b(?:password|passwd|secret|token|api[_-]?key)\s*[=:]\s*["']?)[^\s"']{8,}/gi,
|
|
12
|
+
];
|
|
13
|
+
/** Files whose whole content is withheld once a tool has read them. */
|
|
14
|
+
const SECRET_FILE = /(^|[\s/"'=])(\.env(\.[\w-]+)?|\.npmrc|\.netrc|id_(rsa|ed25519|ecdsa)|[\w-]+\.pem|credentials(\.json)?)(?=$|[\s"'])/;
|
|
15
|
+
const MASK = "[redacted]";
|
|
16
|
+
/** Names under which a value is a secret, as an environment variable or a request header. */
|
|
17
|
+
const SECRET_NAME = /KEY|TOKEN|SECRET|PASSWORD|CREDENTIAL|AUTHORIZATION|COOKIE/i;
|
|
18
|
+
/** The values of environment variables named like secrets. */
|
|
19
|
+
export function secretValues(env) {
|
|
20
|
+
return Object.entries(env).flatMap(([name, value]) => value !== undefined && value.length >= 8 && SECRET_NAME.test(name) ? [value] : []);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* The secrets MCP servers are configured with, from headers and environment named like secrets: each whole value and
|
|
24
|
+
* each long word of it, so a token is masked with or without the scheme before it.
|
|
25
|
+
*/
|
|
26
|
+
export function mcpSecrets(servers) {
|
|
27
|
+
return Object.values(servers)
|
|
28
|
+
.flatMap((server) => secretValues("command" in server ? server.env ?? {} : server.headers ?? {}))
|
|
29
|
+
.flatMap((value) => [value, ...value.split(/\s+/).filter((word) => word.length >= 8 && word !== value)]);
|
|
30
|
+
}
|
|
31
|
+
/** `text` with every known secret value and every common secret format masked. */
|
|
32
|
+
export function redactText(text, values) {
|
|
33
|
+
let redacted = text;
|
|
34
|
+
for (const value of [...values].sort((a, b) => b.length - a.length))
|
|
35
|
+
redacted = redacted.split(value).join(MASK);
|
|
36
|
+
for (const format of SECRET_FORMATS)
|
|
37
|
+
redacted = redacted.replace(format, MASK);
|
|
38
|
+
return redacted;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* A transcript fit to leave the session: secrets masked in every string, and the output of any tool call that touched
|
|
42
|
+
* a secret file replaced by the fact that it did.
|
|
43
|
+
*/
|
|
44
|
+
export function redactTranscript(entries, values) {
|
|
45
|
+
const withheld = new Set();
|
|
46
|
+
return entries.map((entry) => {
|
|
47
|
+
if (!("notification" in entry))
|
|
48
|
+
return redact(entry, values);
|
|
49
|
+
const update = entry.notification.update;
|
|
50
|
+
if (update.sessionUpdate === "tool_call" && SECRET_FILE.test(JSON.stringify(update.rawInput ?? ""))) {
|
|
51
|
+
withheld.add(update.toolCallId);
|
|
52
|
+
}
|
|
53
|
+
if (update.sessionUpdate === "tool_call_update" && withheld.has(update.toolCallId)) {
|
|
54
|
+
const content = [{ type: "content", content: { type: "text", text: "[output of a secret file withheld]" } }];
|
|
55
|
+
return redact({ ...entry, notification: { ...entry.notification, update: { ...update, content, rawOutput: undefined } } }, values);
|
|
56
|
+
}
|
|
57
|
+
return redact(entry, values);
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
function redact(value, values) {
|
|
61
|
+
if (typeof value === "string")
|
|
62
|
+
return redactText(value, values);
|
|
63
|
+
if (Array.isArray(value))
|
|
64
|
+
return value.map((item) => redact(item, values));
|
|
65
|
+
if (value !== null && typeof value === "object") {
|
|
66
|
+
return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, redact(item, values)]));
|
|
67
|
+
}
|
|
68
|
+
return value;
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=sanitize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sanitize.js","sourceRoot":"","sources":["../../src/evidence/sanitize.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { TranscriptEntry } from "../transcript.js";
|
|
2
|
+
/** A failure at the line where it happened: one the transcript records, or a correction the user confirmed. */
|
|
3
|
+
export type Signal = {
|
|
4
|
+
kind: "tool_failed";
|
|
5
|
+
line: number;
|
|
6
|
+
tool: string;
|
|
7
|
+
toolCallId: string;
|
|
8
|
+
blocked: boolean;
|
|
9
|
+
} | {
|
|
10
|
+
kind: "interrupted";
|
|
11
|
+
line: number;
|
|
12
|
+
} | {
|
|
13
|
+
kind: "turn_failed";
|
|
14
|
+
line: number;
|
|
15
|
+
error: string;
|
|
16
|
+
} | {
|
|
17
|
+
kind: "corrected";
|
|
18
|
+
line: number;
|
|
19
|
+
summary: string;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Failed tool calls, turns the user cut off, and turns that ended without an answer. A call the user declined is no
|
|
23
|
+
* failure of the agent's; what the user says next is a correction to confirm.
|
|
24
|
+
*/
|
|
25
|
+
export declare function signals(entries: readonly TranscriptEntry[]): Signal[];
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { chopinMeta } from "../meta.js";
|
|
2
|
+
/**
|
|
3
|
+
* Failed tool calls, turns the user cut off, and turns that ended without an answer. A call the user declined is no
|
|
4
|
+
* failure of the agent's; what the user says next is a correction to confirm.
|
|
5
|
+
*/
|
|
6
|
+
export function signals(entries) {
|
|
7
|
+
const tools = new Map();
|
|
8
|
+
return entries.flatMap((entry, line) => {
|
|
9
|
+
if ("turnEnd" in entry) {
|
|
10
|
+
if (entry.turnEnd.stopReason === "cancelled")
|
|
11
|
+
return [{ kind: "interrupted", line }];
|
|
12
|
+
const error = chopinMeta(entry.turnEnd)?.error;
|
|
13
|
+
return error === undefined ? [] : [{ kind: "turn_failed", line, error }];
|
|
14
|
+
}
|
|
15
|
+
const update = entry.notification.update;
|
|
16
|
+
if (update.sessionUpdate === "tool_call")
|
|
17
|
+
tools.set(update.toolCallId, update.title);
|
|
18
|
+
if (update.sessionUpdate !== "tool_call_update" || update.status !== "failed")
|
|
19
|
+
return [];
|
|
20
|
+
const diagnostics = chopinMeta(update)?.diagnostics ?? [];
|
|
21
|
+
if (diagnostics.includes("rejected"))
|
|
22
|
+
return [];
|
|
23
|
+
const tool = tools.get(update.toolCallId) ?? update.title ?? "";
|
|
24
|
+
const blocked = diagnostics.includes("blocked");
|
|
25
|
+
return [{ kind: "tool_failed", line, tool, toolCallId: update.toolCallId, blocked }];
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=signals.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signals.js","sourceRoot":"","sources":["../../src/evidence/signals.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAUxC;;;GAGG;AACH,MAAM,UAAU,OAAO,CAAC,OAAmC;IACzD,MAAM,KAAK,GAAG,IAAI,GAAG,EAAkB,CAAC;IACxC,OAAO,OAAO,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,IAAI,EAAY,EAAE;QAC/C,IAAI,SAAS,IAAI,KAAK,EAAE,CAAC;YACvB,IAAI,KAAK,CAAC,OAAO,CAAC,UAAU,KAAK,WAAW;gBAAE,OAAO,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;YACrF,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,KAAK,CAAC;YAC/C,OAAO,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QAC3E,CAAC;QACD,MAAM,MAAM,GAAG,KAAK,CAAC,YAAY,CAAC,MAAM,CAAC;QACzC,IAAI,MAAM,CAAC,aAAa,KAAK,WAAW;YAAE,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;QACrF,IAAI,MAAM,CAAC,aAAa,KAAK,kBAAkB,IAAI,MAAM,CAAC,MAAM,KAAK,QAAQ;YAAE,OAAO,EAAE,CAAC;QACzF,MAAM,WAAW,GAAG,UAAU,CAAC,MAAM,CAAC,EAAE,WAAW,IAAI,EAAE,CAAC;QAC1D,IAAI,WAAW,CAAC,QAAQ,CAAC,UAAU,CAAC;YAAE,OAAO,EAAE,CAAC;QAChD,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC;QAChE,MAAM,OAAO,GAAG,WAAW,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;QAChD,OAAO,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,CAAC,UAAU,EAAE,OAAO,EAAE,CAAC,CAAC;IACvF,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { TranscriptEntry } from "../transcript.js";
|
|
2
|
+
/** How often a transcript used each part of the layer. */
|
|
3
|
+
export interface LayerUsage {
|
|
4
|
+
/** Skill reads, by skill name. */
|
|
5
|
+
skills: Map<string, number>;
|
|
6
|
+
/** Tool calls, by MCP server. */
|
|
7
|
+
mcpServers: Map<string, number>;
|
|
8
|
+
/** Tool calls a hook refused, such as a gate. */
|
|
9
|
+
blocked: number;
|
|
10
|
+
}
|
|
11
|
+
/** What `entries` used of a layer whose MCP servers are `servers`. */
|
|
12
|
+
export declare function layerUsage(entries: readonly TranscriptEntry[], servers: readonly string[]): LayerUsage;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { isMcpToolOf } from "../layer/mcp.js";
|
|
2
|
+
import { chopinMeta } from "../meta.js";
|
|
3
|
+
/** What `entries` used of a layer whose MCP servers are `servers`. */
|
|
4
|
+
export function layerUsage(entries, servers) {
|
|
5
|
+
const used = { skills: new Map(), mcpServers: new Map(), blocked: 0 };
|
|
6
|
+
const bump = (counts, key) => counts.set(key, (counts.get(key) ?? 0) + 1);
|
|
7
|
+
for (const entry of entries) {
|
|
8
|
+
if (!("notification" in entry))
|
|
9
|
+
continue;
|
|
10
|
+
const update = entry.notification.update;
|
|
11
|
+
if (update.sessionUpdate === "tool_call") {
|
|
12
|
+
const name = update.rawInput?.name;
|
|
13
|
+
if (update.title === "skill" && typeof name === "string")
|
|
14
|
+
bump(used.skills, name);
|
|
15
|
+
const server = servers.find((candidate) => isMcpToolOf(candidate, update.title));
|
|
16
|
+
if (server !== undefined)
|
|
17
|
+
bump(used.mcpServers, server);
|
|
18
|
+
}
|
|
19
|
+
if (update.sessionUpdate === "tool_call_update" && chopinMeta(update)?.diagnostics?.includes("blocked"))
|
|
20
|
+
used.blocked += 1;
|
|
21
|
+
}
|
|
22
|
+
return used;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=usage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usage.js","sourceRoot":"","sources":["../../src/evidence/usage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAaxC,sEAAsE;AACtE,MAAM,UAAU,UAAU,CAAC,OAAmC,EAAE,OAA0B;IACxF,MAAM,IAAI,GAAe,EAAE,MAAM,EAAE,IAAI,GAAG,EAAE,EAAE,UAAU,EAAE,IAAI,GAAG,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;IAClF,MAAM,IAAI,GAAG,CAAC,MAA2B,EAAE,GAAW,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IACvG,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,IAAI,CAAC,CAAC,cAAc,IAAI,KAAK,CAAC;YAAE,SAAS;QACzC,MAAM,MAAM,GAAG,KAAK,CAAC,YAAY,CAAC,MAAM,CAAC;QACzC,IAAI,MAAM,CAAC,aAAa,KAAK,WAAW,EAAE,CAAC;YACzC,MAAM,IAAI,GAAI,MAAM,CAAC,QAA2C,EAAE,IAAI,CAAC;YACvE,IAAI,MAAM,CAAC,KAAK,KAAK,OAAO,IAAI,OAAO,IAAI,KAAK,QAAQ;gBAAE,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;YAClF,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,WAAW,CAAC,SAAS,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;YACjF,IAAI,MAAM,KAAK,SAAS;gBAAE,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;QAC1D,CAAC;QACD,IAAI,MAAM,CAAC,aAAa,KAAK,kBAAkB,IAAI,UAAU,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,QAAQ,CAAC,SAAS,CAAC;YAAE,IAAI,CAAC,OAAO,IAAI,CAAC,CAAC;IAC7H,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
|
package/dist/fault.d.ts
ADDED
package/dist/fault.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fault.js","sourceRoot":"","sources":["../src/fault.ts"],"names":[],"mappings":"AAAA,oIAAoI;AACpI,MAAM,OAAO,SAAU,SAAQ,KAAK;CAAG"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/** What an admission leaves behind for the user: what was compared, how much, and how strong the evidence was. */
|
|
2
|
+
export interface Certificate {
|
|
3
|
+
candidate: string;
|
|
4
|
+
kind: "instruction" | "skill";
|
|
5
|
+
scope: {
|
|
6
|
+
kind: "global";
|
|
7
|
+
} | {
|
|
8
|
+
kind: "project";
|
|
9
|
+
project: string;
|
|
10
|
+
};
|
|
11
|
+
/** The version it was compared against. */
|
|
12
|
+
incumbent: number;
|
|
13
|
+
pairs: number;
|
|
14
|
+
evidence: number;
|
|
15
|
+
level: number;
|
|
16
|
+
kept: boolean;
|
|
17
|
+
/** Decision points where, over all their pairs, it did better than the incumbent, and the reverse. */
|
|
18
|
+
fixed: string[];
|
|
19
|
+
broken: string[];
|
|
20
|
+
/** In how many of its continuations the change was used, as a skill read. */
|
|
21
|
+
used: number;
|
|
22
|
+
/** Refused for mending none of the failures it was first tried on, before the test proper. */
|
|
23
|
+
screenedOut: boolean;
|
|
24
|
+
/** The pairs at failures, and in how many of them each version made the failure again. */
|
|
25
|
+
atFailures: {
|
|
26
|
+
pairs: number;
|
|
27
|
+
incumbentRepeated: number;
|
|
28
|
+
candidateRepeated: number;
|
|
29
|
+
};
|
|
30
|
+
prediction: string;
|
|
31
|
+
model: string;
|
|
32
|
+
harness: string;
|
|
33
|
+
at: string;
|
|
34
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"admission.js","sourceRoot":"","sources":["../../src/gate/admission.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import type { FailureSignature } from "../evidence/classify.js";
|
|
2
|
+
import type { EquivalenceVerdict, RepeatVerdict } from "../judge/continuation.js";
|
|
3
|
+
import type { Certificate } from "./admission.js";
|
|
4
|
+
import type { AdmissionLedger } from "./ledger.js";
|
|
5
|
+
/**
|
|
6
|
+
* How many pairs at failures a candidate is first tried on alone. One that mends none of them is refused there: it
|
|
7
|
+
* claims to mend that failure, and stopping a test early only ever keeps less.
|
|
8
|
+
*/
|
|
9
|
+
export declare const SCREEN_PAIRS = 6;
|
|
10
|
+
/** How many continuations a kind's failures are tried with for whether they recur, and how many repeats are enough. */
|
|
11
|
+
export declare const RECURRENCE: {
|
|
12
|
+
readonly tries: 8;
|
|
13
|
+
readonly enough: 3;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* How many distinct decision points where the agent failed a change must mend to be kept. More draws at one point
|
|
17
|
+
* show only that the agent follows the change there, not that it carries over to another time the failure happened.
|
|
18
|
+
*/
|
|
19
|
+
export declare const MENDED_POINTS = 2;
|
|
20
|
+
/**
|
|
21
|
+
* The smallest mean gain per pair worth testing on for: a change shown to fall short of it is refused there. Set
|
|
22
|
+
* higher, it would stop sooner but begin to refuse changes that mend about four failures in ten.
|
|
23
|
+
*/
|
|
24
|
+
export declare const SMALLEST_GAIN = 0.2;
|
|
25
|
+
/** A decision point where the agent once failed. */
|
|
26
|
+
export interface FixPoint {
|
|
27
|
+
id: string;
|
|
28
|
+
context: string;
|
|
29
|
+
failure: FailureSignature;
|
|
30
|
+
/** What the user said to correct the step taken there, when the failure is known from that. */
|
|
31
|
+
correction?: string;
|
|
32
|
+
}
|
|
33
|
+
/** A decision point where the agent went right, and the step it took there. */
|
|
34
|
+
export interface ProtectPoint {
|
|
35
|
+
id: string;
|
|
36
|
+
context: string;
|
|
37
|
+
original: string;
|
|
38
|
+
}
|
|
39
|
+
/** Which version continues: the candidate or the incumbent. */
|
|
40
|
+
export type Arm = "candidate" | "incumbent";
|
|
41
|
+
/** One continued step, and whether it used the change under test. */
|
|
42
|
+
export interface Continued {
|
|
43
|
+
step: string;
|
|
44
|
+
used: boolean;
|
|
45
|
+
}
|
|
46
|
+
export interface DailyAdmission {
|
|
47
|
+
candidate: string;
|
|
48
|
+
kind: Certificate["kind"];
|
|
49
|
+
scope: Certificate["scope"];
|
|
50
|
+
incumbent: number;
|
|
51
|
+
fixPoints: readonly FixPoint[];
|
|
52
|
+
protectPoints: readonly ProtectPoint[];
|
|
53
|
+
/** `undefined` when the point could not be continued, which leaves it out of the test. */
|
|
54
|
+
continueAt: (arm: Arm, point: FixPoint | ProtectPoint) => Promise<Continued | undefined>;
|
|
55
|
+
judgeRepeat: (point: FixPoint, step: string) => Promise<RepeatVerdict | undefined>;
|
|
56
|
+
judgeEquivalent: (point: ProtectPoint, step: string) => Promise<EquivalenceVerdict | undefined>;
|
|
57
|
+
ledger: AdmissionLedger;
|
|
58
|
+
/** The most pairs the comparison may take. */
|
|
59
|
+
limit: number;
|
|
60
|
+
/** Whether the change must be seen in use before it is kept, as a skill must be read. */
|
|
61
|
+
mustBeUsed: boolean;
|
|
62
|
+
prediction: string;
|
|
63
|
+
model: string;
|
|
64
|
+
random?: () => number;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Whether the failures recur at least at the rate of `enough` in `tries`, at which a screen of `SCREEN_PAIRS` pairs
|
|
68
|
+
* almost always sees the incumbent make the failure; below it a screen mostly sees nothing a change could mend.
|
|
69
|
+
*/
|
|
70
|
+
export declare function recursOften(found: {
|
|
71
|
+
repeats: number;
|
|
72
|
+
tried: number;
|
|
73
|
+
}): boolean;
|
|
74
|
+
/**
|
|
75
|
+
* The failures that recur when the incumbent continues their sessions: each point is continued in turn, and again
|
|
76
|
+
* once all have been, until `tries` continuations are told or `enough` repeat, so a kind with few failures is
|
|
77
|
+
* measured as fully as one with many. `recurs` gives `undefined` for a continuation it could not tell, which is not
|
|
78
|
+
* counted. A change can be shown to mend a failure only where it still happens; the continuations that found it
|
|
79
|
+
* serve no other purpose, so the test's own stay unbiased.
|
|
80
|
+
*/
|
|
81
|
+
export declare function recurringPoints(points: readonly FixPoint[], recurs: (point: FixPoint, draw: number) => Promise<boolean | undefined>): Promise<{
|
|
82
|
+
recurring: FixPoint[];
|
|
83
|
+
repeats: number;
|
|
84
|
+
tried: number;
|
|
85
|
+
}>;
|
|
86
|
+
/**
|
|
87
|
+
* Decides on a candidate from continued decision points: one paired, one-sided test of its net effect. After the
|
|
88
|
+
* first pairs, all at failures, pairs come in turn from where the agent once failed (scored on not repeating the
|
|
89
|
+
* failure) and from where it went right (scored on doing as well), so a fix that breaks other things does not pass.
|
|
90
|
+
* The outcome goes in the ledger at the level it set, and the certificate says what was compared and how it came out.
|
|
91
|
+
*/
|
|
92
|
+
export declare function admitDaily(input: DailyAdmission): Promise<Certificate>;
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { PairedComparison } from "./sequential.js";
|
|
2
|
+
/**
|
|
3
|
+
* How many pairs at failures a candidate is first tried on alone. One that mends none of them is refused there: it
|
|
4
|
+
* claims to mend that failure, and stopping a test early only ever keeps less.
|
|
5
|
+
*/
|
|
6
|
+
export const SCREEN_PAIRS = 6;
|
|
7
|
+
/** How many continuations a kind's failures are tried with for whether they recur, and how many repeats are enough. */
|
|
8
|
+
export const RECURRENCE = { tries: 8, enough: 3 };
|
|
9
|
+
/**
|
|
10
|
+
* How many distinct decision points where the agent failed a change must mend to be kept. More draws at one point
|
|
11
|
+
* show only that the agent follows the change there, not that it carries over to another time the failure happened.
|
|
12
|
+
*/
|
|
13
|
+
export const MENDED_POINTS = 2;
|
|
14
|
+
/**
|
|
15
|
+
* The smallest mean gain per pair worth testing on for: a change shown to fall short of it is refused there. Set
|
|
16
|
+
* higher, it would stop sooner but begin to refuse changes that mend about four failures in ten.
|
|
17
|
+
*/
|
|
18
|
+
export const SMALLEST_GAIN = 0.2;
|
|
19
|
+
/**
|
|
20
|
+
* Whether the failures recur at least at the rate of `enough` in `tries`, at which a screen of `SCREEN_PAIRS` pairs
|
|
21
|
+
* almost always sees the incumbent make the failure; below it a screen mostly sees nothing a change could mend.
|
|
22
|
+
*/
|
|
23
|
+
export function recursOften(found) {
|
|
24
|
+
return found.tried > 0 && found.repeats * RECURRENCE.tries >= RECURRENCE.enough * found.tried;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* The failures that recur when the incumbent continues their sessions: each point is continued in turn, and again
|
|
28
|
+
* once all have been, until `tries` continuations are told or `enough` repeat, so a kind with few failures is
|
|
29
|
+
* measured as fully as one with many. `recurs` gives `undefined` for a continuation it could not tell, which is not
|
|
30
|
+
* counted. A change can be shown to mend a failure only where it still happens; the continuations that found it
|
|
31
|
+
* serve no other purpose, so the test's own stay unbiased.
|
|
32
|
+
*/
|
|
33
|
+
export async function recurringPoints(points, recurs) {
|
|
34
|
+
const recurring = new Set();
|
|
35
|
+
let repeats = 0;
|
|
36
|
+
let tried = 0;
|
|
37
|
+
const done = () => tried === RECURRENCE.tries || repeats === RECURRENCE.enough;
|
|
38
|
+
for (let draw = 0; points.length > 0 && !done(); draw += 1) {
|
|
39
|
+
const before = tried;
|
|
40
|
+
for (const point of points) {
|
|
41
|
+
if (done())
|
|
42
|
+
break;
|
|
43
|
+
const recurred = await recurs(point, draw);
|
|
44
|
+
if (recurred === undefined)
|
|
45
|
+
continue;
|
|
46
|
+
tried += 1;
|
|
47
|
+
if (recurred) {
|
|
48
|
+
repeats += 1;
|
|
49
|
+
recurring.add(point);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
if (tried === before)
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
return { recurring: [...recurring], repeats, tried };
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Decides on a candidate from continued decision points: one paired, one-sided test of its net effect. After the
|
|
59
|
+
* first pairs, all at failures, pairs come in turn from where the agent once failed (scored on not repeating the
|
|
60
|
+
* failure) and from where it went right (scored on doing as well), so a fix that breaks other things does not pass.
|
|
61
|
+
* The outcome goes in the ledger at the level it set, and the certificate says what was compared and how it came out.
|
|
62
|
+
*/
|
|
63
|
+
export async function admitDaily(input) {
|
|
64
|
+
const random = input.random ?? Math.random;
|
|
65
|
+
const level = await input.ledger.nextLevel();
|
|
66
|
+
const test = new PairedComparison({ margin: 0, futilityMargin: SMALLEST_GAIN, alpha: level, limit: input.limit });
|
|
67
|
+
// A point is mended, or broken, by its net effect over all its pairs, not by one draw that went either way.
|
|
68
|
+
const net = new Map();
|
|
69
|
+
const mended = () => [...net].filter(([, sum]) => sum > 0).map(([id]) => id);
|
|
70
|
+
let used = 0;
|
|
71
|
+
const pick = (points) => points[Math.floor(random() * points.length)];
|
|
72
|
+
const fixPool = [...input.fixPoints];
|
|
73
|
+
const pools = [fixPool, [...input.protectPoints]];
|
|
74
|
+
let screened = 0;
|
|
75
|
+
let screenedOut = false;
|
|
76
|
+
const atFailures = { pairs: 0, incumbentRepeated: 0, candidateRepeated: 0 };
|
|
77
|
+
for (let round = 0; test.standing === "continue" && round < input.limit * 2; round += 1) {
|
|
78
|
+
const live = pools.filter((pool) => pool.length > 0);
|
|
79
|
+
const screening = screened < SCREEN_PAIRS && input.fixPoints.length > 0;
|
|
80
|
+
// A screen needs failures to continue; with none left, nothing can be shown mended.
|
|
81
|
+
if (live.length === 0 || (screening && fixPool.length === 0))
|
|
82
|
+
break;
|
|
83
|
+
const point = screening ? pick(fixPool) : pick(live[round % live.length]);
|
|
84
|
+
const [mine, theirs] = await Promise.all([input.continueAt("candidate", point), input.continueAt("incumbent", point)]);
|
|
85
|
+
if (mine === undefined || theirs === undefined) {
|
|
86
|
+
const pool = pools.find((candidates) => candidates.includes(point));
|
|
87
|
+
pool.splice(pool.indexOf(point), 1);
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
if (mine.used)
|
|
91
|
+
used += 1;
|
|
92
|
+
let difference;
|
|
93
|
+
if ("failure" in point) {
|
|
94
|
+
const verdicts = await Promise.all([
|
|
95
|
+
input.judgeRepeat(point, theirs.step),
|
|
96
|
+
input.judgeRepeat(point, mine.step),
|
|
97
|
+
]);
|
|
98
|
+
difference = scored(verdicts, "repeats");
|
|
99
|
+
if (difference !== undefined) {
|
|
100
|
+
atFailures.pairs += 1;
|
|
101
|
+
atFailures.incumbentRepeated += Number(verdicts[0] === "repeats");
|
|
102
|
+
atFailures.candidateRepeated += Number(verdicts[1] === "repeats");
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
difference = scored(await Promise.all([
|
|
107
|
+
input.judgeEquivalent(point, mine.step),
|
|
108
|
+
input.judgeEquivalent(point, theirs.step),
|
|
109
|
+
]), "as_good");
|
|
110
|
+
}
|
|
111
|
+
if (difference === undefined)
|
|
112
|
+
continue;
|
|
113
|
+
test.add(difference);
|
|
114
|
+
net.set(point.id, (net.get(point.id) ?? 0) + difference);
|
|
115
|
+
if (screening && (screened += 1) === SCREEN_PAIRS && mended().length === 0) {
|
|
116
|
+
screenedOut = true;
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
const failures = new Set(input.fixPoints.map((point) => point.id));
|
|
121
|
+
const fixed = mended();
|
|
122
|
+
const broken = [...net].filter(([, sum]) => sum < 0).map(([id]) => id);
|
|
123
|
+
const passed = test.standing === "accept" && fixed.filter((id) => failures.has(id)).length >= MENDED_POINTS && !(input.mustBeUsed && used === 0);
|
|
124
|
+
// Every test is recorded, kept or not, so the levels of later ones stay valid.
|
|
125
|
+
const { kept } = await input.ledger.record(input.candidate, passed ? test.evidence : Math.min(test.evidence, 1));
|
|
126
|
+
return {
|
|
127
|
+
candidate: input.candidate, kind: input.kind, scope: input.scope, incumbent: input.incumbent, pairs: test.for.count,
|
|
128
|
+
evidence: test.evidence, level, kept, fixed, broken, used, screenedOut, atFailures, prediction: input.prediction,
|
|
129
|
+
model: input.model, harness: "pi", at: new Date().toISOString(),
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
/** The pair as a difference in [-1, 1]: 1 when only the first verdict is `hit`, -1 when only the second is. */
|
|
133
|
+
function scored([first, second], hit) {
|
|
134
|
+
return first === undefined || second === undefined ? undefined : Number(first === hit) - Number(second === hit);
|
|
135
|
+
}
|
|
136
|
+
//# sourceMappingURL=daily.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"daily.js","sourceRoot":"","sources":["../../src/gate/daily.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/** The share of kept changes that may be false, over the loop's whole life. */
|
|
2
|
+
export declare const FALSE_DISCOVERY_RATE = 0.1;
|
|
3
|
+
/**
|
|
4
|
+
* The discount sequence of the test levels; it sums to about one over all tests, so the level of later tests shrinks
|
|
5
|
+
* only as fast as it must.
|
|
6
|
+
*/
|
|
7
|
+
export declare function discount(test: number): number;
|
|
8
|
+
/**
|
|
9
|
+
* Every admission test the loop has run, in order, as JSON Lines chained by hash so an edit shows. Levels follow
|
|
10
|
+
* e-LOND: the t-th test keeps its candidate when the evidence reaches 1 / (rate · discount(t) · (kept so far + 1)),
|
|
11
|
+
* which bounds the share of false changes kept under any dependence between the tests.
|
|
12
|
+
*/
|
|
13
|
+
export declare class AdmissionLedger {
|
|
14
|
+
#private;
|
|
15
|
+
readonly path: string;
|
|
16
|
+
readonly rate: number;
|
|
17
|
+
constructor(path: string, rate?: number);
|
|
18
|
+
/** The level the next test must meet: its evidence has to reach the inverse. */
|
|
19
|
+
nextLevel(): Promise<number>;
|
|
20
|
+
/** Records a finished test; it keeps its candidate when its evidence met the level it was given. */
|
|
21
|
+
record(candidate: string, evidence: number): Promise<{
|
|
22
|
+
kept: boolean;
|
|
23
|
+
level: number;
|
|
24
|
+
}>;
|
|
25
|
+
/** How many leading entries are as they were written; the rest were edited or appended out of turn. */
|
|
26
|
+
intactCount(): Promise<number>;
|
|
27
|
+
/** Cuts the ledger back to its intact entries, saying how many it dropped. */
|
|
28
|
+
recover(): Promise<number>;
|
|
29
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { appendFile, mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
3
|
+
import { dirname } from "node:path";
|
|
4
|
+
/** The share of kept changes that may be false, over the loop's whole life. */
|
|
5
|
+
export const FALSE_DISCOVERY_RATE = 0.1;
|
|
6
|
+
/**
|
|
7
|
+
* The discount sequence of the test levels; it sums to about one over all tests, so the level of later tests shrinks
|
|
8
|
+
* only as fast as it must.
|
|
9
|
+
*/
|
|
10
|
+
export function discount(test) {
|
|
11
|
+
return (0.07720838 * Math.log(Math.max(test, 2))) / (test * Math.exp(Math.sqrt(Math.log(test))));
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Every admission test the loop has run, in order, as JSON Lines chained by hash so an edit shows. Levels follow
|
|
15
|
+
* e-LOND: the t-th test keeps its candidate when the evidence reaches 1 / (rate · discount(t) · (kept so far + 1)),
|
|
16
|
+
* which bounds the share of false changes kept under any dependence between the tests.
|
|
17
|
+
*/
|
|
18
|
+
export class AdmissionLedger {
|
|
19
|
+
path;
|
|
20
|
+
rate;
|
|
21
|
+
constructor(path, rate = FALSE_DISCOVERY_RATE) {
|
|
22
|
+
this.path = path;
|
|
23
|
+
this.rate = rate;
|
|
24
|
+
}
|
|
25
|
+
/** The level the next test must meet: its evidence has to reach the inverse. */
|
|
26
|
+
async nextLevel() {
|
|
27
|
+
const entries = await this.#entries();
|
|
28
|
+
const kept = entries.filter((entry) => entry.kept).length;
|
|
29
|
+
return this.rate * discount(entries.length + 1) * (kept + 1);
|
|
30
|
+
}
|
|
31
|
+
/** Records a finished test; it keeps its candidate when its evidence met the level it was given. */
|
|
32
|
+
async record(candidate, evidence) {
|
|
33
|
+
const entries = await this.#entries();
|
|
34
|
+
const level = await this.nextLevel();
|
|
35
|
+
const kept = evidence >= 1 / level;
|
|
36
|
+
const previous = entries.at(-1)?.hash ?? "";
|
|
37
|
+
const body = { test: entries.length + 1, candidate, level, evidence, kept, previous };
|
|
38
|
+
const entry = { ...body, hash: digest(body) };
|
|
39
|
+
await mkdir(dirname(this.path), { recursive: true });
|
|
40
|
+
await appendFile(this.path, `${JSON.stringify(entry)}\n`);
|
|
41
|
+
return { kept, level };
|
|
42
|
+
}
|
|
43
|
+
/** How many leading entries are as they were written; the rest were edited or appended out of turn. */
|
|
44
|
+
async intactCount() {
|
|
45
|
+
const lines = await this.#lines();
|
|
46
|
+
let previous = "";
|
|
47
|
+
for (const [index, line] of lines.entries()) {
|
|
48
|
+
let entry;
|
|
49
|
+
try {
|
|
50
|
+
entry = JSON.parse(line);
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return index;
|
|
54
|
+
}
|
|
55
|
+
const { hash, ...body } = entry;
|
|
56
|
+
if (entry.previous !== previous || entry.test !== index + 1 || digest(body) !== hash)
|
|
57
|
+
return index;
|
|
58
|
+
previous = hash;
|
|
59
|
+
}
|
|
60
|
+
return lines.length;
|
|
61
|
+
}
|
|
62
|
+
/** Cuts the ledger back to its intact entries, saying how many it dropped. */
|
|
63
|
+
async recover() {
|
|
64
|
+
const lines = await this.#lines();
|
|
65
|
+
const intact = await this.intactCount();
|
|
66
|
+
if (intact === lines.length)
|
|
67
|
+
return 0;
|
|
68
|
+
const temporary = `${this.path}.${process.pid}`;
|
|
69
|
+
await writeFile(temporary, lines.slice(0, intact).map((line) => `${line}\n`).join(""));
|
|
70
|
+
await rename(temporary, this.path);
|
|
71
|
+
return lines.length - intact;
|
|
72
|
+
}
|
|
73
|
+
async #entries() {
|
|
74
|
+
return (await this.#lines()).map((line) => JSON.parse(line));
|
|
75
|
+
}
|
|
76
|
+
async #lines() {
|
|
77
|
+
const text = await readFile(this.path, "utf8").catch((error) => {
|
|
78
|
+
if (error.code === "ENOENT")
|
|
79
|
+
return "";
|
|
80
|
+
throw error;
|
|
81
|
+
});
|
|
82
|
+
return text.split("\n").filter((line) => line !== "");
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function digest(body) {
|
|
86
|
+
return createHash("sha256").update(JSON.stringify(body)).digest("hex");
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=ledger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ledger.js","sourceRoot":"","sources":["../../src/gate/ledger.ts"],"names":[],"mappings":"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"}
|