a2acalling 0.6.39 → 0.6.41
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/bin/cli.js +5 -5
- package/package.json +1 -1
- package/src/lib/conversation-driver.js +77 -1
package/bin/cli.js
CHANGED
|
@@ -1245,7 +1245,7 @@ a2a add "${inviteUrl}" "${ownerText || 'friend'}" && a2a call "${ownerText || 'f
|
|
|
1245
1245
|
|
|
1246
1246
|
if (!target || !message) {
|
|
1247
1247
|
console.error('Usage: a2a call <contact_or_url> <message>');
|
|
1248
|
-
console.error(' --
|
|
1248
|
+
console.error(' --single Single-turn call (one message, one response)');
|
|
1249
1249
|
console.error(' --min-turns N Minimum turns before close (default: 8)');
|
|
1250
1250
|
console.error(' --max-turns N Maximum turns (default: 25)');
|
|
1251
1251
|
process.exit(1);
|
|
@@ -1262,10 +1262,10 @@ a2a add "${inviteUrl}" "${ownerText || 'friend'}" && a2a call "${ownerText || 'f
|
|
|
1262
1262
|
}
|
|
1263
1263
|
}
|
|
1264
1264
|
|
|
1265
|
-
const
|
|
1265
|
+
const single = Boolean(args.flags.single);
|
|
1266
1266
|
const callerName = args.flags.name || 'CLI User';
|
|
1267
1267
|
|
|
1268
|
-
if (
|
|
1268
|
+
if (!single) {
|
|
1269
1269
|
// Multi-turn conversation via ConversationDriver
|
|
1270
1270
|
const { ConversationDriver } = require('../src/lib/conversation-driver');
|
|
1271
1271
|
const { createRuntimeAdapter } = require('../src/lib/runtime-adapter');
|
|
@@ -2292,8 +2292,8 @@ Conversations:
|
|
|
2292
2292
|
conversations end <id> End and summarize conversation
|
|
2293
2293
|
|
|
2294
2294
|
Calling:
|
|
2295
|
-
call <contact|url> <msg> Call a contact (
|
|
2296
|
-
--
|
|
2295
|
+
call <contact|url> <msg> Call a contact (multi-turn by default)
|
|
2296
|
+
--single Single-turn call (one message, one response)
|
|
2297
2297
|
--min-turns N Minimum turns before close (default: 8)
|
|
2298
2298
|
--max-turns N Maximum turns (default: 25)
|
|
2299
2299
|
ping <url> Check if agent is reachable
|
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 };
|