a2acalling 0.6.38 → 0.6.40
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
|
@@ -24,6 +24,67 @@ const { createLogger } = require('./logger');
|
|
|
24
24
|
|
|
25
25
|
const logger = createLogger({ component: 'a2a.conversation-driver' });
|
|
26
26
|
|
|
27
|
+
/**
|
|
28
|
+
* Detect remote termination intent from response text.
|
|
29
|
+
* Returns true if the remote agent is clearly trying to end the conversation.
|
|
30
|
+
*/
|
|
31
|
+
const TERMINATION_PATTERNS = [
|
|
32
|
+
/\[TERMINAT/i,
|
|
33
|
+
/\[DISCONNECT/i,
|
|
34
|
+
/\[END.?CALL\]/i,
|
|
35
|
+
/\[CLOSING\]/i,
|
|
36
|
+
/\bREFUSING\s+(TO\s+)?(CONTINU|RESPOND|ENGAG)/i,
|
|
37
|
+
/\bcall\s+complet(ed|e)\b/i,
|
|
38
|
+
/\bconversation\s+(is\s+)?(over|ended|closed|complet)/i,
|
|
39
|
+
/\bclosing\s+this\s+(call|conversation|connection)\b/i,
|
|
40
|
+
/\bgoodbye\b.*\b(end|clos|terminat)/i,
|
|
41
|
+
/\bdisconnect(ing|ed)\b/i
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
function detectRemoteTermination(text) {
|
|
45
|
+
if (!text || typeof text !== 'string') return false;
|
|
46
|
+
return TERMINATION_PATTERNS.some(pattern => pattern.test(text));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Infer collaboration state progression when the runtime doesn't emit
|
|
51
|
+
* <collab_state> tags (generic/fallback mode). Advances phase based on
|
|
52
|
+
* turn count and estimates overlap from remote text analysis.
|
|
53
|
+
*/
|
|
54
|
+
function inferStateProgression(collabState, remoteText, turn) {
|
|
55
|
+
const patch = {};
|
|
56
|
+
|
|
57
|
+
// Phase progression based on turn count
|
|
58
|
+
if (collabState.phase === 'handshake' && turn >= 2) {
|
|
59
|
+
patch.phase = 'exploring';
|
|
60
|
+
} else if (collabState.phase === 'exploring' && turn >= 5) {
|
|
61
|
+
patch.phase = 'deepening';
|
|
62
|
+
} else if (collabState.phase === 'deepening' && turn >= 8) {
|
|
63
|
+
patch.phase = 'converging';
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Estimate overlap from remote text length and engagement signals
|
|
67
|
+
const text = (remoteText || '').toLowerCase();
|
|
68
|
+
const engagementSignals = [
|
|
69
|
+
/\binteresting\b/, /\bgreat\b/, /\bexciting\b/, /\bagree\b/,
|
|
70
|
+
/\bcollaborat/i, /\bpartner/i, /\btogether\b/, /\bshare\b/,
|
|
71
|
+
/\bopportunit/i, /\bsynerg/i, /\bcomplement/i, /\balign/i,
|
|
72
|
+
/\?/ // questions indicate engagement
|
|
73
|
+
];
|
|
74
|
+
const matchCount = engagementSignals.filter(p => p.test(text)).length;
|
|
75
|
+
const textLengthFactor = Math.min(text.length / 500, 1); // longer = more engaged
|
|
76
|
+
const engagement = (matchCount / engagementSignals.length) * 0.5 + textLengthFactor * 0.3;
|
|
77
|
+
|
|
78
|
+
// Blend current overlap with new engagement signal
|
|
79
|
+
const newOverlap = collabState.overlapScore * 0.6 + engagement * 0.4;
|
|
80
|
+
patch.overlapScore = Math.max(0.1, Math.min(0.95, newOverlap));
|
|
81
|
+
|
|
82
|
+
// Confidence increases over turns
|
|
83
|
+
patch.confidence = Math.min(0.9, 0.25 + turn * 0.08);
|
|
84
|
+
|
|
85
|
+
return patch;
|
|
86
|
+
}
|
|
87
|
+
|
|
27
88
|
class ConversationDriver {
|
|
28
89
|
/**
|
|
29
90
|
* @param {object} options
|
|
@@ -231,6 +292,15 @@ Be concise but specific. No filler.`;
|
|
|
231
292
|
break;
|
|
232
293
|
}
|
|
233
294
|
|
|
295
|
+
// 3b. Detect termination intent from remote text even if can_continue is true
|
|
296
|
+
if (detectRemoteTermination(remoteText)) {
|
|
297
|
+
logger.info('Remote text indicates termination intent', {
|
|
298
|
+
event: 'driver_remote_text_close',
|
|
299
|
+
data: { turn: turn + 1, conversationId, preview: remoteText.slice(0, 80) }
|
|
300
|
+
});
|
|
301
|
+
break;
|
|
302
|
+
}
|
|
303
|
+
|
|
234
304
|
if (collabState.closeSignal && collabState.turnCount >= this.minTurns) {
|
|
235
305
|
logger.info('Local close signal met minimum turns', {
|
|
236
306
|
event: 'driver_local_close',
|
|
@@ -309,6 +379,12 @@ Be concise but specific. No filler.`;
|
|
|
309
379
|
if (parsed.statePatch.confidence != null) {
|
|
310
380
|
collabState.confidence = Math.max(0, Math.min(1, parsed.statePatch.confidence));
|
|
311
381
|
}
|
|
382
|
+
} else {
|
|
383
|
+
// No <collab_state> in response (generic/fallback runtime) — infer progression
|
|
384
|
+
const inferred = inferStateProgression(collabState, remoteText, turn + 1);
|
|
385
|
+
if (inferred.phase) collabState.phase = inferred.phase;
|
|
386
|
+
if (inferred.overlapScore != null) collabState.overlapScore = inferred.overlapScore;
|
|
387
|
+
if (inferred.confidence != null) collabState.confidence = inferred.confidence;
|
|
312
388
|
}
|
|
313
389
|
|
|
314
390
|
// 7. Persist collab state to DB
|
|
@@ -376,4 +452,4 @@ Be concise but specific. No filler.`;
|
|
|
376
452
|
}
|
|
377
453
|
}
|
|
378
454
|
|
|
379
|
-
module.exports = { ConversationDriver };
|
|
455
|
+
module.exports = { ConversationDriver, detectRemoteTermination, inferStateProgression };
|