sensorium-mcp 3.0.3 → 3.0.5
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/dashboard/routes/data.d.ts.map +1 -1
- package/dist/dashboard/routes/data.js +2 -1
- package/dist/dashboard/routes/data.js.map +1 -1
- package/dist/dashboard/routes/threads.js +1 -1
- package/dist/dashboard/routes/threads.js.map +1 -1
- package/dist/dashboard/routes.d.ts.map +1 -1
- package/dist/dashboard/routes.js +1 -3
- package/dist/dashboard/routes.js.map +1 -1
- package/dist/data/memory/migration-runner.d.ts +1 -1
- package/dist/data/memory/migration-runner.d.ts.map +1 -1
- package/dist/data/memory/migration-runner.js +59 -3
- package/dist/data/memory/migration-runner.js.map +1 -1
- package/dist/data/memory/schema-ddl.d.ts +1 -1
- package/dist/data/memory/schema-ddl.d.ts.map +1 -1
- package/dist/data/memory/schema-ddl.js +2 -1
- package/dist/data/memory/schema-ddl.js.map +1 -1
- package/dist/data/memory/thread-registry.js +1 -1
- package/dist/data/memory/thread-registry.js.map +1 -1
- package/dist/http-server.d.ts.map +1 -1
- package/dist/http-server.js +1 -9
- package/dist/http-server.js.map +1 -1
- package/dist/index.js +3 -6
- package/dist/index.js.map +1 -1
- package/dist/server/factory.js +1 -1
- package/dist/server/factory.js.map +1 -1
- package/dist/services/agent-spawn.service.d.ts +7 -1
- package/dist/services/agent-spawn.service.d.ts.map +1 -1
- package/dist/services/agent-spawn.service.js +69 -45
- package/dist/services/agent-spawn.service.js.map +1 -1
- package/dist/services/consolidation.service.d.ts.map +1 -1
- package/dist/services/consolidation.service.js +49 -35
- package/dist/services/consolidation.service.js.map +1 -1
- package/dist/services/keeper.service.d.ts +21 -0
- package/dist/services/keeper.service.d.ts.map +1 -0
- package/dist/services/keeper.service.js +195 -0
- package/dist/services/keeper.service.js.map +1 -0
- package/dist/services/maintenance-signal.d.ts +2 -0
- package/dist/services/maintenance-signal.d.ts.map +1 -1
- package/dist/services/maintenance-signal.js +7 -1
- package/dist/services/maintenance-signal.js.map +1 -1
- package/dist/services/process.service.d.ts +19 -2
- package/dist/services/process.service.d.ts.map +1 -1
- package/dist/services/process.service.js +104 -10
- package/dist/services/process.service.js.map +1 -1
- package/dist/services/reconnect-snapshot.service.d.ts.map +1 -1
- package/dist/services/reconnect-snapshot.service.js +20 -3
- package/dist/services/reconnect-snapshot.service.js.map +1 -1
- package/dist/services/thread-lifecycle.service.d.ts +5 -0
- package/dist/services/thread-lifecycle.service.d.ts.map +1 -1
- package/dist/services/thread-lifecycle.service.js +33 -8
- package/dist/services/thread-lifecycle.service.js.map +1 -1
- package/dist/services/worker-cleanup.service.d.ts +14 -1
- package/dist/services/worker-cleanup.service.d.ts.map +1 -1
- package/dist/services/worker-cleanup.service.js +48 -27
- package/dist/services/worker-cleanup.service.js.map +1 -1
- package/dist/sessions.d.ts +0 -5
- package/dist/sessions.d.ts.map +1 -1
- package/dist/sessions.js +0 -7
- package/dist/sessions.js.map +1 -1
- package/dist/stdio-server.d.ts.map +1 -1
- package/dist/stdio-server.js +1 -7
- package/dist/stdio-server.js.map +1 -1
- package/dist/tools/delegate-tool.d.ts.map +1 -1
- package/dist/tools/delegate-tool.js +2 -2
- package/dist/tools/delegate-tool.js.map +1 -1
- package/dist/tools/session-tools.js +1 -1
- package/dist/tools/session-tools.js.map +1 -1
- package/dist/tools/start-session-tool.d.ts.map +1 -1
- package/dist/tools/start-session-tool.js +8 -9
- package/dist/tools/start-session-tool.js.map +1 -1
- package/dist/tools/wait/message-processing.d.ts.map +1 -1
- package/dist/tools/wait/message-processing.js +28 -0
- package/dist/tools/wait/message-processing.js.map +1 -1
- package/dist/tools/wait/poll-loop.js +1 -1
- package/dist/tools/wait/poll-loop.js.map +1 -1
- package/package.json +1 -1
- package/dist/tools/thread-lifecycle.d.ts +0 -6
- package/dist/tools/thread-lifecycle.d.ts.map +0 -1
- package/dist/tools/thread-lifecycle.js +0 -6
- package/dist/tools/thread-lifecycle.js.map +0 -1
- package/supervisor/config.go +0 -253
- package/supervisor/config_test.go +0 -78
- package/supervisor/go.mod +0 -15
- package/supervisor/go.sum +0 -20
- package/supervisor/health.go +0 -433
- package/supervisor/health_test.go +0 -93
- package/supervisor/keeper.go +0 -309
- package/supervisor/keeper_test.go +0 -27
- package/supervisor/lock.go +0 -57
- package/supervisor/lock_test.go +0 -54
- package/supervisor/log.go +0 -195
- package/supervisor/log_test.go +0 -125
- package/supervisor/main.go +0 -461
- package/supervisor/main_test.go +0 -130
- package/supervisor/notify.go +0 -53
- package/supervisor/process.go +0 -294
- package/supervisor/process_test.go +0 -108
- package/supervisor/process_unix.go +0 -14
- package/supervisor/process_windows.go +0 -15
- package/supervisor/secrets.go +0 -95
- package/supervisor/secrets_securevault_test.go +0 -98
- package/supervisor/secrets_test.go +0 -119
- package/supervisor/self_update.go +0 -282
- package/supervisor/self_update_test.go +0 -177
- package/supervisor/service_restart_stub.go +0 -9
- package/supervisor/service_restart_windows.go +0 -63
- package/supervisor/service_stub.go +0 -15
- package/supervisor/service_windows.go +0 -194
- package/supervisor/update_state.go +0 -264
- package/supervisor/update_state_test.go +0 -306
- package/supervisor/updater.go +0 -613
- package/supervisor/updater_test.go +0 -64
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { log } from "../logger.js";
|
|
2
|
+
import { findAliveThreadViaPidFile, killProcessTree } from "./process.service.js";
|
|
3
|
+
import { readThreadHeartbeat } from "../data/file-storage.js";
|
|
4
|
+
import { dispatchSpawn } from "./agent-spawn.service.js";
|
|
5
|
+
import { getKeepAliveThreads, getThread } from "../data/memory/thread-registry.js";
|
|
6
|
+
import { ThreadState } from "./thread-lifecycle.service.js";
|
|
7
|
+
import { errorMessage } from "../utils.js";
|
|
8
|
+
const KEEPER_CHECK_INTERVAL_MS = 120_000; // 2 min
|
|
9
|
+
const KEEPER_MAX_RETRIES = 5;
|
|
10
|
+
const KEEPER_COOLDOWN_MS = 300_000; // 5 min
|
|
11
|
+
const FAST_EXIT_THRESHOLD_MS = 60_000; // 60s
|
|
12
|
+
const FAST_EXIT_MAX_COUNT = 3;
|
|
13
|
+
const FAST_EXIT_BASE_COOLDOWN_MS = 600_000; // 10 min
|
|
14
|
+
const FAST_EXIT_MAX_COOLDOWN_MS = 14_400_000; // 4 hours
|
|
15
|
+
const STUCK_THRESHOLD_MS = 1_800_000; // 30 min
|
|
16
|
+
export class KeeperService {
|
|
17
|
+
deps;
|
|
18
|
+
keepers = new Map();
|
|
19
|
+
syncTimer = null;
|
|
20
|
+
constructor(deps) {
|
|
21
|
+
this.deps = deps;
|
|
22
|
+
}
|
|
23
|
+
start() {
|
|
24
|
+
if (this.syncTimer !== null)
|
|
25
|
+
return;
|
|
26
|
+
this.syncKeepers();
|
|
27
|
+
this.syncTimer = setInterval(() => this.syncKeepers(), KEEPER_CHECK_INTERVAL_MS);
|
|
28
|
+
}
|
|
29
|
+
stop() {
|
|
30
|
+
if (this.syncTimer !== null) {
|
|
31
|
+
clearInterval(this.syncTimer);
|
|
32
|
+
this.syncTimer = null;
|
|
33
|
+
}
|
|
34
|
+
for (const entry of this.keepers.values()) {
|
|
35
|
+
entry.stopped = true;
|
|
36
|
+
}
|
|
37
|
+
this.keepers.clear();
|
|
38
|
+
}
|
|
39
|
+
syncKeepers() {
|
|
40
|
+
let threads;
|
|
41
|
+
try {
|
|
42
|
+
const db = this.deps.getMemoryDb();
|
|
43
|
+
threads = getKeepAliveThreads(db);
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
log.warn(`[keeper] Failed to fetch keepAlive threads: ${errorMessage(err)}`);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const wanted = new Set(threads.map((t) => t.threadId));
|
|
50
|
+
// Remove keepers no longer in the keepAlive list
|
|
51
|
+
for (const [threadId, entry] of this.keepers.entries()) {
|
|
52
|
+
if (!wanted.has(threadId)) {
|
|
53
|
+
entry.stopped = true;
|
|
54
|
+
this.keepers.delete(threadId);
|
|
55
|
+
log.info(`[keeper] Stopped keeper for removed thread ${threadId}`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
// Add new keepers
|
|
59
|
+
for (const thread of threads) {
|
|
60
|
+
if (!this.keepers.has(thread.threadId)) {
|
|
61
|
+
this.keepers.set(thread.threadId, {
|
|
62
|
+
threadId: thread.threadId,
|
|
63
|
+
retryCount: 0,
|
|
64
|
+
fastExitCount: 0,
|
|
65
|
+
fastExitEscalation: 0,
|
|
66
|
+
lastStartTime: 0,
|
|
67
|
+
cooldownUntil: 0,
|
|
68
|
+
stopped: false,
|
|
69
|
+
checking: false,
|
|
70
|
+
});
|
|
71
|
+
log.info(`[keeper] Started keeper for thread ${thread.threadId} ('${thread.name}')`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// Check all active keepers (skip if already in-flight to prevent duplicate spawns)
|
|
75
|
+
for (const entry of this.keepers.values()) {
|
|
76
|
+
if (!entry.stopped && !entry.checking) {
|
|
77
|
+
entry.checking = true;
|
|
78
|
+
this.checkAndRestart(entry)
|
|
79
|
+
.catch((err) => {
|
|
80
|
+
log.warn(`[keeper] checkAndRestart error for thread ${entry.threadId}: ${errorMessage(err)}`);
|
|
81
|
+
})
|
|
82
|
+
.finally(() => { entry.checking = false; });
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
async checkAndRestart(entry) {
|
|
87
|
+
const now = Date.now();
|
|
88
|
+
// 1. Check cooldown
|
|
89
|
+
if (entry.cooldownUntil > now) {
|
|
90
|
+
log.debug(`[keeper] Thread ${entry.threadId} in cooldown for ${Math.round((entry.cooldownUntil - now) / 1000)}s`);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
// 2. Check if thread is running — reads PID file directly (authoritative OS evidence)
|
|
94
|
+
const alivePid = findAliveThreadViaPidFile(entry.threadId);
|
|
95
|
+
if (alivePid !== undefined) {
|
|
96
|
+
// 3. Check if stuck via heartbeat
|
|
97
|
+
const heartbeat = readThreadHeartbeat(entry.threadId);
|
|
98
|
+
if (heartbeat !== null && (now - heartbeat) > STUCK_THRESHOLD_MS) {
|
|
99
|
+
log.warn(`[keeper] Thread ${entry.threadId} is stuck (no heartbeat for ${Math.round((now - heartbeat) / 60000)}m) — killing`);
|
|
100
|
+
try {
|
|
101
|
+
const stuckDb = this.deps.getMemoryDb();
|
|
102
|
+
this.deps.threadLifecycle.transitionThread(stuckDb, entry.threadId, ThreadState.Stuck);
|
|
103
|
+
}
|
|
104
|
+
catch (err) {
|
|
105
|
+
log.warn(`[keeper] Active→Stuck transition failed for ${entry.threadId}: ${errorMessage(err)}`);
|
|
106
|
+
}
|
|
107
|
+
killProcessTree(alivePid, entry.threadId);
|
|
108
|
+
entry.retryCount = 0;
|
|
109
|
+
// Fall through to restart
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
// Healthy — reset counters
|
|
113
|
+
if (entry.retryCount > 0) {
|
|
114
|
+
log.info(`[keeper] Thread ${entry.threadId} is healthy again (was at retry ${entry.retryCount})`);
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
log.debug(`[keeper] Thread ${entry.threadId} is healthy`);
|
|
118
|
+
}
|
|
119
|
+
entry.retryCount = 0;
|
|
120
|
+
entry.fastExitCount = 0;
|
|
121
|
+
entry.fastExitEscalation = 0;
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
// Thread is not running (or was stuck and killed)
|
|
126
|
+
// 4. Fast-exit detection
|
|
127
|
+
if (entry.lastStartTime > 0 && (now - entry.lastStartTime) < FAST_EXIT_THRESHOLD_MS) {
|
|
128
|
+
entry.fastExitCount++;
|
|
129
|
+
if (entry.fastExitCount >= FAST_EXIT_MAX_COUNT) {
|
|
130
|
+
const cooldown = Math.min(FAST_EXIT_BASE_COOLDOWN_MS * Math.pow(2, entry.fastExitEscalation), FAST_EXIT_MAX_COOLDOWN_MS);
|
|
131
|
+
entry.cooldownUntil = now + cooldown;
|
|
132
|
+
entry.fastExitEscalation++;
|
|
133
|
+
log.warn(`[keeper] Thread ${entry.threadId}: ${entry.fastExitCount} consecutive fast exits — backing off ${Math.round(cooldown / 60000)}m`);
|
|
134
|
+
await this.notifyDeath(entry.threadId, "repeated fast exits — check credits/API key", cooldown);
|
|
135
|
+
entry.fastExitCount = 0;
|
|
136
|
+
entry.retryCount = 0;
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
else if (entry.lastStartTime > 0) {
|
|
141
|
+
// Previous start was long ago — not a fast exit pattern
|
|
142
|
+
entry.fastExitCount = 0;
|
|
143
|
+
entry.fastExitEscalation = 0;
|
|
144
|
+
}
|
|
145
|
+
// 5. Max retries check
|
|
146
|
+
if (entry.retryCount >= KEEPER_MAX_RETRIES) {
|
|
147
|
+
entry.cooldownUntil = now + KEEPER_COOLDOWN_MS;
|
|
148
|
+
log.warn(`[keeper] Thread ${entry.threadId}: max retries (${KEEPER_MAX_RETRIES}) exceeded — cooling down for ${KEEPER_COOLDOWN_MS / 60000}m`);
|
|
149
|
+
await this.notifyDeath(entry.threadId, "max retries exceeded", KEEPER_COOLDOWN_MS);
|
|
150
|
+
entry.retryCount = 0;
|
|
151
|
+
entry.fastExitCount = 0;
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
// 6. Restart: re-check stopped (may have been set by syncKeepers while in-flight)
|
|
155
|
+
if (entry.stopped)
|
|
156
|
+
return;
|
|
157
|
+
// Re-verify keepAlive before restarting
|
|
158
|
+
const db = this.deps.getMemoryDb();
|
|
159
|
+
const thread = getThread(db, entry.threadId);
|
|
160
|
+
if (!thread || !thread.keepAlive) {
|
|
161
|
+
log.info(`[keeper] Thread ${entry.threadId} no longer has keepAlive=true — will stop at next sync`);
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
log.info(`[keeper] Thread ${entry.threadId} not running — restarting (attempt ${entry.retryCount + 1}/${KEEPER_MAX_RETRIES})`);
|
|
165
|
+
const result = dispatchSpawn((thread.client || "claude"), thread.name, entry.threadId, this.deps.threadLifecycle, thread.workingDirectory ?? undefined);
|
|
166
|
+
if ("pid" in result) {
|
|
167
|
+
entry.lastStartTime = Date.now();
|
|
168
|
+
entry.retryCount = 0;
|
|
169
|
+
log.info(`[keeper] Thread ${entry.threadId} restarted successfully (PID=${result.pid})`);
|
|
170
|
+
try {
|
|
171
|
+
this.deps.threadLifecycle.activateThread(db, entry.threadId);
|
|
172
|
+
}
|
|
173
|
+
catch (err) {
|
|
174
|
+
log.warn(`[keeper] activateThread failed for ${entry.threadId}: ${errorMessage(err)}`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
entry.retryCount++;
|
|
179
|
+
log.warn(`[keeper] Thread ${entry.threadId} restart failed (attempt ${entry.retryCount}/${KEEPER_MAX_RETRIES}): ${result.error}`);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
async notifyDeath(threadId, reason, cooldownMs) {
|
|
183
|
+
try {
|
|
184
|
+
const db = this.deps.getMemoryDb();
|
|
185
|
+
const thread = getThread(db, threadId);
|
|
186
|
+
const name = thread?.name ?? `Thread ${threadId}`;
|
|
187
|
+
const suffix = cooldownMs ? `cooling down for ${Math.round(cooldownMs / 60000)}m` : "will retry";
|
|
188
|
+
await this.deps.telegram.sendMessage(this.deps.chatId, `💀 <b>${name}</b> session died (${reason}) — ${suffix}`, "HTML", threadId);
|
|
189
|
+
}
|
|
190
|
+
catch (err) {
|
|
191
|
+
log.warn(`[keeper] notifyDeath failed for thread ${threadId}: ${errorMessage(err)}`);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
//# sourceMappingURL=keeper.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keeper.service.js","sourceRoot":"","sources":["../../src/services/keeper.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
* requiring callers to poll. The exported emitMaintenanceSignal() is
|
|
8
8
|
* available for manual/test triggering.
|
|
9
9
|
*/
|
|
10
|
+
/** Close the filesystem watcher (call during shutdown). */
|
|
11
|
+
export declare function closeMaintenanceWatcher(): void;
|
|
10
12
|
/** Manually fire the maintenance signal (e.g. from tests or from code that
|
|
11
13
|
* writes the flag directly without going through the file system watcher). */
|
|
12
14
|
export declare function emitMaintenanceSignal(): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"maintenance-signal.d.ts","sourceRoot":"","sources":["../../src/services/maintenance-signal.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;
|
|
1
|
+
{"version":3,"file":"maintenance-signal.d.ts","sourceRoot":"","sources":["../../src/services/maintenance-signal.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AA+BH,2DAA2D;AAC3D,wBAAgB,uBAAuB,IAAI,IAAI,CAG9C;AAED;+EAC+E;AAC/E,wBAAgB,qBAAqB,IAAI,IAAI,CAE5C;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAG9D"}
|
|
@@ -20,8 +20,9 @@ emitter.setMaxListeners(500);
|
|
|
20
20
|
// Watch the data directory for maintenance.flag creation / modification.
|
|
21
21
|
// On Windows, fs.watch fires "rename" when files are created or deleted;
|
|
22
22
|
// on Linux/macOS it fires "rename" on creation and "change" on modification.
|
|
23
|
+
let watcher = null;
|
|
23
24
|
try {
|
|
24
|
-
watch(DATA_DIR, (_eventType, filename) => {
|
|
25
|
+
watcher = watch(DATA_DIR, (_eventType, filename) => {
|
|
25
26
|
if (filename === FLAG_NAME) {
|
|
26
27
|
emitter.emit("maintenance");
|
|
27
28
|
}
|
|
@@ -32,6 +33,11 @@ catch (err) {
|
|
|
32
33
|
// won't fire from the watcher — emitMaintenanceSignal() still works.
|
|
33
34
|
log.warn(`[maintenance-signal] Could not watch ${DATA_DIR}: ${err}`);
|
|
34
35
|
}
|
|
36
|
+
/** Close the filesystem watcher (call during shutdown). */
|
|
37
|
+
export function closeMaintenanceWatcher() {
|
|
38
|
+
watcher?.close();
|
|
39
|
+
watcher = null;
|
|
40
|
+
}
|
|
35
41
|
/** Manually fire the maintenance signal (e.g. from tests or from code that
|
|
36
42
|
* writes the flag directly without going through the file system watcher). */
|
|
37
43
|
export function emitMaintenanceSignal() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"maintenance-signal.js","sourceRoot":"","sources":["../../src/services/maintenance-signal.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AAEnC,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,qBAAqB,CAAC,CAAC;AACxD,MAAM,SAAS,GAAG,kBAAkB,CAAC;AAErC,MAAM,OAAO,GAAG,IAAI,YAAY,EAAE,CAAC;AACnC,wEAAwE;AACxE,OAAO,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC;AAE7B,yEAAyE;AACzE,yEAAyE;AACzE,6EAA6E;AAC7E,IAAI,CAAC;IACH,KAAK,CAAC,QAAQ,EAAE,CAAC,UAAU,EAAE,QAAQ,EAAE,EAAE;
|
|
1
|
+
{"version":3,"file":"maintenance-signal.js","sourceRoot":"","sources":["../../src/services/maintenance-signal.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AAEnC,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,qBAAqB,CAAC,CAAC;AACxD,MAAM,SAAS,GAAG,kBAAkB,CAAC;AAErC,MAAM,OAAO,GAAG,IAAI,YAAY,EAAE,CAAC;AACnC,wEAAwE;AACxE,OAAO,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC;AAE7B,yEAAyE;AACzE,yEAAyE;AACzE,6EAA6E;AAC7E,IAAI,OAAO,GAAoC,IAAI,CAAC;AACpD,IAAI,CAAC;IACH,OAAO,GAAG,KAAK,CAAC,QAAQ,EAAE,CAAC,UAAU,EAAE,QAAQ,EAAE,EAAE;QACjD,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,OAAO,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC;AAAC,OAAO,GAAG,EAAE,CAAC;IACb,uEAAuE;IACvE,qEAAqE;IACrE,GAAG,CAAC,IAAI,CAAC,wCAAwC,QAAQ,KAAK,GAAG,EAAE,CAAC,CAAC;AACvE,CAAC;AAED,2DAA2D;AAC3D,MAAM,UAAU,uBAAuB;IACrC,OAAO,EAAE,KAAK,EAAE,CAAC;IACjB,OAAO,GAAG,IAAI,CAAC;AACjB,CAAC;AAED;+EAC+E;AAC/E,MAAM,UAAU,qBAAqB;IACnC,OAAO,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;AAC9B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,EAAc;IAChD,OAAO,CAAC,EAAE,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC;IAC9B,OAAO,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC;AAC9C,CAAC"}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { Database } from "../data/memory/schema.js";
|
|
2
|
+
import type { ThreadLifecycleService } from "./thread-lifecycle.service.js";
|
|
1
3
|
export declare const PENDING_TASKS_DIR: string;
|
|
2
4
|
export declare const PROCESS_BASE_DIR: string;
|
|
3
5
|
export declare const PROCESS_LOGS_DIR: string;
|
|
@@ -24,8 +26,23 @@ export declare function isProcessAlive(pid: number): boolean;
|
|
|
24
26
|
export declare function readPidFiles(): PidFileEntry[];
|
|
25
27
|
export declare function findAliveThread(threadId: number): SpawnedThread | undefined;
|
|
26
28
|
export declare const isThreadRunning: (threadId: number) => boolean;
|
|
29
|
+
export declare function getActiveThreadIds(): number[];
|
|
27
30
|
export declare function ensureDirs(): void;
|
|
28
|
-
|
|
29
|
-
|
|
31
|
+
/**
|
|
32
|
+
* Check liveness of a thread directly via its PID file, bypassing the
|
|
33
|
+
* spawnedThreads[] in-memory cache. Returns the live PID, or undefined
|
|
34
|
+
* if no PID file exists or the process is dead (stale file is deleted).
|
|
35
|
+
*/
|
|
36
|
+
export declare function findAliveThreadViaPidFile(threadId: number): number | undefined;
|
|
37
|
+
/**
|
|
38
|
+
* Reconcile in-memory state (spawnedThreads[]) with the two authoritative
|
|
39
|
+
* sources of truth at startup:
|
|
40
|
+
* - SQLite thread_registry (intent / configuration)
|
|
41
|
+
* - PID files on disk (OS evidence of running processes)
|
|
42
|
+
*
|
|
43
|
+
* Replaces the old restoreFromPidFiles() + cleanupStalePidFiles() pair.
|
|
44
|
+
*/
|
|
45
|
+
export declare function reconcileState(db: Database, threadLifecycle: ThreadLifecycleService): void;
|
|
46
|
+
export declare function killProcessTree(pid: number, threadId: number): void;
|
|
30
47
|
export {};
|
|
31
48
|
//# sourceMappingURL=process.service.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"process.service.d.ts","sourceRoot":"","sources":["../../src/services/process.service.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"process.service.d.ts","sourceRoot":"","sources":["../../src/services/process.service.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAEzD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AAM5E,eAAO,MAAM,iBAAiB,QAAkC,CAAC;AACjE,eAAO,MAAM,gBAAgB,QAAW,CAAC;AACzC,eAAO,MAAM,gBAAgB,QAAW,CAAC;AACzC,eAAO,MAAM,gBAAgB,QAAW,CAAC;AAEzC,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,UAAU,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC;CAClC;AAED,UAAU,YAAY;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,eAAO,MAAM,cAAc,EAAE,aAAa,EAAO,CAAC;AAElD,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAOnD;AAED,wBAAgB,YAAY,IAAI,YAAY,EAAE,CAqB7C;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CAuB3E;AAED,eAAO,MAAM,eAAe,GAAI,UAAU,MAAM,KAAG,OAAkD,CAAC;AAEtG,wBAAgB,kBAAkB,IAAI,MAAM,EAAE,CAE7C;AAED,wBAAgB,UAAU,IAAI,IAAI,CAIjC;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAQ9E;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,EAAE,EAAE,QAAQ,EAAE,eAAe,EAAE,sBAAsB,GAAG,IAAI,CAqD1F;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAanE"}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { execSync } from "node:child_process";
|
|
2
|
-
import {
|
|
2
|
+
import { mkdirSync, readdirSync, readFileSync, unlinkSync } from "node:fs";
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
import { log } from "../logger.js";
|
|
6
|
+
import { errorMessage } from "../utils.js";
|
|
7
|
+
import { getAllThreads } from "../data/memory/thread-registry.js";
|
|
6
8
|
const BASE_DIR = join(homedir(), ".remote-copilot-mcp");
|
|
7
9
|
const LOGS_DIR = join(BASE_DIR, "logs");
|
|
8
10
|
const PIDS_DIR = join(BASE_DIR, "pids");
|
|
@@ -79,22 +81,114 @@ export function findAliveThread(threadId) {
|
|
|
79
81
|
return restored;
|
|
80
82
|
}
|
|
81
83
|
export const isThreadRunning = (threadId) => findAliveThread(threadId) !== undefined;
|
|
84
|
+
export function getActiveThreadIds() {
|
|
85
|
+
return spawnedThreads.filter(t => isProcessAlive(t.pid)).map(t => t.threadId);
|
|
86
|
+
}
|
|
82
87
|
export function ensureDirs() {
|
|
83
88
|
mkdirSync(PENDING_TASKS_DIR, { recursive: true });
|
|
84
89
|
mkdirSync(LOGS_DIR, { recursive: true });
|
|
85
90
|
mkdirSync(PIDS_DIR, { recursive: true });
|
|
86
91
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
92
|
+
/**
|
|
93
|
+
* Check liveness of a thread directly via its PID file, bypassing the
|
|
94
|
+
* spawnedThreads[] in-memory cache. Returns the live PID, or undefined
|
|
95
|
+
* if no PID file exists or the process is dead (stale file is deleted).
|
|
96
|
+
*/
|
|
97
|
+
export function findAliveThreadViaPidFile(threadId) {
|
|
98
|
+
const entry = readPidFiles().find((e) => e.threadId === threadId);
|
|
99
|
+
if (!entry)
|
|
100
|
+
return undefined;
|
|
101
|
+
if (!isProcessAlive(entry.pid)) {
|
|
102
|
+
try {
|
|
103
|
+
unlinkSync(entry.filePath);
|
|
104
|
+
}
|
|
105
|
+
catch { }
|
|
106
|
+
return undefined;
|
|
107
|
+
}
|
|
108
|
+
return entry.pid;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Reconcile in-memory state (spawnedThreads[]) with the two authoritative
|
|
112
|
+
* sources of truth at startup:
|
|
113
|
+
* - SQLite thread_registry (intent / configuration)
|
|
114
|
+
* - PID files on disk (OS evidence of running processes)
|
|
115
|
+
*
|
|
116
|
+
* Replaces the old restoreFromPidFiles() + cleanupStalePidFiles() pair.
|
|
117
|
+
*/
|
|
118
|
+
export function reconcileState(db, threadLifecycle) {
|
|
119
|
+
const pidEntries = readPidFiles();
|
|
120
|
+
const dbThreadMap = new Map(getAllThreads(db)
|
|
121
|
+
.filter((t) => ["active", "exited", "exiting", "spawning", "stuck"].includes(t.status))
|
|
122
|
+
.map((t) => [t.threadId, t]));
|
|
123
|
+
// 1. Process each PID file: alive → populate spawnedThreads[], dead → clean up
|
|
124
|
+
for (const pidEntry of pidEntries) {
|
|
125
|
+
const dbThread = dbThreadMap.get(pidEntry.threadId);
|
|
126
|
+
if (!dbThread) {
|
|
127
|
+
// PID file references a thread with no DB record (very old orphan) — remove it
|
|
128
|
+
log.warn(`[reconcileState] PID file for thread ${pidEntry.threadId} has no DB record — removing`);
|
|
129
|
+
try {
|
|
130
|
+
unlinkSync(pidEntry.filePath);
|
|
131
|
+
}
|
|
132
|
+
catch { }
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
if (isProcessAlive(pidEntry.pid)) {
|
|
136
|
+
// Alive: populate spawnedThreads[] if not already present
|
|
137
|
+
if (!spawnedThreads.some((t) => t.threadId === pidEntry.threadId)) {
|
|
138
|
+
spawnedThreads.push({
|
|
139
|
+
pid: pidEntry.pid,
|
|
140
|
+
threadId: pidEntry.threadId,
|
|
141
|
+
name: pidEntry.name ?? dbThread.name,
|
|
142
|
+
startedAt: Date.now(),
|
|
143
|
+
createdAt: Date.now(),
|
|
144
|
+
logFile: "",
|
|
145
|
+
});
|
|
146
|
+
log.info(`[reconcileState] Restored thread ${pidEntry.threadId} PID=${pidEntry.pid} from PID file`);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
// Dead: clean up stale PID file and mark thread exited in DB if active
|
|
94
151
|
try {
|
|
95
|
-
unlinkSync(filePath);
|
|
152
|
+
unlinkSync(pidEntry.filePath);
|
|
96
153
|
}
|
|
97
154
|
catch { }
|
|
155
|
+
log.info(`[reconcileState] Cleaned stale PID file for thread ${pidEntry.threadId} (PID=${pidEntry.pid} dead)`);
|
|
156
|
+
if (["active", "exiting", "spawning", "stuck"].includes(dbThread.status)) {
|
|
157
|
+
try {
|
|
158
|
+
threadLifecycle.markExited(db, pidEntry.threadId);
|
|
159
|
+
}
|
|
160
|
+
catch (err) {
|
|
161
|
+
log.warn(`[reconcileState] markExited failed for thread ${pidEntry.threadId}: ${errorMessage(err)}`);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
// 2. Remove spawnedThreads[] entries that have no DB record
|
|
167
|
+
for (let i = spawnedThreads.length - 1; i >= 0; i--) {
|
|
168
|
+
const thread = spawnedThreads[i];
|
|
169
|
+
if (!dbThreadMap.has(thread.threadId)) {
|
|
170
|
+
log.warn(`[reconcileState] spawnedThreads has thread ${thread.threadId} with no DB record — removing`);
|
|
171
|
+
spawnedThreads.splice(i, 1);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
export function killProcessTree(pid, threadId) {
|
|
176
|
+
try {
|
|
177
|
+
if (process.platform === "win32") {
|
|
178
|
+
execSync(`taskkill /F /T /PID ${pid}`, { timeout: 10000 });
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
process.kill(pid, "SIGTERM");
|
|
182
|
+
}
|
|
183
|
+
log.info(`[process] Killed process tree for thread ${threadId} PID=${pid}`);
|
|
184
|
+
}
|
|
185
|
+
catch (err) {
|
|
186
|
+
log.debug(`[process] Kill process ${pid} (thread ${threadId}): ${errorMessage(err)}`);
|
|
187
|
+
}
|
|
188
|
+
const pidFile = join(PROCESS_PIDS_DIR, `${threadId}.pid`);
|
|
189
|
+
try {
|
|
190
|
+
unlinkSync(pidFile);
|
|
191
|
+
}
|
|
192
|
+
catch { }
|
|
98
193
|
}
|
|
99
|
-
export const pidDirExists = () => existsSync(PIDS_DIR);
|
|
100
194
|
//# sourceMappingURL=process.service.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"process.service.js","sourceRoot":"","sources":["../../src/services/process.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"process.service.js","sourceRoot":"","sources":["../../src/services/process.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reconnect-snapshot.service.d.ts","sourceRoot":"","sources":["../../src/services/reconnect-snapshot.service.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;
|
|
1
|
+
{"version":3,"file":"reconnect-snapshot.service.d.ts","sourceRoot":"","sources":["../../src/services/reconnect-snapshot.service.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAuBH;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,IAAI,CAchE;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAgC9D;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,IAAI,IAAI,CAS7C"}
|
|
@@ -6,13 +6,19 @@
|
|
|
6
6
|
* The snapshot is valid for 10 minutes. After that it is either deleted
|
|
7
7
|
* by the auto-cleanup timer or ignored (too old) by isReconnectCandidate.
|
|
8
8
|
*/
|
|
9
|
-
import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
|
|
9
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
|
|
10
10
|
import { homedir } from "node:os";
|
|
11
11
|
import { join } from "node:path";
|
|
12
12
|
import { log } from "../logger.js";
|
|
13
13
|
const DATA_DIR = join(homedir(), ".remote-copilot-mcp");
|
|
14
14
|
const SNAPSHOT_PATH = join(DATA_DIR, "active-sessions.json");
|
|
15
15
|
const SNAPSHOT_MAX_AGE_MS = 10 * 60 * 1000; // 10 minutes
|
|
16
|
+
/** Write via temp file + rename to prevent partial reads from concurrent access. */
|
|
17
|
+
function atomicWriteSnapshot(data) {
|
|
18
|
+
const tmp = `${SNAPSHOT_PATH}.tmp.${process.pid}`;
|
|
19
|
+
writeFileSync(tmp, data, "utf-8");
|
|
20
|
+
renameSync(tmp, SNAPSHOT_PATH);
|
|
21
|
+
}
|
|
16
22
|
/**
|
|
17
23
|
* Write the set of active thread IDs to the reconnect snapshot file.
|
|
18
24
|
* Called just before the server process exits.
|
|
@@ -24,7 +30,7 @@ export function writeReconnectSnapshot(threadIds) {
|
|
|
24
30
|
threadIds,
|
|
25
31
|
timestamp: new Date().toISOString(),
|
|
26
32
|
};
|
|
27
|
-
|
|
33
|
+
atomicWriteSnapshot(JSON.stringify(snapshot, null, 2));
|
|
28
34
|
log.info(`[reconnect-snapshot] Wrote snapshot with ${threadIds.length} thread(s): ${threadIds.join(", ")}`);
|
|
29
35
|
}
|
|
30
36
|
catch (err) {
|
|
@@ -56,7 +62,18 @@ export function isReconnectCandidate(threadId) {
|
|
|
56
62
|
}
|
|
57
63
|
// Consume: remove this threadId so it can't match again
|
|
58
64
|
snapshot.threadIds = snapshot.threadIds.filter(id => id !== threadId);
|
|
59
|
-
|
|
65
|
+
try {
|
|
66
|
+
if (snapshot.threadIds.length === 0) {
|
|
67
|
+
unlinkSync(SNAPSHOT_PATH);
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
atomicWriteSnapshot(JSON.stringify(snapshot, null, 2));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
catch (writeErr) {
|
|
74
|
+
log.warn(`[reconnect-snapshot] Matched thread ${threadId} but failed to persist consume: ${writeErr}`);
|
|
75
|
+
// Still return true — the match was valid, and the 10-min TTL bounds the risk
|
|
76
|
+
}
|
|
60
77
|
log.info(`[reconnect-snapshot] Consumed reconnect slot for thread ${threadId}.`);
|
|
61
78
|
return true;
|
|
62
79
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reconnect-snapshot.service.js","sourceRoot":"","sources":["../../src/services/reconnect-snapshot.service.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"reconnect-snapshot.service.js","sourceRoot":"","sources":["../../src/services/reconnect-snapshot.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import type { Database } from "../data/memory/schema.js";
|
|
2
2
|
import type { IThreadRepository, ISessionRepository } from "../data/interfaces.js";
|
|
3
3
|
export declare enum ThreadState {
|
|
4
|
+
Created = "created",
|
|
5
|
+
Spawning = "spawning",
|
|
4
6
|
Active = "active",
|
|
5
7
|
Dormant = "dormant",
|
|
8
|
+
Stuck = "stuck",
|
|
9
|
+
Exiting = "exiting",
|
|
6
10
|
Exited = "exited",
|
|
7
11
|
Archived = "archived",
|
|
8
12
|
Expired = "expired"
|
|
@@ -42,6 +46,7 @@ export declare class ThreadLifecycleService {
|
|
|
42
46
|
archiveThread(db: Database, threadId: number): ThreadRecord;
|
|
43
47
|
remapTopic(db: Database, input: RemapTopicInput): ThreadRecord;
|
|
44
48
|
getThreadState(db: Database, threadId: number): ThreadState | null;
|
|
49
|
+
transitionThread(db: Database, threadId: number, targetState: ThreadState): ThreadRecord;
|
|
45
50
|
private requireThread;
|
|
46
51
|
private assertTransition;
|
|
47
52
|
private syncTopicMappings;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"thread-lifecycle.service.d.ts","sourceRoot":"","sources":["../../src/services/thread-lifecycle.service.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,KAAK,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAEnF,oBAAY,WAAW;IACrB,MAAM,WAAW;IACjB,OAAO,YAAY;IACnB,MAAM,WAAW;IACjB,QAAQ,aAAa;IACrB,OAAO,YAAY;CACpB;AAED,KAAK,YAAY,GAAG,WAAW,CAAC,UAAU,CAAC,iBAAiB,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;AAG5E,KAAK,mBAAmB,GAAG,UAAU,CAAC,iBAAiB,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG;IAC9E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC,CAAC;AAEF,KAAK,mBAAmB,GAAG,OAAO,CAChC,IAAI,CACF,YAAY,EACZ,QAAQ,GAAG,WAAW,GAAG,cAAc,GAAG,iBAAiB,GAAG,kBAAkB,CACjF,CACF,CAAC;AAEF,KAAK,gBAAgB,GAAG,OAAO,CAC7B,IAAI,CAAC,YAAY,EAAE,cAAc,GAAG,iBAAiB,CAAC,CACvD,CAAC;AAEF,KAAK,eAAe,GAAG;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB,CAAC;AAEF,MAAM,WAAW,8BAA8B;IAC7C,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACpG;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;
|
|
1
|
+
{"version":3,"file":"thread-lifecycle.service.d.ts","sourceRoot":"","sources":["../../src/services/thread-lifecycle.service.ts"],"names":[],"mappings":"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"}
|