instar 0.25.2 → 0.25.3

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.
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/commands/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AA0PH,UAAU,YAAY;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;2DACuD;IACvD,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAsnCD,wBAAsB,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAy9GtE;AAED,wBAAsB,UAAU,CAAC,OAAO,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAsDzE;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAuD5E"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/commands/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AA0PH,UAAU,YAAY;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;2DACuD;IACvD,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAsnCD,wBAAsB,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAikHtE;AAED,wBAAsB,UAAU,CAAC,OAAO,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAsDzE;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAuD5E"}
@@ -2856,32 +2856,63 @@ export async function startServer(options) {
2856
2856
  console.log(pc.green(' Session Watchdog enabled'));
2857
2857
  }
2858
2858
  // StallTriageNurse — LLM-powered session recovery (uses shared intelligence)
2859
+ // Platform-aware: works with Telegram topics AND Slack channels
2859
2860
  let triageNurse;
2860
- if (config.monitoring.triage?.enabled && telegram) {
2861
+ if (config.monitoring.triage?.enabled && (telegram || _slackAdapter)) {
2861
2862
  triageNurse = new StallTriageNurse({
2862
2863
  captureSessionOutput: (name, lines) => sessionManager.captureOutput(name, lines),
2863
2864
  isSessionAlive: (name) => sessionManager.isSessionAlive(name),
2864
2865
  sendKey: (name, key) => sessionManager.sendKey(name, key),
2865
2866
  sendInput: (name, text) => sessionManager.sendInput(name, text),
2866
2867
  getTopicHistory: (topicId, limit) => {
2867
- const entries = telegram.getTopicHistory(topicId, limit);
2868
- return entries.map(e => ({
2869
- text: e.text,
2870
- fromUser: e.fromUser,
2871
- timestamp: e.timestamp,
2872
- }));
2868
+ // Check if this is a Slack synthetic ID
2869
+ const slackChId = slackProxyChannelMap.get(topicId);
2870
+ if (slackChId && _slackAdapter) {
2871
+ const msgs = _slackAdapter.getChannelMessages(slackChId, limit);
2872
+ return msgs.map(m => ({ text: m.text, fromUser: true, timestamp: new Date(parseFloat(m.ts) * 1000).toISOString() }));
2873
+ }
2874
+ if (telegram) {
2875
+ const entries = telegram.getTopicHistory(topicId, limit);
2876
+ return entries.map(e => ({ text: e.text, fromUser: e.fromUser, timestamp: e.timestamp }));
2877
+ }
2878
+ return [];
2879
+ },
2880
+ sendToTopic: async (topicId, text) => {
2881
+ const slackChId = slackProxyChannelMap.get(topicId);
2882
+ if (slackChId && _slackAdapter) {
2883
+ await _slackAdapter.sendToChannel(slackChId, text);
2884
+ return;
2885
+ }
2886
+ if (telegram)
2887
+ await telegram.sendToTopic(topicId, text);
2888
+ },
2889
+ respawnSession: (name, topicId, options) => {
2890
+ if (telegram) {
2891
+ return respawnSessionForTopic(sessionManager, telegram, name, topicId, undefined, topicMemory, undefined, undefined, options);
2892
+ }
2893
+ // Slack respawn: kill and let next message trigger fresh session
2894
+ const stuckSession = sessionManager.listRunningSessions().find(s => s.tmuxSession === name);
2895
+ if (stuckSession)
2896
+ sessionManager.killSession(stuckSession.id);
2897
+ return Promise.resolve();
2898
+ },
2899
+ clearStallForTopic: (topicId) => {
2900
+ const slackChId = slackProxyChannelMap.get(topicId);
2901
+ if (slackChId && _slackAdapter) {
2902
+ _slackAdapter.clearStallTracking(slackChId);
2903
+ return;
2904
+ }
2905
+ if (telegram)
2906
+ telegram.clearStallTracking(topicId);
2873
2907
  },
2874
- sendToTopic: (topicId, text) => telegram.sendToTopic(topicId, text),
2875
- respawnSession: (name, topicId, options) => respawnSessionForTopic(sessionManager, telegram, name, topicId, undefined, topicMemory, undefined, undefined, options),
2876
- clearStallForTopic: (topicId) => telegram.clearStallTracking(topicId),
2877
2908
  }, {
2878
2909
  config: config.monitoring.triage,
2879
2910
  state,
2880
2911
  intelligence: sharedIntelligence,
2881
2912
  });
2882
- // Wire nurse into TelegramAdapter stall detection
2913
+ // Wire nurse into stall detection — both Telegram and Slack
2883
2914
  // Note: presenceProxy may be set later — use late-binding check
2884
- telegram.onStallDetected = async (topicId, sessionName, messageText, injectedAt) => {
2915
+ const stallTriageHandler = async (topicId, sessionName, messageText, injectedAt) => {
2885
2916
  // If PresenceProxy Tier 3 is actively handling this topic, defer to it
2886
2917
  if (presenceProxy) {
2887
2918
  const proxyState = presenceProxy.getState(topicId);
@@ -2896,27 +2927,58 @@ export async function startServer(options) {
2896
2927
  const result = await triageNurse.triage(topicId, sessionName, messageText, injectedAt, 'telegram_stall');
2897
2928
  return { resolved: result.resolved };
2898
2929
  };
2930
+ if (telegram) {
2931
+ telegram.onStallDetected = stallTriageHandler;
2932
+ }
2899
2933
  console.log(pc.green(' Stall Triage Nurse enabled'));
2900
2934
  }
2901
2935
  // TriageOrchestrator — next-gen session recovery with scoped Claude Code sessions
2936
+ // Platform-aware: works with Telegram topics AND Slack channels
2902
2937
  let triageOrchestrator;
2903
- if (config.monitoring.triageOrchestrator?.enabled && telegram) {
2938
+ if (config.monitoring.triageOrchestrator?.enabled && (telegram || _slackAdapter)) {
2904
2939
  triageOrchestrator = new TriageOrchestrator({
2905
2940
  captureSessionOutput: (name, lines) => sessionManager.captureOutput(name, lines),
2906
2941
  isSessionAlive: (name) => sessionManager.isSessionAlive(name),
2907
2942
  sendKey: (name, key) => sessionManager.sendKey(name, key),
2908
2943
  sendInput: (name, text) => sessionManager.sendInput(name, text),
2909
2944
  getTopicHistory: (topicId, limit) => {
2910
- const entries = telegram.getTopicHistory(topicId, limit);
2911
- return entries.map(e => ({
2912
- text: e.text,
2913
- fromUser: e.fromUser,
2914
- timestamp: e.timestamp,
2915
- }));
2945
+ const slackChId = slackProxyChannelMap.get(topicId);
2946
+ if (slackChId && _slackAdapter) {
2947
+ const msgs = _slackAdapter.getChannelMessages(slackChId, limit);
2948
+ return msgs.map(m => ({ text: m.text, fromUser: true, timestamp: new Date(parseFloat(m.ts) * 1000).toISOString() }));
2949
+ }
2950
+ if (telegram) {
2951
+ const entries = telegram.getTopicHistory(topicId, limit);
2952
+ return entries.map(e => ({ text: e.text, fromUser: e.fromUser, timestamp: e.timestamp }));
2953
+ }
2954
+ return [];
2955
+ },
2956
+ sendToTopic: async (topicId, text) => {
2957
+ const slackChId = slackProxyChannelMap.get(topicId);
2958
+ if (slackChId && _slackAdapter) {
2959
+ await _slackAdapter.sendToChannel(slackChId, text);
2960
+ return;
2961
+ }
2962
+ if (telegram)
2963
+ await telegram.sendToTopic(topicId, text);
2964
+ },
2965
+ respawnSession: (name, topicId, options) => {
2966
+ if (telegram)
2967
+ return respawnSessionForTopic(sessionManager, telegram, name, topicId, undefined, topicMemory, undefined, undefined, options);
2968
+ const stuckSession = sessionManager.listRunningSessions().find(s => s.tmuxSession === name);
2969
+ if (stuckSession)
2970
+ sessionManager.killSession(stuckSession.id);
2971
+ return Promise.resolve();
2972
+ },
2973
+ clearStallForTopic: (topicId) => {
2974
+ const slackChId = slackProxyChannelMap.get(topicId);
2975
+ if (slackChId && _slackAdapter) {
2976
+ _slackAdapter.clearStallTracking(slackChId);
2977
+ return;
2978
+ }
2979
+ if (telegram)
2980
+ telegram.clearStallTracking(topicId);
2916
2981
  },
2917
- sendToTopic: (topicId, text) => telegram.sendToTopic(topicId, text),
2918
- respawnSession: (name, topicId, options) => respawnSessionForTopic(sessionManager, telegram, name, topicId, undefined, topicMemory, undefined, undefined, options),
2919
- clearStallForTopic: (topicId) => telegram.clearStallTracking(topicId),
2920
2982
  spawnTriageSession: (name, options) => sessionManager.spawnTriageSession(name, options),
2921
2983
  getTriageSessionUuid: (sessionName) => {
2922
2984
  return _topicResumeMap?.findUuidForSession(sessionName) ?? undefined;
@@ -2957,33 +3019,37 @@ export async function startServer(options) {
2957
3019
  state,
2958
3020
  });
2959
3021
  // TriageOrchestrator takes over stall detection from StallTriageNurse
2960
- telegram.onStallDetected = async (topicId, sessionName, messageText, injectedAt) => {
3022
+ const triageStallHandler = async (topicId, sessionName, messageText, injectedAt) => {
2961
3023
  const result = await triageOrchestrator.activate(topicId, sessionName, 'stall_detector', messageText, injectedAt);
2962
3024
  return { resolved: result.resolved };
2963
3025
  };
2964
- // Cancel triage when stall tracking clears (session responded)
2965
- const origClearStall = telegram.clearStallTracking.bind(telegram);
2966
- telegram.clearStallTracking = (topicId) => {
2967
- origClearStall(topicId);
2968
- triageOrchestrator.onTargetSessionResponded(topicId);
2969
- };
2970
- // Wire /triage command
2971
- telegram.onGetTriageStatus = (topicId) => {
2972
- const ts = triageOrchestrator.getTriageState(topicId);
2973
- if (!ts)
2974
- return null;
2975
- return {
2976
- active: true,
2977
- classification: ts.classification,
2978
- checkCount: ts.checkCount,
2979
- lastCheck: new Date(ts.lastCheckAt).toISOString(),
3026
+ if (telegram) {
3027
+ telegram.onStallDetected = triageStallHandler;
3028
+ // Cancel triage when stall tracking clears (session responded)
3029
+ const origClearStall = telegram.clearStallTracking.bind(telegram);
3030
+ telegram.clearStallTracking = (topicId) => {
3031
+ origClearStall(topicId);
3032
+ triageOrchestrator.onTargetSessionResponded(topicId);
2980
3033
  };
2981
- };
3034
+ // Wire /triage command
3035
+ telegram.onGetTriageStatus = (topicId) => {
3036
+ const ts = triageOrchestrator.getTriageState(topicId);
3037
+ if (!ts)
3038
+ return null;
3039
+ return {
3040
+ active: true,
3041
+ classification: ts.classification,
3042
+ checkCount: ts.checkCount,
3043
+ lastCheck: new Date(ts.lastCheckAt).toISOString(),
3044
+ };
3045
+ };
3046
+ }
2982
3047
  console.log(pc.green(' Triage Orchestrator enabled (replaces Stall Triage Nurse for stall detection)'));
2983
3048
  }
2984
3049
  // SessionRecovery — fast mechanical recovery (JSONL analysis, no LLM)
3050
+ // Platform-aware: works with Telegram topics AND Slack channels
2985
3051
  let sessionRecovery;
2986
- if (telegram) {
3052
+ if (telegram || _slackAdapter) {
2987
3053
  sessionRecovery = new SessionRecovery({ enabled: true, projectDir: config.projectDir }, {
2988
3054
  isSessionAlive: (name) => sessionManager.isSessionAlive(name),
2989
3055
  getPanePid: (name) => {
@@ -2999,6 +3065,13 @@ export async function startServer(options) {
2999
3065
  }
3000
3066
  },
3001
3067
  killSession: (name) => {
3068
+ // Route through SessionManager to fire beforeSessionKill hook
3069
+ const session = sessionManager.listRunningSessions().find(s => s.tmuxSession === name);
3070
+ if (session) {
3071
+ sessionManager.killSession(session.id);
3072
+ return;
3073
+ }
3074
+ // Fallback: direct tmux kill for untracked sessions
3002
3075
  try {
3003
3076
  const tmux = detectTmuxPath();
3004
3077
  if (!tmux)
@@ -3008,32 +3081,86 @@ export async function startServer(options) {
3008
3081
  catch { /* may already be dead */ }
3009
3082
  },
3010
3083
  respawnSession: async (topicId, _sessionName, recoveryPrompt) => {
3011
- const targetSession = telegram.getSessionForTopic(topicId);
3012
- if (!targetSession)
3084
+ // Check Slack first (synthetic IDs are negative)
3085
+ const slackChId = slackProxyChannelMap.get(topicId);
3086
+ if (slackChId && _slackAdapter) {
3087
+ // Slack respawn: kill existing, next message triggers fresh session
3088
+ const session = sessionManager.listRunningSessions().find(s => s.tmuxSession === _sessionName);
3089
+ if (session)
3090
+ sessionManager.killSession(session.id);
3091
+ return;
3092
+ }
3093
+ if (telegram) {
3094
+ const targetSession = telegram.getSessionForTopic(topicId);
3095
+ if (!targetSession)
3096
+ return;
3097
+ await respawnSessionForTopic(sessionManager, telegram, targetSession, topicId, undefined, topicMemory, undefined, recoveryPrompt, { silent: true });
3098
+ }
3099
+ },
3100
+ sendToTopic: async (topicId, message) => {
3101
+ const slackChId = slackProxyChannelMap.get(topicId);
3102
+ if (slackChId && _slackAdapter) {
3103
+ await _slackAdapter.sendToChannel(slackChId, message);
3013
3104
  return;
3014
- await respawnSessionForTopic(sessionManager, telegram, targetSession, topicId, undefined, topicMemory, undefined, recoveryPrompt, { silent: true });
3105
+ }
3106
+ if (telegram)
3107
+ await telegram.sendToTopic(topicId, message);
3015
3108
  },
3016
- sendToTopic: async (topicId, message) => { await telegram.sendToTopic(topicId, message); },
3017
3109
  });
3018
3110
  console.log(pc.green(' Session Recovery enabled (mechanical fast-path)'));
3019
3111
  }
3020
3112
  // SessionMonitor — proactive session health monitoring
3113
+ // Platform-aware: monitors both Telegram and Slack sessions
3021
3114
  let sessionMonitor;
3022
- if (telegram) {
3115
+ if (telegram || _slackAdapter) {
3023
3116
  sessionMonitor = new SessionMonitor({
3024
- getActiveTopicSessions: () => telegram.getActiveTopicSessions(),
3117
+ getActiveTopicSessions: () => {
3118
+ const sessions = new Map();
3119
+ // Telegram topic sessions
3120
+ if (telegram && telegram.getActiveTopicSessions) {
3121
+ const telegramSessions = telegram.getActiveTopicSessions();
3122
+ for (const [topicId, sessionName] of telegramSessions) {
3123
+ sessions.set(topicId, sessionName);
3124
+ }
3125
+ }
3126
+ // Slack channel sessions (using synthetic IDs)
3127
+ if (_slackAdapter) {
3128
+ const registry = _slackAdapter.getChannelRegistry();
3129
+ for (const [channelId, entry] of Object.entries(registry)) {
3130
+ const syntheticId = slackChannelToSyntheticId(channelId);
3131
+ sessions.set(syntheticId, entry.sessionName);
3132
+ }
3133
+ }
3134
+ return sessions;
3135
+ },
3025
3136
  captureSessionOutput: (name, lines) => sessionManager.captureOutput(name, lines),
3026
3137
  isSessionAlive: (name) => sessionManager.isSessionAlive(name),
3027
3138
  getTopicHistory: (topicId, limit) => {
3028
- const history = telegram.getMessageLog?.();
3029
- if (!history)
3030
- return [];
3031
- return history
3032
- .filter((m) => m.topicId === topicId)
3033
- .slice(-limit)
3034
- .map((m) => ({ text: m.text, fromUser: m.fromUser, timestamp: m.timestamp }));
3139
+ const slackChId = slackProxyChannelMap.get(topicId);
3140
+ if (slackChId && _slackAdapter) {
3141
+ const msgs = _slackAdapter.getChannelMessages(slackChId, limit);
3142
+ return msgs.map(m => ({ text: m.text, fromUser: true, timestamp: new Date(parseFloat(m.ts) * 1000).toISOString() }));
3143
+ }
3144
+ if (telegram) {
3145
+ const history = telegram.getMessageLog?.();
3146
+ if (!history)
3147
+ return [];
3148
+ return history
3149
+ .filter((m) => m.topicId === topicId)
3150
+ .slice(-limit)
3151
+ .map((m) => ({ text: m.text, fromUser: m.fromUser, timestamp: m.timestamp }));
3152
+ }
3153
+ return [];
3154
+ },
3155
+ sendToTopic: async (topicId, text) => {
3156
+ const slackChId = slackProxyChannelMap.get(topicId);
3157
+ if (slackChId && _slackAdapter) {
3158
+ await _slackAdapter.sendToChannel(slackChId, text);
3159
+ return;
3160
+ }
3161
+ if (telegram)
3162
+ await telegram.sendToTopic(topicId, text);
3035
3163
  },
3036
- sendToTopic: (topicId, text) => telegram.sendToTopic(topicId, text),
3037
3164
  triggerTriage: triageOrchestrator
3038
3165
  ? async (topicId, sessionName, reason) => {
3039
3166
  const result = await triageOrchestrator.activate(topicId, sessionName, 'watchdog', reason, Date.now());