pi-smart-compact 8.0.6 → 8.0.8
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 +513 -0
- package/CHANGELOG.md +25 -0
- package/README.md +70 -51
- package/SECURITY.md +8 -3
- package/dist/app/mode-policy.d.ts +0 -1
- package/dist/app/mode-policy.d.ts.map +1 -1
- package/dist/app/preflight.d.ts +16 -2
- package/dist/app/preflight.d.ts.map +1 -1
- package/dist/app/steps/extract.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/window.d.ts +3 -1
- package/dist/app/steps/window.d.ts.map +1 -1
- package/dist/constants.d.ts +18 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/domain/telemetry.d.ts +3 -0
- package/dist/domain/telemetry.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +762 -549
- package/dist/infra/context-graph.d.ts.map +1 -1
- package/dist/infra/fs.d.ts +1 -1
- package/dist/infra/fs.d.ts.map +1 -1
- package/dist/infra/git.d.ts.map +1 -1
- package/dist/infra/session-identity.d.ts +4 -0
- package/dist/infra/session-identity.d.ts.map +1 -1
- package/dist/phases/verify.d.ts.map +1 -1
- package/dist/provider-eval.js +113 -22
- package/dist/provider-scenario-eval.js +153 -111
- package/dist/telemetry-report.js +138 -41
- package/dist/ui/dashboard-insights.d.ts.map +1 -1
- package/dist/ui/overlays.d.ts.map +1 -1
- package/dist/utils/cache.d.ts.map +1 -1
- package/dist/utils/extraction.d.ts +5 -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 +1 -1
- package/dist/utils/fingerprint.d.ts.map +1 -1
- package/dist/utils/pruning.d.ts.map +1 -1
- package/dist/utils/state.d.ts +4 -3
- package/dist/utils/state.d.ts.map +1 -1
- package/dist/utils/tokens.d.ts.map +1 -1
- package/docs/MIGRATING_TO_V8.md +30 -13
- package/docs/RELEASE.md +13 -10
- package/package.json +2 -1
- package/dist/app/explore-wrap.d.ts +0 -8
- package/dist/app/explore-wrap.d.ts.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context-graph.d.ts","sourceRoot":"","sources":["../../src/infra/context-graph.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,eAAe,EAAsB,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"context-graph.d.ts","sourceRoot":"","sources":["../../src/infra/context-graph.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,eAAe,EAAsB,MAAM,aAAa,CAAC;AAavE,MAAM,MAAM,iBAAiB,GACzB,MAAM,GAAG,UAAU,GAAG,YAAY,GAAG,OAAO,GAAG,MAAM,GACrD,aAAa,GAAG,UAAU,GAAG,OAAO,GAAG,MAAM,GAC7C,YAAY,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,CAAC;AAEvD,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACpC;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,KAAK,CAAC,EAAE,SAAS,iBAAiB,EAAE,CAAC;CACtC;AAED,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,iBAAiB,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,YAAY,GAAG,QAAQ,CAAC;IAChC,WAAW,EAAE,OAAO,CAAC;IACrB,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,OAAO,CAAC,iBAAiB,EAAE,UAAU,GAAG,YAAY,GAAG,YAAY,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,CAAC,CAAC;IACjH,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,iBAAiB;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAsVD,qFAAqF;AACrF,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe,GAAG,OAAO,CAmFvF;AAqBD,uFAAuF;AACvF,wBAAgB,4BAA4B,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe,GAAG,OAAO,CAa/F;AAED,gEAAgE;AAChE,wBAAgB,2BAA2B,IAAI,IAAI,CAGlD;AAED,0FAA0F;AAC1F,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,kBAAkB,CAAC,MAAM,CAAC,EAChC,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,UAAU,GAAG,YAAY,GAChC,MAAM,CAsBR;AAED,wDAAwD;AACxD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,iBAAiB,EAAE,MAAM,EAAE,IAAI,CAAC,kBAAkB,EAAE,IAAI,CAAC,GAAG,kBAAkB,CAkDtH;AA4ED,kGAAkG;AAClG,wBAAgB,aAAa,CAAC,KAAK,EAAE,iBAAiB,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,oBAAyB,GAAG,mBAAmB,EAAE,CAuEhI;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,mBAAmB,EAAE,EAAE,QAAQ,SAAQ,GAAG,MAAM,CAyB5F;AAED,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,iBAAiB,CAsBzE"}
|
package/dist/infra/fs.d.ts
CHANGED
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
* Lock acquisition is fail-closed: callers never continue an append/trim
|
|
28
28
|
* without ownership. Locks older than 5s are reclaimed with an atomic rename.
|
|
29
29
|
*/
|
|
30
|
-
/** Ensure a directory exists
|
|
30
|
+
/** Ensure a private directory exists and normalize an existing target. */
|
|
31
31
|
export declare function ensureDir(dir: string): void;
|
|
32
32
|
export declare function ensureDirAsync(dir: string): Promise<void>;
|
|
33
33
|
/**
|
package/dist/infra/fs.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fs.d.ts","sourceRoot":"","sources":["../../src/infra/fs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAYH,
|
|
1
|
+
{"version":3,"file":"fs.d.ts","sourceRoot":"","sources":["../../src/infra/fs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAYH,0EAA0E;AAC1E,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAG3C;AAED,wBAAsB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAG/D;AAMD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAYnF;AAED,wBAAsB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAW9F;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,IAAI,CAgD1D;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAUnE;AAED,iFAAiF;AACjF,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,SAAa,GAAG,CAAC,EAAE,CAoB1F;AAED,oEAAoE;AACpE,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAgBzE;AAID,iFAAiF;AACjF,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAQ3E;AAED,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,GAAG,CAAC,GAAG,IAAI,CASxD;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,UAAQ,GAAG,IAAI,CAElF"}
|
package/dist/infra/git.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../../src/infra/git.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAOH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../../src/infra/git.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAOH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAetD;AAED,mDAAmD;AACnD,wBAAgB,0BAA0B,IAAI,IAAI,CAEjD"}
|
|
@@ -32,6 +32,10 @@ import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
|
32
32
|
* object that happens to expose a `sessionManager` field.
|
|
33
33
|
*/
|
|
34
34
|
export type SessionIdentityContext = Pick<ExtensionContext, "sessionManager">;
|
|
35
|
+
/** Extract the complete ordered ancestry exposed by a session branch. */
|
|
36
|
+
export declare function branchEntryIds(branch: Iterable<{
|
|
37
|
+
id?: unknown;
|
|
38
|
+
}>): string[];
|
|
35
39
|
/**
|
|
36
40
|
* Resolve the current pi session id, or mint a per-call sentinel that can
|
|
37
41
|
* never compare equal to another caller's sentinel.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-identity.d.ts","sourceRoot":"","sources":["../../src/infra/session-identity.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAGH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AAExE;;;;;;;GAOG;AACH,MAAM,MAAM,sBAAsB,GAAG,IAAI,CAAC,gBAAgB,EAAE,gBAAgB,CAAC,CAAC;AAI9E;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,sBAAsB,GAAG,MAAM,CAQpE;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAEzD"}
|
|
1
|
+
{"version":3,"file":"session-identity.d.ts","sourceRoot":"","sources":["../../src/infra/session-identity.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAGH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AAExE;;;;;;;GAOG;AACH,MAAM,MAAM,sBAAsB,GAAG,IAAI,CAAC,gBAAgB,EAAE,gBAAgB,CAAC,CAAC;AAI9E,yEAAyE;AACzE,wBAAgB,cAAc,CAAC,MAAM,EAAE,QAAQ,CAAC;IAAE,EAAE,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,GAAG,MAAM,EAAE,CAE3E;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,sBAAsB,GAAG,MAAM,CAQpE;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAEzD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../../src/phases/verify.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACzE,OAAO,KAAK,EAAE,eAAe,EAAE,oBAAoB,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAW9G,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAEjE,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,eAAe,GAAG,MAAM,CAYlE;AAED,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,kBAAkB,GAAG,MAAM,GAAG,IAAI,CAOpF;AAED,wFAAwF;AACxF,qBAAa,qBAAsB,SAAQ,KAAK;IAC9C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAC,MAAM,CAAC,EAAE,CAAC;IAC7C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAE1B,YAAY,MAAM,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,EAO3D;CACF;
|
|
1
|
+
{"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../../src/phases/verify.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACzE,OAAO,KAAK,EAAE,eAAe,EAAE,oBAAoB,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAW9G,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAEjE,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,eAAe,GAAG,MAAM,CAYlE;AAED,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,kBAAkB,GAAG,MAAM,GAAG,IAAI,CAOpF;AAED,wFAAwF;AACxF,qBAAa,qBAAsB,SAAQ,KAAK;IAC9C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAC,MAAM,CAAC,EAAE,CAAC;IAC7C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAE1B,YAAY,MAAM,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,EAO3D;CACF;AAqID,wBAAgB,4BAA4B,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAI1E;AAED,qGAAqG;AACrG,wBAAgB,8BAA8B,CAC5C,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,kBAAkB,EAC1B,UAAU,EAAE,oBAAoB,EAChC,UAAU,GAAE,eAAe,GAAG,IAAW,EACzC,SAAS,SAAI,GACZ;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,kBAAkB,CAAC;IAAC,OAAO,EAAE,eAAe,EAAE,CAAA;CAAE,CAgB7E;AAED,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,oBAAoB,EAChC,UAAU,GAAE,eAAe,GAAG,IAAW,GACxC,kBAAkB,CAgKpB;AAED,iHAAiH;AACjH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,eAAe,EAAE,EACvB,UAAU,EAAE,oBAAoB,EAChC,UAAU,GAAE,eAAe,GAAG,IAAW,GACxC,MAAM,CAwFR;AAED,wBAAsB,YAAY,CAChC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,eAAe,EAAE,EACxC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,eAAe,CAAA;CAAE,EAAE,MAAM,CAAC,EAAE,WAAW,EAC5F,QAAQ,CAAC,EAAE,oBAAoB,GAC9B,OAAO,CAAC,MAAM,CAAC,CAkBjB"}
|
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 = "8.0.
|
|
159
|
+
var VERSION = "8.0.8";
|
|
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: {
|
|
@@ -362,6 +362,27 @@ var EXPLORER_SYSTEM_PROMPT = `You are a conversation analyst. You have determini
|
|
|
362
362
|
` + `After exploration, output ONLY a JSON object (no markdown):
|
|
363
363
|
` + '{"boundaries":[{"afterIndex":N,"topic":"...","priority":"critical|high|normal|low","confidence":0.0-1.0}],"mainGoal":"...","sessionType":"implementation|review|debugging|discussion","enrichedConstraints":[...],"crossReferences":[...],"statusAssessment":{"done":[...],"inProgress":[...],"blocked":[...]},"criticalContext":[...],"keyDecisions":[...]}';
|
|
364
364
|
|
|
365
|
+
// src/utils/lru.ts
|
|
366
|
+
function lruGet(m, key) {
|
|
367
|
+
if (!m.has(key))
|
|
368
|
+
return;
|
|
369
|
+
const v = m.get(key);
|
|
370
|
+
m.delete(key);
|
|
371
|
+
m.set(key, v);
|
|
372
|
+
return v;
|
|
373
|
+
}
|
|
374
|
+
function lruSet(m, key, value, max) {
|
|
375
|
+
if (m.has(key))
|
|
376
|
+
m.delete(key);
|
|
377
|
+
m.set(key, value);
|
|
378
|
+
while (m.size > max) {
|
|
379
|
+
const oldest = m.keys().next().value;
|
|
380
|
+
if (oldest === undefined)
|
|
381
|
+
break;
|
|
382
|
+
m.delete(oldest);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
365
386
|
// src/utils/tokens.ts
|
|
366
387
|
class TokenCalibrationStore {
|
|
367
388
|
maxEntries;
|
|
@@ -376,35 +397,19 @@ class TokenCalibrationStore {
|
|
|
376
397
|
if (!provider)
|
|
377
398
|
return 1;
|
|
378
399
|
const exactKey = calibrationKey(provider, model);
|
|
379
|
-
const exact = this.factors
|
|
380
|
-
if (exact !== undefined)
|
|
381
|
-
this.factors.delete(exactKey);
|
|
382
|
-
this.factors.set(exactKey, exact);
|
|
400
|
+
const exact = lruGet(this.factors, exactKey);
|
|
401
|
+
if (exact !== undefined)
|
|
383
402
|
return exact;
|
|
384
|
-
|
|
385
|
-
const providerKey = calibrationKey(provider);
|
|
386
|
-
const fallback = this.factors.get(providerKey);
|
|
387
|
-
if (fallback !== undefined) {
|
|
388
|
-
this.factors.delete(providerKey);
|
|
389
|
-
this.factors.set(providerKey, fallback);
|
|
390
|
-
}
|
|
391
|
-
return fallback ?? 1;
|
|
403
|
+
return lruGet(this.factors, calibrationKey(provider)) ?? 1;
|
|
392
404
|
}
|
|
393
405
|
calibrate(estimated, actual, provider, model) {
|
|
394
406
|
if (actual <= 0 || estimated <= 0 || !provider)
|
|
395
407
|
return;
|
|
396
408
|
const key = calibrationKey(provider, model);
|
|
397
|
-
const prev = this.factors
|
|
409
|
+
const prev = lruGet(this.factors, key) ?? 1;
|
|
398
410
|
const target = prev * actual / estimated;
|
|
399
411
|
const clamped = Math.max(TUNING.CALIBRATION_CLAMP_MIN, Math.min(TUNING.CALIBRATION_CLAMP_MAX, target));
|
|
400
|
-
this.factors.
|
|
401
|
-
this.factors.set(key, prev * TUNING.EMA_PREV + clamped * TUNING.EMA_SAMPLE);
|
|
402
|
-
while (this.factors.size > Math.max(1, this.maxEntries)) {
|
|
403
|
-
const oldest = this.factors.keys().next().value;
|
|
404
|
-
if (oldest === undefined)
|
|
405
|
-
break;
|
|
406
|
-
this.factors.delete(oldest);
|
|
407
|
-
}
|
|
412
|
+
lruSet(this.factors, key, prev * TUNING.EMA_PREV + clamped * TUNING.EMA_SAMPLE, Math.max(1, this.maxEntries));
|
|
408
413
|
}
|
|
409
414
|
size() {
|
|
410
415
|
return this.factors.size;
|
|
@@ -492,6 +497,92 @@ function extractFileRefs(summary) {
|
|
|
492
497
|
return candidates.filter(isLikelyFileRef);
|
|
493
498
|
}
|
|
494
499
|
|
|
500
|
+
// src/domain/summary-schema.ts
|
|
501
|
+
function classifyHeading(raw) {
|
|
502
|
+
const text = raw.replace(/^#+\s*/, "").replace(/[:\s]+$/, "").trim().toLowerCase();
|
|
503
|
+
if (!text)
|
|
504
|
+
return "unknown";
|
|
505
|
+
if (text === "goal" || text === "goals" || text === "objective" || text === "objectives")
|
|
506
|
+
return "goal";
|
|
507
|
+
if (text.startsWith("constraint") || text.includes("preference"))
|
|
508
|
+
return "constraints";
|
|
509
|
+
if (text === "progress" || text === "status")
|
|
510
|
+
return "progress";
|
|
511
|
+
if (text.includes("key decision") || text === "decisions")
|
|
512
|
+
return "decisions";
|
|
513
|
+
if (text.includes("file") && text.includes("modif"))
|
|
514
|
+
return "files-modified";
|
|
515
|
+
if (text.includes("file") && (text.includes("read") || text.includes("viewed")))
|
|
516
|
+
return "files-read";
|
|
517
|
+
if (text.includes("next step") || text === "next actions")
|
|
518
|
+
return "next-steps";
|
|
519
|
+
if (text.includes("critical context") || text === "important context")
|
|
520
|
+
return "critical-context";
|
|
521
|
+
if (text === "topics" || text.includes("topics covered"))
|
|
522
|
+
return "topics";
|
|
523
|
+
if (text.includes("open loop") || text.includes("unresolved"))
|
|
524
|
+
return "open-loops";
|
|
525
|
+
if (text.includes("changes since") || text === "changes")
|
|
526
|
+
return "changes";
|
|
527
|
+
if (text.includes("verification"))
|
|
528
|
+
return "verification-note";
|
|
529
|
+
return "unknown";
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
// src/domain/summary-parse.ts
|
|
533
|
+
var HEADING_RE = /^(#{1,3})\s+(.+?)\s*$/;
|
|
534
|
+
function summaryEvidenceLine(value, maxLength) {
|
|
535
|
+
return value.replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]/g, " ").replace(/\s+/g, " ").trim().replace(/^(?:(?:#{1,6}|[-*+]|>)\s+)+/, "").slice(0, maxLength).trim();
|
|
536
|
+
}
|
|
537
|
+
function mergeBodies(first, second) {
|
|
538
|
+
const seen = new Set;
|
|
539
|
+
return [first, second].filter(Boolean).flatMap((body) => body.split(`
|
|
540
|
+
`)).filter((line) => seen.has(line) ? false : (seen.add(line), true)).join(`
|
|
541
|
+
`).trim();
|
|
542
|
+
}
|
|
543
|
+
function parseSummary(markdown) {
|
|
544
|
+
const sections = [];
|
|
545
|
+
const lines = markdown.split(`
|
|
546
|
+
`);
|
|
547
|
+
let currentHeading = "";
|
|
548
|
+
let currentKind = "unknown";
|
|
549
|
+
let bodyLines = [];
|
|
550
|
+
let started = false;
|
|
551
|
+
const flush = () => {
|
|
552
|
+
if (!started)
|
|
553
|
+
return;
|
|
554
|
+
const body = bodyLines.join(`
|
|
555
|
+
`).trim();
|
|
556
|
+
const existing = currentKind === "unknown" ? undefined : sections.find((s) => s.kind === currentKind);
|
|
557
|
+
if (existing)
|
|
558
|
+
existing.body = mergeBodies(existing.body, body);
|
|
559
|
+
else
|
|
560
|
+
sections.push({ kind: currentKind, heading: currentHeading.trim(), body });
|
|
561
|
+
};
|
|
562
|
+
for (const line of lines) {
|
|
563
|
+
const m = line.match(HEADING_RE);
|
|
564
|
+
if (m) {
|
|
565
|
+
const kind = classifyHeading(m[2]);
|
|
566
|
+
if (m[1].length <= 2 || kind !== "unknown") {
|
|
567
|
+
flush();
|
|
568
|
+
currentHeading = "## " + m[2].trim();
|
|
569
|
+
currentKind = kind;
|
|
570
|
+
bodyLines = [];
|
|
571
|
+
started = true;
|
|
572
|
+
continue;
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
if (started)
|
|
576
|
+
bodyLines.push(line);
|
|
577
|
+
}
|
|
578
|
+
flush();
|
|
579
|
+
return { sections };
|
|
580
|
+
}
|
|
581
|
+
function findSection(summary, kind) {
|
|
582
|
+
const parsed = typeof summary === "string" ? parseSummary(summary) : summary;
|
|
583
|
+
return parsed.sections.find((s) => s.kind === kind);
|
|
584
|
+
}
|
|
585
|
+
|
|
495
586
|
// src/utils/extraction.ts
|
|
496
587
|
var CONSTRAINT_PATTERNS = [
|
|
497
588
|
{ re: /\b(?:must|need|require|has to|important)\b.*\b(?:be|use|have|include|support)\b/i, cat: "requirement", conf: TUNING.CONFIDENCE_HIGH },
|
|
@@ -138,7 +138,7 @@ function getLlmClient() {
|
|
|
138
138
|
}
|
|
139
139
|
|
|
140
140
|
// src/constants.ts
|
|
141
|
-
var VERSION = "8.0.
|
|
141
|
+
var VERSION = "8.0.8";
|
|
142
142
|
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
143
|
var PROFILES = {
|
|
144
144
|
light: {
|
|
@@ -347,6 +347,27 @@ var EXPLORER_SYSTEM_PROMPT = `You are a conversation analyst. You have determini
|
|
|
347
347
|
// src/utils/cache.ts
|
|
348
348
|
import fs2 from "fs";
|
|
349
349
|
|
|
350
|
+
// src/utils/lru.ts
|
|
351
|
+
function lruGet(m, key) {
|
|
352
|
+
if (!m.has(key))
|
|
353
|
+
return;
|
|
354
|
+
const v = m.get(key);
|
|
355
|
+
m.delete(key);
|
|
356
|
+
m.set(key, v);
|
|
357
|
+
return v;
|
|
358
|
+
}
|
|
359
|
+
function lruSet(m, key, value, max) {
|
|
360
|
+
if (m.has(key))
|
|
361
|
+
m.delete(key);
|
|
362
|
+
m.set(key, value);
|
|
363
|
+
while (m.size > max) {
|
|
364
|
+
const oldest = m.keys().next().value;
|
|
365
|
+
if (oldest === undefined)
|
|
366
|
+
break;
|
|
367
|
+
m.delete(oldest);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
350
371
|
// src/utils/tokens.ts
|
|
351
372
|
class TokenCalibrationStore {
|
|
352
373
|
maxEntries;
|
|
@@ -361,35 +382,19 @@ class TokenCalibrationStore {
|
|
|
361
382
|
if (!provider)
|
|
362
383
|
return 1;
|
|
363
384
|
const exactKey = calibrationKey(provider, model);
|
|
364
|
-
const exact = this.factors
|
|
365
|
-
if (exact !== undefined)
|
|
366
|
-
this.factors.delete(exactKey);
|
|
367
|
-
this.factors.set(exactKey, exact);
|
|
385
|
+
const exact = lruGet(this.factors, exactKey);
|
|
386
|
+
if (exact !== undefined)
|
|
368
387
|
return exact;
|
|
369
|
-
|
|
370
|
-
const providerKey = calibrationKey(provider);
|
|
371
|
-
const fallback = this.factors.get(providerKey);
|
|
372
|
-
if (fallback !== undefined) {
|
|
373
|
-
this.factors.delete(providerKey);
|
|
374
|
-
this.factors.set(providerKey, fallback);
|
|
375
|
-
}
|
|
376
|
-
return fallback ?? 1;
|
|
388
|
+
return lruGet(this.factors, calibrationKey(provider)) ?? 1;
|
|
377
389
|
}
|
|
378
390
|
calibrate(estimated, actual, provider, model) {
|
|
379
391
|
if (actual <= 0 || estimated <= 0 || !provider)
|
|
380
392
|
return;
|
|
381
393
|
const key = calibrationKey(provider, model);
|
|
382
|
-
const prev = this.factors
|
|
394
|
+
const prev = lruGet(this.factors, key) ?? 1;
|
|
383
395
|
const target = prev * actual / estimated;
|
|
384
396
|
const clamped = Math.max(TUNING.CALIBRATION_CLAMP_MIN, Math.min(TUNING.CALIBRATION_CLAMP_MAX, target));
|
|
385
|
-
this.factors.
|
|
386
|
-
this.factors.set(key, prev * TUNING.EMA_PREV + clamped * TUNING.EMA_SAMPLE);
|
|
387
|
-
while (this.factors.size > Math.max(1, this.maxEntries)) {
|
|
388
|
-
const oldest = this.factors.keys().next().value;
|
|
389
|
-
if (oldest === undefined)
|
|
390
|
-
break;
|
|
391
|
-
this.factors.delete(oldest);
|
|
392
|
-
}
|
|
397
|
+
lruSet(this.factors, key, prev * TUNING.EMA_PREV + clamped * TUNING.EMA_SAMPLE, Math.max(1, this.maxEntries));
|
|
393
398
|
}
|
|
394
399
|
size() {
|
|
395
400
|
return this.factors.size;
|
|
@@ -477,6 +482,92 @@ function extractFileRefs(summary) {
|
|
|
477
482
|
return candidates.filter(isLikelyFileRef);
|
|
478
483
|
}
|
|
479
484
|
|
|
485
|
+
// src/domain/summary-schema.ts
|
|
486
|
+
function classifyHeading(raw) {
|
|
487
|
+
const text = raw.replace(/^#+\s*/, "").replace(/[:\s]+$/, "").trim().toLowerCase();
|
|
488
|
+
if (!text)
|
|
489
|
+
return "unknown";
|
|
490
|
+
if (text === "goal" || text === "goals" || text === "objective" || text === "objectives")
|
|
491
|
+
return "goal";
|
|
492
|
+
if (text.startsWith("constraint") || text.includes("preference"))
|
|
493
|
+
return "constraints";
|
|
494
|
+
if (text === "progress" || text === "status")
|
|
495
|
+
return "progress";
|
|
496
|
+
if (text.includes("key decision") || text === "decisions")
|
|
497
|
+
return "decisions";
|
|
498
|
+
if (text.includes("file") && text.includes("modif"))
|
|
499
|
+
return "files-modified";
|
|
500
|
+
if (text.includes("file") && (text.includes("read") || text.includes("viewed")))
|
|
501
|
+
return "files-read";
|
|
502
|
+
if (text.includes("next step") || text === "next actions")
|
|
503
|
+
return "next-steps";
|
|
504
|
+
if (text.includes("critical context") || text === "important context")
|
|
505
|
+
return "critical-context";
|
|
506
|
+
if (text === "topics" || text.includes("topics covered"))
|
|
507
|
+
return "topics";
|
|
508
|
+
if (text.includes("open loop") || text.includes("unresolved"))
|
|
509
|
+
return "open-loops";
|
|
510
|
+
if (text.includes("changes since") || text === "changes")
|
|
511
|
+
return "changes";
|
|
512
|
+
if (text.includes("verification"))
|
|
513
|
+
return "verification-note";
|
|
514
|
+
return "unknown";
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
// src/domain/summary-parse.ts
|
|
518
|
+
var HEADING_RE = /^(#{1,3})\s+(.+?)\s*$/;
|
|
519
|
+
function summaryEvidenceLine(value, maxLength) {
|
|
520
|
+
return value.replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]/g, " ").replace(/\s+/g, " ").trim().replace(/^(?:(?:#{1,6}|[-*+]|>)\s+)+/, "").slice(0, maxLength).trim();
|
|
521
|
+
}
|
|
522
|
+
function mergeBodies(first, second) {
|
|
523
|
+
const seen = new Set;
|
|
524
|
+
return [first, second].filter(Boolean).flatMap((body) => body.split(`
|
|
525
|
+
`)).filter((line) => seen.has(line) ? false : (seen.add(line), true)).join(`
|
|
526
|
+
`).trim();
|
|
527
|
+
}
|
|
528
|
+
function parseSummary(markdown) {
|
|
529
|
+
const sections = [];
|
|
530
|
+
const lines = markdown.split(`
|
|
531
|
+
`);
|
|
532
|
+
let currentHeading = "";
|
|
533
|
+
let currentKind = "unknown";
|
|
534
|
+
let bodyLines = [];
|
|
535
|
+
let started = false;
|
|
536
|
+
const flush = () => {
|
|
537
|
+
if (!started)
|
|
538
|
+
return;
|
|
539
|
+
const body = bodyLines.join(`
|
|
540
|
+
`).trim();
|
|
541
|
+
const existing = currentKind === "unknown" ? undefined : sections.find((s) => s.kind === currentKind);
|
|
542
|
+
if (existing)
|
|
543
|
+
existing.body = mergeBodies(existing.body, body);
|
|
544
|
+
else
|
|
545
|
+
sections.push({ kind: currentKind, heading: currentHeading.trim(), body });
|
|
546
|
+
};
|
|
547
|
+
for (const line of lines) {
|
|
548
|
+
const m = line.match(HEADING_RE);
|
|
549
|
+
if (m) {
|
|
550
|
+
const kind = classifyHeading(m[2]);
|
|
551
|
+
if (m[1].length <= 2 || kind !== "unknown") {
|
|
552
|
+
flush();
|
|
553
|
+
currentHeading = "## " + m[2].trim();
|
|
554
|
+
currentKind = kind;
|
|
555
|
+
bodyLines = [];
|
|
556
|
+
started = true;
|
|
557
|
+
continue;
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
if (started)
|
|
561
|
+
bodyLines.push(line);
|
|
562
|
+
}
|
|
563
|
+
flush();
|
|
564
|
+
return { sections };
|
|
565
|
+
}
|
|
566
|
+
function findSection(summary, kind) {
|
|
567
|
+
const parsed = typeof summary === "string" ? parseSummary(summary) : summary;
|
|
568
|
+
return parsed.sections.find((s) => s.kind === kind);
|
|
569
|
+
}
|
|
570
|
+
|
|
480
571
|
// src/utils/extraction.ts
|
|
481
572
|
var CONSTRAINT_PATTERNS = [
|
|
482
573
|
{ re: /\b(?:must|need|require|has to|important)\b.*\b(?:be|use|have|include|support)\b/i, cat: "requirement", conf: TUNING.CONFIDENCE_HIGH },
|
|
@@ -936,92 +1027,6 @@ function readMetricsLog(limit = 100) {
|
|
|
936
1027
|
}
|
|
937
1028
|
}
|
|
938
1029
|
|
|
939
|
-
// src/domain/summary-schema.ts
|
|
940
|
-
function classifyHeading(raw) {
|
|
941
|
-
const text = raw.replace(/^#+\s*/, "").replace(/[:\s]+$/, "").trim().toLowerCase();
|
|
942
|
-
if (!text)
|
|
943
|
-
return "unknown";
|
|
944
|
-
if (text === "goal" || text === "goals" || text === "objective" || text === "objectives")
|
|
945
|
-
return "goal";
|
|
946
|
-
if (text.startsWith("constraint") || text.includes("preference"))
|
|
947
|
-
return "constraints";
|
|
948
|
-
if (text === "progress" || text === "status")
|
|
949
|
-
return "progress";
|
|
950
|
-
if (text.includes("key decision") || text === "decisions")
|
|
951
|
-
return "decisions";
|
|
952
|
-
if (text.includes("file") && text.includes("modif"))
|
|
953
|
-
return "files-modified";
|
|
954
|
-
if (text.includes("file") && (text.includes("read") || text.includes("viewed")))
|
|
955
|
-
return "files-read";
|
|
956
|
-
if (text.includes("next step") || text === "next actions")
|
|
957
|
-
return "next-steps";
|
|
958
|
-
if (text.includes("critical context") || text === "important context")
|
|
959
|
-
return "critical-context";
|
|
960
|
-
if (text === "topics" || text.includes("topics covered"))
|
|
961
|
-
return "topics";
|
|
962
|
-
if (text.includes("open loop") || text.includes("unresolved"))
|
|
963
|
-
return "open-loops";
|
|
964
|
-
if (text.includes("changes since") || text === "changes")
|
|
965
|
-
return "changes";
|
|
966
|
-
if (text.includes("verification"))
|
|
967
|
-
return "verification-note";
|
|
968
|
-
return "unknown";
|
|
969
|
-
}
|
|
970
|
-
|
|
971
|
-
// src/domain/summary-parse.ts
|
|
972
|
-
var HEADING_RE = /^(#{1,3})\s+(.+?)\s*$/;
|
|
973
|
-
function summaryEvidenceLine(value, maxLength) {
|
|
974
|
-
return value.replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]/g, " ").replace(/\s+/g, " ").trim().replace(/^(?:(?:#{1,6}|[-*+]|>)\s+)+/, "").slice(0, maxLength).trim();
|
|
975
|
-
}
|
|
976
|
-
function mergeBodies(first, second) {
|
|
977
|
-
const seen = new Set;
|
|
978
|
-
return [first, second].filter(Boolean).flatMap((body) => body.split(`
|
|
979
|
-
`)).filter((line) => seen.has(line) ? false : (seen.add(line), true)).join(`
|
|
980
|
-
`).trim();
|
|
981
|
-
}
|
|
982
|
-
function parseSummary(markdown) {
|
|
983
|
-
const sections = [];
|
|
984
|
-
const lines = markdown.split(`
|
|
985
|
-
`);
|
|
986
|
-
let currentHeading = "";
|
|
987
|
-
let currentKind = "unknown";
|
|
988
|
-
let bodyLines = [];
|
|
989
|
-
let started = false;
|
|
990
|
-
const flush = () => {
|
|
991
|
-
if (!started)
|
|
992
|
-
return;
|
|
993
|
-
const body = bodyLines.join(`
|
|
994
|
-
`).trim();
|
|
995
|
-
const existing = currentKind === "unknown" ? undefined : sections.find((s) => s.kind === currentKind);
|
|
996
|
-
if (existing)
|
|
997
|
-
existing.body = mergeBodies(existing.body, body);
|
|
998
|
-
else
|
|
999
|
-
sections.push({ kind: currentKind, heading: currentHeading.trim(), body });
|
|
1000
|
-
};
|
|
1001
|
-
for (const line of lines) {
|
|
1002
|
-
const m = line.match(HEADING_RE);
|
|
1003
|
-
if (m) {
|
|
1004
|
-
const kind = classifyHeading(m[2]);
|
|
1005
|
-
if (m[1].length <= 2 || kind !== "unknown") {
|
|
1006
|
-
flush();
|
|
1007
|
-
currentHeading = "## " + m[2].trim();
|
|
1008
|
-
currentKind = kind;
|
|
1009
|
-
bodyLines = [];
|
|
1010
|
-
started = true;
|
|
1011
|
-
continue;
|
|
1012
|
-
}
|
|
1013
|
-
}
|
|
1014
|
-
if (started)
|
|
1015
|
-
bodyLines.push(line);
|
|
1016
|
-
}
|
|
1017
|
-
flush();
|
|
1018
|
-
return { sections };
|
|
1019
|
-
}
|
|
1020
|
-
function findSection(summary, kind) {
|
|
1021
|
-
const parsed = typeof summary === "string" ? parseSummary(summary) : summary;
|
|
1022
|
-
return parsed.sections.find((s) => s.kind === kind);
|
|
1023
|
-
}
|
|
1024
|
-
|
|
1025
1030
|
// src/phases/verify.ts
|
|
1026
1031
|
var NEGATION_MARKERS = new Set([
|
|
1027
1032
|
"no",
|
|
@@ -1050,6 +1055,17 @@ var CONDITION_MARKERS = new Set([
|
|
|
1050
1055
|
"gerekli",
|
|
1051
1056
|
"gerektirir"
|
|
1052
1057
|
]);
|
|
1058
|
+
var POLARITY_INVERTING_GUARDS = new Set([
|
|
1059
|
+
"skip",
|
|
1060
|
+
"skipp",
|
|
1061
|
+
"forget",
|
|
1062
|
+
"forgett",
|
|
1063
|
+
"omit",
|
|
1064
|
+
"omitt",
|
|
1065
|
+
"neglect",
|
|
1066
|
+
"fail",
|
|
1067
|
+
"avoid"
|
|
1068
|
+
]);
|
|
1053
1069
|
var SEMANTIC_STOP = new Set([
|
|
1054
1070
|
"the",
|
|
1055
1071
|
"and",
|
|
@@ -1099,6 +1115,24 @@ function evidenceFragments(text) {
|
|
|
1099
1115
|
function hasNearbyMarker(tokens, anchor, markers) {
|
|
1100
1116
|
return tokens.some((token, index) => token === anchor && tokens.slice(Math.max(0, index - 2), index + 3).some((near) => markers.has(near)));
|
|
1101
1117
|
}
|
|
1118
|
+
function hasEffectiveTargetNegation(tokens, anchor) {
|
|
1119
|
+
return tokens.some((token, anchorIndex) => {
|
|
1120
|
+
if (token !== anchor)
|
|
1121
|
+
return false;
|
|
1122
|
+
const nearbyStart = Math.max(0, anchorIndex - 2);
|
|
1123
|
+
const nearbyNegations = tokens.slice(nearbyStart, anchorIndex + 3).map((near, offset) => NEGATION_MARKERS.has(near) ? nearbyStart + offset : -1).filter((index) => index >= 0);
|
|
1124
|
+
const governingStart = Math.max(0, anchorIndex - 3);
|
|
1125
|
+
const preceding = tokens.slice(governingStart, anchorIndex);
|
|
1126
|
+
const nearbyGuards = preceding.map((near, offset) => POLARITY_INVERTING_GUARDS.has(near) ? governingStart + offset : -1).filter((index) => index >= 0);
|
|
1127
|
+
const governingIndex = preceding.findIndex((near, offset) => NEGATION_MARKERS.has(near) && POLARITY_INVERTING_GUARDS.has(preceding[offset + 1] ?? ""));
|
|
1128
|
+
if (governingIndex < 0)
|
|
1129
|
+
return nearbyNegations.length > 0 || nearbyGuards.length > 0;
|
|
1130
|
+
const absoluteGoverningIndex = governingStart + governingIndex;
|
|
1131
|
+
const guardIndex = absoluteGoverningIndex + 1;
|
|
1132
|
+
const nested = tokens.slice(guardIndex + 1, anchorIndex).some((inner) => NEGATION_MARKERS.has(inner) || POLARITY_INVERTING_GUARDS.has(inner));
|
|
1133
|
+
return nested || nearbyNegations.some((index) => index !== absoluteGoverningIndex && index !== guardIndex) || nearbyGuards.some((index) => index !== guardIndex);
|
|
1134
|
+
});
|
|
1135
|
+
}
|
|
1102
1136
|
function semanticShape(source) {
|
|
1103
1137
|
const sourceTokens = semanticTokens(source);
|
|
1104
1138
|
const concepts = Array.from(new Set(sourceTokens.filter((token) => !/^\d+$/.test(token) && !SEMANTIC_STOP.has(token) && !NEGATION_MARKERS.has(token) && !CONDITION_MARKERS.has(token))));
|
|
@@ -1117,11 +1151,14 @@ function hasSemanticEvidence(source, target) {
|
|
|
1117
1151
|
const overlap = concepts.filter((concept) => tokens.includes(concept)).length;
|
|
1118
1152
|
if (overlap < required)
|
|
1119
1153
|
return false;
|
|
1120
|
-
|
|
1154
|
+
const targetNegative = hasEffectiveTargetNegation(tokens, anchor);
|
|
1155
|
+
if (negative && !targetNegative) {
|
|
1121
1156
|
const conditionalRestatement = sourceTokens.includes("without") && tokens.some((token) => CONDITION_MARKERS.has(token)) && overlap >= Math.min(2, concepts.length);
|
|
1122
1157
|
if (!conditionalRestatement)
|
|
1123
1158
|
return false;
|
|
1124
1159
|
}
|
|
1160
|
+
if (!negative && targetNegative)
|
|
1161
|
+
return false;
|
|
1125
1162
|
if (conditional && !negative && !tokens.some((token) => CONDITION_MARKERS.has(token)))
|
|
1126
1163
|
return false;
|
|
1127
1164
|
return true;
|
|
@@ -1139,10 +1176,13 @@ function hasSemanticContradiction(source, target) {
|
|
|
1139
1176
|
const overlap = concepts.filter((concept) => tokens.includes(concept)).length;
|
|
1140
1177
|
if (overlap < required)
|
|
1141
1178
|
return false;
|
|
1142
|
-
|
|
1179
|
+
const targetNegative = hasEffectiveTargetNegation(tokens, anchor);
|
|
1180
|
+
if (negative && !targetNegative) {
|
|
1143
1181
|
const validConditional = sourceTokens.includes("without") && tokens.some((token) => CONDITION_MARKERS.has(token)) && overlap >= Math.min(2, concepts.length);
|
|
1144
1182
|
return !validConditional;
|
|
1145
1183
|
}
|
|
1184
|
+
if (!negative && targetNegative)
|
|
1185
|
+
return true;
|
|
1146
1186
|
return conditional && !negative && !tokens.some((token) => CONDITION_MARKERS.has(token));
|
|
1147
1187
|
});
|
|
1148
1188
|
}
|
|
@@ -1187,9 +1227,11 @@ function verifySummary(summary, extraction, continuity = null) {
|
|
|
1187
1227
|
}
|
|
1188
1228
|
}
|
|
1189
1229
|
const modifiedPaths = extraction.modifiedFiles.map((file) => file.path);
|
|
1230
|
+
const listedModifiedPaths = new Set((findSection(parsed, "files-modified")?.body ?? "").split(`
|
|
1231
|
+
`).map((line) => line.replace(/^\s*(?:[-*+]|\d+[.)])\s+/, "").replace(/^\[[ x]\]\s+/i, "").trim()).map((line) => line.startsWith("`") && line.endsWith("`") ? line.slice(1, -1) : line).filter(Boolean).map(normalizePath));
|
|
1190
1232
|
for (const file of extraction.modifiedFiles) {
|
|
1191
1233
|
const needles = buildUniquePathNeedles(file.path, modifiedPaths);
|
|
1192
|
-
if (!needles.some((needle) => lower.includes(needle))) {
|
|
1234
|
+
if (!listedModifiedPaths.has(normalizePath(file.path)) && !needles.some((needle) => lower.includes(needle))) {
|
|
1193
1235
|
gaps.push({ kind: "missing-file", path: file.path });
|
|
1194
1236
|
score -= 5;
|
|
1195
1237
|
}
|