portable-agent-layer 0.81.0 → 0.81.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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "portable-agent-layer",
3
- "version": "0.81.0",
3
+ "version": "0.81.1",
4
4
  "description": "PAL — Portable Agent Layer: persistent personal context for AI coding assistants",
5
5
  "type": "module",
6
6
  "bin": {
@@ -8,11 +8,6 @@ import { extractKeywords, similarity } from "./text-similarity";
8
8
 
9
9
  export type Reaction = "corrected" | "repeated" | "approved" | "new-topic" | "follow-up";
10
10
 
11
- export interface ReplyContext {
12
- previousPrompt?: string;
13
- replyKeywords: string[];
14
- }
15
-
16
11
  const CORRECTION_RE =
17
12
  /^(?:(?:no|nope|wrong)(?:[,.!]|$)|(?:that'?s|this is|it'?s) (?:wrong|not)\b|not what i\b|i said\b|i told you\b|you forgot\b|you missed\b|still (?:broken|failing|wrong)\b)/i;
18
13
 
@@ -20,15 +15,16 @@ const APPROVAL_RE =
20
15
  /^(?:good|great|nice|perfect|cool|amazing|awesome|excellent|brilliant|works|it works|thanks|thank you|thx|y+e+s+|yep|yeah|yup|sure|exactly|correct|agreed|lgtm|well done|love it|sounds good|looks good|that'?s it|(?:it|this|that) (?:is|'s) (?:fine|good|great|right))\b/i;
21
16
 
22
17
  const GO_AHEAD_RE =
23
- /^(?:go ahead|go|do it|proceed|continue|merge|push|ship|commit|build|approved|let'?s (?:see|do|go))\b/i;
18
+ /^(?:go ahead|go|do it|proceed|continue|merge|push|ship|commit|build|start|fix (?:all|them|both)|approved|let'?s (?:see|do|go))\b/i;
19
+
20
+ const NEW_TOPIC_RE =
21
+ /^(?:(?:ok(?:ay)?|btw|also),?\s+)?(?:new (?:\w+ ){0,2}(?:task|topic|question|thing)|(?:different|unrelated|separate|another) (?:task|topic|question|thing)|unrelated\b|switching (?:gears|topics?)|change of (?:topic|subject))/i;
24
22
 
25
23
  const GO_AHEAD_MAX_WORDS = 6;
26
24
  const BARE_OK_RE = /^(?:ok(?:ay)?|k)[,.!]?(?:\s+\S+){0,2}$/i;
27
25
  const HEDGE_RE = /\b(?:but|however|though|except|instead)\b/i;
28
26
  const REPEAT_SIMILARITY = 0.6;
29
27
  const REPEAT_MIN_KEYWORDS = 5;
30
- const NEW_TOPIC_MIN_KEYWORDS = 4;
31
- const NEW_TOPIC_MAX_OVERLAP = 0.15;
32
28
 
33
29
  export function isCorrection(text: string): boolean {
34
30
  return CORRECTION_RE.test(text.trim());
@@ -39,33 +35,36 @@ export function isRepeat(text: string, previous: string | undefined): boolean {
39
35
  return similarity(text, previous) >= REPEAT_SIMILARITY;
40
36
  }
41
37
 
38
+ function isShortGoAhead(sentence: string): boolean {
39
+ return GO_AHEAD_RE.test(sentence) && sentence.split(/\s+/).length <= GO_AHEAD_MAX_WORDS;
40
+ }
41
+
42
+ function lastSentence(text: string): string {
43
+ return (
44
+ text
45
+ .split(/[.!\n]\s*/)
46
+ .filter(Boolean)
47
+ .at(-1)
48
+ ?.trim() ?? ""
49
+ );
50
+ }
51
+
42
52
  function isApproval(text: string): boolean {
43
53
  const trimmed = text.trim();
44
- if (HEDGE_RE.test(trimmed.split(/[.!?\n]/)[0])) return false;
54
+ if (trimmed.includes("?") || HEDGE_RE.test(trimmed.split(/[.!?\n]/)[0])) return false;
45
55
  if (APPROVAL_RE.test(trimmed) || BARE_OK_RE.test(trimmed)) return true;
46
- return GO_AHEAD_RE.test(trimmed) && trimmed.split(/\s+/).length <= GO_AHEAD_MAX_WORDS;
56
+ return isShortGoAhead(trimmed) || isShortGoAhead(lastSentence(trimmed));
47
57
  }
48
58
 
49
- /** Few of the message's words appear in the exchange it follows. */
50
- function isNewTopic(text: string, context: ReplyContext): boolean {
51
- const words = extractKeywords(text);
52
- if (words.size < NEW_TOPIC_MIN_KEYWORDS) return false;
53
- const exchange = new Set([
54
- ...context.replyKeywords,
55
- ...extractKeywords(context.previousPrompt ?? ""),
56
- ]);
57
- const shared = [...words].filter((w) => exchange.has(w)).length;
58
- return shared / words.size <= NEW_TOPIC_MAX_OVERLAP;
59
+ /** Word overlap was measured against real messages and does not separate topics; only saying so does. */
60
+ function isNewTopic(text: string): boolean {
61
+ return NEW_TOPIC_RE.test(text.trim());
59
62
  }
60
63
 
61
- export function reactionTo(text: string, context: ReplyContext): Reaction {
64
+ export function reactionTo(text: string, previousPrompt: string | undefined): Reaction {
62
65
  if (isCorrection(text)) return "corrected";
63
- if (isRepeat(text, context.previousPrompt)) return "repeated";
66
+ if (isRepeat(text, previousPrompt)) return "repeated";
64
67
  if (isApproval(text)) return "approved";
65
- if (isNewTopic(text, context)) return "new-topic";
68
+ if (isNewTopic(text)) return "new-topic";
66
69
  return "follow-up";
67
70
  }
68
-
69
- export function replyKeywords(reply: string): string[] {
70
- return [...extractKeywords(reply)];
71
- }
@@ -19,7 +19,6 @@ import {
19
19
  isRepeat,
20
20
  type Reaction,
21
21
  reactionTo,
22
- replyKeywords,
23
22
  } from "./interaction-reaction";
24
23
  import { keepSample, replyEnd } from "./interaction-samples";
25
24
  import { ensureDir, paths } from "./paths";
@@ -50,7 +49,6 @@ interface SessionTrack {
50
49
  lastPrompt?: string;
51
50
  lastPromptAt?: string;
52
51
  reply?: ReplyShape;
53
- replyKeywords?: string[];
54
52
  replyEnd?: string;
55
53
  recent: TurnEvent[];
56
54
  mood?: string;
@@ -117,12 +115,7 @@ function measureTurn(
117
115
  repeated: isRepeat(text, track.lastPrompt),
118
116
  corrected: isCorrection(text),
119
117
  reply: reply ? replyFeatures(reply) : null,
120
- reaction: reply
121
- ? reactionTo(text, {
122
- previousPrompt: track.lastPrompt,
123
- replyKeywords: track.replyKeywords ?? [],
124
- })
125
- : null,
118
+ reaction: reply ? reactionTo(text, track.lastPrompt) : null,
126
119
  };
127
120
  }
128
121
 
@@ -211,7 +204,6 @@ export function recordReply(
211
204
  ...track,
212
205
  updated: now.toISOString(),
213
206
  reply: replyShape(reply, now),
214
- replyKeywords: replyKeywords(reply),
215
207
  replyEnd: replyEnd(reply),
216
208
  });
217
209
  }