@vizuh/sabi 0.1.4 → 0.1.5
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/mod/sabi.mjs +370 -51
- package/package.json +1 -1
package/mod/sabi.mjs
CHANGED
|
@@ -1,6 +1,260 @@
|
|
|
1
|
-
// @vizuh/sabi 0.1.
|
|
1
|
+
// @vizuh/sabi 0.1.5 — generated by pack.mjs from packages/adapters/command-code/mod/sabi.ts.
|
|
2
2
|
// Source and docs: https://github.com/vizuh/sabi
|
|
3
3
|
|
|
4
|
+
// packages/core/src/telemetry.ts
|
|
5
|
+
var ALLOWLIST = /* @__PURE__ */ new Set([
|
|
6
|
+
"error-line",
|
|
7
|
+
"python-traceback",
|
|
8
|
+
"panic",
|
|
9
|
+
"exception",
|
|
10
|
+
"typescript-error",
|
|
11
|
+
"fail-marker",
|
|
12
|
+
"command-failed",
|
|
13
|
+
"nonzero-exit",
|
|
14
|
+
"failure-count",
|
|
15
|
+
"command-not-found",
|
|
16
|
+
"permission-denied",
|
|
17
|
+
"missing-file",
|
|
18
|
+
"soft-warning",
|
|
19
|
+
"soft-deprecated",
|
|
20
|
+
"soft-retrying",
|
|
21
|
+
"soft-timeout",
|
|
22
|
+
"tool-error",
|
|
23
|
+
"permission-denial",
|
|
24
|
+
"rate-limited",
|
|
25
|
+
"quota-exceeded",
|
|
26
|
+
"timeout",
|
|
27
|
+
"mutation",
|
|
28
|
+
"verification-receipt",
|
|
29
|
+
"summary-claim",
|
|
30
|
+
"scope-observed",
|
|
31
|
+
"constraint",
|
|
32
|
+
"prior-failure",
|
|
33
|
+
"context-boundary",
|
|
34
|
+
"observation"
|
|
35
|
+
]);
|
|
36
|
+
function allowlisted(value) {
|
|
37
|
+
const code = value.split(":")[0]?.trim();
|
|
38
|
+
return ALLOWLIST.has(code);
|
|
39
|
+
}
|
|
40
|
+
function telemetryPolicy(config) {
|
|
41
|
+
const captureSnippets = config?.captureSnippets === true;
|
|
42
|
+
const captureChars = config?.captureChars ?? 800;
|
|
43
|
+
return {
|
|
44
|
+
captureSnippets,
|
|
45
|
+
allowlisted: (value) => allowlisted(value),
|
|
46
|
+
snippet: (value) => captureSnippets ? String(value).slice(0, captureChars) : ""
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
function sanitizeReason(reason, policy) {
|
|
50
|
+
if (!policy.captureSnippets) {
|
|
51
|
+
if (!reason || allowlisted(reason)) return reason;
|
|
52
|
+
const segments = reason.split(":").map((segment) => segment.trim());
|
|
53
|
+
const last = segments[segments.length - 1] ?? "";
|
|
54
|
+
if (allowlisted(last)) return reason;
|
|
55
|
+
return "reason withheld (telemetry.allowlistOnly)";
|
|
56
|
+
}
|
|
57
|
+
return policy.snippet(reason);
|
|
58
|
+
}
|
|
59
|
+
var KEYWORD_REDACT = /\b(bearer|authorization|api[-_]?\s*key|token|secret|password|passwd)\b["']?\s*[:=\s]\s*["']?[^\s"'{},\]]+/gi;
|
|
60
|
+
var BARE_TOKEN_REDACT = [
|
|
61
|
+
[/\bsk-[A-Za-z0-9_-]{8,}\b/g, "sk-[REDACTED]"],
|
|
62
|
+
[/\bAKIA[0-9A-Z]{16}\b/g, "AKIA[REDACTED]"],
|
|
63
|
+
[/\bAIza[0-9A-Za-z_-]{30,}\b/g, "AIza[REDACTED]"],
|
|
64
|
+
[/\bghp_[A-Za-z0-9]{8,}\b/g, "ghp_[REDACTED]"],
|
|
65
|
+
[/\bghu_[A-Za-z0-9]{8,}\b/g, "ghu_[REDACTED]"],
|
|
66
|
+
[/\bghs_[A-Za-z0-9]{8,}\b/g, "ghs_[REDACTED]"],
|
|
67
|
+
[/\bgithub_pat_[A-Za-z0-9_]{8,}\b/g, "github_pat_[REDACTED]"]
|
|
68
|
+
];
|
|
69
|
+
var CREDENTIAL_URL_REDACT = /([a-zA-Z][a-zA-Z0-9+.-]*:\/\/)[^:@\s/]+:[^@\s/]+@/g;
|
|
70
|
+
function sanitizeError(error) {
|
|
71
|
+
const first = String(error ?? "").split("\n")[0]?.slice(0, 200) ?? "";
|
|
72
|
+
if (!first.trim()) return "unknown error";
|
|
73
|
+
let redacted = first.replace(KEYWORD_REDACT, "$1=REDACTED");
|
|
74
|
+
for (const [pattern, replacement] of BARE_TOKEN_REDACT) redacted = redacted.replace(pattern, replacement);
|
|
75
|
+
redacted = redacted.replace(CREDENTIAL_URL_REDACT, "$1REDACTED@");
|
|
76
|
+
if (looksLikeCanary(redacted)) return "upstream error redacted (possible secret)";
|
|
77
|
+
return redacted || "unknown error";
|
|
78
|
+
}
|
|
79
|
+
var CANARY_PATTERNS = [
|
|
80
|
+
/\b(BEGIN|END)\s+(RSA|OPENSSH|EC|DSA)\s+PRIVATE\s+KEY/i,
|
|
81
|
+
/\bsk-[A-Za-z0-9_-]{8,}\b/,
|
|
82
|
+
/\bAKIA[0-9A-Z]{16}\b/,
|
|
83
|
+
/\bAIza[0-9A-Za-z_-]{30,}\b/,
|
|
84
|
+
/\bghp_[A-Za-z0-9]{8,}\b/,
|
|
85
|
+
/\bghu_[A-Za-z0-9]{8,}\b/,
|
|
86
|
+
/\bghs_[A-Za-z0-9]{8,}\b/,
|
|
87
|
+
/\bgithub_pat_[A-Za-z0-9_]{8,}\b/,
|
|
88
|
+
/[a-zA-Z][a-zA-Z0-9+.-]*:\/\/[^:@\s/]+:[^@\s/]+@/,
|
|
89
|
+
/(?:password|passwd|pwd|secret)\s*[:=]\s*\S+/i
|
|
90
|
+
];
|
|
91
|
+
function looksLikeCanary(text) {
|
|
92
|
+
return CANARY_PATTERNS.some((re) => re.test(text));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// packages/core/src/evidence.ts
|
|
96
|
+
var MAX_EVIDENCE_ITEMS = 16;
|
|
97
|
+
var MAX_EVIDENCE_DETAIL_CHARS = 240;
|
|
98
|
+
var MAX_SCOPE_ITEMS = 32;
|
|
99
|
+
var MAX_SCOPE_LABEL_CHARS = 120;
|
|
100
|
+
var MAX_RECEIPT_ID_CHARS = 128;
|
|
101
|
+
var EVIDENCE_CODES = /* @__PURE__ */ new Set([
|
|
102
|
+
"error-line",
|
|
103
|
+
"python-traceback",
|
|
104
|
+
"panic",
|
|
105
|
+
"exception",
|
|
106
|
+
"typescript-error",
|
|
107
|
+
"fail-marker",
|
|
108
|
+
"command-failed",
|
|
109
|
+
"nonzero-exit",
|
|
110
|
+
"failure-count",
|
|
111
|
+
"command-not-found",
|
|
112
|
+
"permission-denied",
|
|
113
|
+
"missing-file",
|
|
114
|
+
"soft-warning",
|
|
115
|
+
"soft-deprecated",
|
|
116
|
+
"soft-retrying",
|
|
117
|
+
"soft-timeout",
|
|
118
|
+
"tool-error",
|
|
119
|
+
"permission-denial",
|
|
120
|
+
"rate-limited",
|
|
121
|
+
"quota-exceeded",
|
|
122
|
+
"timeout",
|
|
123
|
+
"mutation",
|
|
124
|
+
"verification-receipt",
|
|
125
|
+
"summary-claim",
|
|
126
|
+
"scope-observed",
|
|
127
|
+
"constraint",
|
|
128
|
+
"prior-failure",
|
|
129
|
+
"context-boundary",
|
|
130
|
+
"observation"
|
|
131
|
+
]);
|
|
132
|
+
var EVIDENCE_SOURCES = /* @__PURE__ */ new Set(["tool", "user", "harness", "judge", "summary"]);
|
|
133
|
+
var EVIDENCE_STATUSES = /* @__PURE__ */ new Set(["observed", "verified", "unverified", "contradicted"]);
|
|
134
|
+
function boundedText(value, maxChars) {
|
|
135
|
+
if (typeof value !== "string") return void 0;
|
|
136
|
+
const text = value.replace(/[\u0000-\u001f\u007f]/g, " ").trim();
|
|
137
|
+
if (!text || looksLikeCanary(text)) return void 0;
|
|
138
|
+
return text.slice(0, maxChars);
|
|
139
|
+
}
|
|
140
|
+
function generationOf(value) {
|
|
141
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : 0;
|
|
142
|
+
}
|
|
143
|
+
function isEvidenceCode(value) {
|
|
144
|
+
return typeof value === "string" && EVIDENCE_CODES.has(value);
|
|
145
|
+
}
|
|
146
|
+
function isEvidenceSource(value) {
|
|
147
|
+
return typeof value === "string" && EVIDENCE_SOURCES.has(value);
|
|
148
|
+
}
|
|
149
|
+
function isEvidenceStatus(value) {
|
|
150
|
+
return typeof value === "string" && EVIDENCE_STATUSES.has(value);
|
|
151
|
+
}
|
|
152
|
+
function normalizeTrajectoryEvidence(value, fallbackGeneration = 0) {
|
|
153
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
154
|
+
const item = value;
|
|
155
|
+
if (!isEvidenceCode(item.code) || !isEvidenceSource(item.source) || !isEvidenceStatus(item.status)) return void 0;
|
|
156
|
+
const detail = boundedText(item.detail, MAX_EVIDENCE_DETAIL_CHARS);
|
|
157
|
+
return {
|
|
158
|
+
code: item.code,
|
|
159
|
+
source: item.source,
|
|
160
|
+
status: item.status,
|
|
161
|
+
contextGeneration: generationOf(item.contextGeneration ?? fallbackGeneration),
|
|
162
|
+
...detail ? { detail } : {}
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
function boundTrajectoryEvidence(values, maxItems = MAX_EVIDENCE_ITEMS) {
|
|
166
|
+
const result = [];
|
|
167
|
+
if (maxItems <= 0) return result;
|
|
168
|
+
const seen = /* @__PURE__ */ new Set();
|
|
169
|
+
for (const value of values ?? []) {
|
|
170
|
+
const item = normalizeTrajectoryEvidence(value);
|
|
171
|
+
if (!item) continue;
|
|
172
|
+
const key = JSON.stringify(item);
|
|
173
|
+
if (seen.has(key)) continue;
|
|
174
|
+
seen.add(key);
|
|
175
|
+
result.push(item);
|
|
176
|
+
if (result.length >= Math.max(0, Math.min(maxItems, MAX_EVIDENCE_ITEMS))) break;
|
|
177
|
+
}
|
|
178
|
+
return result;
|
|
179
|
+
}
|
|
180
|
+
function countScope(value) {
|
|
181
|
+
if (typeof value === "number") return Number.isSafeInteger(value) && value >= 0 ? { count: value } : {};
|
|
182
|
+
if (!Array.isArray(value)) return {};
|
|
183
|
+
const labels = [...new Set(value.map((item) => boundedText(item, MAX_SCOPE_LABEL_CHARS)).filter((item) => Boolean(item)))].slice(0, MAX_SCOPE_ITEMS);
|
|
184
|
+
return { count: labels.length, labels };
|
|
185
|
+
}
|
|
186
|
+
function buildScopeCoverage(input = {}) {
|
|
187
|
+
const expected = countScope(input.expected);
|
|
188
|
+
const observed = countScope(input.observed);
|
|
189
|
+
const explicit = input.source === "explicit" || input.expected !== void 0;
|
|
190
|
+
const source = input.source ?? (explicit ? "explicit" : input.observed !== void 0 ? "inferred" : "unknown");
|
|
191
|
+
const expectedCount = expected.count;
|
|
192
|
+
const observedCount = observed.count;
|
|
193
|
+
const ratio = expectedCount !== void 0 && expectedCount > 0 && observedCount !== void 0 ? Math.min(1, observedCount / expectedCount) : void 0;
|
|
194
|
+
const missing = input.missing ? [...new Set(input.missing.map((item) => boundedText(item, MAX_SCOPE_LABEL_CHARS)).filter((item) => Boolean(item)))].slice(0, MAX_SCOPE_ITEMS) : expected.labels && observed.labels ? expected.labels.filter((item) => !observed.labels.includes(item)).slice(0, MAX_SCOPE_ITEMS) : void 0;
|
|
195
|
+
return {
|
|
196
|
+
...expectedCount !== void 0 ? { expected: expectedCount } : {},
|
|
197
|
+
...observedCount !== void 0 ? { observed: observedCount } : {},
|
|
198
|
+
...ratio !== void 0 ? { ratio } : {},
|
|
199
|
+
source,
|
|
200
|
+
...missing && missing.length > 0 ? { missing } : {}
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
function validReceipt(value, generation) {
|
|
204
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
205
|
+
const item = value;
|
|
206
|
+
const id = boundedText(item.id, MAX_RECEIPT_ID_CHARS);
|
|
207
|
+
const status = item.status === "passed" || item.status === "failed" ? item.status : void 0;
|
|
208
|
+
const source = item.source === "tool" || item.source === "user" || item.source === "harness" ? item.source : void 0;
|
|
209
|
+
const receiptGeneration = generationOf(item.generation);
|
|
210
|
+
if (!id || !status || !source || item.valid === false || receiptGeneration !== generation) return void 0;
|
|
211
|
+
return { id, status, source, generation: receiptGeneration, ...item.valid === true ? { valid: true } : {} };
|
|
212
|
+
}
|
|
213
|
+
function deriveVerificationState(input = {}) {
|
|
214
|
+
const generation = generationOf(input.generation);
|
|
215
|
+
const receiptCandidate = input.receipt;
|
|
216
|
+
if (receiptCandidate !== void 0) {
|
|
217
|
+
const receipt = validReceipt(receiptCandidate, generation);
|
|
218
|
+
if (receipt) return { status: receipt.status, receiptId: receipt.id, generation };
|
|
219
|
+
return { status: "unknown", reason: "invalid-receipt", generation };
|
|
220
|
+
}
|
|
221
|
+
if (input.summaryClaim) return { status: "unknown", reason: "summary-without-receipt", generation };
|
|
222
|
+
if (input.mutation) return { status: "needed", reason: "mutation-without-receipt", generation };
|
|
223
|
+
if (input.verificationAttempted) return { status: "attempted", reason: "missing-receipt", generation };
|
|
224
|
+
return { status: "not-required", generation };
|
|
225
|
+
}
|
|
226
|
+
function evidenceForTrajectory(state, options = {}) {
|
|
227
|
+
const generation = generationOf(state.contextGeneration);
|
|
228
|
+
const evidence = [];
|
|
229
|
+
if (state.roundKind === "implementation") evidence.push({ code: "mutation", source: "tool", status: "observed", contextGeneration: generation });
|
|
230
|
+
for (const code of state.failureEvidence) {
|
|
231
|
+
if (isEvidenceCode(code)) evidence.push({ code, source: "tool", status: "observed", contextGeneration: generation });
|
|
232
|
+
}
|
|
233
|
+
if (options.verificationReceipt) evidence.push({ code: "verification-receipt", source: "harness", status: "verified", contextGeneration: generation });
|
|
234
|
+
if (options.summaryClaim) evidence.push({ code: "summary-claim", source: "summary", status: "unverified", contextGeneration: generation });
|
|
235
|
+
if (generation > 0) evidence.push({ code: "context-boundary", source: "harness", status: "observed", contextGeneration: generation });
|
|
236
|
+
return boundTrajectoryEvidence(evidence);
|
|
237
|
+
}
|
|
238
|
+
function decorateTrajectoryState(state, metadata = {}) {
|
|
239
|
+
const generation = generationOf(metadata.generation ?? state.contextGeneration);
|
|
240
|
+
const receipt = metadata.verificationReceipt;
|
|
241
|
+
const verification = deriveVerificationState({
|
|
242
|
+
mutation: state.roundKind === "implementation",
|
|
243
|
+
verificationAttempted: state.roundKind === "verification",
|
|
244
|
+
summaryClaim: metadata.summaryClaim,
|
|
245
|
+
generation,
|
|
246
|
+
receipt
|
|
247
|
+
});
|
|
248
|
+
const scopeCoverage = metadata.scope ? buildScopeCoverage(metadata.scope) : void 0;
|
|
249
|
+
const evidence = evidenceForTrajectory(state, { summaryClaim: metadata.summaryClaim, verificationReceipt: receipt !== void 0 });
|
|
250
|
+
return {
|
|
251
|
+
...state,
|
|
252
|
+
...evidence.length > 0 ? { evidence } : {},
|
|
253
|
+
verification,
|
|
254
|
+
...scopeCoverage ? { scopeCoverage } : {}
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
|
|
4
258
|
// packages/core/src/state.ts
|
|
5
259
|
var EXPLORE_TOOLS = /* @__PURE__ */ new Set([
|
|
6
260
|
"read",
|
|
@@ -678,6 +932,24 @@ function validateConfig(value, source = "<inline>") {
|
|
|
678
932
|
return { ...config, upstreams, models, aliases, policy };
|
|
679
933
|
}
|
|
680
934
|
|
|
935
|
+
// packages/core/src/recovery-actions.ts
|
|
936
|
+
var RECOVERY_ACTIONS = [
|
|
937
|
+
"continue",
|
|
938
|
+
"retry-same",
|
|
939
|
+
"retry-with-feedback",
|
|
940
|
+
"gather-evidence",
|
|
941
|
+
"escalate-model",
|
|
942
|
+
"fresh-context",
|
|
943
|
+
"rollback-with-reflection",
|
|
944
|
+
"ask-user"
|
|
945
|
+
];
|
|
946
|
+
function isRecoveryAction(value) {
|
|
947
|
+
return typeof value === "string" && RECOVERY_ACTIONS.includes(value);
|
|
948
|
+
}
|
|
949
|
+
function normalizeRecoveryAction(value, fallback = "ask-user") {
|
|
950
|
+
return isRecoveryAction(value) ? value : fallback;
|
|
951
|
+
}
|
|
952
|
+
|
|
681
953
|
// packages/core/src/log.ts
|
|
682
954
|
import { appendFileSync, chmodSync, existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, writeFileSync } from "node:fs";
|
|
683
955
|
import { createHmac, randomBytes, randomUUID } from "node:crypto";
|
|
@@ -774,12 +1046,101 @@ function recordLogWriteFailure(logFile, error) {
|
|
|
774
1046
|
}
|
|
775
1047
|
function appendDecision(record, logFile = defaultLogPath()) {
|
|
776
1048
|
try {
|
|
777
|
-
appendPrivateLine(logFile, `${
|
|
1049
|
+
appendPrivateLine(logFile, `${serializeDecisionRecord(record)}
|
|
778
1050
|
`);
|
|
779
1051
|
} catch (error) {
|
|
780
1052
|
recordLogWriteFailure(logFile, error);
|
|
781
1053
|
}
|
|
782
1054
|
}
|
|
1055
|
+
function boundedStringList(values, maxItems, maxChars) {
|
|
1056
|
+
if (!Array.isArray(values)) return [];
|
|
1057
|
+
return values.filter((value) => typeof value === "string" && value.trim().length > 0).map((value) => value.replace(/[\u0000-\u001f\u007f]/g, " ").slice(0, maxChars)).slice(0, maxItems);
|
|
1058
|
+
}
|
|
1059
|
+
function isPlainObject(value) {
|
|
1060
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1061
|
+
}
|
|
1062
|
+
function sanitizeState(state, config) {
|
|
1063
|
+
const policy = telemetryPolicy(config);
|
|
1064
|
+
const failureEvidence = boundedStringList(state.failureEvidence, 8, 64).filter((value) => policy.allowlisted(value));
|
|
1065
|
+
const evidence = boundTrajectoryEvidence(state.evidence);
|
|
1066
|
+
const clean = {
|
|
1067
|
+
messageCount: state.messageCount,
|
|
1068
|
+
assistantTurns: state.assistantTurns,
|
|
1069
|
+
toolMessages: state.toolMessages,
|
|
1070
|
+
lastRole: String(state.lastRole ?? "").slice(0, 32),
|
|
1071
|
+
contextChars: state.contextChars,
|
|
1072
|
+
estimatedTokens: state.estimatedTokens,
|
|
1073
|
+
hasTools: state.hasTools,
|
|
1074
|
+
toolNames: boundedStringList(state.toolNames, 40, 120),
|
|
1075
|
+
lastToolNames: boundedStringList(state.lastToolNames, 12, 120),
|
|
1076
|
+
roundKind: state.roundKind,
|
|
1077
|
+
failure: state.failure,
|
|
1078
|
+
failureEvidence,
|
|
1079
|
+
...state.contextTokens !== void 0 ? { contextTokens: state.contextTokens } : {},
|
|
1080
|
+
...state.contextKnown !== void 0 ? { contextKnown: state.contextKnown } : {},
|
|
1081
|
+
...state.contextGeneration !== void 0 ? { contextGeneration: state.contextGeneration } : {},
|
|
1082
|
+
...state.repeatedFailure !== void 0 ? { repeatedFailure: state.repeatedFailure } : {},
|
|
1083
|
+
...state.failureStreak !== void 0 ? { failureStreak: state.failureStreak } : {},
|
|
1084
|
+
...state.contextWindow !== void 0 ? { contextWindow: state.contextWindow } : {},
|
|
1085
|
+
...state.inputModalities ? { inputModalities: state.inputModalities.slice(0, 5) } : {},
|
|
1086
|
+
...state.mediaCounts ? { mediaCounts: state.mediaCounts } : {},
|
|
1087
|
+
...evidence.length > 0 ? { evidence } : {},
|
|
1088
|
+
...state.verification ? { verification: { ...state.verification, receiptId: state.verification.receiptId?.slice(0, 128) } } : {},
|
|
1089
|
+
...state.scopeCoverage ? {
|
|
1090
|
+
scopeCoverage: {
|
|
1091
|
+
...state.scopeCoverage,
|
|
1092
|
+
...state.scopeCoverage.missing ? { missing: boundedStringList(state.scopeCoverage.missing, 32, 120) } : {}
|
|
1093
|
+
}
|
|
1094
|
+
} : {}
|
|
1095
|
+
};
|
|
1096
|
+
return clean;
|
|
1097
|
+
}
|
|
1098
|
+
function sanitizeDecisionRecord(record, config) {
|
|
1099
|
+
const policy = telemetryPolicy(config);
|
|
1100
|
+
const state = isPlainObject(record.state) ? sanitizeState(record.state, config) : void 0;
|
|
1101
|
+
const sanitized = {
|
|
1102
|
+
ts: record.ts,
|
|
1103
|
+
sessionId: record.sessionId,
|
|
1104
|
+
sessionKnown: record.sessionKnown,
|
|
1105
|
+
requestId: record.requestId,
|
|
1106
|
+
client: record.client,
|
|
1107
|
+
turnId: record.turnId,
|
|
1108
|
+
servedModel: record.servedModel,
|
|
1109
|
+
alias: record.alias,
|
|
1110
|
+
mode: record.mode,
|
|
1111
|
+
rule: record.rule,
|
|
1112
|
+
tier: record.tier,
|
|
1113
|
+
reason: sanitizeReason(String(record.reason ?? ""), policy),
|
|
1114
|
+
upstream: record.upstream,
|
|
1115
|
+
upstreamModel: record.upstreamModel,
|
|
1116
|
+
stream: record.stream,
|
|
1117
|
+
state,
|
|
1118
|
+
judge: record.judge,
|
|
1119
|
+
usage: record.usage,
|
|
1120
|
+
cost: record.cost,
|
|
1121
|
+
latencyMs: record.latencyMs,
|
|
1122
|
+
ttftMs: record.ttftMs,
|
|
1123
|
+
outcome: record.outcome,
|
|
1124
|
+
...record.error ? { error: sanitizeError(record.error) } : {},
|
|
1125
|
+
transport: record.transport,
|
|
1126
|
+
fallback: record.fallback,
|
|
1127
|
+
...record.recovery ? {
|
|
1128
|
+
recovery: {
|
|
1129
|
+
...record.recovery,
|
|
1130
|
+
action: normalizeRecoveryAction(record.recovery.action),
|
|
1131
|
+
evidenceGrade: record.recovery.evidenceGrade === "matched" || record.recovery.evidenceGrade === "replayed" ? record.recovery.evidenceGrade : "observed",
|
|
1132
|
+
failureSignature: String(record.recovery.failureSignature).slice(0, 64),
|
|
1133
|
+
stateFingerprint: String(record.recovery.stateFingerprint).slice(0, 64),
|
|
1134
|
+
...record.recovery.route ? { route: String(record.recovery.route).slice(0, 160) } : {},
|
|
1135
|
+
...record.recovery.receiptId ? { receiptId: String(record.recovery.receiptId).slice(0, 128) } : {}
|
|
1136
|
+
}
|
|
1137
|
+
} : {}
|
|
1138
|
+
};
|
|
1139
|
+
return sanitized;
|
|
1140
|
+
}
|
|
1141
|
+
function serializeDecisionRecord(record, config) {
|
|
1142
|
+
return JSON.stringify(sanitizeDecisionRecord(record, config));
|
|
1143
|
+
}
|
|
783
1144
|
function hashIdentity(kind, ...parts) {
|
|
784
1145
|
return createHmac("sha256", getIdentitySalt()).update(JSON.stringify([kind, ...parts])).digest("hex");
|
|
785
1146
|
}
|
|
@@ -804,7 +1165,7 @@ function trajectoryFromRound(round, previous) {
|
|
|
804
1165
|
const sameFailure = previous?.failure === "hard" && failure === "hard";
|
|
805
1166
|
const repeatedFailure = sameFailure === true;
|
|
806
1167
|
const failureStreak = repeatedFailure ? 2 : failure === "hard" ? 1 : 0;
|
|
807
|
-
|
|
1168
|
+
const state = {
|
|
808
1169
|
messageCount: round.messageCount,
|
|
809
1170
|
assistantTurns: round.assistantTurns,
|
|
810
1171
|
toolMessages: round.calls.length,
|
|
@@ -825,6 +1186,12 @@ function trajectoryFromRound(round, previous) {
|
|
|
825
1186
|
...round.inputModalities ? { inputModalities: round.inputModalities } : {},
|
|
826
1187
|
...round.mediaCounts && Object.keys(round.mediaCounts).length ? { inputModalities: round.inputModalities ?? modalitiesOf(round.mediaCounts), mediaCounts: round.mediaCounts } : {}
|
|
827
1188
|
};
|
|
1189
|
+
return decorateTrajectoryState(state, {
|
|
1190
|
+
generation: round.contextGeneration,
|
|
1191
|
+
verificationReceipt: round.verificationReceipt,
|
|
1192
|
+
summaryClaim: round.summaryClaim,
|
|
1193
|
+
scope: round.scope
|
|
1194
|
+
});
|
|
828
1195
|
}
|
|
829
1196
|
function roundMedia(round) {
|
|
830
1197
|
return { counts: round.mediaCounts ?? {}, payloadChars: 0 };
|
|
@@ -856,54 +1223,6 @@ function planRound(state, policy, tiers, options = {}) {
|
|
|
856
1223
|
};
|
|
857
1224
|
}
|
|
858
1225
|
|
|
859
|
-
// packages/core/src/telemetry.ts
|
|
860
|
-
var ALLOWLIST = /* @__PURE__ */ new Set([
|
|
861
|
-
"error-line",
|
|
862
|
-
"python-traceback",
|
|
863
|
-
"panic",
|
|
864
|
-
"exception",
|
|
865
|
-
"typescript-error",
|
|
866
|
-
"fail-marker",
|
|
867
|
-
"command-failed",
|
|
868
|
-
"nonzero-exit",
|
|
869
|
-
"failure-count",
|
|
870
|
-
"command-not-found",
|
|
871
|
-
"permission-denied",
|
|
872
|
-
"missing-file",
|
|
873
|
-
"soft-warning",
|
|
874
|
-
"soft-deprecated",
|
|
875
|
-
"soft-retrying",
|
|
876
|
-
"soft-timeout",
|
|
877
|
-
"tool-error",
|
|
878
|
-
"permission-denial",
|
|
879
|
-
"rate-limited",
|
|
880
|
-
"quota-exceeded",
|
|
881
|
-
"timeout"
|
|
882
|
-
]);
|
|
883
|
-
function allowlisted(value) {
|
|
884
|
-
const code = value.split(":")[0]?.trim();
|
|
885
|
-
return ALLOWLIST.has(code);
|
|
886
|
-
}
|
|
887
|
-
function telemetryPolicy(config) {
|
|
888
|
-
const captureSnippets = config?.captureSnippets === true;
|
|
889
|
-
const captureChars = config?.captureChars ?? 800;
|
|
890
|
-
return {
|
|
891
|
-
captureSnippets,
|
|
892
|
-
allowlisted: (value) => allowlisted(value),
|
|
893
|
-
snippet: (value) => captureSnippets ? String(value).slice(0, captureChars) : ""
|
|
894
|
-
};
|
|
895
|
-
}
|
|
896
|
-
function sanitizeReason(reason, policy) {
|
|
897
|
-
if (!policy.captureSnippets) {
|
|
898
|
-
if (!reason || allowlisted(reason)) return reason;
|
|
899
|
-
const segments = reason.split(":").map((segment) => segment.trim());
|
|
900
|
-
const last = segments[segments.length - 1] ?? "";
|
|
901
|
-
if (allowlisted(last)) return reason;
|
|
902
|
-
return "reason withheld (telemetry.allowlistOnly)";
|
|
903
|
-
}
|
|
904
|
-
return policy.snippet(reason);
|
|
905
|
-
}
|
|
906
|
-
|
|
907
1226
|
// packages/core/src/prompt.ts
|
|
908
1227
|
import * as readline from "node:readline/promises";
|
|
909
1228
|
|
package/package.json
CHANGED