instar 1.2.62 → 1.2.63
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 +60 -24
- package/dist/commands/server.js.map +1 -1
- package/dist/core/SessionManager.d.ts +18 -0
- package/dist/core/SessionManager.d.ts.map +1 -1
- package/dist/core/SessionManager.js +34 -0
- package/dist/core/SessionManager.js.map +1 -1
- package/dist/monitoring/sentinelWiring.d.ts +28 -0
- package/dist/monitoring/sentinelWiring.d.ts.map +1 -1
- package/dist/monitoring/sentinelWiring.js +48 -0
- package/dist/monitoring/sentinelWiring.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +3 -3
- package/upgrades/1.2.63.md +84 -0
- package/upgrades/side-effects/rate-limit-recovery-reachability.md +116 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/commands/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAuQH,UAAU,YAAY;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;2DACuD;IACvD,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAiqDD,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;AAuQH,UAAU,YAAY;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;2DACuD;IACvD,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAiqDD,wBAAsB,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CA+6LtE;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
|
@@ -4750,31 +4750,67 @@ export async function startServer(options) {
|
|
|
4750
4750
|
// docs/specs/rate-limit-sentinel.md.
|
|
4751
4751
|
const { RateLimitSentinel } = await import('../monitoring/RateLimitSentinel.js');
|
|
4752
4752
|
const getClaudeSessionIdForName = (sessionName) => sessionManager.listRunningSessions().find(s => s.tmuxSession === sessionName)?.claudeSessionId;
|
|
4753
|
-
//
|
|
4754
|
-
//
|
|
4755
|
-
//
|
|
4756
|
-
|
|
4757
|
-
|
|
4758
|
-
|
|
4759
|
-
|
|
4760
|
-
|
|
4761
|
-
|
|
4762
|
-
|
|
4763
|
-
|
|
4764
|
-
|
|
4765
|
-
|
|
4766
|
-
|
|
4767
|
-
|
|
4768
|
-
|
|
4769
|
-
|
|
4770
|
-
|
|
4771
|
-
|
|
4772
|
-
|
|
4773
|
-
|
|
4774
|
-
|
|
4775
|
-
|
|
4776
|
-
|
|
4753
|
+
// Recovery-reachability audit trail. Both recovery paths below ALWAYS leave
|
|
4754
|
+
// a record: a recovery-reached/recovery-unreachable line in the sentinel
|
|
4755
|
+
// audit log, and — when delivery to the user fails entirely — an entry in
|
|
4756
|
+
// .instar/sentinel-alerts.json so the dashboard surfaces it even without
|
|
4757
|
+
// Telegram. The defining bug this closes: a non-topic-bound session (e.g. a
|
|
4758
|
+
// developer's interactive Claude Code window) used to make both recovery
|
|
4759
|
+
// paths silently no-op, so the throttle never recovered and nothing reached
|
|
4760
|
+
// the user. Reachability is now unconditional — see the Sentinel
|
|
4761
|
+
// Reachability spec.
|
|
4762
|
+
const rlReachLogPath = path.join(config.stateDir, '..', 'logs', 'sentinel-events.jsonl');
|
|
4763
|
+
const rlAlertsPath = path.join(config.stateDir, 'sentinel-alerts.json');
|
|
4764
|
+
const recordRecovery = (kind, sessionName, detail, fallbackTried) => {
|
|
4765
|
+
const entry = { timestamp: new Date().toISOString(), kind, sentinel: 'rate-limit', sessionName, detail, fallbackTried };
|
|
4766
|
+
console.log(`[sentinel:${kind}] rate-limit/${sessionName} — ${detail}`);
|
|
4767
|
+
try {
|
|
4768
|
+
fs.appendFileSync(rlReachLogPath, JSON.stringify(entry) + '\n');
|
|
4769
|
+
}
|
|
4770
|
+
catch { /* best-effort */ }
|
|
4771
|
+
if (kind === 'recovery-unreachable') {
|
|
4772
|
+
// Append-only alert log the dashboard reads when Telegram can't be reached.
|
|
4773
|
+
try {
|
|
4774
|
+
let alerts = [];
|
|
4775
|
+
if (fs.existsSync(rlAlertsPath)) {
|
|
4776
|
+
try {
|
|
4777
|
+
alerts = JSON.parse(fs.readFileSync(rlAlertsPath, 'utf-8'));
|
|
4778
|
+
}
|
|
4779
|
+
catch {
|
|
4780
|
+
alerts = [];
|
|
4781
|
+
}
|
|
4782
|
+
if (!Array.isArray(alerts))
|
|
4783
|
+
alerts = [];
|
|
4784
|
+
}
|
|
4785
|
+
alerts.push(entry);
|
|
4786
|
+
fs.writeFileSync(rlAlertsPath, JSON.stringify(alerts.slice(-200), null, 2));
|
|
4787
|
+
}
|
|
4788
|
+
catch { /* best-effort */ }
|
|
4789
|
+
}
|
|
4777
4790
|
};
|
|
4791
|
+
// Reachability deps (extracted to sentinelWiring.buildRateLimitRecoveryDeps
|
|
4792
|
+
// so the topic / lifeline / audit branching is unit-testable). Topic-bound
|
|
4793
|
+
// sessions get a topic-tagged nudge + topic notice; non-topic-bound sessions
|
|
4794
|
+
// (e.g. an interactive dev window) get a trusted internal nudge + a lifeline
|
|
4795
|
+
// notice; if no channel is reachable, a recovery-unreachable audit event is
|
|
4796
|
+
// recorded instead of a silent no-op.
|
|
4797
|
+
const { buildRateLimitRecoveryDeps } = await import('../monitoring/sentinelWiring.js');
|
|
4798
|
+
const { resumeFn: rateLimitResume, notifyFn: rateLimitNotify } = buildRateLimitRecoveryDeps({
|
|
4799
|
+
isSessionAlive: (name) => sessionManager.isSessionAlive(name),
|
|
4800
|
+
injectTopicNudge: (name, topicId, text) => sessionManager.injectMessage(name, `[telegram:${topicId}] ${text}`),
|
|
4801
|
+
injectInternalNudge: (name, text) => sessionManager.injectInternalMessage(name, text, 'sentinel-recovery'),
|
|
4802
|
+
getTopicForSession: (name) => telegram?.getTopicForSession(name),
|
|
4803
|
+
getLifelineTopicId: () => telegram?.getLifelineTopicId?.(),
|
|
4804
|
+
deliverNotice: async (topicId, text) => {
|
|
4805
|
+
const resp = await fetch(`http://localhost:${config.port}/telegram/reply/${topicId}`, {
|
|
4806
|
+
method: 'POST',
|
|
4807
|
+
headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${config.authToken}` },
|
|
4808
|
+
body: JSON.stringify({ text }),
|
|
4809
|
+
});
|
|
4810
|
+
return resp.ok;
|
|
4811
|
+
},
|
|
4812
|
+
recordRecovery,
|
|
4813
|
+
});
|
|
4778
4814
|
const rlsCfg = config.monitoring?.rateLimitSentinel ?? { enabled: true };
|
|
4779
4815
|
const rateLimitSentinel = new RateLimitSentinel({
|
|
4780
4816
|
resumeFn: rateLimitResume,
|