pi-smart-compact 8.0.8 → 9.1.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/ARCHITECTURE.md +90 -70
- package/CHANGELOG.md +45 -2
- package/README.md +52 -30
- package/SECURITY.md +1 -1
- package/dist/app/native-continuity-bridge.d.ts.map +1 -1
- package/dist/app/pending-slot.d.ts.map +1 -1
- package/dist/app/preflight.d.ts +2 -1
- package/dist/app/preflight.d.ts.map +1 -1
- package/dist/app/run-context.d.ts +10 -10
- package/dist/app/run-context.d.ts.map +1 -1
- package/dist/app/run-smart-compact.d.ts +18 -2
- package/dist/app/run-smart-compact.d.ts.map +1 -1
- package/dist/app/session-run-lock.d.ts.map +1 -1
- package/dist/app/smart-compact-input.d.ts +23 -0
- package/dist/app/smart-compact-input.d.ts.map +1 -0
- package/dist/app/steps/extract.d.ts.map +1 -1
- package/dist/app/steps/metrics.d.ts +4 -4
- package/dist/app/steps/metrics.d.ts.map +1 -1
- package/dist/app/steps/persist.d.ts +2 -3
- package/dist/app/steps/persist.d.ts.map +1 -1
- package/dist/app/steps/prepare.d.ts +4 -6
- package/dist/app/steps/prepare.d.ts.map +1 -1
- package/dist/app/steps/recover.d.ts +1 -1
- package/dist/app/steps/recover.d.ts.map +1 -1
- package/dist/app/steps/state.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/app/steps/window.d.ts +5 -1
- package/dist/app/steps/window.d.ts.map +1 -1
- package/dist/constants.d.ts +25 -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 +1 -1
- package/dist/domain/summary-parse.d.ts.map +1 -1
- package/dist/domain/summary-schema.d.ts +1 -1
- package/dist/domain/summary-schema.d.ts.map +1 -1
- package/dist/domain/tool-semantics.d.ts +15 -9
- package/dist/domain/tool-semantics.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3105 -1389
- package/dist/infra/ai-messages.d.ts +6 -0
- package/dist/infra/ai-messages.d.ts.map +1 -1
- package/dist/infra/context-graph.d.ts.map +1 -1
- package/dist/infra/fs.d.ts +13 -15
- package/dist/infra/fs.d.ts.map +1 -1
- package/dist/infra/git.d.ts +5 -11
- package/dist/infra/git.d.ts.map +1 -1
- package/dist/infra/llm-client.d.ts +2 -0
- package/dist/infra/llm-client.d.ts.map +1 -1
- package/dist/infra/paths.d.ts +4 -2
- package/dist/infra/paths.d.ts.map +1 -1
- package/dist/infra/session-identity.d.ts +11 -0
- package/dist/infra/session-identity.d.ts.map +1 -1
- package/dist/infra/synthesis-cache.d.ts.map +1 -1
- package/dist/phases/explore.d.ts +2 -1
- package/dist/phases/explore.d.ts.map +1 -1
- package/dist/phases/synthesize.d.ts +4 -1
- package/dist/phases/synthesize.d.ts.map +1 -1
- package/dist/phases/verify.d.ts +10 -3
- package/dist/phases/verify.d.ts.map +1 -1
- package/dist/provider-eval.js +278 -27
- package/dist/provider-scenario-eval.js +422 -49
- package/dist/telemetry-report.js +278 -27
- package/dist/types.d.ts +51 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/ui/overlays.d.ts +2 -1
- package/dist/ui/overlays.d.ts.map +1 -1
- package/dist/utils/backups.d.ts +31 -0
- package/dist/utils/backups.d.ts.map +1 -0
- package/dist/utils/cache.d.ts +3 -2
- package/dist/utils/cache.d.ts.map +1 -1
- package/dist/utils/damage.d.ts.map +1 -1
- package/dist/utils/extraction.d.ts +8 -1
- package/dist/utils/extraction.d.ts.map +1 -1
- package/dist/utils/fingerprint.d.ts +5 -1
- package/dist/utils/fingerprint.d.ts.map +1 -1
- package/dist/utils/helpers.d.ts +15 -37
- package/dist/utils/helpers.d.ts.map +1 -1
- package/dist/utils/pruning.d.ts.map +1 -1
- package/dist/utils/session-log.d.ts +10 -18
- package/dist/utils/session-log.d.ts.map +1 -1
- package/dist/utils/state.d.ts +2 -2
- package/dist/utils/state.d.ts.map +1 -1
- package/dist/utils/tokens.d.ts +2 -0
- package/dist/utils/tokens.d.ts.map +1 -1
- package/dist/utils/type-guards.d.ts +2 -0
- package/dist/utils/type-guards.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 = "
|
|
159
|
+
var VERSION = "9.1.0";
|
|
160
160
|
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.";
|
|
161
161
|
var PROFILES = {
|
|
162
162
|
light: {
|
|
@@ -198,7 +198,7 @@ var DEFAULT_CONFIG = {
|
|
|
198
198
|
backupEnabled: true,
|
|
199
199
|
backupDir: "",
|
|
200
200
|
minContextPercent: 60,
|
|
201
|
-
requireApproval:
|
|
201
|
+
requireApproval: true,
|
|
202
202
|
scrubSecrets: true,
|
|
203
203
|
scrubPii: false,
|
|
204
204
|
maxLlmCalls: 8,
|
|
@@ -238,6 +238,8 @@ Output EXACTLY this format:
|
|
|
238
238
|
- **[Decision]**: [Rationale]
|
|
239
239
|
` + `## Files Modified
|
|
240
240
|
- [Verified list from deterministic extraction]
|
|
241
|
+
` + `## Files Deleted
|
|
242
|
+
- [Verified deleted paths from deterministic extraction]
|
|
241
243
|
` + `## Files Read
|
|
242
244
|
- [Verified list from deterministic extraction]
|
|
243
245
|
` + `## Next Steps
|
|
@@ -261,6 +263,7 @@ Rules for Accuracy:
|
|
|
261
263
|
` + `**Summary**: [2-4 sentences: what happened, errors, code changes with paths]
|
|
262
264
|
` + `**Decisions**: [comma-separated, or "None"]
|
|
263
265
|
` + `**Modified**: [comma-separated paths, or "None"]
|
|
266
|
+
` + `**Deleted**: [comma-separated paths, or "None"]
|
|
264
267
|
` + `**Read**: [comma-separated paths, or "None"]
|
|
265
268
|
`;
|
|
266
269
|
var ASSEMBLY_PROMPT_PREFIX = `Merge these topic summaries into ONE coherent summary.
|
|
@@ -292,6 +295,8 @@ var ASSEMBLY_PROMPT_PREFIX = `Merge these topic summaries into ONE coherent summ
|
|
|
292
295
|
- **[Decision]**: [Rationale]
|
|
293
296
|
` + `## Files Modified
|
|
294
297
|
- [Verified deterministic list]
|
|
298
|
+
` + `## Files Deleted
|
|
299
|
+
- [Verified deterministic deleted paths]
|
|
295
300
|
` + `## Files Read
|
|
296
301
|
- [Verified deterministic list]
|
|
297
302
|
` + `## Next Steps
|
|
@@ -307,6 +312,14 @@ var FIVE_MINUTES_MS = 5 * 60 * 1000;
|
|
|
307
312
|
var ONE_HOUR_MS = 60 * 60 * 1000;
|
|
308
313
|
var SEVEN_DAYS_MS = 7 * 24 * 60 * 60 * 1000;
|
|
309
314
|
var THIRTY_DAYS_MS = 30 * 24 * 60 * 60 * 1000;
|
|
315
|
+
var ID_PREFIX = {
|
|
316
|
+
PROJECT: "proj-",
|
|
317
|
+
COMPACT_SESSION: "sc-",
|
|
318
|
+
MULTI_TOOL_USE_SYNTHETIC: "mtu_",
|
|
319
|
+
OPEN_LOOP: "loop-",
|
|
320
|
+
DECISION: "decision-",
|
|
321
|
+
ERROR: "error-"
|
|
322
|
+
};
|
|
310
323
|
var TUNING = {
|
|
311
324
|
EMA_PREV: 0.7,
|
|
312
325
|
EMA_SAMPLE: 0.3,
|
|
@@ -346,6 +359,7 @@ var TRUNC = {
|
|
|
346
359
|
BACKUP_PREVIEW_LINES: 5,
|
|
347
360
|
FINGERPRINT_SEG: 2
|
|
348
361
|
};
|
|
362
|
+
var LIKELY_ERROR_RE = /(?:command not found|no such file|permission denied|syntax error|cannot find|module not found|compilation error|build failed|test failed|^FAIL\b|ERROR:)/i;
|
|
349
363
|
var METRICS_BUFFER_MAX = 200;
|
|
350
364
|
var RUNTIME_LOG_MAX_BYTES = 5 * 1024 * 1024;
|
|
351
365
|
var EXPLORER_SYSTEM_PROMPT = `You are a conversation analyst. You have deterministic extraction data and can query the raw conversation using tools.
|
|
@@ -421,6 +435,15 @@ function calibrationKey(provider, model) {
|
|
|
421
435
|
}
|
|
422
436
|
|
|
423
437
|
// src/utils/type-guards.ts
|
|
438
|
+
function isRecord(value) {
|
|
439
|
+
return typeof value === "object" && value !== null;
|
|
440
|
+
}
|
|
441
|
+
function isTextBlock(c) {
|
|
442
|
+
return isRecord(c) && c.type === "text" && typeof c.text === "string";
|
|
443
|
+
}
|
|
444
|
+
function isToolCallBlock(c) {
|
|
445
|
+
return isRecord(c) && c.type === "toolCall" && typeof c.name === "string" && isRecord(c.arguments);
|
|
446
|
+
}
|
|
424
447
|
var KNOWN_METHODS = new Set(["eesv", "single-pass", "heuristic"]);
|
|
425
448
|
var KNOWN_PROFILES = new Set(["light", "balanced", "aggressive"]);
|
|
426
449
|
var KNOWN_MODES = new Set(["balanced", "aggressive", "fast", "thorough"]);
|
|
@@ -479,6 +502,74 @@ function isKnownPathReference(ref, knownPaths) {
|
|
|
479
502
|
});
|
|
480
503
|
}
|
|
481
504
|
|
|
505
|
+
// src/domain/tool-semantics.ts
|
|
506
|
+
var PATH_KEYS = [
|
|
507
|
+
"path",
|
|
508
|
+
"file_path",
|
|
509
|
+
"filePath",
|
|
510
|
+
"filename",
|
|
511
|
+
"file",
|
|
512
|
+
"target_file",
|
|
513
|
+
"file_uri",
|
|
514
|
+
"absolute_path"
|
|
515
|
+
];
|
|
516
|
+
var PAYLOAD_KEYS = [
|
|
517
|
+
"content",
|
|
518
|
+
"newText",
|
|
519
|
+
"oldText",
|
|
520
|
+
"new_str",
|
|
521
|
+
"old_str",
|
|
522
|
+
"new_string",
|
|
523
|
+
"old_string",
|
|
524
|
+
"edits",
|
|
525
|
+
"patch",
|
|
526
|
+
"replacement"
|
|
527
|
+
];
|
|
528
|
+
var COMMAND_KEYS = ["command", "cmd", "script"];
|
|
529
|
+
function hasPresent(args, keys) {
|
|
530
|
+
return keys.some((k) => args[k] != null);
|
|
531
|
+
}
|
|
532
|
+
function extractToolPath(args) {
|
|
533
|
+
if (!args || typeof args !== "object")
|
|
534
|
+
return;
|
|
535
|
+
const a = args;
|
|
536
|
+
for (const k of PATH_KEYS) {
|
|
537
|
+
const v = a[k];
|
|
538
|
+
if (typeof v === "string" && v.length > 0)
|
|
539
|
+
return v;
|
|
540
|
+
}
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
function normalizeToolName(name) {
|
|
544
|
+
if (typeof name !== "string")
|
|
545
|
+
return "";
|
|
546
|
+
return name.replace(/^functions[.:/_-]+/i, "").replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
|
|
547
|
+
}
|
|
548
|
+
function nameHas(name, hints) {
|
|
549
|
+
const words = name.split("_");
|
|
550
|
+
return hints.some((hint) => words.includes(hint));
|
|
551
|
+
}
|
|
552
|
+
function classifyToolOperation(args, toolName) {
|
|
553
|
+
const a = args && typeof args === "object" ? args : {};
|
|
554
|
+
const name = normalizeToolName(toolName);
|
|
555
|
+
const hasPath = extractToolPath(a) !== undefined;
|
|
556
|
+
if (hasPath && hasPresent(a, PAYLOAD_KEYS))
|
|
557
|
+
return "mutate";
|
|
558
|
+
if (hasPresent(a, COMMAND_KEYS))
|
|
559
|
+
return "execute";
|
|
560
|
+
if (hasPath && hasPresent(a, ["text"]) && nameHas(name, ["write", "edit", "patch", "replace", "append", "create", "update", "insert"]))
|
|
561
|
+
return "mutate";
|
|
562
|
+
if (hasPath && nameHas(name, ["delete", "remove", "unlink"]))
|
|
563
|
+
return "delete";
|
|
564
|
+
if (hasPresent(a, ["pattern", "query", "glob"]) || nameHas(name, ["grep", "search", "find", "glob", "rg"]))
|
|
565
|
+
return "search";
|
|
566
|
+
if (nameHas(name, ["list", "ls", "tree"]))
|
|
567
|
+
return "list";
|
|
568
|
+
if (hasPath || nameHas(name, ["read"]))
|
|
569
|
+
return "read";
|
|
570
|
+
return "unknown";
|
|
571
|
+
}
|
|
572
|
+
|
|
482
573
|
// src/utils/file-ref-detect.ts
|
|
483
574
|
var CODE_EXT_RE = /\.(ts|tsx|js|jsx|mjs|cjs|rs|py|go|java|rb|cs|cpp|c|h|hpp|swift|kt|scala|php|css|scss|html|json|yaml|yml|toml|md|mdx|sh|sql|tf|ini|env|lock|gradle|xml)$/i;
|
|
484
575
|
var VERSION_RE = /^v?\d+(?:\.\d+)+(?:[-+][\w.-]+)?$/i;
|
|
@@ -514,6 +605,8 @@ function classifyHeading(raw) {
|
|
|
514
605
|
return "files-modified";
|
|
515
606
|
if (text.includes("file") && (text.includes("read") || text.includes("viewed")))
|
|
516
607
|
return "files-read";
|
|
608
|
+
if (text.includes("file") && (text.includes("delet") || text.includes("remov")))
|
|
609
|
+
return "files-deleted";
|
|
517
610
|
if (text.includes("next step") || text === "next actions")
|
|
518
611
|
return "next-steps";
|
|
519
612
|
if (text.includes("critical context") || text === "important context")
|
|
@@ -547,6 +640,7 @@ function parseSummary(markdown) {
|
|
|
547
640
|
let currentHeading = "";
|
|
548
641
|
let currentKind = "unknown";
|
|
549
642
|
let bodyLines = [];
|
|
643
|
+
let fence = null;
|
|
550
644
|
let started = false;
|
|
551
645
|
const flush = () => {
|
|
552
646
|
if (!started)
|
|
@@ -560,16 +654,31 @@ function parseSummary(markdown) {
|
|
|
560
654
|
sections.push({ kind: currentKind, heading: currentHeading.trim(), body });
|
|
561
655
|
};
|
|
562
656
|
for (const line of lines) {
|
|
563
|
-
const
|
|
564
|
-
if (
|
|
565
|
-
const
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
657
|
+
const fenceMatch = line.match(/^\s{0,3}(`{3,}|~{3,})(.*)$/);
|
|
658
|
+
if (fenceMatch) {
|
|
659
|
+
const marker = fenceMatch[1][0];
|
|
660
|
+
const markerLength = fenceMatch[1].length;
|
|
661
|
+
if (!fence) {
|
|
662
|
+
fence = { marker, length: markerLength };
|
|
663
|
+
} else if (marker === fence.marker && markerLength >= fence.length && !fenceMatch[2].trim()) {
|
|
664
|
+
fence = null;
|
|
665
|
+
}
|
|
666
|
+
if (started)
|
|
667
|
+
bodyLines.push(line);
|
|
668
|
+
continue;
|
|
669
|
+
}
|
|
670
|
+
if (!fence) {
|
|
671
|
+
const heading = line.match(HEADING_RE);
|
|
672
|
+
if (heading) {
|
|
673
|
+
const kind = classifyHeading(heading[2]);
|
|
674
|
+
if (heading[1].length <= 2 || kind !== "unknown") {
|
|
675
|
+
flush();
|
|
676
|
+
currentHeading = "## " + heading[2].trim();
|
|
677
|
+
currentKind = kind;
|
|
678
|
+
bodyLines = [];
|
|
679
|
+
started = true;
|
|
680
|
+
continue;
|
|
681
|
+
}
|
|
573
682
|
}
|
|
574
683
|
}
|
|
575
684
|
if (started)
|
|
@@ -584,12 +693,68 @@ function findSection(summary, kind) {
|
|
|
584
693
|
}
|
|
585
694
|
|
|
586
695
|
// src/utils/extraction.ts
|
|
696
|
+
function nestedToolCallId(wrapperId, messageIndex, toolIndex, nestedId) {
|
|
697
|
+
return typeof nestedId === "string" ? nestedId : wrapperId ? wrapperId + "_" + toolIndex : ID_PREFIX.MULTI_TOOL_USE_SYNTHETIC + messageIndex + "_" + toolIndex;
|
|
698
|
+
}
|
|
699
|
+
function flattenToolCallBlock(b) {
|
|
700
|
+
if (!isToolCallBlock(b))
|
|
701
|
+
return [];
|
|
702
|
+
if (b.name === "multi_tool_use.parallel" && Array.isArray(b.arguments?.tool_uses)) {
|
|
703
|
+
return b.arguments.tool_uses.map((u) => {
|
|
704
|
+
const recipient = u?.recipient_name ?? "";
|
|
705
|
+
return {
|
|
706
|
+
name: recipient.replace(/^functions\./, ""),
|
|
707
|
+
id: u?.id ?? undefined,
|
|
708
|
+
arguments: u?.parameters ?? {}
|
|
709
|
+
};
|
|
710
|
+
});
|
|
711
|
+
}
|
|
712
|
+
return [{ name: b.name, id: b.id, arguments: b.arguments }];
|
|
713
|
+
}
|
|
714
|
+
function extractText(content) {
|
|
715
|
+
if (typeof content === "string")
|
|
716
|
+
return content;
|
|
717
|
+
if (!Array.isArray(content))
|
|
718
|
+
return "";
|
|
719
|
+
return content.map((b) => {
|
|
720
|
+
if (typeof b === "string")
|
|
721
|
+
return b;
|
|
722
|
+
if (isTextBlock(b))
|
|
723
|
+
return b.text;
|
|
724
|
+
return "";
|
|
725
|
+
}).join("");
|
|
726
|
+
}
|
|
727
|
+
function buildToolCallIndex(msgs) {
|
|
728
|
+
const idx = new Map;
|
|
729
|
+
for (let i = 0;i < msgs.length; i++) {
|
|
730
|
+
const m = msgs[i];
|
|
731
|
+
if (m.role !== "assistant")
|
|
732
|
+
continue;
|
|
733
|
+
const blocks = Array.isArray(m.content) ? m.content : [];
|
|
734
|
+
for (const b of blocks) {
|
|
735
|
+
if (!isToolCallBlock(b))
|
|
736
|
+
continue;
|
|
737
|
+
if (b.id) {
|
|
738
|
+
idx.set(b.id, { name: b.name, arguments: b.arguments, msgIndex: i });
|
|
739
|
+
}
|
|
740
|
+
if (b.name === "multi_tool_use.parallel" && Array.isArray(b.arguments?.tool_uses)) {
|
|
741
|
+
const nested = flattenToolCallBlock(b);
|
|
742
|
+
for (let t = 0;t < nested.length; t++) {
|
|
743
|
+
const tool = nested[t];
|
|
744
|
+
const id = nestedToolCallId(b.id, i, t, tool.id);
|
|
745
|
+
idx.set(id, { name: tool.name, arguments: tool.arguments, msgIndex: i });
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
return idx;
|
|
751
|
+
}
|
|
587
752
|
var CONSTRAINT_PATTERNS = [
|
|
588
753
|
{ re: /\b(?:must|need|require|has to|important)\b.*\b(?:be|use|have|include|support)\b/i, cat: "requirement", conf: TUNING.CONFIDENCE_HIGH },
|
|
589
754
|
{ re: /\b(?:don't|never|avoid|shouldn't|must not|do not|no\s+(?:need|want))\b/i, cat: "prohibition", conf: TUNING.CONFIDENCE_MEDIUM },
|
|
590
755
|
{ re: /\b(?:prefer|like|want|would rather|should)\b.*\b(?:use|be|have|with)\b/i, cat: "preference", conf: TUNING.CONFIDENCE_LOW },
|
|
591
|
-
{ re: /(?<![A-Za-z0-9_])(?:
|
|
592
|
-
{ re: /(?<![A-Za-z0-9_])(?:
|
|
756
|
+
{ re: /(?<![A-Za-z0-9_])(?:yapma|kullanma|sak\u0131n|sak\u0131nha|asla(?:\s+(?:kullanma|yapma|getirme))?|bunu yapma)(?![A-Za-z0-9_])/iu, cat: "prohibition", conf: TUNING.CONFIDENCE_MEDIUM },
|
|
757
|
+
{ 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, cat: "requirement", conf: TUNING.CONFIDENCE_MEDIUM },
|
|
593
758
|
{ re: /(?<![A-Za-z0-9_])(?:tercih|isterim|olsun|kullanal\u0131m|yapal\u0131m|istiyorum)(?![A-Za-z0-9_])/iu, cat: "preference", conf: TUNING.CONFIDENCE_LOW }
|
|
594
759
|
];
|
|
595
760
|
function isDiagnosticConstraintText(text) {
|
|
@@ -656,7 +821,6 @@ function readJsonlTail(target, limit, maxBytes = 512 * 1024) {
|
|
|
656
821
|
}
|
|
657
822
|
return values.slice(-limit);
|
|
658
823
|
}
|
|
659
|
-
var scheduledTailTrims = new Set;
|
|
660
824
|
|
|
661
825
|
// src/infra/clock.ts
|
|
662
826
|
var systemClock = {
|
|
@@ -741,6 +905,35 @@ function assertSuccessful(message) {
|
|
|
741
905
|
}
|
|
742
906
|
return message;
|
|
743
907
|
}
|
|
908
|
+
async function withProviderDeadline(opts, invoke) {
|
|
909
|
+
if (opts.signal?.aborted)
|
|
910
|
+
throw new Error("LLM request aborted before dispatch");
|
|
911
|
+
const controller = new AbortController;
|
|
912
|
+
const watchdogMs = resolveCodexWatchdogMs(opts.maxTokens, opts.codexWatchdogMs);
|
|
913
|
+
const abort = Promise.withResolvers();
|
|
914
|
+
const abortFromCaller = () => {
|
|
915
|
+
controller.abort(opts.signal?.reason);
|
|
916
|
+
abort.reject(new Error("LLM request aborted by caller"));
|
|
917
|
+
};
|
|
918
|
+
opts.signal?.addEventListener("abort", abortFromCaller, { once: true });
|
|
919
|
+
const timeout = Promise.withResolvers();
|
|
920
|
+
const timer = setTimeout(() => {
|
|
921
|
+
controller.abort("provider-watchdog");
|
|
922
|
+
timeout.reject(new Error("Provider watchdog stopped generation after " + watchdogMs + "ms"));
|
|
923
|
+
}, watchdogMs);
|
|
924
|
+
if (typeof timer === "object" && "unref" in timer)
|
|
925
|
+
timer.unref();
|
|
926
|
+
try {
|
|
927
|
+
return await Promise.race([
|
|
928
|
+
invoke({ ...opts, signal: controller.signal }),
|
|
929
|
+
abort.promise,
|
|
930
|
+
timeout.promise
|
|
931
|
+
]);
|
|
932
|
+
} finally {
|
|
933
|
+
clearTimeout(timer);
|
|
934
|
+
opts.signal?.removeEventListener("abort", abortFromCaller);
|
|
935
|
+
}
|
|
936
|
+
}
|
|
744
937
|
async function completeChatGptCodex(model, body, opts) {
|
|
745
938
|
const controller = new AbortController;
|
|
746
939
|
const watchdogMs = resolveCodexWatchdogMs(opts.maxTokens, opts.codexWatchdogMs);
|
|
@@ -784,10 +977,12 @@ async function completeChatGptCodex(model, body, opts) {
|
|
|
784
977
|
var rawLlmClient = {
|
|
785
978
|
complete: async (model, body, originalOpts) => {
|
|
786
979
|
const opts = withCodexWireLimit(model, originalOpts);
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
980
|
+
return withProviderDeadline(opts, async (bounded) => {
|
|
981
|
+
if (isChatGptCodex(model))
|
|
982
|
+
return completeChatGptCodex(model, body, bounded);
|
|
983
|
+
const response = bounded.reasoning === undefined ? await (await resolveComplete())(model, body, bounded) : await (await resolveCompleteSimple())(model, body, bounded);
|
|
984
|
+
return assertSuccessful(response);
|
|
985
|
+
});
|
|
791
986
|
}
|
|
792
987
|
};
|
|
793
988
|
var defaultLlmClient = rawLlmClient;
|
|
@@ -803,14 +998,23 @@ import crypto from "crypto";
|
|
|
803
998
|
var SECRET_PATTERNS = [
|
|
804
999
|
{ kind: "private-key", regex: /-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g },
|
|
805
1000
|
{ kind: "aws-access-key", regex: /\bAKIA[0-9A-Z]{16}\b/g },
|
|
806
|
-
{ kind: "
|
|
1001
|
+
{ kind: "google-api-key", regex: /\bAIza[0-9A-Za-z_-]{30,}\b/g },
|
|
1002
|
+
{ kind: "stripe-key", regex: /\b[rs]k_(?:live|test)_[0-9A-Za-z]{16,}\b/g },
|
|
1003
|
+
{ kind: "gitlab-token", regex: /\bglpat-[0-9A-Za-z_-]{20,}\b/g },
|
|
1004
|
+
{ kind: "npm-token", regex: /\bnpm_[0-9A-Za-z]{30,}\b/g },
|
|
1005
|
+
{ kind: "github-token", regex: /\bgh[pousr]_[A-Za-z0-9]{20,}\b/g },
|
|
807
1006
|
{ kind: "api-key", regex: /\bsk-(?:ant-)?[A-Za-z0-9_-]{20,}\b/g },
|
|
808
|
-
{ kind: "slack-token", regex: /\bxox[baprs]-[A-Za-z0-9-]{
|
|
1007
|
+
{ kind: "slack-token", regex: /\bxox[baprs]-[A-Za-z0-9-]{10,}\b/g },
|
|
809
1008
|
{ kind: "jwt", regex: /\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/g },
|
|
810
|
-
{ kind: "bearer-token", regex: /\bBearer\s+[A-Za-z0-9._~+\/-]{
|
|
1009
|
+
{ kind: "bearer-token", regex: /\bBearer\s+[A-Za-z0-9._~+\/-]{12,}=*/gi, replacement: () => "Bearer [REDACTED:bearer-token]" },
|
|
1010
|
+
{
|
|
1011
|
+
kind: "connection-password",
|
|
1012
|
+
regex: /\b([a-z][a-z0-9+.-]*:\/\/[^:\s/@]+:)[^@\s/]+(@)/gi,
|
|
1013
|
+
replacement: (prefix, suffix) => prefix + "[REDACTED:password]" + suffix
|
|
1014
|
+
},
|
|
811
1015
|
{
|
|
812
1016
|
kind: "credential",
|
|
813
|
-
regex: /\b(api[_-]?key|access[_-]?token|auth[_-]?token|token|password|passwd|secret)\s*([:=])\s*["']?([
|
|
1017
|
+
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,
|
|
814
1018
|
replacement: (name, separator) => name + separator + "[REDACTED:credential]"
|
|
815
1019
|
}
|
|
816
1020
|
];
|
|
@@ -838,6 +1042,42 @@ function mergeFindings(target, findings) {
|
|
|
838
1042
|
for (const finding of findings)
|
|
839
1043
|
target.set(finding.kind, (target.get(finding.kind) ?? 0) + finding.count);
|
|
840
1044
|
}
|
|
1045
|
+
var SECRET_KEY_NAMES = {
|
|
1046
|
+
api_key: true,
|
|
1047
|
+
apikey: true,
|
|
1048
|
+
access_token: true,
|
|
1049
|
+
auth_token: true,
|
|
1050
|
+
authorization: true,
|
|
1051
|
+
password: true,
|
|
1052
|
+
passwd: true,
|
|
1053
|
+
secret: true,
|
|
1054
|
+
secret_key: true,
|
|
1055
|
+
secret_access_key: true,
|
|
1056
|
+
client_secret: true,
|
|
1057
|
+
private_key: true,
|
|
1058
|
+
database_url: true,
|
|
1059
|
+
connection_string: true,
|
|
1060
|
+
token: true,
|
|
1061
|
+
refresh_token: true,
|
|
1062
|
+
session_token: true,
|
|
1063
|
+
credential: true,
|
|
1064
|
+
credentials: true,
|
|
1065
|
+
cookie: true,
|
|
1066
|
+
set_cookie: true,
|
|
1067
|
+
otp: true,
|
|
1068
|
+
one_time_password: true,
|
|
1069
|
+
pin: true,
|
|
1070
|
+
passcode: true
|
|
1071
|
+
};
|
|
1072
|
+
function normalizeObjectKey(key) {
|
|
1073
|
+
return key.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
|
|
1074
|
+
}
|
|
1075
|
+
function isSecretBearingKey(key) {
|
|
1076
|
+
const normalized = normalizeObjectKey(key);
|
|
1077
|
+
if (SECRET_KEY_NAMES[normalized])
|
|
1078
|
+
return true;
|
|
1079
|
+
return /(?:^|_)(?:api_key|access_token|auth_token|password|passwd|secret_access_key|client_secret|private_key|refresh_token|session_token|one_time_password|passcode)(?:_|$)/.test(normalized);
|
|
1080
|
+
}
|
|
841
1081
|
|
|
842
1082
|
class SecretScrubber {
|
|
843
1083
|
secretsEnabled;
|
|
@@ -867,6 +1107,10 @@ class SecretScrubber {
|
|
|
867
1107
|
scrubValue(input) {
|
|
868
1108
|
const findings = new Map;
|
|
869
1109
|
const seen = new WeakMap;
|
|
1110
|
+
const recordCredential = () => {
|
|
1111
|
+
findings.set("credential", (findings.get("credential") ?? 0) + 1);
|
|
1112
|
+
this.total++;
|
|
1113
|
+
};
|
|
870
1114
|
const visit = (value2) => {
|
|
871
1115
|
if (typeof value2 === "string") {
|
|
872
1116
|
const result = this.scrubText(value2);
|
|
@@ -887,8 +1131,15 @@ class SecretScrubber {
|
|
|
887
1131
|
}
|
|
888
1132
|
const output = {};
|
|
889
1133
|
seen.set(value2, output);
|
|
890
|
-
for (const [key, item] of Object.entries(value2))
|
|
891
|
-
|
|
1134
|
+
for (const [key, item] of Object.entries(value2)) {
|
|
1135
|
+
const carriesSecret = typeof item === "string" ? item.length > 0 : item != null;
|
|
1136
|
+
if (this.secretsEnabled && isSecretBearingKey(key) && carriesSecret) {
|
|
1137
|
+
output[key] = "[REDACTED:credential]";
|
|
1138
|
+
recordCredential();
|
|
1139
|
+
} else {
|
|
1140
|
+
output[key] = visit(item);
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
892
1143
|
return output;
|
|
893
1144
|
};
|
|
894
1145
|
const value = visit(input);
|
|
@@ -1147,8 +1398,8 @@ function readMetricsLog(limit = 100) {
|
|
|
1147
1398
|
const fd = fs2.openSync(logPath, "r");
|
|
1148
1399
|
try {
|
|
1149
1400
|
const buf = Buffer.alloc(wantBytes);
|
|
1150
|
-
fs2.readSync(fd, buf, 0, wantBytes, startPos);
|
|
1151
|
-
let text = buf.toString("utf8");
|
|
1401
|
+
const bytesRead = fs2.readSync(fd, buf, 0, wantBytes, startPos);
|
|
1402
|
+
let text = buf.subarray(0, bytesRead).toString("utf8");
|
|
1152
1403
|
if (startPos > 0) {
|
|
1153
1404
|
const nl = text.indexOf(`
|
|
1154
1405
|
`);
|