a2acalling 0.6.47 → 0.6.49

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/src/server.js CHANGED
@@ -22,6 +22,11 @@ const {
22
22
  } = require('./lib/prompt-template');
23
23
  const { findAvailablePort } = require('./lib/port-scanner');
24
24
  const { createLogger } = require('./lib/logger');
25
+ const { writePidFile, removePidFile } = require('./lib/pid-file');
26
+ const { buildUnifiedSummaryPrompt } = require('./lib/summary-prompt');
27
+ const { A2AConfig } = require('./lib/config');
28
+ const { UpdateManager } = require('./lib/update-manager');
29
+ const { spawn } = require('child_process');
25
30
 
26
31
  const DEFAULT_PORTS = [80, 3001, 8080, 8443, 9001];
27
32
  const requestedPort = process.env.PORT ? parseInt(process.env.PORT, 10)
@@ -60,6 +65,7 @@ function loadAgentContext() {
60
65
 
61
66
  const agentContext = loadAgentContext();
62
67
  const tokenStore = new TokenStore();
68
+ const config = new A2AConfig();
63
69
  const runtime = createRuntimeAdapter({
64
70
  workspaceDir,
65
71
  agentContext,
@@ -690,30 +696,61 @@ async function callAgent(message, a2aContext) {
690
696
  * Generate strategic summary via sub-agent
691
697
  */
692
698
  async function generateSummary(messages, callerInfo) {
693
- const messageText = messages.map(m => {
694
- const role = m.direction === 'inbound' ? `[${callerInfo?.name || 'Caller'}]` : '[You]';
695
- return `${role}: ${m.content}`;
696
- }).join('\n');
697
-
698
- const callerDesc = `${callerInfo?.name || 'Unknown'}${callerInfo?.owner ? ` (${callerInfo.owner}'s agent)` : ''}`;
699
-
700
- const prompt = `Summarize this A2A call for the owner. Write from the owner's perspective.
701
-
702
- Conversation with ${callerDesc}:
703
- ${messageText}
704
-
705
- Structure your summary with these sections:
706
-
707
- **Who:** Who called, who they represent, key facts about them.
708
- **Key Discoveries:** What was learned about the other side — capabilities, interests, blind spots.
709
- **Collaboration Potential:** Rate HIGH/MEDIUM/LOW. List specific opportunities identified.
710
- **What We Learned vs Shared:** Brief information exchange audit — what did we get, what did we give.
711
- **Recommended Follow-Up:**
712
- - [ ] Actionable item 1
713
- - [ ] Actionable item 2
714
- **Assessment:** One-sentence strategic value judgment.
699
+ // Look up collaboration state if we have a conversation_id
700
+ const conversationId = callerInfo?.conversation_id || callerInfo?.conversationId;
701
+ let collaborationState = null;
702
+ if (conversationId) {
703
+ const collabSession = collaborationSessions.get(conversationId);
704
+ if (collabSession) {
705
+ collaborationState = {
706
+ phase: collabSession.phase,
707
+ overlapScore: collabSession.overlapScore,
708
+ turnCount: collabSession.turnCount,
709
+ activeThreads: collabSession.activeThreads,
710
+ candidateCollaborations: collabSession.candidateCollaborations,
711
+ closeSignal: collabSession.closeSignal
712
+ };
713
+ }
714
+ }
715
715
 
716
- Be concise but specific. No filler.`;
716
+ // Load disclosure manifest for the caller's tier
717
+ const tier = callerInfo?.tier || 'public';
718
+ let disclosure = null;
719
+ try {
720
+ const tierTopics = getTopicsForTier(tier);
721
+ if (tierTopics) {
722
+ disclosure = {
723
+ topics: tierTopics.topics || [],
724
+ objectives: tierTopics.objectives || [],
725
+ doNotDiscuss: tierTopics.do_not_discuss || [],
726
+ neverDisclose: tierTopics.never_disclose || []
727
+ };
728
+ }
729
+ } catch (e) {
730
+ // Disclosure is optional — continue without it
731
+ }
732
+
733
+ // Build transcript in unified format
734
+ const transcript = messages.map(m => ({
735
+ direction: m.direction,
736
+ content: m.content
737
+ }));
738
+
739
+ const prompt = buildUnifiedSummaryPrompt({
740
+ transcript,
741
+ callerInfo: {
742
+ name: callerInfo?.name || null,
743
+ owner: callerInfo?.owner || null,
744
+ context: callerInfo?.context || null
745
+ },
746
+ disclosure,
747
+ collaborationState,
748
+ ownerContext: {
749
+ agentName: agentContext.name,
750
+ ownerName: agentContext.owner,
751
+ goals: []
752
+ }
753
+ });
717
754
 
718
755
  try {
719
756
  return await runtime.summarize({
@@ -722,13 +759,13 @@ Be concise but specific. No filler.`;
722
759
  messages,
723
760
  callerInfo,
724
761
  traceId: callerInfo?.trace_id || callerInfo?.traceId,
725
- conversationId: callerInfo?.conversation_id || callerInfo?.conversationId
762
+ conversationId
726
763
  });
727
764
  } catch (err) {
728
765
  logger.error('Summary generation failed', {
729
766
  event: 'summary_generation_failed',
730
767
  traceId: callerInfo?.trace_id || callerInfo?.traceId,
731
- conversationId: callerInfo?.conversation_id || callerInfo?.conversationId,
768
+ conversationId,
732
769
  error_code: 'SUMMARY_GENERATION_FAILED',
733
770
  hint: 'Check summarizer runtime and command configuration for summary stage.',
734
771
  error: err,
@@ -772,17 +809,23 @@ async function notifyOwner({ level, token, caller, message, conversation_id, tra
772
809
 
773
810
  const app = express();
774
811
  app.use(express.json());
812
+ let activeCallMonitor = null;
813
+ let updateManager = null;
775
814
 
776
815
  // Minimal owner dashboard (local by default unless A2A_ADMIN_TOKEN is provided)
777
816
  // All routes under /api/a2a/* so reverse proxy config stays simple.
778
817
  app.use('/api/a2a/dashboard', createDashboardApiRouter({
779
818
  tokenStore,
780
819
  agentContext,
820
+ config,
821
+ getUpdateManager: () => updateManager,
781
822
  logger: logger.child({ component: 'a2a.dashboard' })
782
823
  }));
783
824
  app.use('/api/a2a/dashboard', createDashboardUiRouter({
784
825
  tokenStore,
785
826
  agentContext,
827
+ config,
828
+ getUpdateManager: () => updateManager,
786
829
  logger: logger.child({ component: 'a2a.dashboard' })
787
830
  }));
788
831
 
@@ -801,6 +844,9 @@ app.use('/callbook', createCallbookRouter());
801
844
  app.use('/api/a2a', createRoutes({
802
845
  tokenStore,
803
846
  logger: logger.child({ component: 'a2a.routes' }),
847
+ onCallMonitor: (monitor) => {
848
+ activeCallMonitor = monitor;
849
+ },
804
850
 
805
851
  async handleMessage(message, context, options) {
806
852
  const traceId = context.trace_id || null;
@@ -903,6 +949,51 @@ async function startServer() {
903
949
  features: ['adaptive collaboration', 'auto-contacts', 'summaries', 'dashboard']
904
950
  }
905
951
  });
952
+ writePidFile(process.pid);
953
+
954
+ if (!updateManager) {
955
+ const pkg = require('../package.json');
956
+ const restartFn = async () => {
957
+ const cliPath = path.join(__dirname, '..', 'bin', 'cli.js');
958
+ const helperScript = `
959
+ const { spawn } = require('child_process');
960
+ const startNext = () => {
961
+ const child = spawn(process.execPath, [${JSON.stringify(cliPath)}, 'server', '--port', ${JSON.stringify(String(port))}], {
962
+ detached: true,
963
+ stdio: 'ignore',
964
+ env: process.env
965
+ });
966
+ child.unref();
967
+ process.exit(0);
968
+ };
969
+ setTimeout(startNext, 1500);
970
+ `;
971
+ const helper = spawn(process.execPath, ['-e', helperScript], {
972
+ detached: true,
973
+ stdio: 'ignore',
974
+ env: process.env
975
+ });
976
+ helper.unref();
977
+ setTimeout(() => {
978
+ process.kill(process.pid, 'SIGTERM');
979
+ }, 150);
980
+ };
981
+
982
+ updateManager = new UpdateManager({
983
+ currentVersion: pkg.version,
984
+ config,
985
+ logger: logger.child({ component: 'a2a.updater' }),
986
+ getCallMonitor: () => activeCallMonitor,
987
+ restartFn
988
+ });
989
+ updateManager.start();
990
+ updateManager.triggerCheck({ reason: 'startup' }).catch((err) => {
991
+ logger.warn('Initial auto-update check failed', {
992
+ event: 'updater_startup_check_failed',
993
+ error: err
994
+ });
995
+ });
996
+ }
906
997
  });
907
998
 
908
999
  server.on('error', (err) => {
@@ -917,6 +1008,16 @@ async function startServer() {
917
1008
  }
918
1009
  throw err;
919
1010
  });
1011
+
1012
+ function shutdown() {
1013
+ if (updateManager) updateManager.stop();
1014
+ removePidFile();
1015
+ server.close(() => process.exit(0));
1016
+ // Force exit after 5s if connections won't close
1017
+ setTimeout(() => process.exit(0), 5000).unref();
1018
+ }
1019
+ process.on('SIGTERM', shutdown);
1020
+ process.on('SIGINT', shutdown);
920
1021
  }
921
1022
 
922
1023
  startServer();