agentera 3.0.0-dev.72 → 3.0.0-dev.75
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/README.md +111 -0
- package/bundle/.agentera-build-source.json +8 -0
- package/bundle/CHANGELOG.md +17 -17
- package/bundle/extract-corpus-parity.json +1 -1
- package/bundle/references/adapters/package-publication.json +49 -53
- package/bundle/references/adapters/package-registry.yaml +5 -1
- package/bundle/references/adapters/package-surface-characterization.md +6 -4
- package/bundle/references/analysis/evidence-tier-authority.yaml +45 -4
- package/bundle/references/analysis/personal-glossary-evaluation-authority.yaml +175 -0
- package/bundle/references/analysis/personal-glossary-evaluation-corpus.yaml +291 -0
- package/bundle/references/analysis/personal-glossary-holdout.yaml +192 -0
- package/bundle/references/analysis/verification-policy.yaml +8 -13
- package/bundle/references/artifacts/glossary-entry-contract.yaml +1533 -35
- package/bundle/references/cli/update-channels.yaml +2 -2
- package/bundle/references/cli/vocabulary.md +1 -1
- package/bundle/references/meta/retained-reference-authority.yaml +16 -1
- package/bundle/skills/agentera/capabilities/profile/schemas/artifacts.yaml +11 -7
- package/bundle/skills/agentera/capabilities/profile/schemas/exit.yaml +6 -3
- package/bundle/skills/agentera/capabilities/profile/schemas/validation.yaml +13 -7
- package/bundle/skills/agentera/schemas/artifacts/glossary.yaml +5 -0
- package/bundle/skills/agentera/schemas/artifacts/plan.yaml +7 -0
- package/dist/.agentera-build-source.json +8 -0
- package/dist/analytics/extractCorpus/copilotSessions.js +13 -4
- package/dist/analytics/extractCorpus/core.js +223 -3
- package/dist/analytics/extractCorpus/corpus.js +18 -1
- package/dist/analytics/extractCorpus/cursorSessions.js +22 -4
- package/dist/analytics/extractCorpus/evidenceTiers.js +123 -17
- package/dist/analytics/extractCorpus/filesystemSources.js +4 -0
- package/dist/analytics/extractCorpus/index.js +2 -2
- package/dist/analytics/extractCorpus/jsonlSessions.js +52 -3
- package/dist/analytics/extractCorpus/sqliteSessions.js +12 -3
- package/dist/analytics/personalGlossaryAdmission.js +26 -46
- package/dist/analytics/personalGlossaryCandidateProjection.js +692 -0
- package/dist/analytics/personalGlossaryCandidateProjectionExcerpts.js +144 -0
- package/dist/analytics/personalGlossaryCandidateReadView.js +23 -0
- package/dist/analytics/personalGlossaryCurrentGeneration.js +25 -0
- package/dist/analytics/personalGlossaryDecision.js +203 -0
- package/dist/analytics/personalGlossaryExplicit.js +788 -0
- package/dist/analytics/personalGlossaryExplicitMining.js +285 -0
- package/dist/analytics/personalGlossaryExplicitScope.js +41 -0
- package/dist/analytics/personalGlossaryExplicitSegments.js +418 -0
- package/dist/analytics/personalGlossaryExplicitTypes.js +26 -0
- package/dist/analytics/personalGlossaryLexicalClassifier.js +175 -0
- package/dist/analytics/personalGlossaryProfile.js +13 -3
- package/dist/analytics/personalGlossaryRecurrence.js +618 -0
- package/dist/analytics/personalGlossaryReviewRecordStorage.js +428 -0
- package/dist/analytics/personalGlossaryReviewRecords.js +332 -0
- package/dist/capabilities/index.js +5 -1
- package/dist/capabilities/profile/instructions.js +50 -15
- package/dist/cli/commands/personalGlossaryCandidateReads.js +744 -0
- package/dist/cli/commands/personalGlossaryDecision.js +349 -0
- package/dist/cli/commands/personalGlossaryPublish.js +484 -0
- package/dist/cli/commands/personalGlossaryReviewRecordReads.js +209 -0
- package/dist/cli/commands/personalGlossaryReviewRecords.js +623 -0
- package/dist/cli/commands/prime/briefOrientation.js +118 -19
- package/dist/cli/commands/report.js +18 -2
- package/dist/cli/commands/schema.js +179 -0
- package/dist/cli/commands/verify.js +51 -9
- package/dist/cli/dispatch/lifecycle.js +51 -6
- package/dist/cli/help.js +48 -2
- package/dist/cli/migrationRequired.js +4 -1
- package/dist/core/atomicWriter.js +4 -1
- package/dist/core/developmentInvocation.js +4 -1
- package/dist/core/yaml.js +53 -1
- package/dist/eval/glossaryEvaluation.js +751 -0
- package/dist/eval/glossaryEvaluationProcess.js +29 -0
- package/dist/eval/glossaryEvaluationRunner.js +571 -0
- package/dist/eval/glossaryEvaluationSuccessReport.js +297 -0
- package/dist/registries/activationTuples.js +31 -3
- package/dist/registries/evidenceTierContract.js +4 -0
- package/dist/registries/explicitSegmentGrammarContract.js +619 -0
- package/dist/registries/glossaryCandidateContractPaths.js +6 -0
- package/dist/registries/glossaryCandidateContracts.js +820 -0
- package/dist/registries/glossaryCandidateDecisionAuthority.js +119 -0
- package/dist/registries/glossaryCandidateDecisionContract.js +62 -0
- package/dist/registries/glossaryCandidateProjectionAuthority.js +197 -0
- package/dist/registries/glossaryCandidateProjectionContract.js +104 -0
- package/dist/registries/glossaryEntryContract.js +105 -103
- package/dist/registries/glossaryEntryTemporal.js +12 -0
- package/dist/registries/glossaryMiningAuthority.js +764 -0
- package/dist/registries/glossaryProfileFullContract.js +24 -0
- package/dist/registries/glossaryReviewRecordsAuthority.js +426 -0
- package/dist/registries/glossaryReviewRecordsContract.js +129 -0
- package/dist/registries/glossaryTermIdentity.js +122 -0
- package/dist/registries/packagePublication.js +3 -3
- package/dist/registries/personalGlossaryContracts.js +1 -0
- package/dist/release/releaseMetadata.js +15 -8
- package/dist/upgrade/npxPlatformStatus.js +2 -1
- package/dist/upgrade/projectIntegration.js +7 -1
- package/dist/validate/activationArtifactEvidence.js +18 -4
- package/dist/validate/activationEvidenceManifest.js +101 -22
- package/package.json +2 -4
- package/dist/cli/commands/personalGlossary.js +0 -176
|
@@ -0,0 +1,418 @@
|
|
|
1
|
+
import { EXPLICIT_SEGMENT_REASONS, explicitSegmentTransition, } from "../registries/explicitSegmentGrammarContract.js";
|
|
2
|
+
import { stableGlossaryTermIdentity } from "../registries/glossaryTermIdentity.js";
|
|
3
|
+
import { EXPLICIT_GLOSSARY_REASONS, } from "./personalGlossaryExplicitTypes.js";
|
|
4
|
+
const LETTER_OR_NUMBER_RE = /[\p{L}\p{N}]/u;
|
|
5
|
+
const EXAMPLE_RE = /\b(?:for\s+example|e\.g\.?|example|such\s+as)\b/iu;
|
|
6
|
+
const HYPOTHETICAL_RE = /\b(?:if|when|unless|whenever|suppose|assuming|hypothetically|imagine|would|could|might|may)\b/iu;
|
|
7
|
+
const QUESTION_RE = /\?|^\s*(?:does|do|did|can|could|would|should|what|why|how)\b/iu;
|
|
8
|
+
const INDIRECT_QUESTION_RE = /\b(?:i\s+(?:wonder|ask|want\s+to\s+know)|tell\s+me|whether|what|why|how)\b/iu;
|
|
9
|
+
const FUTURE_RE = /\b(?:will|shall|going\s+to|plan\s+to|intend\s+to|in\s+the\s+future|from\s+now\s+on|later)\b/iu;
|
|
10
|
+
const FOLLOWING_CUE_REFERENCE_RE = /\b(?:the\s+)?following\s+(?:definition|term|meaning)\b/iu;
|
|
11
|
+
const PREVIOUS_CUE_REFERENCE_RE = /\bthis\s+(?:definition|term|meaning)\b/iu;
|
|
12
|
+
function lineIndexAt(lines, position) {
|
|
13
|
+
let low = 0;
|
|
14
|
+
let high = lines.length - 1;
|
|
15
|
+
while (low <= high) {
|
|
16
|
+
const middle = Math.floor((low + high) / 2);
|
|
17
|
+
const line = lines[middle];
|
|
18
|
+
if (position < line.start)
|
|
19
|
+
high = middle - 1;
|
|
20
|
+
else if (position > line.end)
|
|
21
|
+
low = middle + 1;
|
|
22
|
+
else
|
|
23
|
+
return middle;
|
|
24
|
+
}
|
|
25
|
+
return Math.max(0, Math.min(lines.length - 1, low));
|
|
26
|
+
}
|
|
27
|
+
function hasListMarker(value) {
|
|
28
|
+
return /^\s*(?:[-*+]\s+|\d+[.)]\s+)/u.test(value) || /^\s*---\s*$/u.test(value);
|
|
29
|
+
}
|
|
30
|
+
function hasBlockScalarHeader(value) {
|
|
31
|
+
return /^\s*[^:#\n][^:\n]*:\s*[|>](?:[1-9])?(?:[+-])?\s*$/u.test(value);
|
|
32
|
+
}
|
|
33
|
+
function hasHeadingMarker(value) {
|
|
34
|
+
return /^\s*#{1,6}(?:\s|$)/u.test(value);
|
|
35
|
+
}
|
|
36
|
+
function safeMarkerPrefix(value) {
|
|
37
|
+
return /^\s*(?:[-*+]\s+)?(?:definition|term|correction|sarcasm)\s*:\s*$/iu.test(value);
|
|
38
|
+
}
|
|
39
|
+
function linePlans(text, raws, ranges, deps) {
|
|
40
|
+
const lines = deps.lineRanges(text);
|
|
41
|
+
const byLine = lines.map(() => []);
|
|
42
|
+
for (const raw of raws) {
|
|
43
|
+
if (raw.termStart === raw.termEnd && raw.meaningStart === raw.meaningEnd)
|
|
44
|
+
continue;
|
|
45
|
+
byLine[lineIndexAt(lines, raw.termStart)].push(raw);
|
|
46
|
+
}
|
|
47
|
+
return lines.map((line, index) => {
|
|
48
|
+
const comment = deps.commentStart(text, line, ranges);
|
|
49
|
+
const scanEnd = comment < 0 ? line.end : comment;
|
|
50
|
+
const value = text.slice(line.start, scanEnd);
|
|
51
|
+
const trimmed = value.trim();
|
|
52
|
+
const rawsOnLine = byLine[index].sort((left, right) => deps.rawCueOrder(left, right, text));
|
|
53
|
+
const firstColon = deps.colonOutsideRanges(text, line.start, scanEnd, ranges);
|
|
54
|
+
const firstRaw = rawsOnLine[0];
|
|
55
|
+
const markerPrefix = firstColon >= 0 && firstRaw !== undefined
|
|
56
|
+
? safeMarkerPrefix(text.slice(line.start, firstColon + 1))
|
|
57
|
+
: false;
|
|
58
|
+
const unsafePrefix = firstColon >= 0 && firstRaw !== undefined && firstColon < firstRaw.termStart && !markerPrefix;
|
|
59
|
+
const leadingWhitespace = /^\s+\S/u.test(value);
|
|
60
|
+
const commentOnly = text.slice(line.start, line.end).trim().startsWith("#");
|
|
61
|
+
const listMarker = hasListMarker(value);
|
|
62
|
+
const heading = hasHeadingMarker(value);
|
|
63
|
+
const blockScalar = hasBlockScalarHeader(value);
|
|
64
|
+
const approved = rawsOnLine.length > 0 &&
|
|
65
|
+
!leadingWhitespace &&
|
|
66
|
+
!commentOnly &&
|
|
67
|
+
!heading &&
|
|
68
|
+
!blockScalar &&
|
|
69
|
+
!unsafePrefix;
|
|
70
|
+
let kind;
|
|
71
|
+
if (commentOnly)
|
|
72
|
+
kind = "comment";
|
|
73
|
+
else if (trimmed.length === 0)
|
|
74
|
+
kind = "blank";
|
|
75
|
+
else if (leadingWhitespace)
|
|
76
|
+
kind = "indentation";
|
|
77
|
+
else if (approved)
|
|
78
|
+
kind = "approved_cue";
|
|
79
|
+
else if (listMarker)
|
|
80
|
+
kind = "list_boundary";
|
|
81
|
+
else if (heading || blockScalar || firstColon >= 0)
|
|
82
|
+
kind = "structural_fragment";
|
|
83
|
+
else
|
|
84
|
+
kind = "prose";
|
|
85
|
+
const input = kind === "blank"
|
|
86
|
+
? "blank_line"
|
|
87
|
+
: kind === "comment"
|
|
88
|
+
? "comment"
|
|
89
|
+
: kind === "indentation"
|
|
90
|
+
? "indentation"
|
|
91
|
+
: kind === "list_boundary"
|
|
92
|
+
? "list_boundary"
|
|
93
|
+
: kind === "structural_fragment"
|
|
94
|
+
? blockScalar
|
|
95
|
+
? "block_scalar"
|
|
96
|
+
: heading || trimmed.startsWith("#")
|
|
97
|
+
? "comment"
|
|
98
|
+
: "structural_fragment"
|
|
99
|
+
: kind === "approved_cue"
|
|
100
|
+
? "approved_cue"
|
|
101
|
+
: "prose";
|
|
102
|
+
return { ...line, scanEnd, kind, input, raws: rawsOnLine };
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
function structuralRejection(line, reason) {
|
|
106
|
+
return {
|
|
107
|
+
kind: "structural_fragment",
|
|
108
|
+
termStart: line.start,
|
|
109
|
+
termEnd: line.start,
|
|
110
|
+
meaningStart: line.start,
|
|
111
|
+
meaningEnd: line.start,
|
|
112
|
+
sentenceStart: line.start,
|
|
113
|
+
sentenceEnd: line.end,
|
|
114
|
+
rejectionReason: reason,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
function loadedStructuralReason(transition) {
|
|
118
|
+
return transition.reason === EXPLICIT_SEGMENT_REASONS.structuralFragment
|
|
119
|
+
? transition.reason
|
|
120
|
+
: EXPLICIT_GLOSSARY_REASONS.structuralFragment;
|
|
121
|
+
}
|
|
122
|
+
function segmentRawCues(text, initial, ranges, grammar, deps) {
|
|
123
|
+
const plans = linePlans(text, initial, ranges, deps);
|
|
124
|
+
const accepted = [];
|
|
125
|
+
let state = grammar.initialState;
|
|
126
|
+
for (const plan of plans) {
|
|
127
|
+
if (state === "structural_config") {
|
|
128
|
+
const transition = explicitSegmentTransition(grammar, state, plan.input);
|
|
129
|
+
if (plan.kind === "prose") {
|
|
130
|
+
accepted.push(structuralRejection(plan, loadedStructuralReason(transition)));
|
|
131
|
+
state = transition.to;
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
if (plan.kind !== "blank" && plan.kind !== "comment") {
|
|
135
|
+
accepted.push(structuralRejection(plan, loadedStructuralReason(transition)));
|
|
136
|
+
}
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
if (plan.kind === "approved_cue") {
|
|
140
|
+
const transition = explicitSegmentTransition(grammar, state, "approved_cue");
|
|
141
|
+
if (transition.to === "cue")
|
|
142
|
+
accepted.push(...plan.raws);
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
if (plan.kind === "structural_fragment" || plan.kind === "list_boundary") {
|
|
146
|
+
const transition = explicitSegmentTransition(grammar, state, plan.input);
|
|
147
|
+
if (transition.to === "structural_config") {
|
|
148
|
+
state = transition.to;
|
|
149
|
+
accepted.push(structuralRejection(plan, loadedStructuralReason(transition)));
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
else if (plan.kind === "comment" && /:\s*/u.test(text.slice(plan.start, plan.end))) {
|
|
153
|
+
const transition = explicitSegmentTransition(grammar, state, "structural_fragment");
|
|
154
|
+
accepted.push(structuralRejection(plan, loadedStructuralReason(transition)));
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return { raws: accepted, plans };
|
|
158
|
+
}
|
|
159
|
+
function firstRawAfter(plan, position, text, deps) {
|
|
160
|
+
return plan.raws
|
|
161
|
+
.filter((raw) => raw.termStart > position)
|
|
162
|
+
.sort((left, right) => deps.rawCueOrder(left, right, text))[0];
|
|
163
|
+
}
|
|
164
|
+
function cueBoundaryStart(text, cue, closeToOpen) {
|
|
165
|
+
if (cue.kind === "definition_list") {
|
|
166
|
+
const floor = Math.max(text.lastIndexOf("\n", cue.termStart - 1), text.lastIndexOf(";", cue.termStart - 1)) + 1;
|
|
167
|
+
const prefix = text.slice(floor, cue.termStart);
|
|
168
|
+
if (/^\s*(?:[-*+]\s+)?(?:definition|term)\s*:\s*$/iu.test(prefix)) {
|
|
169
|
+
const first = prefix.search(/\S/u);
|
|
170
|
+
if (first >= 0)
|
|
171
|
+
return floor + first;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
const opening = closeToOpen.get(text[cue.termEnd] ?? "");
|
|
175
|
+
if (opening !== undefined) {
|
|
176
|
+
const start = text.lastIndexOf(opening, cue.termStart);
|
|
177
|
+
if (start >= 0)
|
|
178
|
+
return start;
|
|
179
|
+
}
|
|
180
|
+
return cue.termStart;
|
|
181
|
+
}
|
|
182
|
+
function isBoundaryLine(plan) {
|
|
183
|
+
return ["blank", "comment", "indentation", "list_boundary", "structural_fragment"].includes(plan.kind);
|
|
184
|
+
}
|
|
185
|
+
function sentenceRanges(text, ranges, deps) {
|
|
186
|
+
const boundaries = deps.sentenceBoundaries(text, ranges);
|
|
187
|
+
return [0, ...boundaries].map((start, index, starts) => ({
|
|
188
|
+
start,
|
|
189
|
+
end: starts[index + 1] ?? text.length,
|
|
190
|
+
}));
|
|
191
|
+
}
|
|
192
|
+
function nearMissTermOccurs(text, term, start, end) {
|
|
193
|
+
const literal = escapedTerm(term);
|
|
194
|
+
if (!literal)
|
|
195
|
+
return false;
|
|
196
|
+
const identifierContinue = "\\p{ID_Continue}$\\u200C\\u200D";
|
|
197
|
+
return new RegExp(`(?:(?<![${identifierContinue}])${literal}(?=[${identifierContinue}])|(?<=[${identifierContinue}])${literal}(?![${identifierContinue}]))`, "iu").test(text.slice(start, end));
|
|
198
|
+
}
|
|
199
|
+
function referenceLikeQualifierStart(text, line, raws, ranges, deps) {
|
|
200
|
+
for (const sentence of sentenceRanges(text, ranges, deps)) {
|
|
201
|
+
if (sentence.end <= line.start || sentence.start > line.end)
|
|
202
|
+
continue;
|
|
203
|
+
const value = text.slice(sentence.start, sentence.end);
|
|
204
|
+
if (!referenceQualifier(value))
|
|
205
|
+
continue;
|
|
206
|
+
const following = value.match(new RegExp(FOLLOWING_CUE_REFERENCE_RE.source, "iu"));
|
|
207
|
+
if (following?.index !== undefined)
|
|
208
|
+
return Math.max(sentence.start, line.start);
|
|
209
|
+
const previous = value.match(new RegExp(PREVIOUS_CUE_REFERENCE_RE.source, "iu"));
|
|
210
|
+
if (previous?.index !== undefined)
|
|
211
|
+
return Math.max(sentence.start, line.start);
|
|
212
|
+
if (raws.some((raw) => raw.rejectionReason === undefined &&
|
|
213
|
+
raw.termStart >= sentence.start &&
|
|
214
|
+
raw.termStart < sentence.end))
|
|
215
|
+
continue;
|
|
216
|
+
for (const raw of raws) {
|
|
217
|
+
if (raw.rejectionReason !== undefined || raw.termEnd <= raw.termStart)
|
|
218
|
+
continue;
|
|
219
|
+
const term = text.slice(raw.termStart, raw.termEnd).trim();
|
|
220
|
+
const occurrence = exactTermOccurrences(text, term, sentence.start, sentence.end)[0];
|
|
221
|
+
if (occurrence !== undefined ||
|
|
222
|
+
nearMissTermOccurs(text, term, sentence.start, sentence.end)) {
|
|
223
|
+
return Math.max(sentence.start, line.start);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
229
|
+
function boundedSegmentCues(text, raws, plans, ranges, grammar, deps) {
|
|
230
|
+
const lines = deps.lineRanges(text);
|
|
231
|
+
const structural = raws.filter((raw) => raw.termStart === raw.termEnd && raw.meaningStart === raw.meaningEnd);
|
|
232
|
+
const usable = raws.filter((raw) => !(raw.termStart === raw.termEnd && raw.meaningStart === raw.meaningEnd));
|
|
233
|
+
const bounded = usable.map((raw) => {
|
|
234
|
+
if (raw.kind === "acronym_parenthetical")
|
|
235
|
+
return raw;
|
|
236
|
+
if (raw.kind !== "definition_list") {
|
|
237
|
+
const line = plans[lineIndexAt(lines, raw.termStart)];
|
|
238
|
+
const next = firstRawAfter(line, raw.termStart, text, deps);
|
|
239
|
+
const boundary = next === undefined ? raw.sentenceEnd : cueBoundaryStart(text, next, deps.closeToOpen);
|
|
240
|
+
const end = boundary < raw.sentenceEnd ? boundary : raw.sentenceEnd;
|
|
241
|
+
const [meaningStart, meaningEnd] = deps.unwrapMeaning(text, ...deps.trimMeaning(text, raw.meaningStart, end));
|
|
242
|
+
return { ...raw, meaningStart, meaningEnd };
|
|
243
|
+
}
|
|
244
|
+
const lineIndex = lineIndexAt(lines, raw.termStart);
|
|
245
|
+
const line = plans[lineIndex];
|
|
246
|
+
const nextOnLine = firstRawAfter(line, raw.termStart, text, deps);
|
|
247
|
+
let end = nextOnLine ? cueBoundaryStart(text, nextOnLine, deps.closeToOpen) : line.scanEnd;
|
|
248
|
+
const referenceStart = referenceLikeQualifierStart(text, line, raws, ranges, deps);
|
|
249
|
+
const sameLineReference = referenceStart !== null && referenceStart > raw.meaningStart ? referenceStart : null;
|
|
250
|
+
if (sameLineReference !== null) {
|
|
251
|
+
end = Math.min(end, sameLineReference);
|
|
252
|
+
}
|
|
253
|
+
let state = "cue";
|
|
254
|
+
const inline = deps.trimMeaning(text, raw.meaningStart, end);
|
|
255
|
+
const inlineNonEmpty = LETTER_OR_NUMBER_RE.test(text.slice(...inline));
|
|
256
|
+
const cueTransition = explicitSegmentTransition(grammar, state, inlineNonEmpty ? "non_empty_inline_meaning" : nextOnLine ? "next_cue" : "blank_line");
|
|
257
|
+
if (!inlineNonEmpty) {
|
|
258
|
+
return {
|
|
259
|
+
...raw,
|
|
260
|
+
meaningStart: inline[0],
|
|
261
|
+
meaningEnd: inline[1],
|
|
262
|
+
...(raw.rejectionReason === undefined
|
|
263
|
+
? { rejectionReason: EXPLICIT_GLOSSARY_REASONS.emptyMeaning }
|
|
264
|
+
: {}),
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
state = cueTransition.to;
|
|
268
|
+
let lastIncludedEnd = end;
|
|
269
|
+
for (let index = lineIndex + 1; sameLineReference === null && index < plans.length; index += 1) {
|
|
270
|
+
const nextPlan = plans[index];
|
|
271
|
+
const nextCue = nextPlan.raws.find((candidate) => candidate.termStart > raw.termStart);
|
|
272
|
+
const transition = explicitSegmentTransition(grammar, state, nextPlan.input);
|
|
273
|
+
if (isBoundaryLine(nextPlan)) {
|
|
274
|
+
end = nextPlan.start;
|
|
275
|
+
break;
|
|
276
|
+
}
|
|
277
|
+
if (nextCue !== undefined) {
|
|
278
|
+
end = cueBoundaryStart(text, nextCue, deps.closeToOpen);
|
|
279
|
+
break;
|
|
280
|
+
}
|
|
281
|
+
const referenceStart = referenceLikeQualifierStart(text, nextPlan, raws, ranges, deps);
|
|
282
|
+
if (referenceStart !== null) {
|
|
283
|
+
end = referenceStart;
|
|
284
|
+
break;
|
|
285
|
+
}
|
|
286
|
+
state = transition.to;
|
|
287
|
+
if (state !== "continuation") {
|
|
288
|
+
end = nextPlan.start;
|
|
289
|
+
break;
|
|
290
|
+
}
|
|
291
|
+
lastIncludedEnd = nextPlan.end;
|
|
292
|
+
end = lastIncludedEnd;
|
|
293
|
+
if (nextPlan.kind === "approved_cue") {
|
|
294
|
+
end = nextPlan.start;
|
|
295
|
+
break;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
const [meaningStart, meaningEnd] = deps.unwrapMeaning(text, ...deps.trimMeaning(text, raw.meaningStart, end));
|
|
299
|
+
const overBound = Buffer.byteLength(text.slice(meaningStart, meaningEnd), "utf8") >
|
|
300
|
+
grammar.continuation.maximumUtf8Bytes;
|
|
301
|
+
return {
|
|
302
|
+
...raw,
|
|
303
|
+
meaningStart,
|
|
304
|
+
meaningEnd,
|
|
305
|
+
...(overBound && raw.rejectionReason === undefined
|
|
306
|
+
? { rejectionReason: EXPLICIT_GLOSSARY_REASONS.meaningBoundExceeded }
|
|
307
|
+
: {}),
|
|
308
|
+
};
|
|
309
|
+
});
|
|
310
|
+
return [...bounded, ...structural];
|
|
311
|
+
}
|
|
312
|
+
function escapedTerm(term) {
|
|
313
|
+
return term.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
314
|
+
}
|
|
315
|
+
function exactTermOccurrences(text, term, start, end) {
|
|
316
|
+
const literal = escapedTerm(term);
|
|
317
|
+
if (!literal)
|
|
318
|
+
return [];
|
|
319
|
+
const expression = new RegExp(`(?<![\\p{ID_Continue}$\\u200C\\u200D])${literal}(?![\\p{ID_Continue}$\\u200C\\u200D])`, "giu");
|
|
320
|
+
return [...text.slice(start, end).matchAll(expression)].map((match) => start + (match.index ?? 0));
|
|
321
|
+
}
|
|
322
|
+
function occurrenceInsideMeaning(position, raw) {
|
|
323
|
+
return position >= raw.meaningStart && position < raw.meaningEnd;
|
|
324
|
+
}
|
|
325
|
+
function referenceQualifier(text) {
|
|
326
|
+
if (EXAMPLE_RE.test(text))
|
|
327
|
+
return EXPLICIT_GLOSSARY_REASONS.exampleContext;
|
|
328
|
+
if (HYPOTHETICAL_RE.test(text))
|
|
329
|
+
return EXPLICIT_GLOSSARY_REASONS.hypotheticalDefinition;
|
|
330
|
+
if (INDIRECT_QUESTION_RE.test(text) || QUESTION_RE.test(text)) {
|
|
331
|
+
return EXPLICIT_GLOSSARY_REASONS.indirectQuestion;
|
|
332
|
+
}
|
|
333
|
+
if (FUTURE_RE.test(text))
|
|
334
|
+
return EXPLICIT_GLOSSARY_REASONS.futureDefinition;
|
|
335
|
+
return null;
|
|
336
|
+
}
|
|
337
|
+
function applyReferenceBindings(text, raws, ranges, deps) {
|
|
338
|
+
const sentences = sentenceRanges(text, ranges, deps);
|
|
339
|
+
const cues = raws
|
|
340
|
+
.filter((raw) => raw.rejectionReason === undefined &&
|
|
341
|
+
raw.termEnd > raw.termStart &&
|
|
342
|
+
raw.meaningEnd > raw.meaningStart)
|
|
343
|
+
.sort((left, right) => deps.rawCueOrder(left, right, text));
|
|
344
|
+
const rejected = new Map();
|
|
345
|
+
const missing = [];
|
|
346
|
+
const reject = (target, reason) => {
|
|
347
|
+
if (!rejected.has(target))
|
|
348
|
+
rejected.set(target, reason);
|
|
349
|
+
};
|
|
350
|
+
const cueSentence = (cue) => Math.max(0, sentences.findIndex((sentence) => cue.termStart >= sentence.start && cue.termStart < sentence.end));
|
|
351
|
+
const cuesBySentence = sentences.map((_, index) => cues.filter((cue) => cueSentence(cue) === index));
|
|
352
|
+
for (const [sentenceIndex, sentence] of sentences.entries()) {
|
|
353
|
+
const value = text.slice(sentence.start, sentence.end);
|
|
354
|
+
const qualifier = referenceQualifier(value);
|
|
355
|
+
if (!qualifier)
|
|
356
|
+
continue;
|
|
357
|
+
for (const match of value.matchAll(new RegExp(FOLLOWING_CUE_REFERENCE_RE.source, "giu"))) {
|
|
358
|
+
const position = sentence.start + (match.index ?? 0) + match[0].length;
|
|
359
|
+
const target = (cuesBySentence[sentenceIndex + 1] ?? []).find((cue) => cue.termStart >= position);
|
|
360
|
+
if (target)
|
|
361
|
+
reject(target, qualifier);
|
|
362
|
+
else
|
|
363
|
+
missing.push(structuralRejection({ start: sentence.start, end: sentence.end }, EXPLICIT_GLOSSARY_REASONS.ambiguousReference));
|
|
364
|
+
}
|
|
365
|
+
for (const match of value.matchAll(new RegExp(PREVIOUS_CUE_REFERENCE_RE.source, "giu"))) {
|
|
366
|
+
const position = sentence.start + (match.index ?? 0);
|
|
367
|
+
const target = [...(cuesBySentence[sentenceIndex - 1] ?? [])]
|
|
368
|
+
.reverse()
|
|
369
|
+
.find((cue) => cue.termStart < position);
|
|
370
|
+
if (target)
|
|
371
|
+
reject(target, qualifier);
|
|
372
|
+
else
|
|
373
|
+
missing.push(structuralRejection({ start: sentence.start, end: sentence.end }, EXPLICIT_GLOSSARY_REASONS.ambiguousReference));
|
|
374
|
+
}
|
|
375
|
+
if (cues.some((cue) => cue.termStart >= sentence.start && cue.termStart < sentence.end))
|
|
376
|
+
continue;
|
|
377
|
+
const directExactReference = QUESTION_RE.test(value) ||
|
|
378
|
+
EXAMPLE_RE.test(value) ||
|
|
379
|
+
HYPOTHETICAL_RE.test(value) ||
|
|
380
|
+
FUTURE_RE.test(value);
|
|
381
|
+
const nearbyCues = cues.filter((cue) => Math.abs(cueSentence(cue) - sentenceIndex) <= 1);
|
|
382
|
+
const matches = new Map();
|
|
383
|
+
for (const cue of nearbyCues) {
|
|
384
|
+
const term = text.slice(cue.termStart, cue.termEnd).trim();
|
|
385
|
+
const occurrences = exactTermOccurrences(text, term, sentence.start, sentence.end).filter((position) => !nearbyCues.some((other) => other !== cue &&
|
|
386
|
+
other.rejectionReason === undefined &&
|
|
387
|
+
occurrenceInsideMeaning(position, other) &&
|
|
388
|
+
!directExactReference));
|
|
389
|
+
if (occurrences.length > 0) {
|
|
390
|
+
const identity = stableGlossaryTermIdentity(term);
|
|
391
|
+
matches.set(identity, [...(matches.get(identity) ?? []), cue]);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
for (const group of matches.values()) {
|
|
395
|
+
const unique = [...new Map(group.map((cue) => [cue.termStart, cue])).values()];
|
|
396
|
+
if (unique.length > 1) {
|
|
397
|
+
for (const cue of unique)
|
|
398
|
+
reject(cue, EXPLICIT_GLOSSARY_REASONS.ambiguousReference);
|
|
399
|
+
}
|
|
400
|
+
else if (unique[0])
|
|
401
|
+
reject(unique[0], qualifier);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
return [
|
|
405
|
+
...raws.map((raw) => {
|
|
406
|
+
const reason = rejected.get(raw);
|
|
407
|
+
return reason === undefined || raw.rejectionReason !== undefined
|
|
408
|
+
? raw
|
|
409
|
+
: { ...raw, rejectionReason: reason };
|
|
410
|
+
}),
|
|
411
|
+
...missing,
|
|
412
|
+
];
|
|
413
|
+
}
|
|
414
|
+
export function segmentAndBindExplicitCues(text, initial, ranges, grammar, deps) {
|
|
415
|
+
const segmented = segmentRawCues(text, initial, ranges, grammar, deps);
|
|
416
|
+
const bounded = boundedSegmentCues(text, segmented.raws, segmented.plans, ranges, grammar, deps);
|
|
417
|
+
return applyReferenceBindings(text, bounded, ranges, deps).sort((left, right) => deps.rawCueOrder(left, right, text));
|
|
418
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export const EXPLICIT_GLOSSARY_REASONS = {
|
|
2
|
+
attributedQuotation: "attributed_quotation",
|
|
3
|
+
ambiguousReference: "ambiguous_reference",
|
|
4
|
+
conflictingMeaning: "conflicting_meaning",
|
|
5
|
+
emptyMeaning: "empty_meaning",
|
|
6
|
+
emptyTerm: "empty_term",
|
|
7
|
+
exampleContext: "example_context",
|
|
8
|
+
hypotheticalDefinition: "hypothetical_definition",
|
|
9
|
+
malformedSpan: "malformed_span",
|
|
10
|
+
meaningBoundExceeded: "meaning_bound_exceeded",
|
|
11
|
+
negatedDefinition: "negated_definition",
|
|
12
|
+
indirectQuestion: "indirect_question",
|
|
13
|
+
futureDefinition: "future_definition",
|
|
14
|
+
projectOnlyScope: "project_only_scope",
|
|
15
|
+
questionDefinition: "question_definition",
|
|
16
|
+
retractedDefinition: "retracted_definition",
|
|
17
|
+
sarcasmMarker: "sarcasm_marker",
|
|
18
|
+
staleAnchor: "stale_anchor",
|
|
19
|
+
structuralFragment: "structural_fragment",
|
|
20
|
+
termBoundExceeded: "term_bound_exceeded",
|
|
21
|
+
unsafeSyntax: "unsafe_syntax",
|
|
22
|
+
uncertainScope: "uncertain_scope",
|
|
23
|
+
unresolvedAnchor: "unresolved_anchor",
|
|
24
|
+
provenanceIncomplete: "provenance_incomplete",
|
|
25
|
+
userAuthorshipRequired: "user_authorship_required",
|
|
26
|
+
};
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
const COMMON_GRAMMAR_TERMS = new Set([
|
|
2
|
+
"a",
|
|
3
|
+
"an",
|
|
4
|
+
"and",
|
|
5
|
+
"are",
|
|
6
|
+
"as",
|
|
7
|
+
"at",
|
|
8
|
+
"be",
|
|
9
|
+
"by",
|
|
10
|
+
"can",
|
|
11
|
+
"could",
|
|
12
|
+
"do",
|
|
13
|
+
"each",
|
|
14
|
+
"for",
|
|
15
|
+
"from",
|
|
16
|
+
"has",
|
|
17
|
+
"have",
|
|
18
|
+
"how",
|
|
19
|
+
"if",
|
|
20
|
+
"in",
|
|
21
|
+
"into",
|
|
22
|
+
"is",
|
|
23
|
+
"it",
|
|
24
|
+
"must",
|
|
25
|
+
"no",
|
|
26
|
+
"not",
|
|
27
|
+
"of",
|
|
28
|
+
"on",
|
|
29
|
+
"one",
|
|
30
|
+
"only",
|
|
31
|
+
"or",
|
|
32
|
+
"should",
|
|
33
|
+
"that",
|
|
34
|
+
"the",
|
|
35
|
+
"then",
|
|
36
|
+
"this",
|
|
37
|
+
"to",
|
|
38
|
+
"use",
|
|
39
|
+
"version",
|
|
40
|
+
"when",
|
|
41
|
+
"which",
|
|
42
|
+
"why",
|
|
43
|
+
"with",
|
|
44
|
+
"would",
|
|
45
|
+
"yes",
|
|
46
|
+
"two",
|
|
47
|
+
"three",
|
|
48
|
+
]);
|
|
49
|
+
const ROUTINE_FREQUENCY_TERMS = new Set([
|
|
50
|
+
"always",
|
|
51
|
+
"frequently",
|
|
52
|
+
"generally",
|
|
53
|
+
"never",
|
|
54
|
+
"normally",
|
|
55
|
+
"occasionally",
|
|
56
|
+
"often",
|
|
57
|
+
"rarely",
|
|
58
|
+
"regularly",
|
|
59
|
+
"routinely",
|
|
60
|
+
"seldom",
|
|
61
|
+
"sometimes",
|
|
62
|
+
"typically",
|
|
63
|
+
"usually",
|
|
64
|
+
]);
|
|
65
|
+
const GENERIC_WORKFLOW_TERMS = new Set([
|
|
66
|
+
"change",
|
|
67
|
+
"check",
|
|
68
|
+
"choose",
|
|
69
|
+
"command",
|
|
70
|
+
"configuration",
|
|
71
|
+
"continue",
|
|
72
|
+
"correction",
|
|
73
|
+
"decision",
|
|
74
|
+
"file",
|
|
75
|
+
"keep",
|
|
76
|
+
"make",
|
|
77
|
+
"name",
|
|
78
|
+
"package",
|
|
79
|
+
"personal",
|
|
80
|
+
"project",
|
|
81
|
+
"repository",
|
|
82
|
+
"run",
|
|
83
|
+
"script",
|
|
84
|
+
"scripts",
|
|
85
|
+
]);
|
|
86
|
+
const KNOWN_COMMAND_WORDS = new Set([
|
|
87
|
+
"agentera",
|
|
88
|
+
"bash",
|
|
89
|
+
"build",
|
|
90
|
+
"cargo",
|
|
91
|
+
"cat",
|
|
92
|
+
"cd",
|
|
93
|
+
"check",
|
|
94
|
+
"ci",
|
|
95
|
+
"curl",
|
|
96
|
+
"git",
|
|
97
|
+
"install",
|
|
98
|
+
"lint",
|
|
99
|
+
"ls",
|
|
100
|
+
"make",
|
|
101
|
+
"node",
|
|
102
|
+
"npm",
|
|
103
|
+
"npx",
|
|
104
|
+
"pnpm",
|
|
105
|
+
"python",
|
|
106
|
+
"sh",
|
|
107
|
+
"tcsh",
|
|
108
|
+
"test",
|
|
109
|
+
"tsc",
|
|
110
|
+
"tsx",
|
|
111
|
+
"yarn",
|
|
112
|
+
]);
|
|
113
|
+
const KNOWN_COMMAND_INVOCATIONS = {
|
|
114
|
+
agentera: ["build", "check"],
|
|
115
|
+
cargo: ["build", "test"],
|
|
116
|
+
git: ["add", "branch", "checkout", "commit", "diff", "fetch", "log", "push", "status", "tag"],
|
|
117
|
+
npm: ["install", "test"],
|
|
118
|
+
pnpm: ["build", "install", "lint", "test", "typecheck"],
|
|
119
|
+
yarn: ["build", "install", "lint", "test"],
|
|
120
|
+
};
|
|
121
|
+
const KNOWN_COMMAND_SPELLINGS = new Set(Object.entries(KNOWN_COMMAND_INVOCATIONS).flatMap(([command, actions]) => actions.map((action) => `${command}${action}`)));
|
|
122
|
+
const KNOWN_COMMAND_COMPONENTS = new Set([
|
|
123
|
+
...KNOWN_COMMAND_WORDS,
|
|
124
|
+
...Object.keys(KNOWN_COMMAND_INVOCATIONS),
|
|
125
|
+
...Object.values(KNOWN_COMMAND_INVOCATIONS).flat(),
|
|
126
|
+
]);
|
|
127
|
+
const PATH_COMPONENTS = new Set([
|
|
128
|
+
"dist",
|
|
129
|
+
"home",
|
|
130
|
+
"lib",
|
|
131
|
+
"modules",
|
|
132
|
+
"node",
|
|
133
|
+
"node_modules",
|
|
134
|
+
"path",
|
|
135
|
+
"repo",
|
|
136
|
+
"src",
|
|
137
|
+
"tmp",
|
|
138
|
+
]);
|
|
139
|
+
function isKnownCommandSpelling(value) {
|
|
140
|
+
const lower = value.toLowerCase();
|
|
141
|
+
if (KNOWN_COMMAND_COMPONENTS.has(lower))
|
|
142
|
+
return true;
|
|
143
|
+
const compact = lower.replace(/[\s\-_/\\.:]+/gu, "");
|
|
144
|
+
return KNOWN_COMMAND_SPELLINGS.has(compact);
|
|
145
|
+
}
|
|
146
|
+
function isDirectPathToken(value, text, start, end) {
|
|
147
|
+
if (/[\\/]/u.test(value))
|
|
148
|
+
return true;
|
|
149
|
+
const previous = text[start - 1] ?? "";
|
|
150
|
+
const next = text[end] ?? "";
|
|
151
|
+
const dotIsPathPunctuation = previous === "." || (next === "." && /[\p{L}\p{N}]/u.test(text[end + 1] ?? ""));
|
|
152
|
+
return ["/", "\\"].includes(previous) || ["/", "\\"].includes(next) || dotIsPathPunctuation;
|
|
153
|
+
}
|
|
154
|
+
function isApprovedDerivedPathSpelling(value) {
|
|
155
|
+
const segments = value
|
|
156
|
+
.toLowerCase()
|
|
157
|
+
.split(/[-_.:]+/u)
|
|
158
|
+
.filter(Boolean);
|
|
159
|
+
return segments.length >= 2 && segments.every((segment) => PATH_COMPONENTS.has(segment));
|
|
160
|
+
}
|
|
161
|
+
/** Classify one complete cue without rewriting it or matching arbitrary substrings. */
|
|
162
|
+
export function classifyRecurringLexicalToken(value, text, start, end) {
|
|
163
|
+
const lower = value.toLowerCase();
|
|
164
|
+
if (COMMON_GRAMMAR_TERMS.has(lower) ||
|
|
165
|
+
ROUTINE_FREQUENCY_TERMS.has(lower) ||
|
|
166
|
+
GENERIC_WORKFLOW_TERMS.has(lower)) {
|
|
167
|
+
return "common_term";
|
|
168
|
+
}
|
|
169
|
+
if (isKnownCommandSpelling(value))
|
|
170
|
+
return "known_command";
|
|
171
|
+
if (isDirectPathToken(value, text, start, end) || isApprovedDerivedPathSpelling(value)) {
|
|
172
|
+
return "path_like";
|
|
173
|
+
}
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
2
3
|
import { loadProfileDecayParameters } from "../capabilities/profile/instructions.js";
|
|
3
4
|
import { writeFileAtomic } from "../core/atomicWriter.js";
|
|
4
5
|
import { GlossaryEntryBoundError, validateGlossaryEntry, } from "../registries/glossaryEntryContract.js";
|
|
@@ -146,6 +147,9 @@ function orderedEntry(entry) {
|
|
|
146
147
|
function render(document) {
|
|
147
148
|
return `${START}\n${HEADING}\n\n\`\`\`json\n${JSON.stringify(document, null, 2)}\n\`\`\`\n${END}`;
|
|
148
149
|
}
|
|
150
|
+
function sectionSha256(section) {
|
|
151
|
+
return createHash("sha256").update(section, "utf8").digest("hex");
|
|
152
|
+
}
|
|
149
153
|
export function updatePersonalGlossaryProfile(input) {
|
|
150
154
|
calendarDate(input.asOf);
|
|
151
155
|
const original = fs.readFileSync(input.profilePath, "utf8");
|
|
@@ -213,7 +217,13 @@ export function updatePersonalGlossaryProfile(input) {
|
|
|
213
217
|
? `${original.slice(0, section.start)}${rendered}${original.slice(section.end)}`
|
|
214
218
|
: `${original}${original.endsWith("\n") ? "\n" : "\n\n"}${rendered}\n`;
|
|
215
219
|
const changed = candidate !== original;
|
|
216
|
-
if (changed && !input.dryRun)
|
|
217
|
-
writeFileAtomic(input.profilePath, candidate);
|
|
218
|
-
|
|
220
|
+
if (changed && !input.dryRun) {
|
|
221
|
+
writeFileAtomic(input.profilePath, candidate, "utf8", { preserveTargetMode: true });
|
|
222
|
+
}
|
|
223
|
+
return {
|
|
224
|
+
changed,
|
|
225
|
+
profilePath: input.profilePath,
|
|
226
|
+
profileSectionSha256: sectionSha256(rendered),
|
|
227
|
+
entries: merged,
|
|
228
|
+
};
|
|
219
229
|
}
|