instar 0.28.35 → 0.28.42
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/dashboard/index.html +40 -30
- package/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +105 -46
- package/dist/commands/server.js.map +1 -1
- package/dist/core/CoherenceGate.d.ts +7 -0
- package/dist/core/CoherenceGate.d.ts.map +1 -1
- package/dist/core/CoherenceGate.js +7 -6
- package/dist/core/CoherenceGate.js.map +1 -1
- package/dist/core/CoherenceReviewer.d.ts +20 -5
- package/dist/core/CoherenceReviewer.d.ts.map +1 -1
- package/dist/core/CoherenceReviewer.js +36 -6
- package/dist/core/CoherenceReviewer.js.map +1 -1
- package/dist/core/OutboundDedupGate.d.ts +56 -0
- package/dist/core/OutboundDedupGate.d.ts.map +1 -0
- package/dist/core/OutboundDedupGate.js +90 -0
- package/dist/core/OutboundDedupGate.js.map +1 -0
- package/dist/core/PostUpdateMigrator.d.ts +0 -14
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +2 -123
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/core/SessionManager.d.ts +9 -0
- package/dist/core/SessionManager.d.ts.map +1 -1
- package/dist/core/SessionManager.js +18 -0
- package/dist/core/SessionManager.js.map +1 -1
- package/dist/core/UpdateChecker.d.ts.map +1 -1
- package/dist/core/UpdateChecker.js +6 -2
- package/dist/core/UpdateChecker.js.map +1 -1
- package/dist/core/junk-payload.d.ts +14 -0
- package/dist/core/junk-payload.d.ts.map +1 -0
- package/dist/core/junk-payload.js +32 -0
- package/dist/core/junk-payload.js.map +1 -0
- package/dist/monitoring/CompactionSentinel.d.ts +143 -0
- package/dist/monitoring/CompactionSentinel.d.ts.map +1 -0
- package/dist/monitoring/CompactionSentinel.js +262 -0
- package/dist/monitoring/CompactionSentinel.js.map +1 -0
- package/dist/monitoring/PresenceProxy.d.ts +0 -40
- package/dist/monitoring/PresenceProxy.d.ts.map +1 -1
- package/dist/monitoring/PresenceProxy.js +4 -156
- package/dist/monitoring/PresenceProxy.js.map +1 -1
- package/dist/monitoring/PromptGate.d.ts.map +1 -1
- package/dist/monitoring/PromptGate.js +0 -75
- package/dist/monitoring/PromptGate.js.map +1 -1
- package/dist/threadline/adapters/RESTServer.d.ts +7 -1
- package/dist/threadline/adapters/RESTServer.d.ts.map +1 -1
- package/dist/threadline/adapters/RESTServer.js +62 -1
- package/dist/threadline/adapters/RESTServer.js.map +1 -1
- package/dist/threadline/relay/OfflineQueue.js +1 -1
- package/dist/threadline/relay/OfflineQueue.js.map +1 -1
- package/package.json +1 -1
- package/scripts/check-upgrade-guide.js +9 -115
- package/scripts/collect-metrics.py +25 -120
- package/scripts/upgrade-guide-validator.mjs +221 -0
- package/src/data/builtin-manifest.json +18 -18
- package/upgrades/0.28.36.md +40 -0
- package/upgrades/0.28.37.md +69 -0
- package/upgrades/0.28.38.md +41 -0
- package/upgrades/0.28.39.md +29 -0
- package/upgrades/0.28.40.md +32 -0
- package/upgrades/0.28.42.md +25 -0
- package/upgrades/NEXT.md +18 -0
- package/upgrades/0.28.10.md +0 -19
- package/upgrades/0.28.11.md +0 -19
- package/upgrades/0.28.13.md +0 -11
- package/upgrades/0.28.15.md +0 -11
- package/upgrades/0.28.18.md +0 -12
- package/upgrades/0.28.20.md +0 -19
- package/upgrades/0.28.21.md +0 -23
- package/upgrades/0.28.22.md +0 -23
- package/upgrades/0.28.23.md +0 -19
- package/upgrades/0.28.24.md +0 -18
- package/upgrades/0.28.25.md +0 -21
- package/upgrades/0.28.26.md +0 -20
- package/upgrades/0.28.27.md +0 -20
- package/upgrades/0.28.28.md +0 -20
- package/upgrades/0.28.30.md +0 -25
- package/upgrades/0.28.31.md +0 -38
- package/upgrades/0.28.32.md +0 -22
- package/upgrades/0.28.33.md +0 -22
- package/upgrades/0.28.35.md +0 -37
- package/upgrades/0.28.5.md +0 -17
- package/upgrades/0.28.7.md +0 -24
- package/upgrades/0.28.8.md +0 -21
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CompactionSentinel — Owns the full lifecycle of recovering a Claude session
|
|
3
|
+
* after context compaction.
|
|
4
|
+
*
|
|
5
|
+
* Prior behavior (before this class): three independent triggers —
|
|
6
|
+
* PreCompact hook event, SessionWatchdog 'compaction-idle' poll, and the
|
|
7
|
+
* compaction-recovery.sh endpoint — each called `recoverCompactedSession`
|
|
8
|
+
* fire-and-forget. If the injection didn't wake the session up (session
|
|
9
|
+
* genuinely hung, injection silently dropped, Claude process dead), nobody
|
|
10
|
+
* noticed. The idle-prompt zombie-killer then raced the recovery window
|
|
11
|
+
* and killed the session 15 minutes later, wiping the conversation.
|
|
12
|
+
*
|
|
13
|
+
* What this class adds:
|
|
14
|
+
* 1. Dedupe across triggers — once a session is in recovery, additional
|
|
15
|
+
* reports within the guard window are ignored.
|
|
16
|
+
* 2. Verification — after each inject attempt, watch the session's JSONL
|
|
17
|
+
* file for size/mtime growth, which is the cheapest reliable signal
|
|
18
|
+
* that Claude actually processed the prompt and produced output.
|
|
19
|
+
* 3. Retry with backoff — if verification fails within the window, try
|
|
20
|
+
* injecting again, up to `maxInjectAttempts`.
|
|
21
|
+
* 4. Zombie-kill veto — while a recovery is in flight, SessionManager's
|
|
22
|
+
* idle-prompt cleanup is told to leave the session alone via the
|
|
23
|
+
* activeRecoveryChecker hook.
|
|
24
|
+
* 5. Observable outcomes — emits `compaction:detected`,
|
|
25
|
+
* `compaction:inject-attempted`, `compaction:recovered`,
|
|
26
|
+
* `compaction:failed` events with a single `[Sentinel]` log prefix
|
|
27
|
+
* so the full lifecycle is greppable.
|
|
28
|
+
*
|
|
29
|
+
* Decoupling: the class takes a `recoverFn` in its deps rather than pulling
|
|
30
|
+
* in server.ts. The existing `recoverCompactedSession` helper is passed
|
|
31
|
+
* straight through, which keeps message-bus / topic-routing concerns out
|
|
32
|
+
* of this file.
|
|
33
|
+
*/
|
|
34
|
+
import { EventEmitter } from 'node:events';
|
|
35
|
+
import fs from 'node:fs';
|
|
36
|
+
import path from 'node:path';
|
|
37
|
+
const DEFAULTS = {
|
|
38
|
+
dedupeWindowMs: 60_000, // 1 minute — catch overlapping triggers
|
|
39
|
+
verifyWindowMs: 25_000, // 25s — enough for claude to boot past recovery hook and emit output
|
|
40
|
+
maxInjectAttempts: 3,
|
|
41
|
+
recoveryGuardMs: 10 * 60_000, // 10 minutes — protection from zombie-killer
|
|
42
|
+
};
|
|
43
|
+
export class CompactionSentinel extends EventEmitter {
|
|
44
|
+
deps;
|
|
45
|
+
cfg;
|
|
46
|
+
active = new Map();
|
|
47
|
+
timers = new Map();
|
|
48
|
+
recentReports = new Map(); // sessionName → lastReportedAt
|
|
49
|
+
constructor(deps, config = {}) {
|
|
50
|
+
super();
|
|
51
|
+
this.deps = deps;
|
|
52
|
+
this.cfg = {
|
|
53
|
+
dedupeWindowMs: config.dedupeWindowMs ?? DEFAULTS.dedupeWindowMs,
|
|
54
|
+
verifyWindowMs: config.verifyWindowMs ?? DEFAULTS.verifyWindowMs,
|
|
55
|
+
maxInjectAttempts: config.maxInjectAttempts ?? DEFAULTS.maxInjectAttempts,
|
|
56
|
+
recoveryGuardMs: config.recoveryGuardMs ?? DEFAULTS.recoveryGuardMs,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
now() {
|
|
60
|
+
return this.deps.now ? this.deps.now() : Date.now();
|
|
61
|
+
}
|
|
62
|
+
setTimer(fn, ms) {
|
|
63
|
+
return this.deps.setTimer ? this.deps.setTimer(fn, ms) : setTimeout(fn, ms);
|
|
64
|
+
}
|
|
65
|
+
clearTimer(handle) {
|
|
66
|
+
if (this.deps.clearTimer)
|
|
67
|
+
this.deps.clearTimer(handle);
|
|
68
|
+
else
|
|
69
|
+
clearTimeout(handle);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Report a compaction event for a session. Called by all three trigger paths
|
|
73
|
+
* (PreCompact hook, watchdog poll, recovery-hook endpoint). Deduped — if the
|
|
74
|
+
* same session is already in active recovery or was reported within the
|
|
75
|
+
* dedupe window, this is a no-op.
|
|
76
|
+
*/
|
|
77
|
+
report(sessionName, trigger) {
|
|
78
|
+
const now = this.now();
|
|
79
|
+
if (this.active.has(sessionName)) {
|
|
80
|
+
return; // Recovery already in flight — ignore duplicate trigger.
|
|
81
|
+
}
|
|
82
|
+
const lastReport = this.recentReports.get(sessionName);
|
|
83
|
+
if (lastReport && now - lastReport < this.cfg.dedupeWindowMs) {
|
|
84
|
+
return; // Already reported recently.
|
|
85
|
+
}
|
|
86
|
+
this.recentReports.set(sessionName, now);
|
|
87
|
+
const baseline = this.readJsonlBaseline(sessionName);
|
|
88
|
+
const state = {
|
|
89
|
+
sessionName,
|
|
90
|
+
trigger,
|
|
91
|
+
detectedAt: now,
|
|
92
|
+
attempts: 0,
|
|
93
|
+
lastInjectAt: 0,
|
|
94
|
+
baselineJsonlPath: baseline?.path ?? null,
|
|
95
|
+
baselineJsonlSize: baseline?.size ?? null,
|
|
96
|
+
baselineJsonlMtime: baseline?.mtime ?? null,
|
|
97
|
+
status: 'pending-inject',
|
|
98
|
+
};
|
|
99
|
+
this.active.set(sessionName, state);
|
|
100
|
+
console.log(`[Sentinel] detected compaction on "${sessionName}" via ${trigger}; ` +
|
|
101
|
+
`baseline jsonl: ${state.baselineJsonlPath ? path.basename(state.baselineJsonlPath) : 'none'} ` +
|
|
102
|
+
`size=${state.baselineJsonlSize ?? 'n/a'}`);
|
|
103
|
+
this.emit('compaction:detected', state);
|
|
104
|
+
this.attemptInjection(state);
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Predicate for SessionManager's zombie-killer: is this session currently
|
|
108
|
+
* being recovered? If yes, the zombie-killer should skip it.
|
|
109
|
+
*/
|
|
110
|
+
isRecoveryActive(sessionName) {
|
|
111
|
+
const state = this.active.get(sessionName);
|
|
112
|
+
if (!state)
|
|
113
|
+
return false;
|
|
114
|
+
// Don't claim active for terminal states (defense in depth).
|
|
115
|
+
return state.status !== 'recovered' && state.status !== 'failed';
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Manually clear recovery state. Used when the session completes or is
|
|
119
|
+
* explicitly reset. Exposed mainly for tests and edge cases.
|
|
120
|
+
*/
|
|
121
|
+
clear(sessionName) {
|
|
122
|
+
const timer = this.timers.get(sessionName);
|
|
123
|
+
if (timer) {
|
|
124
|
+
this.clearTimer(timer);
|
|
125
|
+
this.timers.delete(sessionName);
|
|
126
|
+
}
|
|
127
|
+
this.active.delete(sessionName);
|
|
128
|
+
}
|
|
129
|
+
/** Stop all pending timers. Safe to call multiple times. */
|
|
130
|
+
stop() {
|
|
131
|
+
for (const handle of this.timers.values())
|
|
132
|
+
this.clearTimer(handle);
|
|
133
|
+
this.timers.clear();
|
|
134
|
+
this.active.clear();
|
|
135
|
+
this.recentReports.clear();
|
|
136
|
+
}
|
|
137
|
+
/** Get current active recovery state (test introspection). */
|
|
138
|
+
getState(sessionName) {
|
|
139
|
+
return this.active.get(sessionName);
|
|
140
|
+
}
|
|
141
|
+
async attemptInjection(state) {
|
|
142
|
+
state.attempts += 1;
|
|
143
|
+
state.lastInjectAt = this.now();
|
|
144
|
+
state.status = 'pending-inject';
|
|
145
|
+
let accepted = false;
|
|
146
|
+
try {
|
|
147
|
+
accepted = await this.deps.recoverFn(state.sessionName, state.trigger);
|
|
148
|
+
}
|
|
149
|
+
catch (err) {
|
|
150
|
+
console.warn(`[Sentinel] recoverFn threw on "${state.sessionName}" (attempt ${state.attempts}):`, err);
|
|
151
|
+
}
|
|
152
|
+
console.log(`[Sentinel] inject-attempted on "${state.sessionName}" ` +
|
|
153
|
+
`(attempt ${state.attempts}/${this.cfg.maxInjectAttempts}, accepted=${accepted})`);
|
|
154
|
+
this.emit('compaction:inject-attempted', { ...state, accepted });
|
|
155
|
+
if (!accepted) {
|
|
156
|
+
// recoverFn returned false — typically means no unanswered user message,
|
|
157
|
+
// session not alive, or injection blocked. No point retrying; close out.
|
|
158
|
+
this.finalize(state, 'failed', 'recoverFn declined (no pending work or session gone)');
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
state.status = 'verifying';
|
|
162
|
+
const handle = this.setTimer(() => {
|
|
163
|
+
this.verifyRecovery(state).catch(err => {
|
|
164
|
+
console.warn(`[Sentinel] verify threw on "${state.sessionName}":`, err);
|
|
165
|
+
this.finalize(state, 'failed', `verify threw: ${String(err)}`);
|
|
166
|
+
});
|
|
167
|
+
}, this.cfg.verifyWindowMs);
|
|
168
|
+
this.timers.set(state.sessionName, handle);
|
|
169
|
+
}
|
|
170
|
+
async verifyRecovery(state) {
|
|
171
|
+
this.timers.delete(state.sessionName);
|
|
172
|
+
// Has the jsonl file grown? If so, claude processed the prompt and emitted.
|
|
173
|
+
const current = this.readJsonlBaseline(state.sessionName);
|
|
174
|
+
const grew = state.baselineJsonlSize !== null &&
|
|
175
|
+
current !== null &&
|
|
176
|
+
(current.size > state.baselineJsonlSize ||
|
|
177
|
+
(current.path === state.baselineJsonlPath &&
|
|
178
|
+
state.baselineJsonlMtime !== null &&
|
|
179
|
+
current.mtime > state.baselineJsonlMtime));
|
|
180
|
+
if (grew) {
|
|
181
|
+
const delta = (current?.size ?? 0) - (state.baselineJsonlSize ?? 0);
|
|
182
|
+
console.log(`[Sentinel] recovered "${state.sessionName}" after ${state.attempts} attempt(s) ` +
|
|
183
|
+
`(jsonl grew by ${delta} bytes)`);
|
|
184
|
+
this.emit('compaction:recovered', { ...state, jsonlDelta: delta });
|
|
185
|
+
this.finalize(state, 'recovered');
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
// No growth — session didn't respond within the window.
|
|
189
|
+
if (state.attempts >= this.cfg.maxInjectAttempts) {
|
|
190
|
+
this.finalize(state, 'failed', `no jsonl growth after ${state.attempts} attempts (${state.attempts * this.cfg.verifyWindowMs}ms total wait)`);
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
// Retry.
|
|
194
|
+
state.status = 'retrying';
|
|
195
|
+
console.log(`[Sentinel] retry "${state.sessionName}" ` +
|
|
196
|
+
`(attempt ${state.attempts}/${this.cfg.maxInjectAttempts} produced no output, re-injecting)`);
|
|
197
|
+
await this.attemptInjection(state);
|
|
198
|
+
}
|
|
199
|
+
finalize(state, status, reason) {
|
|
200
|
+
state.status = status;
|
|
201
|
+
if (status === 'failed') {
|
|
202
|
+
console.warn(`[Sentinel] failed "${state.sessionName}": ${reason ?? 'unknown'}`);
|
|
203
|
+
this.emit('compaction:failed', { ...state, reason: reason ?? 'unknown' });
|
|
204
|
+
}
|
|
205
|
+
// Keep the state around just long enough to exit the recovery-guard window
|
|
206
|
+
// (so the zombie-killer won't race the next prompt). Then clean up.
|
|
207
|
+
// Also clear the recentReports entry so a *new* compaction on this session
|
|
208
|
+
// can trigger a fresh recovery, even if the second compaction happens
|
|
209
|
+
// within the dedupe window of the first. Without this, the second
|
|
210
|
+
// compaction would be silently suppressed.
|
|
211
|
+
const keepFor = status === 'recovered' ? 5_000 : 30_000;
|
|
212
|
+
const handle = this.setTimer(() => {
|
|
213
|
+
this.timers.delete(state.sessionName);
|
|
214
|
+
this.active.delete(state.sessionName);
|
|
215
|
+
this.recentReports.delete(state.sessionName);
|
|
216
|
+
}, keepFor);
|
|
217
|
+
this.timers.set(state.sessionName, handle);
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Look up the JSONL file for this session. Prefers the exact file by
|
|
221
|
+
* Claude Code session UUID when known (prevents a sibling session's
|
|
222
|
+
* activity from looking like this session recovered). Falls back to the
|
|
223
|
+
* most recently-modified file in the project's jsonl root.
|
|
224
|
+
*/
|
|
225
|
+
readJsonlBaseline(sessionName) {
|
|
226
|
+
try {
|
|
227
|
+
const root = this.deps.jsonlRoot
|
|
228
|
+
|| path.join(process.env.HOME || '/tmp', '.claude', 'projects', this.deps.projectDir.replace(/\//g, '-'));
|
|
229
|
+
if (!fs.existsSync(root))
|
|
230
|
+
return null;
|
|
231
|
+
// Prefer the exact file by claudeSessionId when available.
|
|
232
|
+
const uuid = this.deps.getClaudeSessionId?.(sessionName);
|
|
233
|
+
if (uuid) {
|
|
234
|
+
const exact = path.join(root, `${uuid}.jsonl`);
|
|
235
|
+
if (fs.existsSync(exact)) {
|
|
236
|
+
const st = fs.statSync(exact);
|
|
237
|
+
return { path: exact, size: st.size, mtime: st.mtimeMs };
|
|
238
|
+
}
|
|
239
|
+
// If uuid is known but file doesn't exist, return null rather than
|
|
240
|
+
// falling through — the session genuinely has no jsonl, and picking
|
|
241
|
+
// a sibling's file would be a false signal.
|
|
242
|
+
return null;
|
|
243
|
+
}
|
|
244
|
+
// Fallback: most-recently-modified jsonl in the project.
|
|
245
|
+
const entries = fs.readdirSync(root).filter(f => f.endsWith('.jsonl'));
|
|
246
|
+
if (entries.length === 0)
|
|
247
|
+
return null;
|
|
248
|
+
const latest = entries
|
|
249
|
+
.map(f => {
|
|
250
|
+
const full = path.join(root, f);
|
|
251
|
+
const st = fs.statSync(full);
|
|
252
|
+
return { path: full, size: st.size, mtime: st.mtimeMs };
|
|
253
|
+
})
|
|
254
|
+
.sort((a, b) => b.mtime - a.mtime)[0];
|
|
255
|
+
return latest;
|
|
256
|
+
}
|
|
257
|
+
catch {
|
|
258
|
+
return null;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
//# sourceMappingURL=CompactionSentinel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CompactionSentinel.js","sourceRoot":"","sources":["../../src/monitoring/CompactionSentinel.ts"],"names":[],"mappings":"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"}
|
|
@@ -89,13 +89,6 @@ interface PresenceState {
|
|
|
89
89
|
tier3RecheckCount: number;
|
|
90
90
|
silencedUntil: number | null;
|
|
91
91
|
cancelled: boolean;
|
|
92
|
-
/**
|
|
93
|
-
* Timestamp + fingerprint of the last "session is awaiting your reply"
|
|
94
|
-
* notification we relayed to the user. Used to dedupe across tiers so we
|
|
95
|
-
* don't spam the user with the same pending question on every tick.
|
|
96
|
-
*/
|
|
97
|
-
awaitingInputRelayedAt: number | null;
|
|
98
|
-
awaitingInputFingerprint: string | null;
|
|
99
92
|
llmCallCount: number;
|
|
100
93
|
lastLlmCallAt: number;
|
|
101
94
|
conversationHistory: Array<{
|
|
@@ -125,26 +118,6 @@ export declare function detectQuotaExhaustion(snapshot: string): string | null;
|
|
|
125
118
|
* working and is waiting for new input.
|
|
126
119
|
*/
|
|
127
120
|
export declare function detectSessionIdle(snapshot: string): boolean;
|
|
128
|
-
export interface AwaitingInputDetection {
|
|
129
|
-
/** Which sentinel matched (for telemetry/debugging) */
|
|
130
|
-
label: string;
|
|
131
|
-
/** The single line that matched — used in the relay body */
|
|
132
|
-
matchLine: string;
|
|
133
|
-
/** Up to N trailing non-empty lines of context around the match (includes matchLine) */
|
|
134
|
-
context: string;
|
|
135
|
-
}
|
|
136
|
-
/**
|
|
137
|
-
* Detect whether the terminal snapshot shows the agent paused at a
|
|
138
|
-
* structured "awaiting user input" prompt — a plan, confirmation,
|
|
139
|
-
* or y/n question that needs a reply to unblock.
|
|
140
|
-
*
|
|
141
|
-
* Returns the match details if detected, null otherwise.
|
|
142
|
-
*
|
|
143
|
-
* Only inspects the last ~40 lines to avoid triggering on stale
|
|
144
|
-
* confirmation prompts from earlier in the session that have since
|
|
145
|
-
* been answered.
|
|
146
|
-
*/
|
|
147
|
-
export declare function detectAwaitingUserInput(snapshot: string): AwaitingInputDetection | null;
|
|
148
121
|
export declare class PresenceProxy {
|
|
149
122
|
private config;
|
|
150
123
|
private states;
|
|
@@ -188,19 +161,6 @@ export declare class PresenceProxy {
|
|
|
188
161
|
private buildTier2Prompt;
|
|
189
162
|
private buildTier3Prompt;
|
|
190
163
|
private callLlm;
|
|
191
|
-
/**
|
|
192
|
-
* Try to detect an "awaiting user input" prompt in the snapshot and, if
|
|
193
|
-
* found AND we haven't already relayed this specific question, send a
|
|
194
|
-
* Telegram notification so the user knows their reply is needed.
|
|
195
|
-
*
|
|
196
|
-
* Returns true if a relay was sent (caller should skip the normal
|
|
197
|
-
* idle-cancel or tier-progress logic to avoid noise). Returns false
|
|
198
|
-
* otherwise.
|
|
199
|
-
*
|
|
200
|
-
* Dedupe: keyed on a fingerprint of the match context. The same pending
|
|
201
|
-
* question won't be re-relayed across tiers.
|
|
202
|
-
*/
|
|
203
|
-
private maybeRelayAwaitingInput;
|
|
204
164
|
private sendProxyMessage;
|
|
205
165
|
/** System/delivery messages that should NOT be treated as real agent responses */
|
|
206
166
|
private isSystemMessage;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PresenceProxy.d.ts","sourceRoot":"","sources":["../../src/monitoring/PresenceProxy.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"PresenceProxy.d.ts","sourceRoot":"","sources":["../../src/monitoring/PresenceProxy.ts"],"names":[],"mappings":"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"}
|
|
@@ -160,71 +160,6 @@ export function detectSessionIdle(snapshot) {
|
|
|
160
160
|
const tail = lines.slice(-5);
|
|
161
161
|
return tail.some(line => IDLE_PROMPT_PATTERNS.some(p => p.test(line.trim())));
|
|
162
162
|
}
|
|
163
|
-
// ─── Awaiting User Input Detection ───────────────────────────────────────────
|
|
164
|
-
/**
|
|
165
|
-
* Structured sentinels indicating the agent has paused and is explicitly
|
|
166
|
-
* waiting on the user to reply with a specific answer (confirmation, plan
|
|
167
|
-
* approval, y/n, etc.) — as opposed to merely being finished with its work.
|
|
168
|
-
*
|
|
169
|
-
* Each pattern is intentionally narrow. False positives here would cause
|
|
170
|
-
* spurious Telegram relays; false negatives just fall through to the existing
|
|
171
|
-
* idle → cancel path. We err toward specificity.
|
|
172
|
-
*/
|
|
173
|
-
const AWAITING_INPUT_PATTERNS = [
|
|
174
|
-
// Skills presenting a plan/proposal: autonomous, build, etc.
|
|
175
|
-
{ pattern: /Reply\s+["'`]?(go|yes|y|ok|confirm|proceed)["'`]?\b/i, label: 'skill-confirmation' },
|
|
176
|
-
// Claude Code plan mode
|
|
177
|
-
{ pattern: /Do you want to proceed with this plan\??/i, label: 'plan-mode' },
|
|
178
|
-
{ pattern: /Would you like to proceed\??/i, label: 'proceed-prompt' },
|
|
179
|
-
// Autonomous skill plan preview
|
|
180
|
-
{ pattern: /Shall I proceed\??/i, label: 'shall-i-proceed' },
|
|
181
|
-
{ pattern: /activate the (stop )?hook and start/i, label: 'autonomous-gate' },
|
|
182
|
-
// Setup wizard / conversational confirmation
|
|
183
|
-
{ pattern: /Want to change these\??/i, label: 'wizard-confirm' },
|
|
184
|
-
// y/n inline prompts
|
|
185
|
-
{ pattern: /\[\s*y\s*\/\s*n\s*\]/i, label: 'yn-bracket' },
|
|
186
|
-
{ pattern: /\(\s*y\s*\/\s*n\s*\)/i, label: 'yn-paren' },
|
|
187
|
-
// Explicit "need your confirmation" phrasing
|
|
188
|
-
{ pattern: /need your confirmation/i, label: 'need-confirmation' },
|
|
189
|
-
{ pattern: /awaiting your (reply|response|confirmation)/i, label: 'awaiting-reply' },
|
|
190
|
-
];
|
|
191
|
-
/**
|
|
192
|
-
* Detect whether the terminal snapshot shows the agent paused at a
|
|
193
|
-
* structured "awaiting user input" prompt — a plan, confirmation,
|
|
194
|
-
* or y/n question that needs a reply to unblock.
|
|
195
|
-
*
|
|
196
|
-
* Returns the match details if detected, null otherwise.
|
|
197
|
-
*
|
|
198
|
-
* Only inspects the last ~40 lines to avoid triggering on stale
|
|
199
|
-
* confirmation prompts from earlier in the session that have since
|
|
200
|
-
* been answered.
|
|
201
|
-
*/
|
|
202
|
-
export function detectAwaitingUserInput(snapshot) {
|
|
203
|
-
if (!snapshot)
|
|
204
|
-
return null;
|
|
205
|
-
const rawLines = snapshot.split('\n');
|
|
206
|
-
const tail = rawLines.slice(-40);
|
|
207
|
-
// Search from the bottom up — most recent prompt wins
|
|
208
|
-
for (let i = tail.length - 1; i >= 0; i--) {
|
|
209
|
-
const line = tail[i];
|
|
210
|
-
if (!line.trim())
|
|
211
|
-
continue;
|
|
212
|
-
for (const { pattern, label } of AWAITING_INPUT_PATTERNS) {
|
|
213
|
-
if (pattern.test(line)) {
|
|
214
|
-
// Gather up to 6 lines of surrounding context (above the match),
|
|
215
|
-
// skipping empty lines and box-drawing separators.
|
|
216
|
-
const start = Math.max(0, i - 6);
|
|
217
|
-
const contextLines = tail
|
|
218
|
-
.slice(start, i + 1)
|
|
219
|
-
.map(l => l.trim())
|
|
220
|
-
.filter(l => l.length > 0 && !/^[─━═▔▁_\-]{3,}$/.test(l));
|
|
221
|
-
const context = contextLines.join('\n');
|
|
222
|
-
return { label, matchLine: line.trim(), context };
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
return null;
|
|
227
|
-
}
|
|
228
163
|
// ─── Long-Running Process Whitelist ─────────────────────────────────────────
|
|
229
164
|
const LONG_RUNNING_PATTERNS = [
|
|
230
165
|
/npm\s+(install|ci|run\s+build|run\s+test)/i,
|
|
@@ -429,8 +364,6 @@ export class PresenceProxy {
|
|
|
429
364
|
tier3RecheckCount: 0,
|
|
430
365
|
silencedUntil: existingState?.silencedUntil ?? null,
|
|
431
366
|
cancelled: false,
|
|
432
|
-
awaitingInputRelayedAt: null,
|
|
433
|
-
awaitingInputFingerprint: null,
|
|
434
367
|
llmCallCount: 0,
|
|
435
368
|
lastLlmCallAt: 0,
|
|
436
369
|
conversationHistory: existingState?.conversationHistory ?? [],
|
|
@@ -550,25 +483,6 @@ export class PresenceProxy {
|
|
|
550
483
|
return; // Don't schedule tier 2/3 — nothing more to assess
|
|
551
484
|
}
|
|
552
485
|
}
|
|
553
|
-
// ── Awaiting user input: session paused on a structured question ──
|
|
554
|
-
// If the session is visibly waiting on a confirmation / plan approval /
|
|
555
|
-
// y-n prompt, relay the question directly — no LLM summary needed.
|
|
556
|
-
if (snapshot) {
|
|
557
|
-
const relayed = await this.maybeRelayAwaitingInput(topicId, state, snapshot, 1);
|
|
558
|
-
if (relayed) {
|
|
559
|
-
if (state.cancelled)
|
|
560
|
-
return;
|
|
561
|
-
state.tier1FiredAt = Date.now();
|
|
562
|
-
// Schedule tier 2 so we can escalate if the user still hasn't replied
|
|
563
|
-
if (state.cancelled)
|
|
564
|
-
return;
|
|
565
|
-
const remainingToTier2 = this.tier2DelayMs - (Date.now() - state.userMessageAt);
|
|
566
|
-
if (remainingToTier2 > 0) {
|
|
567
|
-
this.scheduleTier(topicId, 2, remainingToTier2);
|
|
568
|
-
}
|
|
569
|
-
return;
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
486
|
if (!snapshot || snapshot.trim().length < 10) {
|
|
573
487
|
message = `${this.prefix} ${this.config.agentName} is active but hasn't produced visible output yet. Your message has been delivered.`;
|
|
574
488
|
}
|
|
@@ -632,26 +546,10 @@ export class PresenceProxy {
|
|
|
632
546
|
}
|
|
633
547
|
// ── Session idle: agent completed work but didn't relay response ──
|
|
634
548
|
// If the terminal is at an idle prompt with no child processes, the agent
|
|
635
|
-
//
|
|
636
|
-
// needs a user reply. Distinguish via detectAwaitingUserInput: if it's
|
|
637
|
-
// a pending question, relay it; otherwise the agent is done and we
|
|
638
|
-
// cancel further tiers to avoid noise.
|
|
549
|
+
// has finished. Tier 1 already summarized the work — further updates are noise.
|
|
639
550
|
if (snapshot && detectSessionIdle(snapshot)) {
|
|
640
551
|
const processes = this.config.getProcessTree(state.sessionName);
|
|
641
552
|
if (processes.length === 0) {
|
|
642
|
-
const relayed = await this.maybeRelayAwaitingInput(topicId, state, snapshot, 2);
|
|
643
|
-
if (relayed) {
|
|
644
|
-
// Keep the proxy alive and schedule tier 3 so we can escalate if
|
|
645
|
-
// the user still hasn't replied.
|
|
646
|
-
state.tier2FiredAt = Date.now();
|
|
647
|
-
if (state.cancelled)
|
|
648
|
-
return;
|
|
649
|
-
const remainingToTier3 = this.tier3DelayMs - (Date.now() - state.userMessageAt);
|
|
650
|
-
if (remainingToTier3 > 0) {
|
|
651
|
-
this.scheduleTier(topicId, 3, remainingToTier3);
|
|
652
|
-
}
|
|
653
|
-
return;
|
|
654
|
-
}
|
|
655
553
|
state.cancelled = true;
|
|
656
554
|
this.cleanupState(topicId);
|
|
657
555
|
return; // Agent finished — don't send tier 2/3
|
|
@@ -688,25 +586,14 @@ export class PresenceProxy {
|
|
|
688
586
|
}
|
|
689
587
|
// ─── Tier 3: Stall Assessment ──────────────────────────────────────────
|
|
690
588
|
async fireTier3(topicId, state) {
|
|
691
|
-
// ── Session idle: agent completed work
|
|
692
|
-
// Same
|
|
693
|
-
// question first. If one exists and hasn't been relayed yet, send it.
|
|
694
|
-
// Otherwise cancel (agent finished).
|
|
589
|
+
// ── Session idle: agent completed work but didn't relay response ──
|
|
590
|
+
// Same check as tier 2 — if the agent finished, skip tier 3 entirely.
|
|
695
591
|
{
|
|
696
|
-
|
|
697
|
-
const idleRaw = this.config.captureSessionOutput(state.sessionName, 80);
|
|
592
|
+
const idleRaw = this.config.captureSessionOutput(state.sessionName, 10);
|
|
698
593
|
const idleSnapshot = idleRaw ? sanitizeTmuxOutput(idleRaw, this.config.credentialPatterns) : null;
|
|
699
594
|
if (idleSnapshot && detectSessionIdle(idleSnapshot)) {
|
|
700
595
|
const processes = this.config.getProcessTree(state.sessionName);
|
|
701
596
|
if (processes.length === 0) {
|
|
702
|
-
const relayed = await this.maybeRelayAwaitingInput(topicId, state, idleSnapshot, 3);
|
|
703
|
-
if (relayed) {
|
|
704
|
-
// Already notified (or re-notified) — don't run full tier 3 LLM
|
|
705
|
-
// assessment. The proxy stays alive so stall triage can still
|
|
706
|
-
// kick in if the user never replies.
|
|
707
|
-
state.tier3FiredAt = Date.now();
|
|
708
|
-
return;
|
|
709
|
-
}
|
|
710
597
|
state.cancelled = true;
|
|
711
598
|
this.cleanupState(topicId);
|
|
712
599
|
return;
|
|
@@ -1056,43 +943,6 @@ IMPORTANT BIAS: Default to "working" or "waiting" unless there is STRONG evidenc
|
|
|
1056
943
|
return result;
|
|
1057
944
|
}, priority);
|
|
1058
945
|
}
|
|
1059
|
-
/**
|
|
1060
|
-
* Try to detect an "awaiting user input" prompt in the snapshot and, if
|
|
1061
|
-
* found AND we haven't already relayed this specific question, send a
|
|
1062
|
-
* Telegram notification so the user knows their reply is needed.
|
|
1063
|
-
*
|
|
1064
|
-
* Returns true if a relay was sent (caller should skip the normal
|
|
1065
|
-
* idle-cancel or tier-progress logic to avoid noise). Returns false
|
|
1066
|
-
* otherwise.
|
|
1067
|
-
*
|
|
1068
|
-
* Dedupe: keyed on a fingerprint of the match context. The same pending
|
|
1069
|
-
* question won't be re-relayed across tiers.
|
|
1070
|
-
*/
|
|
1071
|
-
async maybeRelayAwaitingInput(topicId, state, snapshot, tier) {
|
|
1072
|
-
if (!snapshot)
|
|
1073
|
-
return false;
|
|
1074
|
-
const match = detectAwaitingUserInput(snapshot);
|
|
1075
|
-
if (!match)
|
|
1076
|
-
return false;
|
|
1077
|
-
const fingerprint = crypto
|
|
1078
|
-
.createHash('sha256')
|
|
1079
|
-
.update(match.context)
|
|
1080
|
-
.digest('hex')
|
|
1081
|
-
.slice(0, 16);
|
|
1082
|
-
// Already relayed this specific question — stay silent.
|
|
1083
|
-
if (state.awaitingInputFingerprint === fingerprint) {
|
|
1084
|
-
return true; // still "handled" — skip normal tier logic
|
|
1085
|
-
}
|
|
1086
|
-
const body = match.context || match.matchLine;
|
|
1087
|
-
const message = `${this.prefix} The session is paused waiting for your reply. ` +
|
|
1088
|
-
`Here's what it's asking:\n\n${body}`;
|
|
1089
|
-
await this.sendProxyMessage(topicId, message, tier);
|
|
1090
|
-
state.awaitingInputRelayedAt = Date.now();
|
|
1091
|
-
state.awaitingInputFingerprint = fingerprint;
|
|
1092
|
-
this.persistState(topicId, state);
|
|
1093
|
-
state.conversationHistory.push({ role: 'proxy', text: message, timestamp: Date.now() });
|
|
1094
|
-
return true;
|
|
1095
|
-
}
|
|
1096
946
|
async sendProxyMessage(topicId, text, tier) {
|
|
1097
947
|
try {
|
|
1098
948
|
await this.config.sendMessage(topicId, text, {
|
|
@@ -1210,8 +1060,6 @@ IMPORTANT BIAS: Default to "working" or "waiting" unless there is STRONG evidenc
|
|
|
1210
1060
|
tier2Snapshot: null,
|
|
1211
1061
|
tier3Summary: null,
|
|
1212
1062
|
cancelled: false,
|
|
1213
|
-
awaitingInputRelayedAt: data.awaitingInputRelayedAt ?? null,
|
|
1214
|
-
awaitingInputFingerprint: data.awaitingInputFingerprint ?? null,
|
|
1215
1063
|
lastLlmCallAt: data.lastLlmCallAt || 0,
|
|
1216
1064
|
conversationHistory: [],
|
|
1217
1065
|
};
|