pi-smart-compact 9.2.1 → 9.3.1
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/ARCHITECTURE.md +2 -2
- package/CHANGELOG.md +101 -0
- package/dist/app/steps/extract.d.ts.map +1 -1
- package/dist/app/steps/synthesize.d.ts.map +1 -1
- package/dist/app/steps/verify.d.ts.map +1 -1
- package/dist/constants.d.ts +1 -4
- package/dist/constants.d.ts.map +1 -1
- package/dist/domain/scrub.d.ts.map +1 -1
- package/dist/domain/summary-parse.d.ts +5 -0
- package/dist/domain/summary-parse.d.ts.map +1 -1
- package/dist/index.js +721 -155
- package/dist/infra/llm-client.d.ts +1 -1
- package/dist/infra/llm-client.d.ts.map +1 -1
- package/dist/infra/paths.d.ts +9 -0
- package/dist/infra/paths.d.ts.map +1 -1
- package/dist/phases/synthesize.d.ts +3 -5
- package/dist/phases/synthesize.d.ts.map +1 -1
- package/dist/phases/verify.d.ts +2 -1
- package/dist/phases/verify.d.ts.map +1 -1
- package/dist/provider-eval.js +393 -58
- package/dist/provider-scenario-eval.js +801 -249
- package/dist/telemetry-report.js +366 -31
- package/dist/utils/extraction.d.ts.map +1 -1
- package/dist/utils/file-needles.d.ts.map +1 -1
- package/dist/utils/file-ref-detect.d.ts.map +1 -1
- package/dist/utils/session-log.d.ts.map +1 -1
- package/dist/utils/state.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/provider-eval.js
CHANGED
|
@@ -156,7 +156,7 @@ function formatProviderEvaluation(report) {
|
|
|
156
156
|
import fs2 from "fs";
|
|
157
157
|
|
|
158
158
|
// src/constants.ts
|
|
159
|
-
var VERSION = "9.
|
|
159
|
+
var VERSION = "9.3.1";
|
|
160
160
|
var SETTLED_TRIGGER_COOLDOWN_MS = 10 * 60000;
|
|
161
161
|
var COMPACT_SYSTEM_PREFIX = "You are an expert conversation summarizer for a coding agent. " + "Produce structured markdown summaries. " + "Follow output format exactly. " + "Use EXACT names \u2014 never paraphrase code identifiers. " + "Trust deterministic extraction data over intuition.";
|
|
162
162
|
var PROFILES = {
|
|
@@ -400,6 +400,188 @@ function lruSet(m, key, value, max) {
|
|
|
400
400
|
}
|
|
401
401
|
|
|
402
402
|
// src/utils/tokens.ts
|
|
403
|
+
var PROVIDER_MAP = {
|
|
404
|
+
"zai-anthropic": {
|
|
405
|
+
maxOutputTokens: 8192,
|
|
406
|
+
supportsTools: "probe",
|
|
407
|
+
jsonReliability: "high",
|
|
408
|
+
instructionFollowing: "high",
|
|
409
|
+
tokenRatioEstimate: 3.5,
|
|
410
|
+
concurrencyLimit: 3,
|
|
411
|
+
cacheStrategy: "anthropic",
|
|
412
|
+
timeoutMultiplier: 1.2,
|
|
413
|
+
singlePassTokenMultiplier: 1,
|
|
414
|
+
multimodal: "metadata-only"
|
|
415
|
+
},
|
|
416
|
+
"kimi-coding": {
|
|
417
|
+
maxOutputTokens: 8192,
|
|
418
|
+
supportsTools: "probe",
|
|
419
|
+
jsonReliability: "high",
|
|
420
|
+
instructionFollowing: "high",
|
|
421
|
+
tokenRatioEstimate: 3.5,
|
|
422
|
+
concurrencyLimit: 2,
|
|
423
|
+
cacheStrategy: "anthropic",
|
|
424
|
+
timeoutMultiplier: 1.5,
|
|
425
|
+
singlePassTokenMultiplier: 0.95,
|
|
426
|
+
multimodal: "metadata-only"
|
|
427
|
+
},
|
|
428
|
+
anthropic: {
|
|
429
|
+
maxOutputTokens: 8192,
|
|
430
|
+
supportsTools: true,
|
|
431
|
+
jsonReliability: "high",
|
|
432
|
+
instructionFollowing: "high",
|
|
433
|
+
tokenRatioEstimate: 3.5,
|
|
434
|
+
concurrencyLimit: 3,
|
|
435
|
+
cacheStrategy: "anthropic",
|
|
436
|
+
timeoutMultiplier: 1.2,
|
|
437
|
+
singlePassTokenMultiplier: 1,
|
|
438
|
+
multimodal: "native"
|
|
439
|
+
},
|
|
440
|
+
openai: {
|
|
441
|
+
maxOutputTokens: 16384,
|
|
442
|
+
supportsTools: true,
|
|
443
|
+
jsonReliability: "high",
|
|
444
|
+
instructionFollowing: "high",
|
|
445
|
+
tokenRatioEstimate: 4,
|
|
446
|
+
concurrencyLimit: 5,
|
|
447
|
+
cacheStrategy: "openai",
|
|
448
|
+
timeoutMultiplier: 1,
|
|
449
|
+
singlePassTokenMultiplier: 1.15,
|
|
450
|
+
multimodal: "native"
|
|
451
|
+
},
|
|
452
|
+
google: {
|
|
453
|
+
maxOutputTokens: 8192,
|
|
454
|
+
supportsTools: true,
|
|
455
|
+
jsonReliability: "high",
|
|
456
|
+
instructionFollowing: "high",
|
|
457
|
+
tokenRatioEstimate: 3.8,
|
|
458
|
+
concurrencyLimit: 3,
|
|
459
|
+
cacheStrategy: "openai",
|
|
460
|
+
timeoutMultiplier: 1.15,
|
|
461
|
+
singlePassTokenMultiplier: 1.1,
|
|
462
|
+
multimodal: "native"
|
|
463
|
+
},
|
|
464
|
+
deepseek: {
|
|
465
|
+
maxOutputTokens: 8192,
|
|
466
|
+
supportsTools: true,
|
|
467
|
+
jsonReliability: "medium",
|
|
468
|
+
instructionFollowing: "medium",
|
|
469
|
+
tokenRatioEstimate: 3.6,
|
|
470
|
+
concurrencyLimit: 2,
|
|
471
|
+
cacheStrategy: "none",
|
|
472
|
+
timeoutMultiplier: 1.5,
|
|
473
|
+
singlePassTokenMultiplier: 0.85,
|
|
474
|
+
multimodal: "metadata-only"
|
|
475
|
+
},
|
|
476
|
+
minimax: {
|
|
477
|
+
maxOutputTokens: 4096,
|
|
478
|
+
supportsTools: "probe",
|
|
479
|
+
jsonReliability: "medium",
|
|
480
|
+
instructionFollowing: "medium",
|
|
481
|
+
tokenRatioEstimate: 3.8,
|
|
482
|
+
concurrencyLimit: 2,
|
|
483
|
+
cacheStrategy: "anthropic",
|
|
484
|
+
timeoutMultiplier: 1.6,
|
|
485
|
+
singlePassTokenMultiplier: 0.8,
|
|
486
|
+
multimodal: "metadata-only"
|
|
487
|
+
},
|
|
488
|
+
"xiaomi-token-plan": {
|
|
489
|
+
maxOutputTokens: 8192,
|
|
490
|
+
supportsTools: "probe",
|
|
491
|
+
jsonReliability: "medium",
|
|
492
|
+
instructionFollowing: "medium",
|
|
493
|
+
tokenRatioEstimate: 3.3,
|
|
494
|
+
concurrencyLimit: 2,
|
|
495
|
+
cacheStrategy: "openai",
|
|
496
|
+
timeoutMultiplier: 1.35,
|
|
497
|
+
singlePassTokenMultiplier: 0.9,
|
|
498
|
+
multimodal: "metadata-only"
|
|
499
|
+
},
|
|
500
|
+
"xiaomi-mimo": {
|
|
501
|
+
maxOutputTokens: 8192,
|
|
502
|
+
supportsTools: "probe",
|
|
503
|
+
jsonReliability: "medium",
|
|
504
|
+
instructionFollowing: "medium",
|
|
505
|
+
tokenRatioEstimate: 3.3,
|
|
506
|
+
concurrencyLimit: 2,
|
|
507
|
+
cacheStrategy: "anthropic",
|
|
508
|
+
timeoutMultiplier: 1.35,
|
|
509
|
+
singlePassTokenMultiplier: 0.9,
|
|
510
|
+
multimodal: "metadata-only"
|
|
511
|
+
},
|
|
512
|
+
crofai: {
|
|
513
|
+
maxOutputTokens: 8192,
|
|
514
|
+
supportsTools: "probe",
|
|
515
|
+
jsonReliability: "medium",
|
|
516
|
+
instructionFollowing: "medium",
|
|
517
|
+
tokenRatioEstimate: 3.8,
|
|
518
|
+
concurrencyLimit: 3,
|
|
519
|
+
cacheStrategy: "none",
|
|
520
|
+
timeoutMultiplier: 1.2,
|
|
521
|
+
singlePassTokenMultiplier: 0.95,
|
|
522
|
+
multimodal: "metadata-only"
|
|
523
|
+
},
|
|
524
|
+
mistral: {
|
|
525
|
+
maxOutputTokens: 8192,
|
|
526
|
+
supportsTools: true,
|
|
527
|
+
jsonReliability: "high",
|
|
528
|
+
instructionFollowing: "high",
|
|
529
|
+
tokenRatioEstimate: 3.5,
|
|
530
|
+
concurrencyLimit: 3,
|
|
531
|
+
cacheStrategy: "openai",
|
|
532
|
+
timeoutMultiplier: 1.2,
|
|
533
|
+
singlePassTokenMultiplier: 1,
|
|
534
|
+
multimodal: "metadata-only"
|
|
535
|
+
},
|
|
536
|
+
xai: {
|
|
537
|
+
maxOutputTokens: 8192,
|
|
538
|
+
supportsTools: true,
|
|
539
|
+
jsonReliability: "medium",
|
|
540
|
+
instructionFollowing: "high",
|
|
541
|
+
tokenRatioEstimate: 3.8,
|
|
542
|
+
concurrencyLimit: 3,
|
|
543
|
+
cacheStrategy: "openai",
|
|
544
|
+
timeoutMultiplier: 1.2,
|
|
545
|
+
singlePassTokenMultiplier: 1,
|
|
546
|
+
multimodal: "native"
|
|
547
|
+
}
|
|
548
|
+
};
|
|
549
|
+
var PROVIDER_ALIASES = [
|
|
550
|
+
{ pattern: /anthropic/i, provider: "anthropic" },
|
|
551
|
+
{ pattern: /kimi/i, provider: "kimi-coding" },
|
|
552
|
+
{ pattern: /zai/i, provider: "zai-anthropic" },
|
|
553
|
+
{ pattern: /openai/i, provider: "openai" },
|
|
554
|
+
{ pattern: /gpt/i, provider: "openai" },
|
|
555
|
+
{ pattern: /google|gemini/i, provider: "google" },
|
|
556
|
+
{ pattern: /deepseek/i, provider: "deepseek" },
|
|
557
|
+
{ pattern: /minimax/i, provider: "minimax" },
|
|
558
|
+
{ pattern: /xiaomi-mimo/i, provider: "xiaomi-mimo" },
|
|
559
|
+
{ pattern: /xiaomi/i, provider: "xiaomi-token-plan" },
|
|
560
|
+
{ pattern: /crofai/i, provider: "crofai" },
|
|
561
|
+
{ pattern: /mistral/i, provider: "mistral" },
|
|
562
|
+
{ pattern: /xai|grok/i, provider: "xai" }
|
|
563
|
+
];
|
|
564
|
+
var DEFAULT_CAPS = {
|
|
565
|
+
maxOutputTokens: 8192,
|
|
566
|
+
supportsTools: "probe",
|
|
567
|
+
jsonReliability: "medium",
|
|
568
|
+
instructionFollowing: "medium",
|
|
569
|
+
tokenRatioEstimate: 3.8,
|
|
570
|
+
concurrencyLimit: 2,
|
|
571
|
+
cacheStrategy: "none",
|
|
572
|
+
timeoutMultiplier: 1.35,
|
|
573
|
+
singlePassTokenMultiplier: 0.9,
|
|
574
|
+
multimodal: "metadata-only"
|
|
575
|
+
};
|
|
576
|
+
function getProviderCaps(provider) {
|
|
577
|
+
if (PROVIDER_MAP[provider])
|
|
578
|
+
return PROVIDER_MAP[provider];
|
|
579
|
+
for (const { pattern, provider: key } of PROVIDER_ALIASES) {
|
|
580
|
+
if (pattern.test(provider))
|
|
581
|
+
return PROVIDER_MAP[key] ?? DEFAULT_CAPS;
|
|
582
|
+
}
|
|
583
|
+
return DEFAULT_CAPS;
|
|
584
|
+
}
|
|
403
585
|
class TokenCalibrationStore {
|
|
404
586
|
maxEntries;
|
|
405
587
|
factors = new Map;
|
|
@@ -497,13 +679,54 @@ function buildUniquePathNeedles(filePath, allPaths) {
|
|
|
497
679
|
});
|
|
498
680
|
}
|
|
499
681
|
function isKnownPathReference(ref, knownPaths) {
|
|
500
|
-
const normalizedRef = normalizePath(ref);
|
|
682
|
+
const normalizedRef = normalizePath(ref).replace(/^\/+/, "");
|
|
683
|
+
if (!normalizedRef)
|
|
684
|
+
return false;
|
|
685
|
+
const pathShaped = normalizedRef.includes("/");
|
|
501
686
|
return knownPaths.some((path) => {
|
|
502
|
-
const normalizedPath = normalizePath(path);
|
|
503
|
-
|
|
687
|
+
const normalizedPath = normalizePath(path).replace(/^\/+/, "");
|
|
688
|
+
if (normalizedPath === normalizedRef || normalizedPath.endsWith("/" + normalizedRef))
|
|
689
|
+
return true;
|
|
690
|
+
if (normalizedPath.endsWith(normalizedRef)) {
|
|
691
|
+
const boundary = normalizedPath[normalizedPath.length - normalizedRef.length - 1];
|
|
692
|
+
if (boundary && !/[\w./-]/.test(boundary))
|
|
693
|
+
return true;
|
|
694
|
+
}
|
|
695
|
+
if (!pathShaped)
|
|
696
|
+
return false;
|
|
697
|
+
return normalizedPath.split("/").some((_, index, parts) => parts.slice(index).join("/").startsWith(normalizedRef + "/"));
|
|
504
698
|
});
|
|
505
699
|
}
|
|
506
700
|
|
|
701
|
+
// src/utils/logger.ts
|
|
702
|
+
var DEBUG = process.env.DEBUG?.includes("smart-compact") ?? false;
|
|
703
|
+
function warn(msg, err) {
|
|
704
|
+
const detail = err instanceof Error ? err.message : err ?? "";
|
|
705
|
+
console.error(LOG_PREFIX + " " + msg + (detail ? ": " + detail : ""));
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
// src/infra/paths.ts
|
|
709
|
+
import path from "path";
|
|
710
|
+
import os from "os";
|
|
711
|
+
function home() {
|
|
712
|
+
return process.env.HOME ?? os.homedir();
|
|
713
|
+
}
|
|
714
|
+
function piAgentDir() {
|
|
715
|
+
return path.join(home(), ".pi", "agent");
|
|
716
|
+
}
|
|
717
|
+
function cacheDir() {
|
|
718
|
+
return path.join(piAgentDir(), ".cache");
|
|
719
|
+
}
|
|
720
|
+
function smartCompactCacheDir() {
|
|
721
|
+
return path.join(cacheDir(), "smart-compact");
|
|
722
|
+
}
|
|
723
|
+
function metricsLogFile() {
|
|
724
|
+
return path.join(cacheDir(), "compact-metrics.jsonl");
|
|
725
|
+
}
|
|
726
|
+
function damageReportsFile() {
|
|
727
|
+
return path.join(smartCompactCacheDir(), "damage-reports.jsonl");
|
|
728
|
+
}
|
|
729
|
+
|
|
507
730
|
// src/domain/tool-semantics.ts
|
|
508
731
|
var PATH_KEYS = [
|
|
509
732
|
"path",
|
|
@@ -586,10 +809,20 @@ function isLikelyFileRef(candidate) {
|
|
|
586
809
|
return CODE_EXT_RE.test(candidate);
|
|
587
810
|
}
|
|
588
811
|
function extractFileRefs(summary) {
|
|
589
|
-
const
|
|
590
|
-
|
|
812
|
+
const matcher = new RegExp(FILE_REF_CANDIDATE_RE.source, FILE_REF_CANDIDATE_RE.flags);
|
|
813
|
+
const refs = [];
|
|
814
|
+
for (const match of summary.matchAll(matcher)) {
|
|
815
|
+
if (/[\\/]/.test(summary[(match.index ?? 0) + match[0].length] ?? ""))
|
|
816
|
+
continue;
|
|
817
|
+
if (isLikelyFileRef(match[0]))
|
|
818
|
+
refs.push(match[0]);
|
|
819
|
+
}
|
|
820
|
+
return refs;
|
|
591
821
|
}
|
|
592
822
|
|
|
823
|
+
// src/domain/summary-parse.ts
|
|
824
|
+
import { createHash } from "crypto";
|
|
825
|
+
|
|
593
826
|
// src/domain/summary-schema.ts
|
|
594
827
|
function classifyHeading(raw) {
|
|
595
828
|
const text = raw.replace(/^#+\s*/, "").replace(/[:\s]+$/, "").trim().toLowerCase();
|
|
@@ -629,6 +862,58 @@ var HEADING_RE = /^(#{1,3})\s+(.+?)\s*$/;
|
|
|
629
862
|
function summaryEvidenceLine(value, maxLength) {
|
|
630
863
|
return value.replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]/g, " ").replace(/\s+/g, " ").trim().replace(/^(?:(?:#{1,6}|[-*+]|>)\s+)+/, "").slice(0, maxLength).trim();
|
|
631
864
|
}
|
|
865
|
+
function summaryPathLine(value) {
|
|
866
|
+
return JSON.stringify(value);
|
|
867
|
+
}
|
|
868
|
+
function compactPathLine(value, maxLength, digest) {
|
|
869
|
+
const minimal = JSON.stringify("#" + digest);
|
|
870
|
+
if (minimal.length >= maxLength)
|
|
871
|
+
return minimal;
|
|
872
|
+
const chars = Array.from(value.replace(/\\/g, "/"));
|
|
873
|
+
let low = 0;
|
|
874
|
+
let high = chars.length;
|
|
875
|
+
let best = minimal;
|
|
876
|
+
while (low <= high) {
|
|
877
|
+
const length = Math.floor((low + high) / 2);
|
|
878
|
+
const candidate = JSON.stringify("\u2026/" + chars.slice(-length).join("") + "#" + digest);
|
|
879
|
+
if (candidate.length <= maxLength) {
|
|
880
|
+
best = candidate;
|
|
881
|
+
low = length + 1;
|
|
882
|
+
} else {
|
|
883
|
+
high = length - 1;
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
return best;
|
|
887
|
+
}
|
|
888
|
+
function buildSummaryPathEvidence(paths, budgetTokens = PROFILES.balanced.summaryBudgetTokens) {
|
|
889
|
+
const unique = Array.from(new Set(paths.filter(Boolean)));
|
|
890
|
+
if (!unique.length)
|
|
891
|
+
return new Map;
|
|
892
|
+
const full = unique.map((path2) => [path2, summaryPathLine(path2)]);
|
|
893
|
+
const minimumPerLine = JSON.stringify("#" + "x".repeat(12)).length + 3;
|
|
894
|
+
const budgetChars = Math.max(unique.length * minimumPerLine, Math.min(20000, Math.max(4000, Math.floor(budgetTokens * 2))));
|
|
895
|
+
if (full.reduce((total, [, line]) => total + line.length + 3, 0) <= budgetChars) {
|
|
896
|
+
return new Map(full);
|
|
897
|
+
}
|
|
898
|
+
const digests = new Map;
|
|
899
|
+
const owners = new Map;
|
|
900
|
+
for (const path2 of unique) {
|
|
901
|
+
const fullDigest = createHash("sha256").update(path2).digest("base64url");
|
|
902
|
+
let digest = fullDigest.slice(0, 12);
|
|
903
|
+
const owner = owners.get(digest);
|
|
904
|
+
if (owner && owner !== path2) {
|
|
905
|
+
digest = fullDigest;
|
|
906
|
+
digests.set(owner, createHash("sha256").update(owner).digest("base64url"));
|
|
907
|
+
}
|
|
908
|
+
owners.set(digest, path2);
|
|
909
|
+
digests.set(path2, digest);
|
|
910
|
+
}
|
|
911
|
+
const perPath = Math.max(JSON.stringify("#" + "x".repeat(12)).length, Math.floor((budgetChars - unique.length * 3) / unique.length));
|
|
912
|
+
return new Map(unique.map((path2) => [
|
|
913
|
+
path2,
|
|
914
|
+
compactPathLine(path2, perPath, digests.get(path2) ?? "")
|
|
915
|
+
]));
|
|
916
|
+
}
|
|
632
917
|
function mergeBodies(first, second) {
|
|
633
918
|
const seen = new Set;
|
|
634
919
|
return [first, second].filter(Boolean).flatMap((body) => body.split(`
|
|
@@ -653,7 +938,11 @@ function parseSummary(markdown) {
|
|
|
653
938
|
if (existing)
|
|
654
939
|
existing.body = mergeBodies(existing.body, body);
|
|
655
940
|
else
|
|
656
|
-
sections.push({
|
|
941
|
+
sections.push({
|
|
942
|
+
kind: currentKind,
|
|
943
|
+
heading: currentHeading.trim(),
|
|
944
|
+
body
|
|
945
|
+
});
|
|
657
946
|
};
|
|
658
947
|
for (const line of lines) {
|
|
659
948
|
const fenceMatch = line.match(/^\s{0,3}(`{3,}|~{3,})(.*)$/);
|
|
@@ -744,7 +1033,11 @@ function buildToolCallIndex(msgs) {
|
|
|
744
1033
|
for (let t = 0;t < nested.length; t++) {
|
|
745
1034
|
const tool = nested[t];
|
|
746
1035
|
const id = nestedToolCallId(b.id, i, t, tool.id);
|
|
747
|
-
idx.set(id, {
|
|
1036
|
+
idx.set(id, {
|
|
1037
|
+
name: tool.name,
|
|
1038
|
+
arguments: tool.arguments,
|
|
1039
|
+
msgIndex: i
|
|
1040
|
+
});
|
|
748
1041
|
}
|
|
749
1042
|
}
|
|
750
1043
|
}
|
|
@@ -752,46 +1045,42 @@ function buildToolCallIndex(msgs) {
|
|
|
752
1045
|
return idx;
|
|
753
1046
|
}
|
|
754
1047
|
var CONSTRAINT_PATTERNS = [
|
|
755
|
-
{
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
{
|
|
1048
|
+
{
|
|
1049
|
+
re: /\b(?:must|need|require|has to|important)\b.*\b(?:be|use|have|include|support)\b/i,
|
|
1050
|
+
cat: "requirement",
|
|
1051
|
+
conf: TUNING.CONFIDENCE_HIGH
|
|
1052
|
+
},
|
|
1053
|
+
{
|
|
1054
|
+
re: /\b(?:don't|never|avoid|shouldn't|must not|do not|no\s+(?:need|want))\b/i,
|
|
1055
|
+
cat: "prohibition",
|
|
1056
|
+
conf: TUNING.CONFIDENCE_MEDIUM
|
|
1057
|
+
},
|
|
1058
|
+
{
|
|
1059
|
+
re: /\b(?:prefer|like|want|would rather|should)\b.*\b(?:use|be|have|with)\b/i,
|
|
1060
|
+
cat: "preference",
|
|
1061
|
+
conf: TUNING.CONFIDENCE_LOW
|
|
1062
|
+
},
|
|
1063
|
+
{
|
|
1064
|
+
re: /(?<![A-Za-z0-9_])(?:yapma|kullanma|sak\u0131n|sak\u0131nha|asla(?:\s+(?:kullanma|yapma|getirme))?|bunu yapma)(?![A-Za-z0-9_])/iu,
|
|
1065
|
+
cat: "prohibition",
|
|
1066
|
+
conf: TUNING.CONFIDENCE_MEDIUM
|
|
1067
|
+
},
|
|
1068
|
+
{
|
|
1069
|
+
re: /(?<![A-Za-z0-9_])(?:kritik|kritikal|\u00F6nemli|onemli|\u015Fart|sart|zorunlu|\u015Fart ko\u015Ful|\u00F6nemli \u015Fart|kesinlikle|kesinlikle \u015Fart|b\u00F6yle olsun|b\u00F6yle yap\u0131n|\u015F\u00F6yle olsun|\u015F\u00F6yle yap\u0131n)(?![A-Za-z0-9_])/iu,
|
|
1070
|
+
cat: "requirement",
|
|
1071
|
+
conf: TUNING.CONFIDENCE_MEDIUM
|
|
1072
|
+
},
|
|
1073
|
+
{
|
|
1074
|
+
re: /(?<![A-Za-z0-9_])(?:tercih|isterim|olsun|kullanal\u0131m|yapal\u0131m|istiyorum)(?![A-Za-z0-9_])/iu,
|
|
1075
|
+
cat: "preference",
|
|
1076
|
+
conf: TUNING.CONFIDENCE_LOW
|
|
1077
|
+
}
|
|
761
1078
|
];
|
|
762
1079
|
function isDiagnosticConstraintText(text) {
|
|
763
1080
|
const candidate = text.replace(/^\s*[-*]\s+/, "").trim();
|
|
764
1081
|
return /^(?:\[[^\]]+\]\s*)?(?:npm\s+(?:error|warn|notice|audit|verbose|info)\b|(?:rg|grep):|command exited\b)/i.test(candidate);
|
|
765
1082
|
}
|
|
766
1083
|
|
|
767
|
-
// src/utils/logger.ts
|
|
768
|
-
var DEBUG = process.env.DEBUG?.includes("smart-compact") ?? false;
|
|
769
|
-
function warn(msg, err) {
|
|
770
|
-
const detail = err instanceof Error ? err.message : err ?? "";
|
|
771
|
-
console.error(LOG_PREFIX + " " + msg + (detail ? ": " + detail : ""));
|
|
772
|
-
}
|
|
773
|
-
|
|
774
|
-
// src/infra/paths.ts
|
|
775
|
-
import path from "path";
|
|
776
|
-
function home() {
|
|
777
|
-
return process.env.HOME ?? "/tmp";
|
|
778
|
-
}
|
|
779
|
-
function piAgentDir() {
|
|
780
|
-
return path.join(home(), ".pi", "agent");
|
|
781
|
-
}
|
|
782
|
-
function cacheDir() {
|
|
783
|
-
return path.join(piAgentDir(), ".cache");
|
|
784
|
-
}
|
|
785
|
-
function smartCompactCacheDir() {
|
|
786
|
-
return path.join(cacheDir(), "smart-compact");
|
|
787
|
-
}
|
|
788
|
-
function metricsLogFile() {
|
|
789
|
-
return path.join(cacheDir(), "compact-metrics.jsonl");
|
|
790
|
-
}
|
|
791
|
-
function damageReportsFile() {
|
|
792
|
-
return path.join(smartCompactCacheDir(), "damage-reports.jsonl");
|
|
793
|
-
}
|
|
794
|
-
|
|
795
1084
|
// src/infra/fs.ts
|
|
796
1085
|
import fs from "fs";
|
|
797
1086
|
function readJsonlTail(target, limit, maxBytes = 512 * 1024) {
|
|
@@ -886,7 +1175,10 @@ function withCodexWireLimit(model, opts) {
|
|
|
886
1175
|
onPayload: async (payload, requestModel) => {
|
|
887
1176
|
const transformed = await previous?.(payload, requestModel);
|
|
888
1177
|
const body = transformed ?? payload;
|
|
889
|
-
return body && typeof body === "object" ? {
|
|
1178
|
+
return body && typeof body === "object" ? {
|
|
1179
|
+
...body,
|
|
1180
|
+
max_output_tokens: opts.maxTokens
|
|
1181
|
+
} : body;
|
|
890
1182
|
}
|
|
891
1183
|
};
|
|
892
1184
|
}
|
|
@@ -907,11 +1199,12 @@ function assertSuccessful(message) {
|
|
|
907
1199
|
}
|
|
908
1200
|
return message;
|
|
909
1201
|
}
|
|
910
|
-
async function withProviderDeadline(opts, invoke) {
|
|
1202
|
+
async function withProviderDeadline(opts, invoke, modelId) {
|
|
911
1203
|
if (opts.signal?.aborted)
|
|
912
1204
|
throw new Error("LLM request aborted before dispatch");
|
|
913
1205
|
const controller = new AbortController;
|
|
914
|
-
const
|
|
1206
|
+
const multiplier = modelId && !((opts.codexWatchdogMs ?? 0) > 0) ? getProviderCaps(modelId).timeoutMultiplier : 1;
|
|
1207
|
+
const watchdogMs = Math.round(resolveCodexWatchdogMs(opts.maxTokens, opts.codexWatchdogMs) * multiplier);
|
|
915
1208
|
const abort = Promise.withResolvers();
|
|
916
1209
|
const abortFromCaller = () => {
|
|
917
1210
|
controller.abort(opts.signal?.reason);
|
|
@@ -984,7 +1277,7 @@ var rawLlmClient = {
|
|
|
984
1277
|
return completeChatGptCodex(model, body, bounded);
|
|
985
1278
|
const response = bounded.reasoning === undefined ? await (await resolveComplete())(model, body, bounded) : await (await resolveCompleteSimple())(model, body, bounded);
|
|
986
1279
|
return assertSuccessful(response);
|
|
987
|
-
});
|
|
1280
|
+
}, model.id);
|
|
988
1281
|
}
|
|
989
1282
|
};
|
|
990
1283
|
var defaultLlmClient = rawLlmClient;
|
|
@@ -998,7 +1291,10 @@ import crypto from "crypto";
|
|
|
998
1291
|
|
|
999
1292
|
// src/domain/scrub.ts
|
|
1000
1293
|
var SECRET_PATTERNS = [
|
|
1001
|
-
{
|
|
1294
|
+
{
|
|
1295
|
+
kind: "private-key",
|
|
1296
|
+
regex: /-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g
|
|
1297
|
+
},
|
|
1002
1298
|
{ kind: "aws-access-key", regex: /\bAKIA[0-9A-Z]{16}\b/g },
|
|
1003
1299
|
{ kind: "google-api-key", regex: /\bAIza[0-9A-Za-z_-]{30,}\b/g },
|
|
1004
1300
|
{ kind: "stripe-key", regex: /\b[rs]k_(?:live|test)_[0-9A-Za-z]{16,}\b/g },
|
|
@@ -1007,8 +1303,15 @@ var SECRET_PATTERNS = [
|
|
|
1007
1303
|
{ kind: "github-token", regex: /\bgh[pousr]_[A-Za-z0-9]{20,}\b/g },
|
|
1008
1304
|
{ kind: "api-key", regex: /\bsk-(?:ant-)?[A-Za-z0-9_-]{20,}\b/g },
|
|
1009
1305
|
{ kind: "slack-token", regex: /\bxox[baprs]-[A-Za-z0-9-]{10,}\b/g },
|
|
1010
|
-
{
|
|
1011
|
-
|
|
1306
|
+
{
|
|
1307
|
+
kind: "jwt",
|
|
1308
|
+
regex: /\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/g
|
|
1309
|
+
},
|
|
1310
|
+
{
|
|
1311
|
+
kind: "bearer-token",
|
|
1312
|
+
regex: /\bBearer\s+[A-Za-z0-9._~+/-]{12,}=*/gi,
|
|
1313
|
+
replacement: () => "Bearer [REDACTED:bearer-token]"
|
|
1314
|
+
},
|
|
1012
1315
|
{
|
|
1013
1316
|
kind: "connection-password",
|
|
1014
1317
|
regex: /\b([a-z][a-z0-9+.-]*:\/\/[^:\s/@]+:)[^@\s/]+(@)/gi,
|
|
@@ -1017,12 +1320,35 @@ var SECRET_PATTERNS = [
|
|
|
1017
1320
|
{
|
|
1018
1321
|
kind: "credential",
|
|
1019
1322
|
regex: /\b((?:[A-Za-z0-9]+[_-])*(?:api[_-]?key|access[_-]?token|auth[_-]?token|token|password|passwd|secret(?:[_-]?(?:access)?[_-]?key)?|client[_-]?secret)(?:[_-][A-Za-z0-9]+)*)\s*([:=])\s*["']?([^\s"']{16,})["']?/gi,
|
|
1020
|
-
replacement: (name, separator) => name + separator + "[REDACTED:credential]"
|
|
1323
|
+
replacement: (name, separator, value, match) => /^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)+$/.test(value) ? match : name + separator + "[REDACTED:credential]"
|
|
1021
1324
|
}
|
|
1022
1325
|
];
|
|
1326
|
+
function passesLuhn(candidate) {
|
|
1327
|
+
const digits = candidate.replace(/\D/g, "");
|
|
1328
|
+
if (digits.length < 13 || digits.length > 19)
|
|
1329
|
+
return false;
|
|
1330
|
+
if (/^(\d)\1+$/.test(digits))
|
|
1331
|
+
return false;
|
|
1332
|
+
let sum = 0, double = false;
|
|
1333
|
+
for (let i = digits.length - 1;i >= 0; i--) {
|
|
1334
|
+
let d = digits.charCodeAt(i) - 48;
|
|
1335
|
+
if (double) {
|
|
1336
|
+
d *= 2;
|
|
1337
|
+
if (d > 9)
|
|
1338
|
+
d -= 9;
|
|
1339
|
+
}
|
|
1340
|
+
sum += d;
|
|
1341
|
+
double = !double;
|
|
1342
|
+
}
|
|
1343
|
+
return sum % 10 === 0;
|
|
1344
|
+
}
|
|
1023
1345
|
var PII_PATTERNS = [
|
|
1024
1346
|
{ kind: "email", regex: /\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/gi },
|
|
1025
|
-
{
|
|
1347
|
+
{
|
|
1348
|
+
kind: "payment-card",
|
|
1349
|
+
regex: /\b(?:\d[ -]*?){13,19}\b/g,
|
|
1350
|
+
replacement: (candidate) => passesLuhn(candidate) ? "[REDACTED:payment-card]" : candidate
|
|
1351
|
+
},
|
|
1026
1352
|
{ kind: "phone", regex: /(?<![\w.])(?:\+?\d[\d ()-]{8,}\d)(?![\w.])/g }
|
|
1027
1353
|
];
|
|
1028
1354
|
function redact(text, patterns) {
|
|
@@ -1030,15 +1356,22 @@ function redact(text, patterns) {
|
|
|
1030
1356
|
let value = text;
|
|
1031
1357
|
for (const pattern of patterns) {
|
|
1032
1358
|
value = value.replace(pattern.regex, (...args) => {
|
|
1033
|
-
|
|
1359
|
+
const match = String(args[0]);
|
|
1360
|
+
let replacement = "[REDACTED:" + pattern.kind + "]";
|
|
1034
1361
|
if (pattern.replacement) {
|
|
1035
1362
|
const groups = args.slice(1, -2).map(String);
|
|
1036
|
-
|
|
1363
|
+
replacement = pattern.replacement(...groups, match);
|
|
1037
1364
|
}
|
|
1038
|
-
|
|
1365
|
+
if (replacement === match)
|
|
1366
|
+
return match;
|
|
1367
|
+
counts.set(pattern.kind, (counts.get(pattern.kind) ?? 0) + 1);
|
|
1368
|
+
return replacement;
|
|
1039
1369
|
});
|
|
1040
1370
|
}
|
|
1041
|
-
return {
|
|
1371
|
+
return {
|
|
1372
|
+
value,
|
|
1373
|
+
findings: [...counts].map(([kind, count]) => ({ kind, count }))
|
|
1374
|
+
};
|
|
1042
1375
|
}
|
|
1043
1376
|
function mergeFindings(target, findings) {
|
|
1044
1377
|
for (const finding of findings)
|
|
@@ -1068,7 +1401,6 @@ var SECRET_KEY_NAMES = {
|
|
|
1068
1401
|
set_cookie: true,
|
|
1069
1402
|
otp: true,
|
|
1070
1403
|
one_time_password: true,
|
|
1071
|
-
pin: true,
|
|
1072
1404
|
passcode: true
|
|
1073
1405
|
};
|
|
1074
1406
|
function normalizeObjectKey(key) {
|
|
@@ -1134,7 +1466,7 @@ class SecretScrubber {
|
|
|
1134
1466
|
const output = {};
|
|
1135
1467
|
seen.set(value2, output);
|
|
1136
1468
|
for (const [key, item] of Object.entries(value2)) {
|
|
1137
|
-
const carriesSecret = typeof item === "string"
|
|
1469
|
+
const carriesSecret = typeof item === "string" && item.length >= 8;
|
|
1138
1470
|
if (this.secretsEnabled && isSecretBearingKey(key) && carriesSecret) {
|
|
1139
1471
|
output[key] = "[REDACTED:credential]";
|
|
1140
1472
|
recordCredential();
|
|
@@ -1145,7 +1477,10 @@ class SecretScrubber {
|
|
|
1145
1477
|
return output;
|
|
1146
1478
|
};
|
|
1147
1479
|
const value = visit(input);
|
|
1148
|
-
return {
|
|
1480
|
+
return {
|
|
1481
|
+
value,
|
|
1482
|
+
findings: [...findings].map(([kind, count]) => ({ kind, count }))
|
|
1483
|
+
};
|
|
1149
1484
|
}
|
|
1150
1485
|
count() {
|
|
1151
1486
|
return this.total;
|