pi-smart-compact 8.0.7 → 9.0.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 +47 -37
- package/CHANGELOG.md +34 -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/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/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.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 +21 -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 +9 -0
- package/dist/domain/tool-semantics.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2082 -1126
- package/dist/infra/context-graph.d.ts.map +1 -1
- package/dist/infra/fs.d.ts +11 -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.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 +113 -43
- package/dist/provider-scenario-eval.js +206 -51
- package/dist/telemetry-report.js +113 -43
- package/dist/types.d.ts +45 -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 +1 -0
- package/dist/utils/cache.d.ts.map +1 -1
- package/dist/utils/damage.d.ts.map +1 -1
- package/dist/utils/extraction.d.ts +6 -0
- package/dist/utils/extraction.d.ts.map +1 -1
- package/dist/utils/file-needles.d.ts +7 -0
- package/dist/utils/file-needles.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 +1 -35
- 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/docs/MIGRATING_TO_V8.md +2 -2
- package/package.json +2 -2
|
@@ -82,6 +82,35 @@ function assertSuccessful(message) {
|
|
|
82
82
|
}
|
|
83
83
|
return message;
|
|
84
84
|
}
|
|
85
|
+
async function withProviderDeadline(opts, invoke) {
|
|
86
|
+
if (opts.signal?.aborted)
|
|
87
|
+
throw new Error("LLM request aborted before dispatch");
|
|
88
|
+
const controller = new AbortController;
|
|
89
|
+
const watchdogMs = resolveCodexWatchdogMs(opts.maxTokens, opts.codexWatchdogMs);
|
|
90
|
+
const abort = Promise.withResolvers();
|
|
91
|
+
const abortFromCaller = () => {
|
|
92
|
+
controller.abort(opts.signal?.reason);
|
|
93
|
+
abort.reject(new Error("LLM request aborted by caller"));
|
|
94
|
+
};
|
|
95
|
+
opts.signal?.addEventListener("abort", abortFromCaller, { once: true });
|
|
96
|
+
const timeout = Promise.withResolvers();
|
|
97
|
+
const timer = setTimeout(() => {
|
|
98
|
+
controller.abort("provider-watchdog");
|
|
99
|
+
timeout.reject(new Error("Provider watchdog stopped generation after " + watchdogMs + "ms"));
|
|
100
|
+
}, watchdogMs);
|
|
101
|
+
if (typeof timer === "object" && "unref" in timer)
|
|
102
|
+
timer.unref();
|
|
103
|
+
try {
|
|
104
|
+
return await Promise.race([
|
|
105
|
+
invoke({ ...opts, signal: controller.signal }),
|
|
106
|
+
abort.promise,
|
|
107
|
+
timeout.promise
|
|
108
|
+
]);
|
|
109
|
+
} finally {
|
|
110
|
+
clearTimeout(timer);
|
|
111
|
+
opts.signal?.removeEventListener("abort", abortFromCaller);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
85
114
|
async function completeChatGptCodex(model, body, opts) {
|
|
86
115
|
const controller = new AbortController;
|
|
87
116
|
const watchdogMs = resolveCodexWatchdogMs(opts.maxTokens, opts.codexWatchdogMs);
|
|
@@ -125,10 +154,12 @@ async function completeChatGptCodex(model, body, opts) {
|
|
|
125
154
|
var rawLlmClient = {
|
|
126
155
|
complete: async (model, body, originalOpts) => {
|
|
127
156
|
const opts = withCodexWireLimit(model, originalOpts);
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
157
|
+
return withProviderDeadline(opts, async (bounded) => {
|
|
158
|
+
if (isChatGptCodex(model))
|
|
159
|
+
return completeChatGptCodex(model, body, bounded);
|
|
160
|
+
const response = bounded.reasoning === undefined ? await (await resolveComplete())(model, body, bounded) : await (await resolveCompleteSimple())(model, body, bounded);
|
|
161
|
+
return assertSuccessful(response);
|
|
162
|
+
});
|
|
132
163
|
}
|
|
133
164
|
};
|
|
134
165
|
var defaultLlmClient = rawLlmClient;
|
|
@@ -138,7 +169,7 @@ function getLlmClient() {
|
|
|
138
169
|
}
|
|
139
170
|
|
|
140
171
|
// src/constants.ts
|
|
141
|
-
var VERSION = "
|
|
172
|
+
var VERSION = "9.0.0";
|
|
142
173
|
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.";
|
|
143
174
|
var PROFILES = {
|
|
144
175
|
light: {
|
|
@@ -180,7 +211,7 @@ var DEFAULT_CONFIG = {
|
|
|
180
211
|
backupEnabled: true,
|
|
181
212
|
backupDir: "",
|
|
182
213
|
minContextPercent: 60,
|
|
183
|
-
requireApproval:
|
|
214
|
+
requireApproval: true,
|
|
184
215
|
scrubSecrets: true,
|
|
185
216
|
scrubPii: false,
|
|
186
217
|
maxLlmCalls: 8,
|
|
@@ -220,6 +251,8 @@ Output EXACTLY this format:
|
|
|
220
251
|
- **[Decision]**: [Rationale]
|
|
221
252
|
` + `## Files Modified
|
|
222
253
|
- [Verified list from deterministic extraction]
|
|
254
|
+
` + `## Files Deleted
|
|
255
|
+
- [Verified deleted paths from deterministic extraction]
|
|
223
256
|
` + `## Files Read
|
|
224
257
|
- [Verified list from deterministic extraction]
|
|
225
258
|
` + `## Next Steps
|
|
@@ -243,6 +276,7 @@ Rules for Accuracy:
|
|
|
243
276
|
` + `**Summary**: [2-4 sentences: what happened, errors, code changes with paths]
|
|
244
277
|
` + `**Decisions**: [comma-separated, or "None"]
|
|
245
278
|
` + `**Modified**: [comma-separated paths, or "None"]
|
|
279
|
+
` + `**Deleted**: [comma-separated paths, or "None"]
|
|
246
280
|
` + `**Read**: [comma-separated paths, or "None"]
|
|
247
281
|
`;
|
|
248
282
|
var ASSEMBLY_PROMPT_PREFIX = `Merge these topic summaries into ONE coherent summary.
|
|
@@ -274,6 +308,8 @@ var ASSEMBLY_PROMPT_PREFIX = `Merge these topic summaries into ONE coherent summ
|
|
|
274
308
|
- **[Decision]**: [Rationale]
|
|
275
309
|
` + `## Files Modified
|
|
276
310
|
- [Verified deterministic list]
|
|
311
|
+
` + `## Files Deleted
|
|
312
|
+
- [Verified deterministic deleted paths]
|
|
277
313
|
` + `## Files Read
|
|
278
314
|
- [Verified deterministic list]
|
|
279
315
|
` + `## Next Steps
|
|
@@ -328,6 +364,7 @@ var TRUNC = {
|
|
|
328
364
|
BACKUP_PREVIEW_LINES: 5,
|
|
329
365
|
FINGERPRINT_SEG: 2
|
|
330
366
|
};
|
|
367
|
+
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;
|
|
331
368
|
var METRICS_BUFFER_MAX = 200;
|
|
332
369
|
var RUNTIME_LOG_MAX_BYTES = 5 * 1024 * 1024;
|
|
333
370
|
var EXPLORER_SYSTEM_PROMPT = `You are a conversation analyst. You have deterministic extraction data and can query the raw conversation using tools.
|
|
@@ -406,6 +443,9 @@ function calibrationKey(provider, model) {
|
|
|
406
443
|
}
|
|
407
444
|
|
|
408
445
|
// src/utils/type-guards.ts
|
|
446
|
+
function isTextBlock(c) {
|
|
447
|
+
return typeof c === "object" && c !== null && c.type === "text" && typeof c.text === "string";
|
|
448
|
+
}
|
|
409
449
|
var KNOWN_METHODS = new Set(["eesv", "single-pass", "heuristic"]);
|
|
410
450
|
var KNOWN_PROFILES = new Set(["light", "balanced", "aggressive"]);
|
|
411
451
|
var KNOWN_MODES = new Set(["balanced", "aggressive", "fast", "thorough"]);
|
|
@@ -425,37 +465,9 @@ var GENERIC_BASENAMES = new Set([
|
|
|
425
465
|
"lib.rs",
|
|
426
466
|
"__init__.py"
|
|
427
467
|
]);
|
|
428
|
-
var MIN_BARE_BASENAME_LEN = 5;
|
|
429
468
|
function normalizePath(filePath) {
|
|
430
469
|
return filePath.replace(/\\/g, "/").replace(/^\.\//, "").toLowerCase();
|
|
431
470
|
}
|
|
432
|
-
function buildPathNeedles(filePath) {
|
|
433
|
-
const parts = normalizePath(filePath).split("/").filter(Boolean);
|
|
434
|
-
if (parts.length === 0)
|
|
435
|
-
return [];
|
|
436
|
-
const needles = [];
|
|
437
|
-
const basename = parts[parts.length - 1];
|
|
438
|
-
if (!GENERIC_BASENAMES.has(basename) && basename.length >= MIN_BARE_BASENAME_LEN) {
|
|
439
|
-
needles.push(basename);
|
|
440
|
-
}
|
|
441
|
-
for (let j = parts.length - 2;j >= 0; j--) {
|
|
442
|
-
needles.push(parts.slice(j).join("/"));
|
|
443
|
-
}
|
|
444
|
-
return needles;
|
|
445
|
-
}
|
|
446
|
-
function buildUniquePathNeedles(filePath, allPaths) {
|
|
447
|
-
const normalized = allPaths.map(normalizePath);
|
|
448
|
-
return buildPathNeedles(filePath).filter((needle) => {
|
|
449
|
-
let owners = 0;
|
|
450
|
-
for (const candidate of normalized) {
|
|
451
|
-
if (candidate === needle || candidate.endsWith("/" + needle))
|
|
452
|
-
owners++;
|
|
453
|
-
if (owners > 1)
|
|
454
|
-
return false;
|
|
455
|
-
}
|
|
456
|
-
return owners === 1;
|
|
457
|
-
});
|
|
458
|
-
}
|
|
459
471
|
function isKnownPathReference(ref, knownPaths) {
|
|
460
472
|
const normalizedRef = normalizePath(ref);
|
|
461
473
|
return knownPaths.some((path) => {
|
|
@@ -499,6 +511,8 @@ function classifyHeading(raw) {
|
|
|
499
511
|
return "files-modified";
|
|
500
512
|
if (text.includes("file") && (text.includes("read") || text.includes("viewed")))
|
|
501
513
|
return "files-read";
|
|
514
|
+
if (text.includes("file") && (text.includes("delet") || text.includes("remov")))
|
|
515
|
+
return "files-deleted";
|
|
502
516
|
if (text.includes("next step") || text === "next actions")
|
|
503
517
|
return "next-steps";
|
|
504
518
|
if (text.includes("critical context") || text === "important context")
|
|
@@ -569,12 +583,25 @@ function findSection(summary, kind) {
|
|
|
569
583
|
}
|
|
570
584
|
|
|
571
585
|
// src/utils/extraction.ts
|
|
586
|
+
function extractText(content) {
|
|
587
|
+
if (typeof content === "string")
|
|
588
|
+
return content;
|
|
589
|
+
if (!Array.isArray(content))
|
|
590
|
+
return "";
|
|
591
|
+
return content.map((b) => {
|
|
592
|
+
if (typeof b === "string")
|
|
593
|
+
return b;
|
|
594
|
+
if (isTextBlock(b))
|
|
595
|
+
return b.text;
|
|
596
|
+
return "";
|
|
597
|
+
}).join("");
|
|
598
|
+
}
|
|
572
599
|
var CONSTRAINT_PATTERNS = [
|
|
573
600
|
{ re: /\b(?:must|need|require|has to|important)\b.*\b(?:be|use|have|include|support)\b/i, cat: "requirement", conf: TUNING.CONFIDENCE_HIGH },
|
|
574
601
|
{ re: /\b(?:don't|never|avoid|shouldn't|must not|do not|no\s+(?:need|want))\b/i, cat: "prohibition", conf: TUNING.CONFIDENCE_MEDIUM },
|
|
575
602
|
{ re: /\b(?:prefer|like|want|would rather|should)\b.*\b(?:use|be|have|with)\b/i, cat: "preference", conf: TUNING.CONFIDENCE_LOW },
|
|
576
|
-
{ re: /(?<![A-Za-z0-9_])(?:
|
|
577
|
-
{ re: /(?<![A-Za-z0-9_])(?:
|
|
603
|
+
{ 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 },
|
|
604
|
+
{ 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 },
|
|
578
605
|
{ re: /(?<![A-Za-z0-9_])(?:tercih|isterim|olsun|kullanal\u0131m|yapal\u0131m|istiyorum)(?![A-Za-z0-9_])/iu, cat: "preference", conf: TUNING.CONFIDENCE_LOW }
|
|
579
606
|
];
|
|
580
607
|
function isDiagnosticConstraintText(text) {
|
|
@@ -641,7 +668,6 @@ function readJsonlTail(target, limit, maxBytes = 512 * 1024) {
|
|
|
641
668
|
}
|
|
642
669
|
return values.slice(-limit);
|
|
643
670
|
}
|
|
644
|
-
var scheduledTailTrims = new Set;
|
|
645
671
|
|
|
646
672
|
// src/infra/clock.ts
|
|
647
673
|
var systemClock = {
|
|
@@ -655,14 +681,23 @@ import crypto from "crypto";
|
|
|
655
681
|
var SECRET_PATTERNS = [
|
|
656
682
|
{ kind: "private-key", regex: /-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g },
|
|
657
683
|
{ kind: "aws-access-key", regex: /\bAKIA[0-9A-Z]{16}\b/g },
|
|
658
|
-
{ kind: "
|
|
684
|
+
{ kind: "google-api-key", regex: /\bAIza[0-9A-Za-z_-]{30,}\b/g },
|
|
685
|
+
{ kind: "stripe-key", regex: /\b[rs]k_(?:live|test)_[0-9A-Za-z]{16,}\b/g },
|
|
686
|
+
{ kind: "gitlab-token", regex: /\bglpat-[0-9A-Za-z_-]{20,}\b/g },
|
|
687
|
+
{ kind: "npm-token", regex: /\bnpm_[0-9A-Za-z]{30,}\b/g },
|
|
688
|
+
{ kind: "github-token", regex: /\bgh[pousr]_[A-Za-z0-9]{20,}\b/g },
|
|
659
689
|
{ kind: "api-key", regex: /\bsk-(?:ant-)?[A-Za-z0-9_-]{20,}\b/g },
|
|
660
|
-
{ kind: "slack-token", regex: /\bxox[baprs]-[A-Za-z0-9-]{
|
|
690
|
+
{ kind: "slack-token", regex: /\bxox[baprs]-[A-Za-z0-9-]{10,}\b/g },
|
|
661
691
|
{ kind: "jwt", regex: /\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/g },
|
|
662
|
-
{ kind: "bearer-token", regex: /\bBearer\s+[A-Za-z0-9._~+\/-]{
|
|
692
|
+
{ kind: "bearer-token", regex: /\bBearer\s+[A-Za-z0-9._~+\/-]{12,}=*/gi, replacement: () => "Bearer [REDACTED:bearer-token]" },
|
|
693
|
+
{
|
|
694
|
+
kind: "connection-password",
|
|
695
|
+
regex: /\b([a-z][a-z0-9+.-]*:\/\/[^:\s/@]+:)[^@\s/]+(@)/gi,
|
|
696
|
+
replacement: (prefix, suffix) => prefix + "[REDACTED:password]" + suffix
|
|
697
|
+
},
|
|
663
698
|
{
|
|
664
699
|
kind: "credential",
|
|
665
|
-
regex: /\b(api[_-]?key|access[_-]?token|auth[_-]?token|token|password|passwd|secret)\s*([:=])\s*["']?([
|
|
700
|
+
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,
|
|
666
701
|
replacement: (name, separator) => name + separator + "[REDACTED:credential]"
|
|
667
702
|
}
|
|
668
703
|
];
|
|
@@ -690,6 +725,31 @@ function mergeFindings(target, findings) {
|
|
|
690
725
|
for (const finding of findings)
|
|
691
726
|
target.set(finding.kind, (target.get(finding.kind) ?? 0) + finding.count);
|
|
692
727
|
}
|
|
728
|
+
var SECRET_KEY_NAMES = new Set([
|
|
729
|
+
"api_key",
|
|
730
|
+
"apikey",
|
|
731
|
+
"access_token",
|
|
732
|
+
"auth_token",
|
|
733
|
+
"authorization",
|
|
734
|
+
"password",
|
|
735
|
+
"passwd",
|
|
736
|
+
"secret",
|
|
737
|
+
"secret_key",
|
|
738
|
+
"secret_access_key",
|
|
739
|
+
"client_secret",
|
|
740
|
+
"private_key",
|
|
741
|
+
"database_url",
|
|
742
|
+
"connection_string"
|
|
743
|
+
]);
|
|
744
|
+
function normalizeObjectKey(key) {
|
|
745
|
+
return key.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
|
|
746
|
+
}
|
|
747
|
+
function isSecretBearingKey(key) {
|
|
748
|
+
const normalized = normalizeObjectKey(key);
|
|
749
|
+
if (SECRET_KEY_NAMES.has(normalized))
|
|
750
|
+
return true;
|
|
751
|
+
return /(?:^|_)(?:api_key|access_token|auth_token|password|passwd|secret_access_key|client_secret|private_key)(?:_|$)/.test(normalized);
|
|
752
|
+
}
|
|
693
753
|
|
|
694
754
|
class SecretScrubber {
|
|
695
755
|
secretsEnabled;
|
|
@@ -719,6 +779,10 @@ class SecretScrubber {
|
|
|
719
779
|
scrubValue(input) {
|
|
720
780
|
const findings = new Map;
|
|
721
781
|
const seen = new WeakMap;
|
|
782
|
+
const recordCredential = () => {
|
|
783
|
+
findings.set("credential", (findings.get("credential") ?? 0) + 1);
|
|
784
|
+
this.total++;
|
|
785
|
+
};
|
|
722
786
|
const visit = (value2) => {
|
|
723
787
|
if (typeof value2 === "string") {
|
|
724
788
|
const result = this.scrubText(value2);
|
|
@@ -739,8 +803,14 @@ class SecretScrubber {
|
|
|
739
803
|
}
|
|
740
804
|
const output = {};
|
|
741
805
|
seen.set(value2, output);
|
|
742
|
-
for (const [key, item] of Object.entries(value2))
|
|
743
|
-
|
|
806
|
+
for (const [key, item] of Object.entries(value2)) {
|
|
807
|
+
if (this.secretsEnabled && isSecretBearingKey(key) && typeof item === "string" && item.length > 0) {
|
|
808
|
+
output[key] = "[REDACTED:credential]";
|
|
809
|
+
recordCredential();
|
|
810
|
+
} else {
|
|
811
|
+
output[key] = visit(item);
|
|
812
|
+
}
|
|
813
|
+
}
|
|
744
814
|
return output;
|
|
745
815
|
};
|
|
746
816
|
const value = visit(input);
|
|
@@ -1028,6 +1098,52 @@ function readMetricsLog(limit = 100) {
|
|
|
1028
1098
|
}
|
|
1029
1099
|
|
|
1030
1100
|
// src/phases/verify.ts
|
|
1101
|
+
var HIGH_RISK_OUTCOME_RE = /(?:\ball\s+tests?\s+(?:pass|passed|passing)\b|\btests?\s+(?:pass|passed|passing)\b|\b(?:build|deployment|migration)\s+(?:completed|succeeded|passed|successful)\b|\b(?:deployed|published|released)\b|\b(?:bug|issue|error)\s+(?:fixed|resolved)\b|\bno\s+(?:errors?|failures?)\b|\bcompleted successfully\b|\btestler?\s+(?:ge\u00E7ti|ba\u015Far\u0131l\u0131)\b|\bba\u015Far\u0131yla\s+(?:tamamland\u0131|da\u011F\u0131t\u0131ld\u0131|yay\u0131nland\u0131)\b|\b(?:deploy edildi|yay\u0131nland\u0131|hata yok)\b)/iu;
|
|
1102
|
+
var NEGATED_OUTCOME_RE = /\b(?:not|never|pending|failed|failing|unresolved|hen\u00FCz|de\u011Fil|ba\u015Far\u0131s\u0131z)\b/iu;
|
|
1103
|
+
function outcomeClaims(summary) {
|
|
1104
|
+
return Array.from(new Set(summary.split(/\r?\n/).map((line) => line.replace(/^\s*(?:[-*+]|\d+[.)])\s+/, "").replace(/^\[[ x]\]\s+/i, "").trim()).filter((line) => line.length > 0 && !line.startsWith("#")).filter((line) => HIGH_RISK_OUTCOME_RE.test(line)).filter((line) => /\bno\s+(?:errors?|failures?)\b/i.test(line) || !NEGATED_OUTCOME_RE.test(line)))).slice(0, 12);
|
|
1105
|
+
}
|
|
1106
|
+
function classifyOutcomeClaim(claim) {
|
|
1107
|
+
const lower = claim.toLowerCase();
|
|
1108
|
+
if (/\btests?\b|\btestler?\b/.test(lower))
|
|
1109
|
+
return "test";
|
|
1110
|
+
if (/\bbuild\b|\bcompil(?:e|ed|ation)\b|\btypecheck\b/.test(lower))
|
|
1111
|
+
return "build";
|
|
1112
|
+
if (/\bdeploy(?:ed|ment)?\b|\bpublish(?:ed)?\b|\breleas(?:e|ed)\b/.test(lower))
|
|
1113
|
+
return "release";
|
|
1114
|
+
if (/\bbug\b|\bissue\b|\berror\b|\bfail(?:ed|ure)?\b|\bhata\b/.test(lower))
|
|
1115
|
+
return "error";
|
|
1116
|
+
if (/\bfile\b|\bdosya\b/.test(lower))
|
|
1117
|
+
return "file";
|
|
1118
|
+
return "generic";
|
|
1119
|
+
}
|
|
1120
|
+
function successfulToolSupportsClaim(claim, messages, extraction) {
|
|
1121
|
+
const shape = semanticShape(claim);
|
|
1122
|
+
const category = classifyOutcomeClaim(claim);
|
|
1123
|
+
if (category === "error" && extraction.errors.some((error) => error.resolved && hasSemanticEvidence(claim, error.message)))
|
|
1124
|
+
return true;
|
|
1125
|
+
if (category === "file" && extraction.modifiedFiles.some((file) => claim.toLowerCase().includes(file.path.toLowerCase())))
|
|
1126
|
+
return true;
|
|
1127
|
+
for (const message of messages) {
|
|
1128
|
+
if (message.role !== "toolResult" || message.isError)
|
|
1129
|
+
continue;
|
|
1130
|
+
const bounded = extractText(message.content).slice(0, 8000);
|
|
1131
|
+
if (!bounded.trim() || LIKELY_ERROR_RE.test(bounded))
|
|
1132
|
+
continue;
|
|
1133
|
+
if (hasSemanticEvidence(claim, bounded))
|
|
1134
|
+
return true;
|
|
1135
|
+
const lower = bounded.toLowerCase();
|
|
1136
|
+
if (category === "test" && /\b\d+\s+(?:tests?\s+)?pass(?:ed)?\b/.test(lower) && !/\b(?:fail(?:ed|ures?)?|errors?)\s*[:=]?\s*[1-9]\d*\b/.test(lower))
|
|
1137
|
+
return true;
|
|
1138
|
+
if (category === "build" && /\b(?:build|compile|typecheck)\b/.test(lower) && /\b(?:succeeded|successful|passed|exit(?:ed)?\s+(?:code\s+)?0)\b/.test(lower))
|
|
1139
|
+
return true;
|
|
1140
|
+
if (category === "release" && /\b(?:publish|release|deploy)\b/.test(lower) && /\b(?:succeeded|successful|completed|published|deployed|released)\b/.test(lower))
|
|
1141
|
+
return true;
|
|
1142
|
+
if (category === "error" && shape.concepts.length > 0 && /\b(?:fixed|resolved|passed|succeeded|successful)\b/.test(lower) && hasSemanticEvidence(claim, bounded))
|
|
1143
|
+
return true;
|
|
1144
|
+
}
|
|
1145
|
+
return false;
|
|
1146
|
+
}
|
|
1031
1147
|
var NEGATION_MARKERS = new Set([
|
|
1032
1148
|
"no",
|
|
1033
1149
|
"not",
|
|
@@ -1186,7 +1302,7 @@ function hasSemanticContradiction(source, target) {
|
|
|
1186
1302
|
return conditional && !negative && !tokens.some((token) => CONDITION_MARKERS.has(token));
|
|
1187
1303
|
});
|
|
1188
1304
|
}
|
|
1189
|
-
function verifySummary(summary, extraction, continuity = null) {
|
|
1305
|
+
function verifySummary(summary, extraction, continuity = null, evidence = {}) {
|
|
1190
1306
|
const parsed = parseSummary(summary);
|
|
1191
1307
|
const gaps = [];
|
|
1192
1308
|
const lower = summary.toLowerCase().replace(/\\/g, "/");
|
|
@@ -1206,9 +1322,18 @@ function verifySummary(summary, extraction, continuity = null) {
|
|
|
1206
1322
|
...extraction.errors.filter((error) => !error.resolved).map((error) => ({ message: error.message })),
|
|
1207
1323
|
...(continuity?.unresolvedErrors ?? []).map((error) => ({ message: error.message }))
|
|
1208
1324
|
], (item) => item.message);
|
|
1325
|
+
const resolvedEvidence = uniqueByText([
|
|
1326
|
+
...extraction.errors.filter((error) => error.resolved).map((error) => ({ message: error.message })),
|
|
1327
|
+
...(continuity?.resolvedErrors ?? []).map((error) => ({ message: error.message }))
|
|
1328
|
+
], (item) => item.message).slice(-5);
|
|
1329
|
+
const steeringConstraints = [
|
|
1330
|
+
evidence.steering?.focus ? { text: "Preserve detail about: " + evidence.steering.focus } : null,
|
|
1331
|
+
evidence.steering?.note ? { text: evidence.steering.note } : null
|
|
1332
|
+
].filter((item) => Boolean(item?.text.trim()));
|
|
1209
1333
|
const constraintEvidence = uniqueByText([
|
|
1210
1334
|
...extraction.constraints.filter((item) => item.confidence >= 0.8).map((item) => ({ text: item.text })),
|
|
1211
|
-
...(continuity?.constraints ?? []).filter((item) => item.confidence >= 0.8).map((item) => ({ text: item.text }))
|
|
1335
|
+
...(continuity?.constraints ?? []).filter((item) => item.confidence >= 0.8).map((item) => ({ text: item.text })),
|
|
1336
|
+
...steeringConstraints
|
|
1212
1337
|
], (item) => item.text).filter((item) => !isDiagnosticConstraintText(item.text));
|
|
1213
1338
|
const decisionEvidence = uniqueByText([
|
|
1214
1339
|
...extraction.decisions.filter((item) => item.type === "explicit").map((item) => ({ summary: item.summary })),
|
|
@@ -1226,14 +1351,29 @@ function verifySummary(summary, extraction, continuity = null) {
|
|
|
1226
1351
|
score -= req.penalty;
|
|
1227
1352
|
}
|
|
1228
1353
|
}
|
|
1354
|
+
const listedPaths = (kind) => new Set((findSection(parsed, kind)?.body ?? "").split(`
|
|
1355
|
+
`).map((line) => line.replace(/^\s*(?:[-*+]|\d+[.)])\s+/, "").replace(/^\[[ x]\]\s+/i, "").trim()).map((line) => line.startsWith("`") && line.endsWith("`") ? line.slice(1, -1) : line).filter((line) => line.length > 0 && !/^none(?: recorded)?[.!]?$/i.test(line)).map(normalizePath));
|
|
1229
1356
|
const modifiedPaths = extraction.modifiedFiles.map((file) => file.path);
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1357
|
+
const modifiedListed = listedPaths("files-modified");
|
|
1358
|
+
const readListed = listedPaths("files-read");
|
|
1359
|
+
const deletedListed = listedPaths("files-deleted");
|
|
1360
|
+
for (const file of modifiedPaths) {
|
|
1361
|
+
if (!modifiedListed.has(normalizePath(file)))
|
|
1362
|
+
gaps.push({ kind: "missing-file", path: file });
|
|
1363
|
+
}
|
|
1364
|
+
for (const file of extraction.readFiles) {
|
|
1365
|
+
if (!readListed.has(normalizePath(file)))
|
|
1366
|
+
gaps.push({ kind: "missing-read-file", path: file });
|
|
1367
|
+
}
|
|
1368
|
+
const deletedEvidence = Array.from(new Set([
|
|
1369
|
+
...extraction.deletedFiles,
|
|
1370
|
+
...continuity?.deletedFiles ?? []
|
|
1371
|
+
]));
|
|
1372
|
+
for (const file of deletedEvidence) {
|
|
1373
|
+
if (!deletedListed.has(normalizePath(file)))
|
|
1374
|
+
gaps.push({ kind: "missing-deleted-file", path: file });
|
|
1236
1375
|
}
|
|
1376
|
+
score -= gaps.filter((gap) => gap.kind === "missing-file" || gap.kind === "missing-read-file" || gap.kind === "missing-deleted-file").length * 5;
|
|
1237
1377
|
for (const error of unresolvedEvidence) {
|
|
1238
1378
|
const snippet = summaryEvidenceLine(error.message, TRUNC.ERROR_SNIPPET).toLowerCase();
|
|
1239
1379
|
if (snippet.length > 5 && !normalizedSummary.includes(snippet)) {
|
|
@@ -1241,6 +1381,13 @@ function verifySummary(summary, extraction, continuity = null) {
|
|
|
1241
1381
|
score -= 5;
|
|
1242
1382
|
}
|
|
1243
1383
|
}
|
|
1384
|
+
for (const error of resolvedEvidence) {
|
|
1385
|
+
const snippet = summaryEvidenceLine(error.message, TRUNC.ERROR_SNIPPET).toLowerCase();
|
|
1386
|
+
if (snippet.length > 5 && !normalizedSummary.includes(snippet)) {
|
|
1387
|
+
gaps.push({ kind: "missing-error", message: error.message, resolved: true });
|
|
1388
|
+
score -= 2;
|
|
1389
|
+
}
|
|
1390
|
+
}
|
|
1244
1391
|
const constraintTarget = [
|
|
1245
1392
|
findSection(parsed, "constraints")?.body ?? "",
|
|
1246
1393
|
findSection(parsed, "critical-context")?.body ?? ""
|
|
@@ -1331,6 +1478,14 @@ function verifySummary(summary, extraction, continuity = null) {
|
|
|
1331
1478
|
gaps.push({ kind: "missing-open-loops", unresolvedCount });
|
|
1332
1479
|
score -= 5;
|
|
1333
1480
|
}
|
|
1481
|
+
if (evidence.sourceMessages) {
|
|
1482
|
+
for (const claim of outcomeClaims(summary)) {
|
|
1483
|
+
if (!successfulToolSupportsClaim(claim, evidence.sourceMessages, extraction)) {
|
|
1484
|
+
gaps.push({ kind: "unsupported-claim", claim });
|
|
1485
|
+
score -= 20;
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1488
|
+
}
|
|
1334
1489
|
const finalScore = Math.max(0, score);
|
|
1335
1490
|
return { ok: gaps.length === 0 && finalScore >= 85, gaps, score: finalScore };
|
|
1336
1491
|
}
|