slopie 1.7.5 → 1.8.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/bin/audit-splash.js +104 -28
- package/dist/audit-native-worker.js +844 -32
- package/dist/audit-native-worker.js.map +4 -4
- package/dist/potsherd.js +13438 -5044
- package/dist/potsherd.js.map +4 -4
- package/package.json +1 -1
|
@@ -6,6 +6,7 @@ import { Worker, parentPort } from "node:worker_threads";
|
|
|
6
6
|
|
|
7
7
|
// packages/core/src/analytics/extract.ts
|
|
8
8
|
import fs from "node:fs";
|
|
9
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
9
10
|
import path from "node:path";
|
|
10
11
|
|
|
11
12
|
// packages/core/src/markers.ts
|
|
@@ -598,8 +599,661 @@ function claim(claimed, s) {
|
|
|
598
599
|
// packages/core/src/analytics/source.ts
|
|
599
600
|
var clean = (text) => redact(elideBinary(text)).text;
|
|
600
601
|
|
|
602
|
+
// packages/core/src/analytics/profanity.ts
|
|
603
|
+
var ENGLISH_EXPLICIT_LEXICON = Object.freeze(["fuck", "fucked", "fucking", "shit", "shitty", "bullshit", "asshole", "bastard"]);
|
|
604
|
+
var PROFANITY_LEXICON_VERSION = "en-explicit-8-v1";
|
|
605
|
+
var words = new Set(ENGLISH_EXPLICIT_LEXICON);
|
|
606
|
+
var LEXICON_HINT = /fuck|shit|asshole|bastard/i;
|
|
607
|
+
var CODE = 2;
|
|
608
|
+
var QUOTE = 1;
|
|
609
|
+
var UNKNOWN = 3;
|
|
610
|
+
var kind = (label) => label === CODE ? "code" : label === QUOTE ? "quoted" : label === UNKNOWN ? "unknown" : "direct_prose";
|
|
611
|
+
var escaped = (text, index) => {
|
|
612
|
+
let count2 = 0;
|
|
613
|
+
for (let i = index - 1; i >= 0 && text[i] === "\\"; i--) count2++;
|
|
614
|
+
return count2 % 2 === 1;
|
|
615
|
+
};
|
|
616
|
+
var wordChar = (char) => char !== void 0 && /[\p{L}\p{N}\p{M}_\u200c\u200d]/u.test(char);
|
|
617
|
+
var labelMemo = /* @__PURE__ */ new Map();
|
|
618
|
+
function labels(text) {
|
|
619
|
+
let hit = labelMemo.get(text);
|
|
620
|
+
if (hit) return hit;
|
|
621
|
+
if (labelMemo.size > 2e4) labelMemo.clear();
|
|
622
|
+
hit = computeLabels(text);
|
|
623
|
+
labelMemo.set(text, hit);
|
|
624
|
+
return hit;
|
|
625
|
+
}
|
|
626
|
+
function computeLabels(text) {
|
|
627
|
+
const map = new Uint8Array(text.length);
|
|
628
|
+
let ambiguous = false, offset = 0, fence = null;
|
|
629
|
+
for (const line of text.split(/(?<=\n)/u)) {
|
|
630
|
+
const marker = line.match(/^ {0,3}(`{3,}|~{3,})/u);
|
|
631
|
+
if (fence) {
|
|
632
|
+
if (marker && marker[1][0] === fence.char && marker[1].length >= fence.length && line.slice(marker[0].length).trim() === "") {
|
|
633
|
+
map.fill(CODE, fence.start, offset + line.length);
|
|
634
|
+
fence = null;
|
|
635
|
+
}
|
|
636
|
+
} else if (marker) fence = { char: marker[1][0], length: marker[1].length, start: offset };
|
|
637
|
+
else if (/^ {0,3}>/u.test(line)) map.fill(QUOTE, offset, offset + line.length);
|
|
638
|
+
else if (/^(?: {4}|\t)\S/u.test(line)) {
|
|
639
|
+
map.fill(UNKNOWN, offset, offset + line.length);
|
|
640
|
+
ambiguous = true;
|
|
641
|
+
}
|
|
642
|
+
offset += line.length;
|
|
643
|
+
}
|
|
644
|
+
if (fence) {
|
|
645
|
+
map.fill(UNKNOWN, fence.start);
|
|
646
|
+
ambiguous = true;
|
|
647
|
+
}
|
|
648
|
+
const sourceRows = text.split(/(?<=\n)/u), timestamp = /^\s*(?:(?:[-*#]+|\*\*)\s*)?\[?\d{1,2}:\d{2}(?::\d{2})?(?:\.\d+)?\]?/u;
|
|
649
|
+
if (/(?:transcript|captions|youtube|youtu\.be|video transcription)/iu.test(text) && sourceRows.filter((line) => timestamp.test(line)).length >= 3) {
|
|
650
|
+
let at = 0;
|
|
651
|
+
for (const line of sourceRows) {
|
|
652
|
+
if (timestamp.test(line)) map.fill(QUOTE, at, at + line.length);
|
|
653
|
+
at += line.length;
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
for (let i = 0; i < text.length; i++) {
|
|
657
|
+
if (map[i] || escaped(text, i)) continue;
|
|
658
|
+
const char = text[i];
|
|
659
|
+
if (char === "`") {
|
|
660
|
+
let count2 = 1;
|
|
661
|
+
while (text[i + count2] === "`") count2++;
|
|
662
|
+
const token = "`".repeat(count2);
|
|
663
|
+
let end2 = text.indexOf(token, i + count2);
|
|
664
|
+
while (end2 >= 0 && (map[end2] !== 0 || text[end2 - 1] === "`" || text[end2 + count2] === "`")) end2 = text.indexOf(token, end2 + count2);
|
|
665
|
+
if (end2 < 0) {
|
|
666
|
+
map.fill(UNKNOWN, i);
|
|
667
|
+
ambiguous = true;
|
|
668
|
+
break;
|
|
669
|
+
}
|
|
670
|
+
map.fill(CODE, i, end2 + count2);
|
|
671
|
+
i = end2 + count2 - 1;
|
|
672
|
+
continue;
|
|
673
|
+
}
|
|
674
|
+
if (!['"', "'", "\u201C", "\u2018"].includes(char)) continue;
|
|
675
|
+
if ((char === "'" || char === "\u2018") && wordChar(text[i - 1]) && wordChar(text[i + 1])) continue;
|
|
676
|
+
const closing = char === "\u201C" ? "\u201D" : char === "\u2018" ? "\u2019" : char;
|
|
677
|
+
let end = i + 1;
|
|
678
|
+
while (end < text.length) {
|
|
679
|
+
if (text[end] === closing && !escaped(text, end) && map[end] === 0 && !((closing === "'" || closing === "\u2019") && wordChar(text[end - 1]) && wordChar(text[end + 1]))) break;
|
|
680
|
+
end++;
|
|
681
|
+
}
|
|
682
|
+
if (end >= text.length) {
|
|
683
|
+
map.fill(UNKNOWN, i);
|
|
684
|
+
ambiguous = true;
|
|
685
|
+
break;
|
|
686
|
+
}
|
|
687
|
+
map.fill(QUOTE, i, end + 1);
|
|
688
|
+
i = end;
|
|
689
|
+
}
|
|
690
|
+
return { labels: map, ambiguous };
|
|
691
|
+
}
|
|
692
|
+
function proseLabels(text) {
|
|
693
|
+
return labels(text).labels;
|
|
694
|
+
}
|
|
695
|
+
var TOKEN = /[\p{L}\p{N}\p{M}_\u200c\u200d]+(?:['’][\p{L}\p{N}\p{M}_\u200c\u200d]+)*/gu;
|
|
696
|
+
function profanityHits(text) {
|
|
697
|
+
if (!LEXICON_HINT.test(text)) return null;
|
|
698
|
+
const spans = labels(text), hits = [];
|
|
699
|
+
TOKEN.lastIndex = 0;
|
|
700
|
+
let token;
|
|
701
|
+
while ((token = TOKEN.exec(text)) !== null) {
|
|
702
|
+
if (!words.has(token[0].toLowerCase())) continue;
|
|
703
|
+
hits.push([token[0], spans.labels[token.index] ?? 0, token.index, token.index + token[0].length]);
|
|
704
|
+
}
|
|
705
|
+
return { hits, ambiguous: spans.ambiguous };
|
|
706
|
+
}
|
|
707
|
+
var knownExcluded = /* @__PURE__ */ new Set(["child_initialization", "maintenance_exclusion_marker", "tool_result", "inherited_native_event", "declared_meta_or_synthetic_input", "declared_synthetic_input"]);
|
|
708
|
+
var languageEligibleInput = (p) => p.languageEligible !== false && (p.eligibleNativeInput === true && !knownExcluded.has(p.excludedReason ?? "") || p.eligibleNativeInput === void 0 && p.eligibleHuman && p.excludedReason === null && ["claude_prompt_id", "codex_human_marker"].includes(p.originBasis));
|
|
709
|
+
function hitsOf(p) {
|
|
710
|
+
if (p.lex) return p.lex.pt ? { hits: p.lex.pt, ambiguous: !!p.lex.pa } : null;
|
|
711
|
+
return profanityHits(p.text);
|
|
712
|
+
}
|
|
713
|
+
function auditProfanity(prompts, partial = false) {
|
|
714
|
+
const eligible = prompts.filter(languageEligibleInput), bucketCounts = new Map(["direct_prose", "quoted", "code", "unknown"].map((k) => [k, { occurrences: 0, prompts: /* @__PURE__ */ new Set() }])), containing = /* @__PURE__ */ new Set(), matches = [];
|
|
715
|
+
const terms = /* @__PURE__ */ new Map();
|
|
716
|
+
let occurrences = 0, ambiguous = false;
|
|
717
|
+
for (const prompt of eligible) {
|
|
718
|
+
const found = hitsOf(prompt);
|
|
719
|
+
if (!found) continue;
|
|
720
|
+
ambiguous ||= found.ambiguous;
|
|
721
|
+
for (const [raw, label, start, end] of found.hits) {
|
|
722
|
+
const bucket = kind(label), counter = bucketCounts.get(bucket);
|
|
723
|
+
occurrences++;
|
|
724
|
+
containing.add(prompt.id);
|
|
725
|
+
counter.occurrences++;
|
|
726
|
+
counter.prompts.add(prompt.id);
|
|
727
|
+
const normalized = raw.toLowerCase(), key = normalized + "\0" + bucket;
|
|
728
|
+
const term = terms.get(key) ?? { term: normalized, kind: bucket, occurrences: 0, ids: /* @__PURE__ */ new Set(), samples: [] };
|
|
729
|
+
term.occurrences++;
|
|
730
|
+
term.ids.add(prompt.id);
|
|
731
|
+
if (term.samples.length < 3) term.samples.push({ promptId: prompt.id, conversationId: prompt.conversationId, startUtf16: start, endUtf16: end, route: prompt.route });
|
|
732
|
+
terms.set(key, term);
|
|
733
|
+
if (matches.length < 8) matches.push({ term: raw, kind: bucket, promptId: prompt.id, conversationId: prompt.conversationId, startUtf16: start, endUtf16: end, route: prompt.route });
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
const measurementBasis = "literal_english_lexicon_observed_native_input_utf16_v2", definition = "Literal occurrences of the documented eight-word English lexicon in eligible observed native user-role inputs after existing redaction/elision; this does not attest universal human authorship. Unicode whole-word tokens; containing inputs counted once. No emotion, intent or universal-language inference. Ranges are relative to the immutable redacted containing input; routes identify that input.";
|
|
737
|
+
return { lexiconVersion: PROFANITY_LEXICON_VERSION, language: "en-explicit-lexicon", measurementBasis, eligiblePrompts: eligible.length, occurrences: { value: occurrences, numerator: occurrences, denominator: eligible.length, unit: "english_lexicon_occurrence", measurementBasis, state: partial ? "partial" : "observed", definition }, containingPrompts: { value: containing.size, numerator: containing.size, denominator: eligible.length, unit: "eligible_input_containing_english_lexicon_match", measurementBasis, state: partial ? "partial" : "observed", definition }, buckets: [...bucketCounts].map(([kind2, value]) => ({ kind: kind2, occurrences: value.occurrences, containingPrompts: value.prompts.size })), coverageGaps: ["outside_english_lexicon_unassessed", "hindi_hinglish_and_other_languages_unassessed", ...ambiguous ? ["ambiguous_quote_or_code_span"] : []], terms: [...terms.values()].map((t) => ({ term: t.term, kind: t.kind, occurrences: t.occurrences, containingPrompts: t.ids.size, samples: t.samples })).sort((a, b) => b.occurrences - a.occurrences || a.term.localeCompare(b.term) || a.kind.localeCompare(b.kind)), matches };
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
// packages/core/src/analytics/language-feedback.ts
|
|
741
|
+
var POSITIVE_HINT = /good (?:work|job)|well done|you understood|you (?:got|nailed)|you(?:['’]re| are)/i;
|
|
742
|
+
var POSITIVE = /\b(?:good (?:work|job)|well done|you understood (?:me|it)|you (?:got|nailed) (?:it|this)|you(?:['’]re| are) (?:fucking )?(?:great|awesome))\b/iu;
|
|
743
|
+
var NEGATED = /\b(?:not|never|don['’]?t|didn['’]?t|no)\b.{0,35}\b(?:good|well done|understood|nailed|great|awesome)\b/iu;
|
|
744
|
+
var NEGATIVE = /\b(?:wrong|broken|stupid|missed|failed|why|mess|didn['’]?t|not|bullshit|fuck(?:ed|ing)? (?:up|off))\b/iu;
|
|
745
|
+
var YOU = /\b(?:you|your|you['’]re)\b/iu;
|
|
746
|
+
var words2 = (s) => s.match(/[\p{L}\p{N}]+/gu)?.length ?? 0;
|
|
747
|
+
function namedModels(text, models) {
|
|
748
|
+
const out = [];
|
|
749
|
+
let labels2 = null;
|
|
750
|
+
for (const model of models) {
|
|
751
|
+
const at = text.indexOf(model);
|
|
752
|
+
if (at < 0) continue;
|
|
753
|
+
labels2 ??= proseLabels(text);
|
|
754
|
+
if (labels2[at] !== 0 || /[\p{L}\p{N}_/-]/u.test(text[at - 1] ?? "") || /[\p{L}\p{N}_/-]/u.test(text[at + model.length] ?? "")) continue;
|
|
755
|
+
out.push(model);
|
|
756
|
+
}
|
|
757
|
+
return out;
|
|
758
|
+
}
|
|
759
|
+
function languageLines(text) {
|
|
760
|
+
const out = [];
|
|
761
|
+
let labelCache = null;
|
|
762
|
+
const L = (i) => (labelCache ??= proseLabels(text))[i];
|
|
763
|
+
let offset = 0;
|
|
764
|
+
for (const line of text.split("\n")) {
|
|
765
|
+
const start = offset;
|
|
766
|
+
offset += line.length + 1;
|
|
767
|
+
const trimmed = line.trim();
|
|
768
|
+
if (!trimmed) continue;
|
|
769
|
+
const at = start + line.indexOf(trimmed), end = at + trimmed.length;
|
|
770
|
+
if (!LEXICON_HINT.test(trimmed) && !POSITIVE_HINT.test(trimmed)) continue;
|
|
771
|
+
const probe = { id: "line", conversationId: "", role: "user", originBasis: "claude_prompt_id", identityBasis: "", eligibleHuman: true, excludedReason: null, eventAt: null, route: { basis: "canonical", refs: [], scope: {} } };
|
|
772
|
+
const local = auditProfanity([{ ...probe, text: line }]);
|
|
773
|
+
const direct = local.terms?.some((t) => t.kind === "direct_prose" && t.samples.some((sample) => L(start + sample.startUtf16) === 0)) ?? false;
|
|
774
|
+
const nonprose = local.terms?.some((t) => t.kind !== "direct_prose") ?? false;
|
|
775
|
+
const positiveMatch = trimmed.match(POSITIVE), positive = !!positiveMatch && L(at + (positiveMatch.index ?? 0)) === 0, negated = NEGATED.test(trimmed);
|
|
776
|
+
const quoted = L(at) !== 0 || /^\s*(?:>|[`~]{3}|["“'])/u.test(line) || /^ {4}|^\t/u.test(line);
|
|
777
|
+
const row = {};
|
|
778
|
+
if (direct) row.d = 1;
|
|
779
|
+
if (nonprose) row.np = 1;
|
|
780
|
+
if (YOU.test(trimmed)) row.y = 1;
|
|
781
|
+
if (positive) row.p = 1;
|
|
782
|
+
if (negated) row.ng = 1;
|
|
783
|
+
if (quoted) row.q = 1;
|
|
784
|
+
if (NEGATIVE.test(trimmed)) row.n = 1;
|
|
785
|
+
if (direct && !quoted && words2(trimmed) >= 2) {
|
|
786
|
+
const spans = trimmed.length <= 160 ? [{ text: trimmed, start: at, end }] : [...trimmed.matchAll(/[^.!?;\n]+[.!?;]?/gu)].map((match) => {
|
|
787
|
+
const value = match[0].trim(), begin = at + match.index + match[0].indexOf(value);
|
|
788
|
+
return { text: value, start: begin, end: begin + value.length };
|
|
789
|
+
}).filter((span) => span.text.length <= 160 && words2(span.text) >= 2 && auditProfanity([{ ...probe, text: span.text }]).terms?.some((t) => t.kind === "direct_prose" && L(span.start + t.samples[0].startUtf16) === 0));
|
|
790
|
+
if (spans.length) row.sp = spans.map((s) => [s.text, s.start, s.end]);
|
|
791
|
+
else row.ns = 1;
|
|
792
|
+
}
|
|
793
|
+
out.push(row);
|
|
794
|
+
}
|
|
795
|
+
return out;
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
// packages/core/src/analytics/project-focus.ts
|
|
799
|
+
var categories = [
|
|
800
|
+
["Documentation", /\b(?:write|update|fix|add|edit|draft|revise)\b.{0,64}\b(?:docs?|documentation|readme|guide|changelog)\b/iu],
|
|
801
|
+
["Review", /\b(?:review|audit|inspect)\b.{0,64}\b(?:code|diff|pr|pull request|implementation|tests?|changes|repo|repository)\b/iu],
|
|
802
|
+
["Debugging", /\b(?:fix|debug|repair|resolve|reproduce)\b.{0,64}\b(?:bug|error|crash|failure|issue|test|broken|timeout|memory|hang|problem)\b/iu],
|
|
803
|
+
["Development", /\b(?:build|implement|add|create|develop|refactor|ship|integrate)\b.{0,64}\b(?:feature|app|api|cli|function|component|command|module|tool|parser|service|code|support)\b/iu],
|
|
804
|
+
["Research", /\b(?:research|look up|investigate|compare|verify)\b.{0,64}\b(?:docs?|source|pricing|model|library|approach|behavior|format|implementation)\b/iu]
|
|
805
|
+
];
|
|
806
|
+
var VERBS = /\b(?:write|update|fix|add|edit|draft|revise|review|audit|inspect|debug|repair|resolve|reproduce|build|implement|create|develop|refactor|ship|integrate|research|look up|investigate|compare|verify)\b/iu;
|
|
807
|
+
function focusLabels(raw) {
|
|
808
|
+
if (!VERBS.test(raw) || !categories.some(([, pattern]) => pattern.test(raw))) return [];
|
|
809
|
+
const labels2 = proseLabels(raw), text = Array.from({ length: raw.length }, (_, i) => labels2[i] === 0 ? raw[i] : " ").join("");
|
|
810
|
+
return categories.filter(([, pattern]) => pattern.test(text)).map(([label]) => label);
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
// packages/core/src/analytics/findings.ts
|
|
814
|
+
var EXACT_REPEAT_LIMITS = Object.freeze({ rows: 20, supports: 8, maxRowBytes: 8192 });
|
|
815
|
+
var PLACEHOLDER = /\[(?:Pasted text|Image) #\d+[^\]]*\]/gi;
|
|
816
|
+
function normalizedLine(text) {
|
|
817
|
+
const n = text.replace(PLACEHOLDER, "").toLowerCase().replace(/\s+/g, " ").trim().replace(/^[ .!?,]+|[ .!?,]+$/g, "");
|
|
818
|
+
return n.length > 0 && n.length <= 60 ? n : null;
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
// packages/core/src/analytics/story-lexicon.ts
|
|
822
|
+
var FLAG = {
|
|
823
|
+
paste: 1,
|
|
824
|
+
image: 2,
|
|
825
|
+
code: 4,
|
|
826
|
+
url: 8,
|
|
827
|
+
pasteSuspect: 16,
|
|
828
|
+
slur: 32,
|
|
829
|
+
polite: 64,
|
|
830
|
+
thanks: 128,
|
|
831
|
+
sorry: 256,
|
|
832
|
+
multiPunct: 512,
|
|
833
|
+
question: 1024,
|
|
834
|
+
delegate: 2048,
|
|
835
|
+
lets: 4096,
|
|
836
|
+
please: 8192,
|
|
837
|
+
history: 16384
|
|
838
|
+
};
|
|
839
|
+
var PLACEHOLDER2 = /\[(?:Pasted text|Image) #\d+[^\]]*\]/gi;
|
|
840
|
+
var URL_RX = /https?:\/\/\S+/g;
|
|
841
|
+
var CODEISH = /```[\s\S]*?```/g;
|
|
842
|
+
var WORD = /[a-z][a-z']*/g;
|
|
843
|
+
var SWEAR_STEMS = [
|
|
844
|
+
"fuck",
|
|
845
|
+
"shit",
|
|
846
|
+
"bullshit",
|
|
847
|
+
"wtf",
|
|
848
|
+
"damn",
|
|
849
|
+
"goddamn",
|
|
850
|
+
"crap",
|
|
851
|
+
"bitch",
|
|
852
|
+
"asshole",
|
|
853
|
+
"dumbass",
|
|
854
|
+
"stfu",
|
|
855
|
+
"ffs",
|
|
856
|
+
"piss",
|
|
857
|
+
"bastard",
|
|
858
|
+
"motherf",
|
|
859
|
+
"bloody",
|
|
860
|
+
"bsdk",
|
|
861
|
+
"chutiya",
|
|
862
|
+
"chutiye",
|
|
863
|
+
"madarchod",
|
|
864
|
+
"bhenchod",
|
|
865
|
+
"behenchod",
|
|
866
|
+
"gandu"
|
|
867
|
+
];
|
|
868
|
+
var SWEAR_EXACT = /* @__PURE__ */ new Set(["ffs", "wtf", "stfu"]);
|
|
869
|
+
var SWEAR_PREFIX = SWEAR_STEMS.filter((s) => !SWEAR_EXACT.has(s));
|
|
870
|
+
var INSULT = ["stupid", "idiot", "dumb", "moron", "useless", "pathetic", "lazy", "incompetent", "garbage", "trash", "retard"];
|
|
871
|
+
var SLUR_STEMS = ["retard"];
|
|
872
|
+
var POLITE = /\b(please|pls|plz|thanks|thank you|thank u|thx|ty|appreciate|great job|good job|well done|awesome|amazing|love it|love this|nice work|brilliant|you're the best|perfect)\b/;
|
|
873
|
+
var THANKS = /\b(thanks|thank you|thank u|thx|ty|appreciate it|great job|good job|well done|nice work)\b/;
|
|
874
|
+
var SORRY = /\b(sorry|my bad|apologi[sz]e)\b/;
|
|
875
|
+
var FRUSTRATION = /\b(again|still not|still doesn't|still isn't|i told you|i said|i already|not working|doesn't work|didn't work|isn't working|why (?:the|did|are|is|would) |what the|seriously|come on|for the last time|how many times|are you (?:kidding|serious|dumb|stupid))\b/g;
|
|
876
|
+
var DELEGATE = /\b(sub-?agents?|agents? in parallel|parallel agents?|spawn|launch (?:\w+ )?agents?|team of agents|multiple agents|\d+ agents)\b/;
|
|
877
|
+
var FILLER = /\b(basically|kind of|sort of|i guess|i mean|you know|like,|um+|uh+|okay so|so yeah|whatever)\b/g;
|
|
878
|
+
var PERFECTION = /\b(best possible|perfect(?:ly)?|exactly|properly|every single|completely|100%|flawless|world[- ]class|insanely|extremely)\b/g;
|
|
879
|
+
var QWORDS = /* @__PURE__ */ new Set(["what", "why", "how", "is", "are", "can", "could", "should", "does", "do", "did", "where", "when", "which", "who", "will", "would"]);
|
|
880
|
+
var WE = /\b(we|we're|we've|let's|us|our)\b/g;
|
|
881
|
+
var I_ = /\b(i|i'm|i've|me|my)\b/g;
|
|
882
|
+
var HINGLISH = /\b(bhai|yaar|kya|hai|nahi|kar|karo|acha|accha|theek|matlab|abhi|bas|chal|haan)\b/g;
|
|
883
|
+
var CAPS = /\b[A-Z][A-Z']{2,}\b/g;
|
|
884
|
+
var CAPS_OK = /* @__PURE__ */ new Set(["API", "URL", "HTML", "CSS", "JSON", "CLI", "PR", "UI", "UX", "MCP", "LLM", "SDK", "README", "TODO", "IST", "PDF", "NPM", "AWS", "GPT", "MD", "SEO", "JS", "TS", "ID", "OK", "AI", "CEO", "USA", "SQL", "DB", "SSH", "HTTP", "HTTPS", "PNG", "SVG", "CTA", "FAQ"]);
|
|
885
|
+
function isSwearWord(w) {
|
|
886
|
+
if (SWEAR_EXACT.has(w)) return true;
|
|
887
|
+
for (const s of SWEAR_PREFIX) if (w.startsWith(s)) return true;
|
|
888
|
+
return false;
|
|
889
|
+
}
|
|
890
|
+
var isInsult = (w) => INSULT.some((s) => w.startsWith(s));
|
|
891
|
+
var isSlur = (w) => SLUR_STEMS.some((s) => w.startsWith(s));
|
|
892
|
+
function mask(w) {
|
|
893
|
+
return w.length <= 2 ? w : w[0] + "*".repeat(w.length - 2) + w[w.length - 1];
|
|
894
|
+
}
|
|
895
|
+
function cleanForTone(text) {
|
|
896
|
+
return text.replace(PLACEHOLDER2, " ").replace(/[‘’]/g, "'").replace(CODEISH, " ").replace(URL_RX, " ");
|
|
897
|
+
}
|
|
898
|
+
var count = (rx, s) => {
|
|
899
|
+
rx.lastIndex = 0;
|
|
900
|
+
let n = 0;
|
|
901
|
+
while (rx.exec(s)) n++;
|
|
902
|
+
rx.lastIndex = 0;
|
|
903
|
+
return n;
|
|
904
|
+
};
|
|
905
|
+
var words3 = (s) => s.match(WORD) ?? [];
|
|
906
|
+
var TICS = [
|
|
907
|
+
"one more thing",
|
|
908
|
+
"each and every",
|
|
909
|
+
"do one thing",
|
|
910
|
+
"every single thing",
|
|
911
|
+
"kind of a thing",
|
|
912
|
+
"and all",
|
|
913
|
+
"and stuff",
|
|
914
|
+
"or something",
|
|
915
|
+
"at the end of the day",
|
|
916
|
+
"to be honest",
|
|
917
|
+
"basically",
|
|
918
|
+
"you know what",
|
|
919
|
+
"let me know",
|
|
920
|
+
"best possible",
|
|
921
|
+
"world class",
|
|
922
|
+
"insanely",
|
|
923
|
+
"make it pop",
|
|
924
|
+
"help me out",
|
|
925
|
+
"what do you think",
|
|
926
|
+
"i don't know",
|
|
927
|
+
"just do it",
|
|
928
|
+
"ship it",
|
|
929
|
+
"go ahead",
|
|
930
|
+
"lfg",
|
|
931
|
+
"let's go",
|
|
932
|
+
"no worries",
|
|
933
|
+
"makes sense",
|
|
934
|
+
"figure it out",
|
|
935
|
+
"deep research",
|
|
936
|
+
"ultra think",
|
|
937
|
+
"ultrathink",
|
|
938
|
+
"step by step",
|
|
939
|
+
"one shot",
|
|
940
|
+
"whatever it takes",
|
|
941
|
+
"end to end",
|
|
942
|
+
"from scratch",
|
|
943
|
+
"like a pro",
|
|
944
|
+
"production ready",
|
|
945
|
+
"properly",
|
|
946
|
+
"exactly",
|
|
947
|
+
"and everything",
|
|
948
|
+
"all of that",
|
|
949
|
+
"the whole thing",
|
|
950
|
+
"you know",
|
|
951
|
+
"i mean",
|
|
952
|
+
"sort of",
|
|
953
|
+
"kind of",
|
|
954
|
+
"so yeah",
|
|
955
|
+
"okay so"
|
|
956
|
+
];
|
|
957
|
+
var CLAUSE_FINAL = /* @__PURE__ */ new Set(["and all", "and stuff", "or something", "and everything", "all of that", "and all that"]);
|
|
958
|
+
var esc = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
959
|
+
var TIC_BY_FIRST = /* @__PURE__ */ new Map();
|
|
960
|
+
TICS.forEach((phrase, index) => {
|
|
961
|
+
const first = phrase.split(" ")[0];
|
|
962
|
+
const list = TIC_BY_FIRST.get(first) ?? [];
|
|
963
|
+
list.push({ phrase, index, final: CLAUSE_FINAL.has(phrase) });
|
|
964
|
+
TIC_BY_FIRST.set(first, list);
|
|
965
|
+
});
|
|
966
|
+
var CLAUSE_END = /^\s*(?:[.,!?;:)\n]|$|and\b|so\b|but\b)/;
|
|
967
|
+
var isWordChar = (c) => c !== void 0 && /[A-Za-z0-9_]/.test(c);
|
|
968
|
+
function ticsIn(low) {
|
|
969
|
+
const found = /* @__PURE__ */ new Set();
|
|
970
|
+
const rx = /[a-z']+/g;
|
|
971
|
+
let m;
|
|
972
|
+
while ((m = rx.exec(low)) !== null) {
|
|
973
|
+
const list = TIC_BY_FIRST.get(m[0]);
|
|
974
|
+
if (!list || isWordChar(low[m.index - 1])) continue;
|
|
975
|
+
for (const t of list) {
|
|
976
|
+
if (found.has(t.index) || !low.startsWith(t.phrase, m.index)) continue;
|
|
977
|
+
const end = m.index + t.phrase.length;
|
|
978
|
+
if (t.final ? CLAUSE_END.test(low.slice(end, end + 12)) : !isWordChar(low[end]) || !isWordChar(low[end - 1])) found.add(t.index);
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
return [...found].sort((a, b) => a - b);
|
|
982
|
+
}
|
|
983
|
+
var ADDRESS = ["dude", "bro", "buddy", "babe", "baby", "my friend", "man", "mate", "boss", "sir", "brother", "bhai", "yaar", "chief", "champ", "my guy", "darling", "love"];
|
|
984
|
+
var ADDRESS_GATE = new RegExp("\\b(" + ADDRESS.map(esc).join("|") + ")\\b", "g");
|
|
985
|
+
var ADDRESS_INDEX = new Map(ADDRESS.map((a, i) => [a, i]));
|
|
986
|
+
var VOCATIVE_BEFORE = /(?:^|[,.!?]\s*|\b(?:hey|yo|ok|okay|thanks|no|come on)\s+)$/;
|
|
987
|
+
function addressIn(low) {
|
|
988
|
+
const found = /* @__PURE__ */ new Set();
|
|
989
|
+
ADDRESS_GATE.lastIndex = 0;
|
|
990
|
+
let m;
|
|
991
|
+
while ((m = ADDRESS_GATE.exec(low)) !== null) {
|
|
992
|
+
const i = ADDRESS_INDEX.get(m[1]);
|
|
993
|
+
if (found.has(i)) continue;
|
|
994
|
+
const end = m.index + m[0].length;
|
|
995
|
+
if (VOCATIVE_BEFORE.test(low.slice(Math.max(0, m.index - 12), m.index)) || /^\s*[,.!?]/.test(low.slice(end, end + 8)) || end === low.length) found.add(i);
|
|
996
|
+
}
|
|
997
|
+
return [...found].sort((a, b) => a - b);
|
|
998
|
+
}
|
|
999
|
+
function featurize(input) {
|
|
1000
|
+
const raw = input.text;
|
|
1001
|
+
let t = cleanForTone(raw);
|
|
1002
|
+
const all = words3(t.toLowerCase());
|
|
1003
|
+
const pasteSuspect = all.length > 1500 || all.length > 600 && !input.typed;
|
|
1004
|
+
if (pasteSuspect) {
|
|
1005
|
+
const toks = t.split(/\s+/).filter(Boolean);
|
|
1006
|
+
t = toks.slice(0, 150).concat("\u2026", toks.slice(-150)).join(" ");
|
|
1007
|
+
}
|
|
1008
|
+
const low = t.toLowerCase();
|
|
1009
|
+
const tone = pasteSuspect ? words3(low) : all;
|
|
1010
|
+
let letters = 0, upper = 0;
|
|
1011
|
+
for (let k = 0; k < t.length; k++) {
|
|
1012
|
+
const c = t.charCodeAt(k);
|
|
1013
|
+
if (c >= 65 && c <= 90) {
|
|
1014
|
+
letters++;
|
|
1015
|
+
upper++;
|
|
1016
|
+
} else if (c >= 97 && c <= 122) letters++;
|
|
1017
|
+
else if (c > 127 && new RegExp("\\p{L}", "u").test(t[k])) {
|
|
1018
|
+
letters++;
|
|
1019
|
+
if (t[k] !== t[k].toLowerCase()) upper++;
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
let caps = 0;
|
|
1023
|
+
for (const m of t.match(CAPS) ?? []) if (!CAPS_OK.has(m)) caps++;
|
|
1024
|
+
const swears = [];
|
|
1025
|
+
let insults = 0, slur = false;
|
|
1026
|
+
for (const w of tone) {
|
|
1027
|
+
if (isSwearWord(w)) swears.push(w);
|
|
1028
|
+
if (isInsult(w)) insults++;
|
|
1029
|
+
if (!slur && isSlur(w)) slur = true;
|
|
1030
|
+
}
|
|
1031
|
+
const first = all[0] ?? "";
|
|
1032
|
+
const rawLow = raw.toLowerCase();
|
|
1033
|
+
const trimmed = raw.trim();
|
|
1034
|
+
let fl = 0;
|
|
1035
|
+
if (/\[Pasted text #/.test(raw) || input.hasPaste) fl |= FLAG.paste;
|
|
1036
|
+
if (input.hasImage || raw.includes("[Image #")) fl |= FLAG.image;
|
|
1037
|
+
if (raw.includes("```")) fl |= FLAG.code;
|
|
1038
|
+
URL_RX.lastIndex = 0;
|
|
1039
|
+
if (URL_RX.test(raw)) fl |= FLAG.url;
|
|
1040
|
+
URL_RX.lastIndex = 0;
|
|
1041
|
+
if (pasteSuspect) fl |= FLAG.pasteSuspect;
|
|
1042
|
+
if (slur) fl |= FLAG.slur;
|
|
1043
|
+
if (POLITE.test(low)) fl |= FLAG.polite;
|
|
1044
|
+
if (THANKS.test(low)) fl |= FLAG.thanks;
|
|
1045
|
+
if (SORRY.test(low)) fl |= FLAG.sorry;
|
|
1046
|
+
if (/[!?]{3,}/.test(raw)) fl |= FLAG.multiPunct;
|
|
1047
|
+
if (trimmed.endsWith("?") || QWORDS.has(first) && all.length < 40) fl |= FLAG.question;
|
|
1048
|
+
if (DELEGATE.test(low)) fl |= FLAG.delegate;
|
|
1049
|
+
if (/\blet'?s\b/.test(rawLow)) fl |= FLAG.lets;
|
|
1050
|
+
if (/\b(please|pls|plz)\b/.test(rawLow)) fl |= FLAG.please;
|
|
1051
|
+
if (input.typed) fl |= FLAG.history;
|
|
1052
|
+
let excl = 0;
|
|
1053
|
+
for (let k = raw.indexOf("!"); k >= 0; k = raw.indexOf("!", k + 1)) excl++;
|
|
1054
|
+
let lines = 1;
|
|
1055
|
+
for (let k = raw.indexOf("\n"); k >= 0; k = raw.indexOf("\n", k + 1)) lines++;
|
|
1056
|
+
const f = {
|
|
1057
|
+
ch: raw.length,
|
|
1058
|
+
w: all.length,
|
|
1059
|
+
ln: lines,
|
|
1060
|
+
fl,
|
|
1061
|
+
sw: swears.length,
|
|
1062
|
+
ins: insults,
|
|
1063
|
+
fr: count(FRUSTRATION, low),
|
|
1064
|
+
cw: caps,
|
|
1065
|
+
cr: letters >= 12 ? Math.round(upper / letters * 1e3) / 1e3 : 0,
|
|
1066
|
+
ex: excl,
|
|
1067
|
+
we: count(WE, low),
|
|
1068
|
+
i: count(I_, low),
|
|
1069
|
+
fi: count(FILLER, low),
|
|
1070
|
+
pf: count(PERFECTION, low),
|
|
1071
|
+
hi: count(HINGLISH, low),
|
|
1072
|
+
fw: first.slice(0, 24)
|
|
1073
|
+
};
|
|
1074
|
+
if (swears.length) f.sws = swears.slice(0, 6);
|
|
1075
|
+
if (raw.length <= 80) {
|
|
1076
|
+
const sl = raw.replace(PLACEHOLDER2, "").toLowerCase().replace(/\s+/g, " ").trim().slice(0, 60);
|
|
1077
|
+
if (sl) f.sl = sl;
|
|
1078
|
+
}
|
|
1079
|
+
const tc = ticsIn(rawLow);
|
|
1080
|
+
if (tc.length) f.tc = tc;
|
|
1081
|
+
const ad = addressIn(rawLow);
|
|
1082
|
+
if (ad.length) f.ad = ad;
|
|
1083
|
+
return f;
|
|
1084
|
+
}
|
|
1085
|
+
var SENSITIVE = /\b(die|dying|suicid\w*|kill (?:my|me)\w*|depress\w*|demons?|deserve|anxiety|therap\w*|panic|lonely|worthless|hate myself|cry(?:ing)?|breakup|girlfriend|boyfriend|wife|husband|sex\w*|porn|nude|drunk|password|token|secret|api[_ -]?key|ssn|salary|loan|debt|visa|lawyer|doctor|hospital|meds?)\b/i;
|
|
1086
|
+
var SECRETISH = /(sk-[A-Za-z0-9]|tvly-|AKIA|ghp_|xox[bp]-|@[\w.-]+\.\w{2,}|[A-Za-z0-9_-]{32,}|\/Users\/|\/home\/|~\/|https?:\/\/|\[REDACTED|<redacted)/;
|
|
1087
|
+
function safeQuote(text, maxLength = 90) {
|
|
1088
|
+
let t = text.replace(PLACEHOLDER2, "").replace(/\s+/g, " ").trim();
|
|
1089
|
+
if (!t || SENSITIVE.test(t) || SECRETISH.test(t)) return null;
|
|
1090
|
+
t = t.slice(0, maxLength + 40).replace(/[A-Za-z']+/g, (w) => {
|
|
1091
|
+
const l = w.toLowerCase();
|
|
1092
|
+
return isSwearWord(l) || isSlur(l) ? mask(w) : w;
|
|
1093
|
+
}) + t.slice(maxLength + 40);
|
|
1094
|
+
if (t.length <= maxLength) return t;
|
|
1095
|
+
const cut = t.slice(0, maxLength - 1);
|
|
1096
|
+
const space = cut.lastIndexOf(" ");
|
|
1097
|
+
return (space > 0 ? cut.slice(0, space) : cut) + "\u2026";
|
|
1098
|
+
}
|
|
1099
|
+
var TOPIC_STOP = new Set("the a an and or of to in on for is it that this i you we be are was with as at by from so if but not do can me my your our just what how all also now like then there they them these those one some any more much very really thing things something everything get got make made want need see know think going go let lets let's okay ok yes no up out into about will would should could have has had been being he she his her him its it's i'm don't can't isn't doesn't which who when where why here same other only even still well right good way time new use using done try give take put come look".split(" "));
|
|
1100
|
+
function vocabularyWords(text) {
|
|
1101
|
+
return words3(cleanForTone(text).toLowerCase()).filter((w) => w.length >= 3 && w.length <= 24);
|
|
1102
|
+
}
|
|
1103
|
+
function topicWords(text) {
|
|
1104
|
+
const out = /* @__PURE__ */ new Set();
|
|
1105
|
+
for (const w of words3(cleanForTone(text).toLowerCase())) if (w.length >= 3 && w.length <= 24 && !TOPIC_STOP.has(w) && !isSwearWord(w)) out.add(w);
|
|
1106
|
+
return out;
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
// packages/core/src/analytics/story-time.ts
|
|
1110
|
+
var WEEKDAY = { Mon: 0, Tue: 1, Wed: 2, Thu: 3, Fri: 4, Sat: 5, Sun: 6 };
|
|
1111
|
+
function localClock(timezone) {
|
|
1112
|
+
const format = new Intl.DateTimeFormat("en-US", {
|
|
1113
|
+
timeZone: timezone,
|
|
1114
|
+
year: "numeric",
|
|
1115
|
+
month: "2-digit",
|
|
1116
|
+
day: "2-digit",
|
|
1117
|
+
hour: "2-digit",
|
|
1118
|
+
minute: "2-digit",
|
|
1119
|
+
weekday: "short",
|
|
1120
|
+
hourCycle: "h23"
|
|
1121
|
+
});
|
|
1122
|
+
const memo = /* @__PURE__ */ new Map();
|
|
1123
|
+
return (at) => {
|
|
1124
|
+
const slot = Math.floor(at / 9e5);
|
|
1125
|
+
let hit = memo.get(slot);
|
|
1126
|
+
if (hit) return { ...hit, minute: hit.minute + Math.floor((at - slot * 9e5) / 6e4) };
|
|
1127
|
+
const parts = {};
|
|
1128
|
+
for (const p of format.formatToParts(new Date(slot * 9e5))) parts[p.type] = p.value;
|
|
1129
|
+
const hour = Number(parts.hour) % 24;
|
|
1130
|
+
hit = { day: `${parts.year}-${parts.month}-${parts.day}`, month: `${parts.year}-${parts.month}`, hour, dow: WEEKDAY[parts.weekday] ?? 0, minute: Number(parts.minute) };
|
|
1131
|
+
memo.set(slot, hit);
|
|
1132
|
+
return { ...hit, minute: hit.minute + Math.floor((at - slot * 9e5) / 6e4) };
|
|
1133
|
+
};
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
// packages/core/src/analytics/story-extract.ts
|
|
1137
|
+
var AGENT_TOOLS = /* @__PURE__ */ new Set(["Task", "Agent", "spawn_agent", "spawn_agents", "TaskCreate"]);
|
|
1138
|
+
var isAgentTool = (name) => AGENT_TOOLS.has(name) || name.startsWith("spawn");
|
|
1139
|
+
function promptLex(text, models) {
|
|
1140
|
+
const lex = {};
|
|
1141
|
+
const hits = profanityHits(text);
|
|
1142
|
+
if (hits?.hits.length) lex.pt = hits.hits;
|
|
1143
|
+
if (hits?.ambiguous) lex.pa = 1;
|
|
1144
|
+
const nl = normalizedLine(text);
|
|
1145
|
+
if (nl !== null) lex.nl = nl;
|
|
1146
|
+
const fc = focusLabels(text);
|
|
1147
|
+
if (fc.length) lex.fc = fc;
|
|
1148
|
+
const nm = namedModels(text, models);
|
|
1149
|
+
if (nm.length) lex.nm = nm;
|
|
1150
|
+
const ll = languageLines(text);
|
|
1151
|
+
if (ll.length) lex.ll = ll;
|
|
1152
|
+
return Object.keys(lex).length ? lex : void 0;
|
|
1153
|
+
}
|
|
1154
|
+
var QUOTE_TOP = 8;
|
|
1155
|
+
var QUOTE_SUBAGENT_TOP = 5;
|
|
1156
|
+
var QUOTE_LAST_PER_PROJECT = 3;
|
|
1157
|
+
function deriveStory(prompts, events, interrupts, options) {
|
|
1158
|
+
const human = prompts.filter((p) => p.kind === "human" && p.at !== null).sort((a, b) => a.at - b.at);
|
|
1159
|
+
const fresh = new Set(prompts.slice(options.from ?? 0));
|
|
1160
|
+
const models = /* @__PURE__ */ new Set();
|
|
1161
|
+
for (const p of prompts) {
|
|
1162
|
+
if (p.before) models.add(p.before);
|
|
1163
|
+
if (p.after) models.add(p.after);
|
|
1164
|
+
}
|
|
1165
|
+
const vocab = options.counters ? options.previous?.vocab ?? /* @__PURE__ */ new Map() : null;
|
|
1166
|
+
const topics = options.counters ? new Map((options.previous?.topics ?? []).map((t) => [t.month, t])) : null;
|
|
1167
|
+
const clock = localClock(options.timezone);
|
|
1168
|
+
for (const p of human) {
|
|
1169
|
+
if (!fresh.has(p)) continue;
|
|
1170
|
+
p.f = featurize({ text: p.text, typed: options.typed });
|
|
1171
|
+
const lex = promptLex(p.text, models);
|
|
1172
|
+
if (lex) p.lex = lex;
|
|
1173
|
+
if (vocab) for (const w of vocabularyWords(p.text)) vocab.set(w, (vocab.get(w) ?? 0) + 1);
|
|
1174
|
+
if (topics) {
|
|
1175
|
+
const { hour, month } = clock(p.at);
|
|
1176
|
+
const slot = hour < 5 ? 0 : hour >= 9 && hour < 19 ? 1 : -1;
|
|
1177
|
+
if (slot >= 0) {
|
|
1178
|
+
let bag = topics.get(month);
|
|
1179
|
+
if (!bag) {
|
|
1180
|
+
bag = { month, night: 0, day: 0, words: /* @__PURE__ */ new Map() };
|
|
1181
|
+
topics.set(month, bag);
|
|
1182
|
+
}
|
|
1183
|
+
if (slot === 0) bag.night++;
|
|
1184
|
+
else bag.day++;
|
|
1185
|
+
for (const w of topicWords(p.text)) {
|
|
1186
|
+
let c = bag.words.get(w);
|
|
1187
|
+
if (!c) {
|
|
1188
|
+
c = [0, 0];
|
|
1189
|
+
bag.words.set(w, c);
|
|
1190
|
+
}
|
|
1191
|
+
c[slot]++;
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
1196
|
+
turnStats(human, events, interrupts);
|
|
1197
|
+
const weight = /* @__PURE__ */ new Map();
|
|
1198
|
+
const slots = /* @__PURE__ */ new Set();
|
|
1199
|
+
if (human.length) slots.add(human[0]);
|
|
1200
|
+
const byProject = /* @__PURE__ */ new Map();
|
|
1201
|
+
for (const p of human) {
|
|
1202
|
+
const k = p.project ?? "";
|
|
1203
|
+
const list = byProject.get(k) ?? [];
|
|
1204
|
+
list.push(p);
|
|
1205
|
+
byProject.set(k, list);
|
|
1206
|
+
}
|
|
1207
|
+
for (const list of byProject.values()) for (const p of list.slice(-QUOTE_LAST_PER_PROJECT)) slots.add(p);
|
|
1208
|
+
turnWeights(human, events, weight);
|
|
1209
|
+
for (const p of [...human].sort((a, b) => (weight.get(b) ?? 0) - (weight.get(a) ?? 0)).slice(0, QUOTE_TOP)) slots.add(p);
|
|
1210
|
+
for (const p of human.filter((p2) => (p2.t?.sa ?? 0) > 0).sort((a, b) => b.t.sa - a.t.sa).slice(0, QUOTE_SUBAGENT_TOP)) slots.add(p);
|
|
1211
|
+
for (const p of slots) {
|
|
1212
|
+
if (!p.text) continue;
|
|
1213
|
+
const q = safeQuote(p.text, 90);
|
|
1214
|
+
if (q) p.q = q;
|
|
1215
|
+
}
|
|
1216
|
+
return { vocab, topics: topics ? [...topics.values()] : null };
|
|
1217
|
+
}
|
|
1218
|
+
function turnStats(human, events, interrupts) {
|
|
1219
|
+
const ev = events.filter((e) => Number.isFinite(e.at)).sort((a, b) => a.at - b.at);
|
|
1220
|
+
const intr = interrupts.filter(Number.isFinite).sort((a, b) => a - b);
|
|
1221
|
+
let j = 0, k = 0, lastAt = null;
|
|
1222
|
+
human.forEach((p, i) => {
|
|
1223
|
+
const start = p.at, next = i + 1 < human.length ? human[i + 1].at : Infinity;
|
|
1224
|
+
while (j < ev.length && ev[j].at < start) lastAt = ev[j++].at;
|
|
1225
|
+
const t = { m: 0, tl: 0, sa: 0, d: null, x: 0, g: lastAt === null ? null : round1((start - lastAt) / 1e3) };
|
|
1226
|
+
let end = null;
|
|
1227
|
+
while (j < ev.length && ev[j].at < next) {
|
|
1228
|
+
const e = ev[j++];
|
|
1229
|
+
t.m++;
|
|
1230
|
+
t.tl += e.tools;
|
|
1231
|
+
t.sa += e.subs;
|
|
1232
|
+
end = e.at;
|
|
1233
|
+
lastAt = e.at;
|
|
1234
|
+
}
|
|
1235
|
+
if (end !== null) t.d = round1((end - start) / 1e3);
|
|
1236
|
+
while (k < intr.length && intr[k] < start) k++;
|
|
1237
|
+
if (k < intr.length && intr[k] < next) t.x = 1;
|
|
1238
|
+
p.t = t;
|
|
1239
|
+
});
|
|
1240
|
+
}
|
|
1241
|
+
function turnWeights(human, events, out) {
|
|
1242
|
+
const ev = events.filter((e) => e.weight > 0).sort((a, b) => a.at - b.at);
|
|
1243
|
+
let j = 0;
|
|
1244
|
+
human.forEach((p, i) => {
|
|
1245
|
+
const next = i + 1 < human.length ? human[i + 1].at : Infinity;
|
|
1246
|
+
while (j < ev.length && ev[j].at < p.at) j++;
|
|
1247
|
+
let w = 0;
|
|
1248
|
+
while (j < ev.length && ev[j].at < next) w += ev[j++].weight;
|
|
1249
|
+
out.set(p, w);
|
|
1250
|
+
});
|
|
1251
|
+
}
|
|
1252
|
+
var round1 = (x) => Math.round(x * 10) / 10;
|
|
1253
|
+
var usageWeight = (input, output, cacheRead, cacheWrite) => input + 5 * output + 1.25 * cacheWrite + 0.1 * cacheRead;
|
|
1254
|
+
|
|
601
1255
|
// packages/core/src/analytics/extract.ts
|
|
602
|
-
var FACTS_VERSION =
|
|
1256
|
+
var FACTS_VERSION = 5;
|
|
603
1257
|
var MAX_PROMPT_CHARS = 4e3;
|
|
604
1258
|
var isRec = (v) => v !== null && typeof v === "object" && !Array.isArray(v);
|
|
605
1259
|
var str = (v) => typeof v === "string" && v.length > 0 ? v : null;
|
|
@@ -684,7 +1338,9 @@ function makeBuilder(file, harness, size, mtimeMs) {
|
|
|
684
1338
|
currentModel: null,
|
|
685
1339
|
currentProvider: null,
|
|
686
1340
|
pending: [],
|
|
687
|
-
seenPrompts: /* @__PURE__ */ new Set()
|
|
1341
|
+
seenPrompts: /* @__PURE__ */ new Set(),
|
|
1342
|
+
events: [],
|
|
1343
|
+
interrupts: []
|
|
688
1344
|
};
|
|
689
1345
|
}
|
|
690
1346
|
function touch(b, at) {
|
|
@@ -707,24 +1363,25 @@ function addUsage(b, u) {
|
|
|
707
1363
|
touch(b, u.at);
|
|
708
1364
|
answered(b, u.model, u.provider);
|
|
709
1365
|
}
|
|
710
|
-
function addPrompt(b, key, at, project,
|
|
1366
|
+
function addPrompt(b, key, at, project, kind2, reason, text) {
|
|
711
1367
|
if (b.seenPrompts.has(key)) return;
|
|
712
1368
|
b.seenPrompts.add(key);
|
|
713
|
-
if (
|
|
1369
|
+
if (kind2 === "human" && hasExclusionMarker(text)) b.facts.excluded = true;
|
|
1370
|
+
if (reason === "interrupt_marker" && at !== null) b.interrupts.push(at);
|
|
714
1371
|
const prompt = {
|
|
715
1372
|
key,
|
|
716
1373
|
at,
|
|
717
1374
|
project,
|
|
718
|
-
kind,
|
|
1375
|
+
kind: kind2,
|
|
719
1376
|
reason,
|
|
720
|
-
text:
|
|
1377
|
+
text: kind2 === "human" ? clean(text.length > MAX_PROMPT_CHARS ? text.slice(0, MAX_PROMPT_CHARS) : text) : "",
|
|
721
1378
|
before: b.currentModel,
|
|
722
1379
|
beforeProvider: b.currentProvider,
|
|
723
1380
|
after: null,
|
|
724
1381
|
afterProvider: null
|
|
725
1382
|
};
|
|
726
1383
|
b.facts.prompts.push(prompt);
|
|
727
|
-
if (
|
|
1384
|
+
if (kind2 === "human") b.pending.push(prompt);
|
|
728
1385
|
touch(b, at);
|
|
729
1386
|
}
|
|
730
1387
|
var SUBAGENT_DIR = `${path.sep}subagents${path.sep}`;
|
|
@@ -783,6 +1440,16 @@ function claudeLine(b, line, subagent, sessions) {
|
|
|
783
1440
|
if (id) fact.replayKey = `${id}:${session}`;
|
|
784
1441
|
if (u.speed === "fast") fact.tier = "fast";
|
|
785
1442
|
addUsage(b, fact);
|
|
1443
|
+
if (at !== null) {
|
|
1444
|
+
let tools = 0, subs = 0;
|
|
1445
|
+
if (Array.isArray(m.content)) {
|
|
1446
|
+
for (const block of m.content) if (isRec(block) && block.type === "tool_use") {
|
|
1447
|
+
tools++;
|
|
1448
|
+
if (typeof block.name === "string" && isAgentTool(block.name)) subs++;
|
|
1449
|
+
}
|
|
1450
|
+
}
|
|
1451
|
+
b.events.push({ at, tools, subs, weight: usageWeight(fact.input, fact.output, fact.cacheRead, fact.cacheWrite) });
|
|
1452
|
+
}
|
|
786
1453
|
return;
|
|
787
1454
|
}
|
|
788
1455
|
if (r.type !== "user" || r.isSidechain === true || subagent) return;
|
|
@@ -791,17 +1458,37 @@ function claudeLine(b, line, subagent, sessions) {
|
|
|
791
1458
|
if (sid) sessions.add(sid);
|
|
792
1459
|
const origin = isRec(r.origin) ? str(r.origin.kind) : null;
|
|
793
1460
|
const flagged = r.isMeta === true ? "is_meta" : r.isCompactSummary === true ? "compaction_summary" : origin && origin !== "human" ? `origin_${origin}` : r.entrypoint === "sdk-cli" ? "headless_claude_p" : r.promptSource === "system" ? "prompt_source_system" : null;
|
|
794
|
-
let { kind, reason, text } = classifyText(textOf(content));
|
|
1461
|
+
let { kind: kind2, reason, text } = classifyText(textOf(content));
|
|
795
1462
|
if (flagged) {
|
|
796
|
-
|
|
1463
|
+
kind2 = "excluded";
|
|
797
1464
|
reason = flagged;
|
|
798
1465
|
} else if (reason === "empty" && origin === "human" && Array.isArray(content) && content.some((block) => isRec(block) && block.type === "image")) {
|
|
799
|
-
|
|
1466
|
+
kind2 = "human";
|
|
800
1467
|
reason = null;
|
|
801
1468
|
text = "[image]";
|
|
802
1469
|
}
|
|
803
|
-
addPrompt(b, str(r.uuid) ?? `${f.sessionId}:${String(r.timestamp)}`, at, str(r.cwd) ?? f.project,
|
|
1470
|
+
addPrompt(b, str(r.uuid) ?? `${f.sessionId}:${String(r.timestamp)}`, at, str(r.cwd) ?? f.project, kind2, reason, text);
|
|
804
1471
|
}
|
|
1472
|
+
var newCodexState = () => ({
|
|
1473
|
+
lineNo: 0,
|
|
1474
|
+
previousTotal: null,
|
|
1475
|
+
tier: void 0,
|
|
1476
|
+
imported: false,
|
|
1477
|
+
records: /* @__PURE__ */ new Map(),
|
|
1478
|
+
compacted: /* @__PURE__ */ new Set(),
|
|
1479
|
+
deltas: /* @__PURE__ */ new Set(),
|
|
1480
|
+
itemPrompts: [],
|
|
1481
|
+
responsePrompts: [],
|
|
1482
|
+
mode: null,
|
|
1483
|
+
converted: 0,
|
|
1484
|
+
seen: /* @__PURE__ */ new Set(),
|
|
1485
|
+
compactionDone: /* @__PURE__ */ new Set()
|
|
1486
|
+
});
|
|
1487
|
+
var ResumeMismatch = class extends Error {
|
|
1488
|
+
constructor() {
|
|
1489
|
+
super("resume_mismatch");
|
|
1490
|
+
}
|
|
1491
|
+
};
|
|
805
1492
|
var U_KEYS = ["input_tokens", "cached_input_tokens", "output_tokens", "reasoning_output_tokens", "total_tokens"];
|
|
806
1493
|
var tuple = (u) => isRec(u) ? U_KEYS.map((k) => int(u[k])) : null;
|
|
807
1494
|
var AUTO_REVIEW_MODELS = [
|
|
@@ -878,7 +1565,9 @@ function codexLine(b, line, s) {
|
|
|
878
1565
|
if (!d || !(d[0] || d[1] || d[2] || d[3])) return;
|
|
879
1566
|
s.deltas.add(d.join(","));
|
|
880
1567
|
const key = totalSig ? `x:${totalSig}|${last ? last.join(",") : ""}` : null;
|
|
881
|
-
|
|
1568
|
+
const fact = codexFact(b, s, time(r.timestamp), String(r.timestamp ?? ""), d, key);
|
|
1569
|
+
addUsage(b, fact);
|
|
1570
|
+
if (fact.at !== null) b.events.push({ at: fact.at, tools: 0, subs: 0, weight: usageWeight(fact.input, fact.output, fact.cacheRead, fact.cacheWrite) });
|
|
882
1571
|
return;
|
|
883
1572
|
}
|
|
884
1573
|
if (head.includes('"turn_context"')) {
|
|
@@ -931,6 +1620,7 @@ function codexLine(b, line, s) {
|
|
|
931
1620
|
s.itemPrompts.push({ key: null, at: time(r.timestamp), text: typeof p.message === "string" ? p.message : "", imported: s.imported, project: f.project });
|
|
932
1621
|
return;
|
|
933
1622
|
}
|
|
1623
|
+
if (codexToolOrAbort(b, head)) return;
|
|
934
1624
|
if (head.includes('"token_usage_record"')) {
|
|
935
1625
|
const r = parse();
|
|
936
1626
|
const p = r && isRec(r.payload) ? r.payload : null;
|
|
@@ -946,21 +1636,45 @@ function codexLine(b, line, s) {
|
|
|
946
1636
|
}
|
|
947
1637
|
}
|
|
948
1638
|
}
|
|
1639
|
+
var TOOL_CALL = /"type":"(?:function_call|custom_tool_call)"/;
|
|
1640
|
+
var NAME = /"name":"([^"]{1,80})"/;
|
|
1641
|
+
var STAMP = /"timestamp":"([^"]{10,40})"/;
|
|
1642
|
+
function codexToolOrAbort(b, head) {
|
|
1643
|
+
if (TOOL_CALL.test(head) && head.includes('"response_item"')) {
|
|
1644
|
+
const at = time(STAMP.exec(head)?.[1]);
|
|
1645
|
+
const name = NAME.exec(head)?.[1] ?? "";
|
|
1646
|
+
if (at !== null) b.events.push({ at, tools: 1, subs: isAgentTool(name) ? 1 : 0, weight: 0 });
|
|
1647
|
+
return true;
|
|
1648
|
+
}
|
|
1649
|
+
if (head.includes('"turn_aborted"') && head.includes('"event_msg"')) {
|
|
1650
|
+
const at = time(STAMP.exec(head)?.[1]);
|
|
1651
|
+
if (at !== null) b.interrupts.push(at);
|
|
1652
|
+
return true;
|
|
1653
|
+
}
|
|
1654
|
+
return false;
|
|
1655
|
+
}
|
|
949
1656
|
function finishCodex(b, s) {
|
|
950
1657
|
const f = b.facts;
|
|
951
1658
|
for (const id of s.compacted) {
|
|
1659
|
+
if (s.compactionDone.has(id)) continue;
|
|
952
1660
|
const record = s.records.get(id);
|
|
953
1661
|
const d = record ? tuple(record.usage) : null;
|
|
954
1662
|
if (!record || !d || !(d[0] || d[1] || d[2] || d[3]) || s.deltas.has(d.join(","))) continue;
|
|
1663
|
+
s.compactionDone.add(id);
|
|
955
1664
|
const fact = codexFact(b, s, record.at, record.at ? new Date(record.at).toISOString() : "", d, `codex-compaction:${id}`);
|
|
956
1665
|
fact.compaction = true;
|
|
957
1666
|
f.usage.push(fact);
|
|
958
1667
|
touch(b, record.at);
|
|
959
1668
|
}
|
|
960
|
-
|
|
1669
|
+
if (s.mode === "responses" && s.itemPrompts.length) throw new ResumeMismatch();
|
|
1670
|
+
const mode = s.mode ?? (s.itemPrompts.length ? "items" : s.responsePrompts.length ? "responses" : null);
|
|
1671
|
+
const list = mode === "items" ? s.itemPrompts : mode === "responses" ? s.responsePrompts : [];
|
|
1672
|
+
s.mode = mode;
|
|
961
1673
|
const reason = f.child ? "subagent_parent_written" : f.automated ? "programmatic_exec_session" : null;
|
|
962
|
-
const seen =
|
|
963
|
-
|
|
1674
|
+
const seen = s.seen, base = s.converted;
|
|
1675
|
+
s.converted += list.length;
|
|
1676
|
+
list.forEach((p, n) => {
|
|
1677
|
+
const i = base + n;
|
|
964
1678
|
if (p.key) {
|
|
965
1679
|
if (seen.has(p.key)) return;
|
|
966
1680
|
seen.add(p.key);
|
|
@@ -1019,6 +1733,10 @@ function piLine(b, line) {
|
|
|
1019
1733
|
};
|
|
1020
1734
|
if (fact.input + fact.output + fact.cacheRead + fact.cacheWrite > 0) addUsage(b, fact);
|
|
1021
1735
|
else answered(b, fact.model, fact.provider);
|
|
1736
|
+
if (at !== null) {
|
|
1737
|
+
const tools = Array.isArray(m.content) ? m.content.filter((c) => isRec(c) && typeof c.type === "string" && c.type.toLowerCase().includes("tool")).length : 0;
|
|
1738
|
+
b.events.push({ at, tools, subs: 0, weight: usageWeight(fact.input, fact.output, fact.cacheRead, fact.cacheWrite) });
|
|
1739
|
+
}
|
|
1022
1740
|
return;
|
|
1023
1741
|
}
|
|
1024
1742
|
if (m.role === "user") {
|
|
@@ -1029,34 +1747,41 @@ function piLine(b, line) {
|
|
|
1029
1747
|
var CHUNK = 4 * 1024 * 1024;
|
|
1030
1748
|
var MAX_LINE = 256 * 1024 * 1024;
|
|
1031
1749
|
var sharedBuffer = null;
|
|
1032
|
-
|
|
1750
|
+
var RESUME_RECENT_MS = 48 * 36e5;
|
|
1751
|
+
function extractFile(file, harness, options = {}, previous) {
|
|
1033
1752
|
const fd = fs.openSync(file, "r");
|
|
1034
1753
|
try {
|
|
1035
1754
|
const stat = fs.fstatSync(fd);
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
previousTotal: null,
|
|
1040
|
-
tier: void 0,
|
|
1041
|
-
imported: false,
|
|
1042
|
-
records: /* @__PURE__ */ new Map(),
|
|
1043
|
-
compacted: /* @__PURE__ */ new Set(),
|
|
1044
|
-
deltas: /* @__PURE__ */ new Set(),
|
|
1045
|
-
itemPrompts: [],
|
|
1046
|
-
responsePrompts: []
|
|
1047
|
-
};
|
|
1755
|
+
let b = makeBuilder(file, harness, stat.size, stat.mtimeMs);
|
|
1756
|
+
let codex = newCodexState();
|
|
1757
|
+
let position = 0, firstNew = 0;
|
|
1048
1758
|
const subagent = file.includes(SUBAGENT_DIR), sessions = /* @__PURE__ */ new Set();
|
|
1759
|
+
if (previous) {
|
|
1760
|
+
const r = previous.resume;
|
|
1761
|
+
if (!r || previous.excluded || r.dev !== stat.dev || r.ino !== stat.ino || r.offset > stat.size || r.offset < 1 || tailHash(fd, r.offset) !== r.tail) throw new ResumeMismatch();
|
|
1762
|
+
const nl = Buffer.alloc(1);
|
|
1763
|
+
fs.readSync(fd, nl, 0, 1, r.offset - 1);
|
|
1764
|
+
if (nl[0] !== 10) throw new ResumeMismatch();
|
|
1765
|
+
b = resumeBuilder(previous, r, stat);
|
|
1766
|
+
if (r.codex) codex = { ...structuredClone(r.codex), itemPrompts: [], responsePrompts: [] };
|
|
1767
|
+
for (const sid of r.sessions ?? []) sessions.add(sid);
|
|
1768
|
+
position = r.offset;
|
|
1769
|
+
firstNew = b.facts.prompts.length;
|
|
1770
|
+
}
|
|
1049
1771
|
const onLine = harness === "claude" ? (line) => claudeLine(b, line, subagent, sessions) : harness === "codex" ? (line) => codexLine(b, line, codex) : (line) => piLine(b, line);
|
|
1050
1772
|
let buffer = sharedBuffer ??= Buffer.allocUnsafe(CHUNK);
|
|
1051
|
-
let filled = 0,
|
|
1773
|
+
let filled = 0, skipping = false, base = position, endsWithNewline = true;
|
|
1052
1774
|
for (; ; ) {
|
|
1053
1775
|
if (filled === buffer.length) {
|
|
1054
1776
|
if (harness === "codex" && !codexWanted(buffer.toString("latin1", 0, Math.min(filled, 400)), codex.lineNo)) {
|
|
1777
|
+
codexToolOrAbort(b, buffer.toString("latin1", 0, Math.min(filled, 400)));
|
|
1055
1778
|
skipping = true;
|
|
1779
|
+
base += filled;
|
|
1056
1780
|
filled = 0;
|
|
1057
1781
|
codex.lineNo++;
|
|
1058
1782
|
} else if (buffer.length >= MAX_LINE) {
|
|
1059
1783
|
skipping = true;
|
|
1784
|
+
base += filled;
|
|
1060
1785
|
filled = 0;
|
|
1061
1786
|
b.facts.malformed++;
|
|
1062
1787
|
} else {
|
|
@@ -1078,29 +1803,116 @@ function extractFile(file, harness) {
|
|
|
1078
1803
|
start = nl + 1;
|
|
1079
1804
|
}
|
|
1080
1805
|
if (n === 0) {
|
|
1081
|
-
if (
|
|
1806
|
+
if (start < end) {
|
|
1807
|
+
endsWithNewline = false;
|
|
1808
|
+
if (!skipping) onLine(buffer.subarray(start, end));
|
|
1809
|
+
}
|
|
1810
|
+
base += start;
|
|
1082
1811
|
break;
|
|
1083
1812
|
}
|
|
1084
1813
|
buffer.copyWithin(0, start, end);
|
|
1814
|
+
base += start;
|
|
1085
1815
|
filled = end - start;
|
|
1086
1816
|
}
|
|
1087
1817
|
if (harness === "codex") finishCodex(b, codex);
|
|
1088
1818
|
if (harness === "claude" && !subagent && sessions.size) b.facts.sessionIds = [...sessions];
|
|
1089
|
-
|
|
1819
|
+
b.facts.size = stat.size;
|
|
1820
|
+
b.facts.mtimeMs = stat.mtimeMs;
|
|
1821
|
+
finishStory(b, harness, options, firstNew);
|
|
1822
|
+
delete b.facts.resume;
|
|
1823
|
+
if (endsWithNewline && !b.facts.excluded && Date.now() - stat.mtimeMs < RESUME_RECENT_MS) {
|
|
1824
|
+
const events = new Float64Array(b.events.length * 4);
|
|
1825
|
+
b.events.forEach((e, i) => {
|
|
1826
|
+
events[i * 4] = e.at;
|
|
1827
|
+
events[i * 4 + 1] = e.tools;
|
|
1828
|
+
events[i * 4 + 2] = e.subs;
|
|
1829
|
+
events[i * 4 + 3] = e.weight;
|
|
1830
|
+
});
|
|
1831
|
+
const { itemPrompts: _items, responsePrompts: _responses, ...codexState } = codex;
|
|
1832
|
+
b.facts.resume = {
|
|
1833
|
+
offset: base,
|
|
1834
|
+
tail: tailHash(fd, base),
|
|
1835
|
+
dev: stat.dev,
|
|
1836
|
+
ino: stat.ino,
|
|
1837
|
+
model: b.currentModel,
|
|
1838
|
+
provider: b.currentProvider,
|
|
1839
|
+
events,
|
|
1840
|
+
interrupts: Float64Array.from(b.interrupts),
|
|
1841
|
+
...sessions.size ? { sessions: [...sessions] } : {},
|
|
1842
|
+
...harness === "codex" ? { codex: codexState } : {}
|
|
1843
|
+
};
|
|
1844
|
+
}
|
|
1090
1845
|
return b.facts;
|
|
1091
1846
|
} finally {
|
|
1092
1847
|
fs.closeSync(fd);
|
|
1093
1848
|
}
|
|
1094
1849
|
}
|
|
1850
|
+
function resumeBuilder(previous, r, stat) {
|
|
1851
|
+
const facts = {
|
|
1852
|
+
...previous,
|
|
1853
|
+
size: stat.size,
|
|
1854
|
+
mtimeMs: stat.mtimeMs,
|
|
1855
|
+
usage: [...previous.usage],
|
|
1856
|
+
prompts: previous.prompts.map((p) => ({ ...p })),
|
|
1857
|
+
...previous.sessionIds ? { sessionIds: [...previous.sessionIds] } : {},
|
|
1858
|
+
...previous.story ? { story: { vocab: previous.story.vocab ? new Map(previous.story.vocab) : null, topics: previous.story.topics ? previous.story.topics.map((t) => ({ ...t, words: new Map([...t.words].map(([w, c]) => [w, [c[0], c[1]]])) })) : null } } : {}
|
|
1859
|
+
};
|
|
1860
|
+
delete facts.resume;
|
|
1861
|
+
const b = makeBuilder(previous.file, previous.harness, stat.size, stat.mtimeMs);
|
|
1862
|
+
b.facts = facts;
|
|
1863
|
+
b.currentModel = r.model;
|
|
1864
|
+
b.currentProvider = r.provider;
|
|
1865
|
+
for (const p of facts.prompts) b.seenPrompts.add(p.key);
|
|
1866
|
+
for (let i = facts.prompts.length - 1; i >= 0; i--) {
|
|
1867
|
+
const p = facts.prompts[i];
|
|
1868
|
+
if (p.kind !== "human") continue;
|
|
1869
|
+
if (p.after !== null) break;
|
|
1870
|
+
b.pending.unshift(p);
|
|
1871
|
+
}
|
|
1872
|
+
for (let i = 0; i < r.events.length; i += 4) b.events.push({ at: r.events[i], tools: r.events[i + 1], subs: r.events[i + 2], weight: r.events[i + 3] });
|
|
1873
|
+
b.interrupts.push(...r.interrupts);
|
|
1874
|
+
return b;
|
|
1875
|
+
}
|
|
1876
|
+
function finishStory(b, harness, options, from = 0) {
|
|
1877
|
+
const f = b.facts;
|
|
1878
|
+
f.tz = options.timezone ?? Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
1879
|
+
if (f.excluded) {
|
|
1880
|
+
for (const p of f.prompts) {
|
|
1881
|
+
p.text = "";
|
|
1882
|
+
delete p.f;
|
|
1883
|
+
delete p.lex;
|
|
1884
|
+
delete p.q;
|
|
1885
|
+
}
|
|
1886
|
+
delete f.story;
|
|
1887
|
+
return;
|
|
1888
|
+
}
|
|
1889
|
+
f.story = deriveStory(f.prompts, b.events, b.interrupts, { typed: false, counters: harness !== "claude" && !f.child, timezone: f.tz, from, previous: f.story });
|
|
1890
|
+
}
|
|
1891
|
+
function stripText(facts) {
|
|
1892
|
+
for (const p of facts.prompts) p.text = "";
|
|
1893
|
+
return facts;
|
|
1894
|
+
}
|
|
1895
|
+
function tailHash(fd, offset) {
|
|
1896
|
+
const start = Math.max(0, offset - TAIL), buffer = Buffer.alloc(offset - start);
|
|
1897
|
+
let read = 0;
|
|
1898
|
+
while (read < buffer.length) {
|
|
1899
|
+
const n = fs.readSync(fd, buffer, read, buffer.length - read, start + read);
|
|
1900
|
+
if (!n) break;
|
|
1901
|
+
read += n;
|
|
1902
|
+
}
|
|
1903
|
+
return createHash2("sha256").update(buffer.subarray(0, read)).digest("hex");
|
|
1904
|
+
}
|
|
1905
|
+
var TAIL = 4096;
|
|
1095
1906
|
|
|
1096
1907
|
// packages/core/src/analytics/scan-pool.ts
|
|
1908
|
+
var readFacts = (job) => stripText(extractFile(job.file, job.harness, { timezone: job.timezone }));
|
|
1097
1909
|
var errorCode = (error) => error instanceof Error && /^[a-z][a-z0-9_]{0,63}$/.test(error.message) ? error.message : "source_unreadable";
|
|
1098
1910
|
function runScanWorker() {
|
|
1099
1911
|
if (!parentPort) throw new Error("scan_worker_requires_parent");
|
|
1100
1912
|
const port = parentPort;
|
|
1101
1913
|
port.on("message", (job) => {
|
|
1102
1914
|
try {
|
|
1103
|
-
port.postMessage({ facts:
|
|
1915
|
+
port.postMessage({ facts: readFacts(job) });
|
|
1104
1916
|
} catch (error) {
|
|
1105
1917
|
port.postMessage({ error: errorCode(error) });
|
|
1106
1918
|
}
|