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.
- package/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +182 -55
- package/dist/commands/server.js.map +1 -1
- package/dist/monitoring/PresenceProxy.d.ts.map +1 -1
- package/dist/monitoring/PresenceProxy.js +11 -2
- package/dist/monitoring/PresenceProxy.js.map +1 -1
- package/package.json +1 -1
- package/scripts/pre-push-gate.js +28 -0
- package/src/data/builtin-manifest.json +2 -2
- package/upgrades/0.25.3.md +25 -0
|
@@ -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,
|
|
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"}
|
package/dist/commands/server.js
CHANGED
|
@@ -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
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
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
|
|
2913
|
+
// Wire nurse into stall detection — both Telegram and Slack
|
|
2883
2914
|
// Note: presenceProxy may be set later — use late-binding check
|
|
2884
|
-
|
|
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
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
origClearStall(
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
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
|
-
|
|
3012
|
-
|
|
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
|
-
|
|
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: () =>
|
|
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
|
|
3029
|
-
if (
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
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());
|