a2acalling 0.6.43 → 0.6.45

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.
@@ -56,7 +56,7 @@ function detectRemoteTermination(text) {
56
56
 
57
57
  /**
58
58
  * Infer collaboration state progression when the runtime doesn't emit
59
- * <collab_state> tags (generic/fallback mode). Advances phase based on
59
+ * <collab_state> tags (e.g. OpenClaw without adaptive mode). Advances phase based on
60
60
  * turn count and estimates overlap from remote text analysis.
61
61
  */
62
62
  function inferStateProgression(collabState, remoteText, turn) {
@@ -128,8 +128,11 @@ class ConversationDriver {
128
128
  this.tier = options.tier || 'public';
129
129
  this.summarizer = options.summarizer || null;
130
130
  this.ownerContext = options.ownerContext || {};
131
+ this.claudeMode = options.runtime?.mode === 'claude';
132
+ this.claudeTimeoutMs = options.claudeTimeoutMs || 180000;
131
133
 
132
- this.client = new A2AClient({ caller: this.caller, timeout: 65000 });
134
+ const clientTimeout = this.claudeMode ? 200000 : 65000;
135
+ this.client = new A2AClient({ caller: this.caller, timeout: clientTimeout });
133
136
  }
134
137
 
135
138
  /**
@@ -351,18 +354,30 @@ Be concise but specific. No filler.`;
351
354
  // 5. Call runtime.runTurn() to generate next message
352
355
  const sessionId = `a2a-${conversationId}`;
353
356
  let rawResponse;
357
+ const contextPayload = {
358
+ conversationId,
359
+ tier: this.tier,
360
+ ownerName: this.agentContext.owner,
361
+ agentName: this.agentContext.name,
362
+ roleContext: 'You initiated this call.'
363
+ };
364
+ if (this.claudeMode) {
365
+ contextPayload.turnCount = turn + 1;
366
+ contextPayload.maxTurns = this.maxTurns;
367
+ contextPayload.phase = collabState.phase;
368
+ contextPayload.overlapScore = collabState.overlapScore;
369
+ contextPayload.activeThreads = collabState.activeThreads;
370
+ contextPayload.candidateCollaborations = collabState.candidateCollaborations;
371
+ contextPayload.closeSignal = collabState.closeSignal;
372
+ }
354
373
  try {
355
374
  rawResponse = await this.runtime.runTurn({
356
375
  sessionId,
357
376
  prompt,
358
377
  message: remoteText,
359
378
  caller: this.caller,
360
- timeoutMs: 65000,
361
- context: {
362
- conversationId,
363
- tier: this.tier,
364
- ownerName: this.agentContext.owner
365
- }
379
+ timeoutMs: this.claudeMode ? this.claudeTimeoutMs : 65000,
380
+ context: contextPayload
366
381
  });
367
382
  } catch (err) {
368
383
  logger.error('Runtime turn failed', {
@@ -374,33 +389,59 @@ Be concise but specific. No filler.`;
374
389
  }
375
390
 
376
391
  // 6. Extract collab state from response
377
- const parsed = extractCollaborationState(rawResponse);
378
- nextMessage = parsed.cleanText || rawResponse;
379
-
380
- if (parsed.hasState && parsed.statePatch) {
381
- if (parsed.statePatch.phase) collabState.phase = parsed.statePatch.phase;
382
- if (parsed.statePatch.overlapScore != null) {
383
- collabState.overlapScore = Math.max(0, Math.min(1, parsed.statePatch.overlapScore));
392
+ // In claude mode, use the side channel (getLastTurnMeta) for state/flags
393
+ const turnMeta = this.claudeMode ? this.runtime.getLastTurnMeta?.(sessionId) : null;
394
+
395
+ if (turnMeta?.statePatch) {
396
+ // Claude subagent returned structured state — apply it directly
397
+ nextMessage = rawResponse;
398
+ const sp = turnMeta.statePatch;
399
+ if (sp.phase) collabState.phase = sp.phase;
400
+ if (sp.overlapScore != null) {
401
+ collabState.overlapScore = Math.max(0, Math.min(1, sp.overlapScore));
384
402
  }
385
- if (Array.isArray(parsed.statePatch.activeThreads)) {
386
- collabState.activeThreads = parsed.statePatch.activeThreads.slice(0, 4);
403
+ if (Array.isArray(sp.activeThreads)) {
404
+ collabState.activeThreads = sp.activeThreads.slice(0, 4);
387
405
  }
388
- if (Array.isArray(parsed.statePatch.candidateCollaborations)) {
389
- collabState.candidateCollaborations = parsed.statePatch.candidateCollaborations.slice(0, 4);
406
+ if (Array.isArray(sp.candidateCollaborations)) {
407
+ collabState.candidateCollaborations = sp.candidateCollaborations.slice(0, 4);
390
408
  }
391
- if (parsed.statePatch.closeSignal != null) {
392
- collabState.closeSignal = Boolean(parsed.statePatch.closeSignal);
409
+ if (sp.closeSignal != null) {
410
+ collabState.closeSignal = Boolean(sp.closeSignal);
393
411
  }
394
- if (parsed.statePatch.confidence != null) {
395
- collabState.confidence = Math.max(0, Math.min(1, parsed.statePatch.confidence));
412
+ if (sp.confidence != null) {
413
+ collabState.confidence = Math.max(0, Math.min(1, sp.confidence));
396
414
  }
397
415
  } else {
398
- // No <collab_state> in response (generic/fallback runtime) — infer progression
399
- const inferred = inferStateProgression(collabState, remoteText, turn + 1);
400
- if (inferred.phase) collabState.phase = inferred.phase;
401
- if (inferred.overlapScore != null) collabState.overlapScore = inferred.overlapScore;
402
- if (inferred.confidence != null) collabState.confidence = inferred.confidence;
403
- if (inferred.closeSignal != null) collabState.closeSignal = inferred.closeSignal;
416
+ // Non-claude path: extract from <collab_state> tags in response text
417
+ const parsed = extractCollaborationState(rawResponse);
418
+ nextMessage = parsed.cleanText || rawResponse;
419
+
420
+ if (parsed.hasState && parsed.statePatch) {
421
+ if (parsed.statePatch.phase) collabState.phase = parsed.statePatch.phase;
422
+ if (parsed.statePatch.overlapScore != null) {
423
+ collabState.overlapScore = Math.max(0, Math.min(1, parsed.statePatch.overlapScore));
424
+ }
425
+ if (Array.isArray(parsed.statePatch.activeThreads)) {
426
+ collabState.activeThreads = parsed.statePatch.activeThreads.slice(0, 4);
427
+ }
428
+ if (Array.isArray(parsed.statePatch.candidateCollaborations)) {
429
+ collabState.candidateCollaborations = parsed.statePatch.candidateCollaborations.slice(0, 4);
430
+ }
431
+ if (parsed.statePatch.closeSignal != null) {
432
+ collabState.closeSignal = Boolean(parsed.statePatch.closeSignal);
433
+ }
434
+ if (parsed.statePatch.confidence != null) {
435
+ collabState.confidence = Math.max(0, Math.min(1, parsed.statePatch.confidence));
436
+ }
437
+ } else {
438
+ // No <collab_state> in response (generic/fallback runtime) — infer progression
439
+ const inferred = inferStateProgression(collabState, remoteText, turn + 1);
440
+ if (inferred.phase) collabState.phase = inferred.phase;
441
+ if (inferred.overlapScore != null) collabState.overlapScore = inferred.overlapScore;
442
+ if (inferred.confidence != null) collabState.confidence = inferred.confidence;
443
+ if (inferred.closeSignal != null) collabState.closeSignal = inferred.closeSignal;
444
+ }
404
445
  }
405
446
 
406
447
  // 6b. Overlap flatline detection — if overlap hasn't changed significantly
@@ -426,6 +467,16 @@ Be concise but specific. No filler.`;
426
467
  }
427
468
  }
428
469
 
470
+ // 7b. Store flags from claude subagent responses
471
+ if (turnMeta?.flags?.length > 0 && this.convStore && dbConversationStarted) {
472
+ this.convStore.addMessage(conversationId, {
473
+ direction: 'outbound',
474
+ role: 'system',
475
+ content: `[flags] ${turnMeta.flags.map(f => f.content || f.type).join('; ')}`,
476
+ metadata: JSON.stringify({ flags: turnMeta.flags, turn: turn + 1 })
477
+ });
478
+ }
479
+
429
480
  // onTurn callback for progress output
430
481
  if (this.onTurn) {
431
482
  try {
@@ -32,7 +32,7 @@ function dedupeByTopic(items) {
32
32
  seen.add(topic.toLowerCase());
33
33
  out.push({
34
34
  topic,
35
- description: normalizeTopic(item && (item.description || item.detail))
35
+ description: normalizeTopic(item && item.description)
36
36
  });
37
37
  }
38
38
  return out;
@@ -152,7 +152,7 @@ function saveManifest(manifest) {
152
152
  */
153
153
  function getTopicsForTier(tier) {
154
154
  const manifest = loadManifest();
155
- const tiers = manifest.tiers || manifest.topics || {};
155
+ const tiers = manifest.tiers || {};
156
156
 
157
157
  const tierIndex = TIER_HIERARCHY.indexOf(tier);
158
158
  if (tierIndex === -1) {
@@ -195,7 +195,7 @@ function getTopicsForTier(tier) {
195
195
  function formatTopicsForPrompt(tierTopics) {
196
196
  const formatTopicList = (items) => {
197
197
  if (!items || items.length === 0) return ' (none specified)';
198
- return items.map(item => ` - ${item.topic}: ${item.description || item.detail || ''}`).join('\n');
198
+ return items.map(item => ` - ${item.topic}: ${item.description || ''}`).join('\n');
199
199
  };
200
200
 
201
201
  const formatObjectiveList = (items) => {
@@ -363,10 +363,9 @@ function validateDisclosureSubmission(data) {
363
363
  return { valid: false, manifest: null, errors: ['Submission must be a non-null object'] };
364
364
  }
365
365
 
366
- // Support both new format (tiers) and legacy format (topics)
367
- const tiersData = data.tiers || data.topics;
366
+ const tiersData = data.tiers;
368
367
  if (!tiersData || typeof tiersData !== 'object' || Array.isArray(tiersData)) {
369
- errors.push('Submission must include a "tiers" object (or legacy "topics" object)');
368
+ errors.push('Submission must include a "tiers" object');
370
369
  return { valid: false, manifest: null, errors };
371
370
  }
372
371