-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathclaim-intent.js
More file actions
158 lines (152 loc) · 5.74 KB
/
claim-intent.js
File metadata and controls
158 lines (152 loc) · 5.74 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
/**
* Heuristic: does this user message contain a durable assertion that
* the agent should consider asserting via codememory_assert_claim?
*
* Bias: false positives (one extra reminder line in context) are cheap;
* false negatives (silent claim drop) are the bug we're fixing. So the
* patterns are deliberately broad — the *agent* makes the final
* decision; we only nudge it to think about it.
*
* Returns one of:
* - null → no signal, stay silent
* - { kind, snippet, suggestion } → matched, emit nudge
*
* `kind` is one of: "preference", "decision", "rejection", "ownership",
* "location". `suggestion` is a hint at the predicate the agent should
* use, NOT a prescribed triple — the agent picks the final subject /
* object based on full context.
*/
/**
* Preference verbs in the first person. We require the verb to follow
* an "I" or "we" pronoun so generic statements about third parties
* ("React is great") don't trigger.
*
* Each pattern captures the snippet that justified the match so the
* nudge can quote it back to the agent.
*/
const PATTERNS = [
{
kind: "preference",
re: /\b(i|we)\s+(love|like|prefer|enjoy|favor|favour)\b[^.!?\n]{1,120}/i,
suggestion: "prefers",
},
{
kind: "preference",
re: /\b(i|we)\s+(want|need|wanna|wish|would\s+like)\s+to\b[^.!?\n]{1,120}/i,
suggestion: "wants-to",
},
{
kind: "rejection",
re: /\b(i|we)\s+(hate|dislike|reject|refuse|don'?t\s+(want|like|use)|won'?t\s+(use|ship|build))\b[^.!?\n]{1,120}/i,
suggestion: "rejected",
},
{
kind: "rejection",
// "let's not …" / "we're not doing …"
re: /\b(let'?s\s+not|we'?re\s+not\s+(using|doing|shipping|building))\b[^.!?\n]{1,120}/i,
suggestion: "rejected",
},
{
kind: "decision",
// Tech-stack assertions: "we use Postgres", "we're using …", "we deploy to …"
re: /\b(we|our\s+(project|team|app|service))\s+(use|uses|using|deploy|deploys|deployed|run|runs|running)\b[^.!?\n]{1,120}/i,
suggestion: "uses",
},
{
kind: "ownership",
re: /\b([A-Z][a-zA-Z]+|i|we)\s+own[s]?\b[^.!?\n]{1,120}/i,
suggestion: "owns",
},
{
kind: "location",
re: /\b(lives?|located|sits?|is)\s+(at|in|under)\s+[`"']?[a-z0-9_\-./]+[`"']?/i,
suggestion: "is-located-at",
},
];
/**
* Pure-question filter. If the entire message is a question the user
* is asking us, no claim — even if a preference verb shows up inside
* ("do we prefer X or Y?"). We detect this by checking whether the
* trimmed message ends with `?` AND has no declarative clause before
* an interrogative one.
*/
function isPureQuestion(text) {
const trimmed = String(text || "").trim();
if (!trimmed.endsWith("?")) return false;
// Mixed prompts ("I love X! how does Y work?") have a clause
// terminator before the question — those still count as containing
// a claim. Treat the message as pure-question only when there's no
// `.` or `!` before the trailing `?`.
const stripped = trimmed.slice(0, -1);
return !/[.!]/.test(stripped);
}
function detectClaimIntent(text) {
if (!text || typeof text !== "string") return null;
if (isPureQuestion(text)) return null;
for (const { kind, re, suggestion } of PATTERNS) {
const m = re.exec(text);
if (m) {
const snippet = String(m[0]).trim().replace(/\s+/g, " ");
return { kind, snippet, suggestion };
}
}
return null;
}
/**
* Format the additionalContext block injected before the agent sees
* the user's message.
*
* Polarity is deliberately flipped: the previous wording ("If this is a
* stable preference, call …") made *skipping* the default — the agent
* could rationalize a no-op for any borderline case. The new wording
* makes *asserting* the default, encloses the loopholes the agent has
* historically used to avoid the call, and demands a one-line written
* justification on skip so silent drops surface in the transcript.
*/
function formatClaimNudge(hit) {
return [
"[code-memory] Durable user assertion detected — ACT BEFORE ANSWERING.",
"",
`Matched (${hit.kind}): "${hit.snippet}"`,
`Suggested triple: user ${hit.suggestion} "<extracted object>"`,
"",
"DEFAULT ACTION: call codememory_assert_claim NOW, in the same response,",
"BEFORE any other tool call or user-facing text.",
"",
" codememory_assert_claim(",
' subject="user",',
` predicate="${hit.suggestion}",`,
' object="<extracted object>",',
' project="<project slug>",',
' evidence_span="<verbatim user quote>"',
" )",
"",
"DO NOT skip because:",
" - the fact is already in CLAUDE.md / AGENTS.md / code (restatement",
" reinforces; assert with confidence=0.85)",
' - the wording is emotional ("love", "hate", "really want") (sentiment',
" verbs ARE preference signals when paired with a noun/tech/pattern)",
" - the user is also asking a question in the same message (handle BOTH:",
" assert first, then answer)",
' - you "are not sure of the scope" (assert with the literal object the',
" user named; refine later if contradicted)",
"",
"SKIP ONLY if ALL of these hold:",
' - the sentence is hypothetical ("if we used X..."), counterfactual,',
" or a quoted third party",
" - OR the user is asking whether they should adopt X (question, not",
" assertion)",
" - OR the user explicitly retracts it in the same message",
" - OR a higher-confidence claim with the same subject+predicate+object",
" was asserted in this session (dedupe)",
"",
'If you skip, state ONE LINE in your response: "skipped claim: <reason>".',
"Silent skips are a bug.",
].join("\n");
}
module.exports = {
detectClaimIntent,
formatClaimNudge,
isPureQuestion,
PATTERNS,
};