portable-agent-layer 0.13.0 → 0.14.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -37,8 +37,8 @@ export interface Opinion {
|
|
|
37
37
|
// ── Confidence Deltas (matching original PAI) ──
|
|
38
38
|
|
|
39
39
|
const CONFIDENCE_DELTAS: Record<EvidenceType, number> = {
|
|
40
|
-
supporting: 0.
|
|
41
|
-
counter: -0.
|
|
40
|
+
supporting: 0.05,
|
|
41
|
+
counter: -0.1,
|
|
42
42
|
confirmation: 0.1,
|
|
43
43
|
contradiction: -0.2,
|
|
44
44
|
};
|
|
@@ -130,13 +130,13 @@ export function findSimilarOpinion(
|
|
|
130
130
|
return null;
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
-
/** Create a new opinion from a recurring note. Starts at confidence 0.
|
|
133
|
+
/** Create a new opinion from a recurring note. Starts at confidence 0.60. */
|
|
134
134
|
export function createOpinion(statement: string, source: string): Opinion {
|
|
135
135
|
const now = new Date().toISOString().slice(0, 10);
|
|
136
136
|
return {
|
|
137
137
|
id: slugify(statement),
|
|
138
138
|
statement,
|
|
139
|
-
confidence: 0.
|
|
139
|
+
confidence: 0.6,
|
|
140
140
|
category: classifyCategory(statement),
|
|
141
141
|
evidence: [{ date: now, type: "supporting", source }],
|
|
142
142
|
created: now,
|
|
@@ -144,12 +144,19 @@ export function createOpinion(statement: string, source: string): Opinion {
|
|
|
144
144
|
};
|
|
145
145
|
}
|
|
146
146
|
|
|
147
|
-
/**
|
|
147
|
+
/** Check if an opinion already has evidence with this exact source text. */
|
|
148
|
+
export function hasEvidence(opinion: Opinion, source: string): boolean {
|
|
149
|
+
return opinion.evidence.some((e) => e.source === source);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** Add evidence to an opinion and adjust its confidence. No-op if duplicate. */
|
|
148
153
|
export function addEvidence(
|
|
149
154
|
opinion: Opinion,
|
|
150
155
|
type: EvidenceType,
|
|
151
156
|
source: string
|
|
152
157
|
): Opinion {
|
|
158
|
+
if (hasEvidence(opinion, source)) return opinion;
|
|
159
|
+
|
|
153
160
|
const now = new Date().toISOString().slice(0, 10);
|
|
154
161
|
const delta = CONFIDENCE_DELTAS[type];
|
|
155
162
|
const newConfidence = Math.min(
|
|
@@ -100,6 +100,32 @@ const STOP_WORDS = new Set([
|
|
|
100
100
|
"own",
|
|
101
101
|
]);
|
|
102
102
|
|
|
103
|
+
/** Strip common English suffixes to normalize word forms. Applied twice to handle stacked suffixes. */
|
|
104
|
+
function stemOnce(word: string): string {
|
|
105
|
+
if (word.length <= 4) return word;
|
|
106
|
+
return word
|
|
107
|
+
.replace(/(?<=.{4})tion$/, "")
|
|
108
|
+
.replace(/(?<=.{4})sion$/, "")
|
|
109
|
+
.replace(/(?<=.{4})ment$/, "")
|
|
110
|
+
.replace(/(?<=.{3})ness$/, "")
|
|
111
|
+
.replace(/(?<=.{3})ings$/, "")
|
|
112
|
+
.replace(/(?<=.{3})ing$/, "")
|
|
113
|
+
.replace(/(?<=.{3})ies$/, "y")
|
|
114
|
+
.replace(/(?<=.{3})ive$/, "")
|
|
115
|
+
.replace(/(?<=.{3})ous$/, "")
|
|
116
|
+
.replace(/(?<=.{3})ful$/, "")
|
|
117
|
+
.replace(/(?<=.{3})ally$/, "")
|
|
118
|
+
.replace(/(?<=.{3})ly$/, "")
|
|
119
|
+
.replace(/(?<=.{3})ed$/, "")
|
|
120
|
+
.replace(/(?<=.{3})er$/, "")
|
|
121
|
+
.replace(/(?<=.{3})es$/, "")
|
|
122
|
+
.replace(/(?<=.{3})s$/, "");
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function stem(word: string): string {
|
|
126
|
+
return stemOnce(stemOnce(word));
|
|
127
|
+
}
|
|
128
|
+
|
|
103
129
|
export function extractKeywords(text: string): Set<string> {
|
|
104
130
|
return new Set(
|
|
105
131
|
text
|
|
@@ -107,6 +133,8 @@ export function extractKeywords(text: string): Set<string> {
|
|
|
107
133
|
.replace(/[^a-z0-9\s-]/g, " ")
|
|
108
134
|
.split(/\s+/)
|
|
109
135
|
.filter((w) => w.length > 2 && !STOP_WORDS.has(w))
|
|
136
|
+
.map((w) => stem(w))
|
|
137
|
+
.filter((w) => w.length > 2)
|
|
110
138
|
);
|
|
111
139
|
}
|
|
112
140
|
|
|
@@ -20,7 +20,6 @@ import {
|
|
|
20
20
|
addEvidence,
|
|
21
21
|
createOpinion,
|
|
22
22
|
findSimilarOpinion,
|
|
23
|
-
getLastReflectDate,
|
|
24
23
|
readOpinions,
|
|
25
24
|
saveOpinion,
|
|
26
25
|
setLastReflectDate,
|
|
@@ -157,12 +156,9 @@ function loadRatings(daysBack: number): Rating[] {
|
|
|
157
156
|
function promoteToOpinions(notes: ParsedNote[], dryRun: boolean): OpinionChange[] {
|
|
158
157
|
const changes: OpinionChange[] = [];
|
|
159
158
|
const opinions = readOpinions();
|
|
160
|
-
const lastReflect = getLastReflectDate();
|
|
161
159
|
|
|
162
|
-
//
|
|
163
|
-
const opinionNotes = notes.filter(
|
|
164
|
-
(n) => n.type === "O" && (!lastReflect || n.date > lastReflect)
|
|
165
|
-
);
|
|
160
|
+
// All O notes in the window — deduplication happens at the evidence level
|
|
161
|
+
const opinionNotes = notes.filter((n) => n.type === "O");
|
|
166
162
|
|
|
167
163
|
// Group similar notes together
|
|
168
164
|
const groups = new Map<string, ParsedNote[]>();
|
|
@@ -202,7 +198,11 @@ function promoteToOpinions(notes: ParsedNote[], dryRun: boolean): OpinionChange[
|
|
|
202
198
|
}
|
|
203
199
|
} else if (group.length >= 2) {
|
|
204
200
|
// New opinion — requires at least 2 occurrences
|
|
205
|
-
|
|
201
|
+
// Store individual note texts as evidence so re-runs deduplicate correctly
|
|
202
|
+
let opinion = createOpinion(representative, group[0].text.slice(0, 120));
|
|
203
|
+
for (const note of group.slice(1)) {
|
|
204
|
+
opinion = addEvidence(opinion, "supporting", note.text.slice(0, 120));
|
|
205
|
+
}
|
|
206
206
|
changes.push({
|
|
207
207
|
statement: representative,
|
|
208
208
|
action: "created",
|