a2acalling 0.6.48 → 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
@@ -23,6 +23,10 @@ const {
23
23
  const { findAvailablePort } = require('./lib/port-scanner');
24
24
  const { createLogger } = require('./lib/logger');
25
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');
26
30
 
27
31
  const DEFAULT_PORTS = [80, 3001, 8080, 8443, 9001];
28
32
  const requestedPort = process.env.PORT ? parseInt(process.env.PORT, 10)
@@ -61,6 +65,7 @@ function loadAgentContext() {
61
65
 
62
66
  const agentContext = loadAgentContext();
63
67
  const tokenStore = new TokenStore();
68
+ const config = new A2AConfig();
64
69
  const runtime = createRuntimeAdapter({
65
70
  workspaceDir,
66
71
  agentContext,
@@ -691,30 +696,61 @@ async function callAgent(message, a2aContext) {
691
696
  * Generate strategic summary via sub-agent
692
697
  */
693
698
  async function generateSummary(messages, callerInfo) {
694
- const messageText = messages.map(m => {
695
- const role = m.direction === 'inbound' ? `[${callerInfo?.name || 'Caller'}]` : '[You]';
696
- return `${role}: ${m.content}`;
697
- }).join('\n');
698
-
699
- const callerDesc = `${callerInfo?.name || 'Unknown'}${callerInfo?.owner ? ` (${callerInfo.owner}'s agent)` : ''}`;
700
-
701
- const prompt = `Summarize this A2A call for the owner. Write from the owner's perspective.
702
-
703
- Conversation with ${callerDesc}:
704
- ${messageText}
705
-
706
- Structure your summary with these sections:
707
-
708
- **Who:** Who called, who they represent, key facts about them.
709
- **Key Discoveries:** What was learned about the other side — capabilities, interests, blind spots.
710
- **Collaboration Potential:** Rate HIGH/MEDIUM/LOW. List specific opportunities identified.
711
- **What We Learned vs Shared:** Brief information exchange audit — what did we get, what did we give.
712
- **Recommended Follow-Up:**
713
- - [ ] Actionable item 1
714
- - [ ] Actionable item 2
715
- **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
+ }
716
715
 
717
- 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
+ });
718
754
 
719
755
  try {
720
756
  return await runtime.summarize({
@@ -723,13 +759,13 @@ Be concise but specific. No filler.`;
723
759
  messages,
724
760
  callerInfo,
725
761
  traceId: callerInfo?.trace_id || callerInfo?.traceId,
726
- conversationId: callerInfo?.conversation_id || callerInfo?.conversationId
762
+ conversationId
727
763
  });
728
764
  } catch (err) {
729
765
  logger.error('Summary generation failed', {
730
766
  event: 'summary_generation_failed',
731
767
  traceId: callerInfo?.trace_id || callerInfo?.traceId,
732
- conversationId: callerInfo?.conversation_id || callerInfo?.conversationId,
768
+ conversationId,
733
769
  error_code: 'SUMMARY_GENERATION_FAILED',
734
770
  hint: 'Check summarizer runtime and command configuration for summary stage.',
735
771
  error: err,
@@ -773,17 +809,23 @@ async function notifyOwner({ level, token, caller, message, conversation_id, tra
773
809
 
774
810
  const app = express();
775
811
  app.use(express.json());
812
+ let activeCallMonitor = null;
813
+ let updateManager = null;
776
814
 
777
815
  // Minimal owner dashboard (local by default unless A2A_ADMIN_TOKEN is provided)
778
816
  // All routes under /api/a2a/* so reverse proxy config stays simple.
779
817
  app.use('/api/a2a/dashboard', createDashboardApiRouter({
780
818
  tokenStore,
781
819
  agentContext,
820
+ config,
821
+ getUpdateManager: () => updateManager,
782
822
  logger: logger.child({ component: 'a2a.dashboard' })
783
823
  }));
784
824
  app.use('/api/a2a/dashboard', createDashboardUiRouter({
785
825
  tokenStore,
786
826
  agentContext,
827
+ config,
828
+ getUpdateManager: () => updateManager,
787
829
  logger: logger.child({ component: 'a2a.dashboard' })
788
830
  }));
789
831
 
@@ -802,6 +844,9 @@ app.use('/callbook', createCallbookRouter());
802
844
  app.use('/api/a2a', createRoutes({
803
845
  tokenStore,
804
846
  logger: logger.child({ component: 'a2a.routes' }),
847
+ onCallMonitor: (monitor) => {
848
+ activeCallMonitor = monitor;
849
+ },
805
850
 
806
851
  async handleMessage(message, context, options) {
807
852
  const traceId = context.trace_id || null;
@@ -905,6 +950,50 @@ async function startServer() {
905
950
  }
906
951
  });
907
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
+ }
908
997
  });
909
998
 
910
999
  server.on('error', (err) => {
@@ -920,8 +1009,8 @@ async function startServer() {
920
1009
  throw err;
921
1010
  });
922
1011
 
923
- // Graceful shutdown: clean up PID file
924
1012
  function shutdown() {
1013
+ if (updateManager) updateManager.stop();
925
1014
  removePidFile();
926
1015
  server.close(() => process.exit(0));
927
1016
  // Force exit after 5s if connections won't close