@grackle-ai/core 0.92.3 → 0.93.0
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/environment-handlers.d.ts.map +1 -1
- package/dist/environment-handlers.js +17 -5
- package/dist/environment-handlers.js.map +1 -1
- package/dist/grpc-proto-converters.d.ts +6 -2
- package/dist/grpc-proto-converters.d.ts.map +1 -1
- package/dist/grpc-proto-converters.js +11 -3
- package/dist/grpc-proto-converters.js.map +1 -1
- package/dist/index.d.ts +6 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -6
- package/dist/index.js.map +1 -1
- package/dist/lifecycle.d.ts +10 -9
- package/dist/lifecycle.d.ts.map +1 -1
- package/dist/lifecycle.js +18 -21
- package/dist/lifecycle.js.map +1 -1
- package/dist/root-task-boot.d.ts +12 -3
- package/dist/root-task-boot.d.ts.map +1 -1
- package/dist/root-task-boot.js +80 -45
- package/dist/root-task-boot.js.map +1 -1
- package/dist/signals/escalation-auto.d.ts +9 -8
- package/dist/signals/escalation-auto.d.ts.map +1 -1
- package/dist/signals/escalation-auto.js +21 -25
- package/dist/signals/escalation-auto.js.map +1 -1
- package/dist/signals/orphan-reparent.d.ts +9 -8
- package/dist/signals/orphan-reparent.d.ts.map +1 -1
- package/dist/signals/orphan-reparent.js +21 -25
- package/dist/signals/orphan-reparent.js.map +1 -1
- package/dist/signals/sigchld.d.ts +9 -4
- package/dist/signals/sigchld.d.ts.map +1 -1
- package/dist/signals/sigchld.js +23 -26
- package/dist/signals/sigchld.js.map +1 -1
- package/dist/stream-registry.d.ts +14 -2
- package/dist/stream-registry.d.ts.map +1 -1
- package/dist/stream-registry.js +22 -0
- package/dist/stream-registry.js.map +1 -1
- package/dist/subscriber-types.d.ts +25 -0
- package/dist/subscriber-types.d.ts.map +1 -0
- package/dist/subscriber-types.js +11 -0
- package/dist/subscriber-types.js.map +1 -0
- package/dist/test-utils/mock-database.d.ts +11 -0
- package/dist/test-utils/mock-database.d.ts.map +1 -1
- package/dist/test-utils/mock-database.js +11 -0
- package/dist/test-utils/mock-database.js.map +1 -1
- package/dist/workspace-handlers.d.ts +4 -0
- package/dist/workspace-handlers.d.ts.map +1 -1
- package/dist/workspace-handlers.js +51 -2
- package/dist/workspace-handlers.js.map +1 -1
- package/package.json +7 -7
package/dist/root-task-boot.js
CHANGED
|
@@ -28,7 +28,7 @@ const BOOT_MAX_FAILURES = 10;
|
|
|
28
28
|
/** Minimum time a session must survive (ms) to be considered stable and reset backoff. */
|
|
29
29
|
const BOOT_STABLE_THRESHOLD_MS = 30_000;
|
|
30
30
|
// ─── State ──────────────────────────────────────────────────
|
|
31
|
-
|
|
31
|
+
/** Create a fresh backoff state. */
|
|
32
32
|
function createInitialState() {
|
|
33
33
|
return {
|
|
34
34
|
failures: 0,
|
|
@@ -45,39 +45,74 @@ function createInitialState() {
|
|
|
45
45
|
* event subscriptions. Each call checks whether the root task needs starting
|
|
46
46
|
* and applies reanimate-first + exponential backoff logic.
|
|
47
47
|
*
|
|
48
|
+
* Each call creates independent backoff state (not shared across calls).
|
|
49
|
+
*
|
|
48
50
|
* @param deps - Injected dependencies for testability.
|
|
49
51
|
* @returns An async function to call on each `environment.changed` event.
|
|
50
52
|
*/
|
|
51
53
|
export function createRootTaskBoot(deps) {
|
|
54
|
+
return createRootTaskBootHandler(deps, createInitialState());
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Create the root task boot subscriber.
|
|
58
|
+
*
|
|
59
|
+
* Creates independent backoff state (not shared with other factory calls)
|
|
60
|
+
* and subscribes to `environment.changed` and `setting.changed` events.
|
|
61
|
+
* Returns a Disposable that unsubscribes the handler.
|
|
62
|
+
*
|
|
63
|
+
* @param ctx - Plugin context providing event-bus access.
|
|
64
|
+
* @param deps - Injected dependencies for testability.
|
|
65
|
+
* @returns A Disposable that unsubscribes the handler.
|
|
66
|
+
*/
|
|
67
|
+
export function createRootTaskBootSubscriber(ctx, deps) {
|
|
68
|
+
const bootState = createInitialState();
|
|
69
|
+
const tryBoot = createRootTaskBootHandler(deps, bootState);
|
|
70
|
+
const unsubscribe = ctx.subscribe((event) => {
|
|
71
|
+
if (event.type === "environment.changed") {
|
|
72
|
+
tryBoot().catch(() => { });
|
|
73
|
+
}
|
|
74
|
+
// Also try when onboarding completes — the environment is already
|
|
75
|
+
// connected but boot was deferred until the user chose a runtime.
|
|
76
|
+
if (event.type === "setting.changed") {
|
|
77
|
+
const payload = event.payload;
|
|
78
|
+
if (payload.key === "onboarding_completed" && payload.value === "true") {
|
|
79
|
+
tryBoot().catch(() => { });
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
return {
|
|
84
|
+
dispose() {
|
|
85
|
+
unsubscribe();
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
// ─── Internal ───────────────────────────────────────────────
|
|
90
|
+
/**
|
|
91
|
+
* Create an async boot handler that uses the given backoff state.
|
|
92
|
+
* Shared implementation for both {@link createRootTaskBoot} and
|
|
93
|
+
* {@link createRootTaskBootSubscriber}.
|
|
94
|
+
*/
|
|
95
|
+
function createRootTaskBootHandler(deps, s) {
|
|
52
96
|
return async () => {
|
|
53
97
|
// Guard: prevent concurrent boot attempts
|
|
54
|
-
if (
|
|
98
|
+
if (s.inProgress) {
|
|
55
99
|
return;
|
|
56
100
|
}
|
|
57
|
-
|
|
101
|
+
s.inProgress = true;
|
|
58
102
|
try {
|
|
59
|
-
await attemptBoot(deps);
|
|
103
|
+
await attemptBoot(deps, s);
|
|
60
104
|
}
|
|
61
105
|
catch (err) {
|
|
62
|
-
recordFailure();
|
|
106
|
+
recordFailure(s);
|
|
63
107
|
logger.warn({ err }, "Root task boot failed with unexpected exception");
|
|
64
108
|
}
|
|
65
109
|
finally {
|
|
66
|
-
|
|
110
|
+
s.inProgress = false; // eslint-disable-line require-atomic-updates -- single-threaded, flag guards re-entry
|
|
67
111
|
}
|
|
68
112
|
};
|
|
69
113
|
}
|
|
70
|
-
/**
|
|
71
|
-
* Reset in-memory backoff state. For use in tests only.
|
|
72
|
-
*
|
|
73
|
-
* @internal
|
|
74
|
-
*/
|
|
75
|
-
export function _resetForTesting() {
|
|
76
|
-
state = createInitialState();
|
|
77
|
-
}
|
|
78
|
-
// ─── Internal ───────────────────────────────────────────────
|
|
79
114
|
/** Core boot logic — separated from the guard/error wrapper for clarity. */
|
|
80
|
-
async function attemptBoot(deps) {
|
|
115
|
+
async function attemptBoot(deps, s) {
|
|
81
116
|
// 0. Don't auto-start before onboarding — the user hasn't chosen their
|
|
82
117
|
// runtime yet, so the root task would launch with the default "claude-code".
|
|
83
118
|
if (deps.isOnboarded && !deps.isOnboarded()) {
|
|
@@ -92,28 +127,28 @@ async function attemptBoot(deps) {
|
|
|
92
127
|
const sessions = deps.listSessionsForTask(ROOT_TASK_ID);
|
|
93
128
|
const { status } = deps.computeTaskStatus(rootTask.status, sessions);
|
|
94
129
|
if (status === TASK_STATUS.WORKING) {
|
|
95
|
-
checkStabilityReset();
|
|
130
|
+
checkStabilityReset(s);
|
|
96
131
|
return;
|
|
97
132
|
}
|
|
98
133
|
// 2b. Crash-loop detection: if we recently started a session but the task
|
|
99
134
|
// is no longer working before the stability threshold, count it as a failure.
|
|
100
135
|
// This catches the case where startTaskSession succeeded but the session
|
|
101
136
|
// crashed shortly after (the main crash-loop scenario from issue #959).
|
|
102
|
-
if (
|
|
103
|
-
const sinceLastStart = Date.now() -
|
|
137
|
+
if (s.lastSessionStartedAt > 0) {
|
|
138
|
+
const sinceLastStart = Date.now() - s.lastSessionStartedAt;
|
|
104
139
|
if (sinceLastStart < BOOT_STABLE_THRESHOLD_MS) {
|
|
105
140
|
// Session crashed before reaching stability — record as failure
|
|
106
|
-
if (
|
|
141
|
+
if (s.lastFailureAt < s.lastSessionStartedAt) {
|
|
107
142
|
// Only record once per start (guard against multiple environment.changed events)
|
|
108
|
-
recordFailure();
|
|
109
|
-
logger.info({ survivedMs: sinceLastStart, failures:
|
|
143
|
+
recordFailure(s);
|
|
144
|
+
logger.info({ survivedMs: sinceLastStart, failures: s.failures }, "Root task session crashed before stability threshold — recording failure");
|
|
110
145
|
}
|
|
111
146
|
}
|
|
112
147
|
else {
|
|
113
148
|
// Session survived past stability threshold but is now stopped — reset backoff
|
|
114
|
-
resetBackoff(sinceLastStart);
|
|
149
|
+
resetBackoff(s, sinceLastStart);
|
|
115
150
|
}
|
|
116
|
-
|
|
151
|
+
s.lastSessionStartedAt = 0;
|
|
117
152
|
}
|
|
118
153
|
// 3. Find connected environment
|
|
119
154
|
const connectedEnv = deps.findFirstConnectedEnvironment();
|
|
@@ -121,16 +156,16 @@ async function attemptBoot(deps) {
|
|
|
121
156
|
return;
|
|
122
157
|
}
|
|
123
158
|
// 4. Check backoff
|
|
124
|
-
if (
|
|
159
|
+
if (s.failures >= BOOT_MAX_FAILURES) {
|
|
125
160
|
// Log only once when the threshold is first reached to avoid spam
|
|
126
|
-
if (
|
|
127
|
-
logger.error({ failures:
|
|
161
|
+
if (s.failures === BOOT_MAX_FAILURES) {
|
|
162
|
+
logger.error({ failures: s.failures }, "Root task boot exhausted all retries (%d failures) — giving up until server restart", s.failures);
|
|
128
163
|
}
|
|
129
164
|
return;
|
|
130
165
|
}
|
|
131
|
-
if (
|
|
132
|
-
const delay = Math.min(BOOT_INITIAL_DELAY_MS * Math.pow(BOOT_BACKOFF_MULTIPLIER,
|
|
133
|
-
const elapsed = Date.now() -
|
|
166
|
+
if (s.failures > 0) {
|
|
167
|
+
const delay = Math.min(BOOT_INITIAL_DELAY_MS * Math.pow(BOOT_BACKOFF_MULTIPLIER, s.failures - 1), BOOT_MAX_DELAY_MS);
|
|
168
|
+
const elapsed = Date.now() - s.lastFailureAt;
|
|
134
169
|
if (elapsed < delay) {
|
|
135
170
|
return; // backoff not elapsed yet
|
|
136
171
|
}
|
|
@@ -155,45 +190,45 @@ async function attemptBoot(deps) {
|
|
|
155
190
|
notes: ROOT_TASK_INITIAL_PROMPT,
|
|
156
191
|
});
|
|
157
192
|
if (err) {
|
|
158
|
-
recordFailure();
|
|
193
|
+
recordFailure(s);
|
|
159
194
|
logger.warn({ err }, "Root task auto-start failed");
|
|
160
195
|
return;
|
|
161
196
|
}
|
|
162
197
|
logger.info({ environmentId: connectedEnv.id }, "Root task auto-started (fresh spawn)");
|
|
163
198
|
}
|
|
164
199
|
// 7. Track session start time for stability detection
|
|
165
|
-
|
|
200
|
+
s.lastSessionStartedAt = Date.now(); // eslint-disable-line require-atomic-updates -- single-threaded
|
|
166
201
|
}
|
|
167
202
|
/** Record a boot failure: increment counter and timestamp. */
|
|
168
|
-
function recordFailure() {
|
|
169
|
-
|
|
170
|
-
|
|
203
|
+
function recordFailure(s) {
|
|
204
|
+
s.failures++;
|
|
205
|
+
s.lastFailureAt = Date.now();
|
|
171
206
|
}
|
|
172
207
|
/**
|
|
173
208
|
* If the root task has been WORKING long enough (past the stability threshold),
|
|
174
209
|
* reset the backoff counter. Called when we detect the task is already running.
|
|
175
210
|
*/
|
|
176
|
-
function checkStabilityReset() {
|
|
177
|
-
if (
|
|
211
|
+
function checkStabilityReset(s) {
|
|
212
|
+
if (s.failures === 0) {
|
|
178
213
|
return;
|
|
179
214
|
}
|
|
180
|
-
if (
|
|
181
|
-
const elapsed = Date.now() -
|
|
215
|
+
if (s.lastSessionStartedAt > 0) {
|
|
216
|
+
const elapsed = Date.now() - s.lastSessionStartedAt;
|
|
182
217
|
if (elapsed >= BOOT_STABLE_THRESHOLD_MS) {
|
|
183
|
-
resetBackoff(elapsed);
|
|
218
|
+
resetBackoff(s, elapsed);
|
|
184
219
|
}
|
|
185
220
|
}
|
|
186
221
|
else {
|
|
187
222
|
// Task is WORKING but we didn't start it (e.g., session recovery
|
|
188
223
|
// reanimated it externally). Begin tracking stability from now so
|
|
189
224
|
// backoff can eventually reset — otherwise MAX_FAILURES is permanent.
|
|
190
|
-
|
|
225
|
+
s.lastSessionStartedAt = Date.now();
|
|
191
226
|
}
|
|
192
227
|
}
|
|
193
228
|
/** Zero out the backoff state after a session has proven stable. */
|
|
194
|
-
function resetBackoff(survivedMs) {
|
|
195
|
-
logger.info({ survivedMs, previousFailures:
|
|
196
|
-
|
|
197
|
-
|
|
229
|
+
function resetBackoff(s, survivedMs) {
|
|
230
|
+
logger.info({ survivedMs, previousFailures: s.failures }, "Root task session stable — resetting backoff");
|
|
231
|
+
s.failures = 0;
|
|
232
|
+
s.lastFailureAt = 0;
|
|
198
233
|
}
|
|
199
234
|
//# sourceMappingURL=root-task-boot.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"root-task-boot.js","sourceRoot":"","sources":["../src/root-task-boot.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,YAAY,EAAE,wBAAwB,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"root-task-boot.js","sourceRoot":"","sources":["../src/root-task-boot.ts"],"names":[],"mappings":"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"}
|
|
@@ -6,14 +6,15 @@
|
|
|
6
6
|
* This is the safety net for simple topologies where there is no orchestrator
|
|
7
7
|
* to explicitly call `escalate_to_human`.
|
|
8
8
|
*/
|
|
9
|
+
import type { Disposable, PluginContext } from "../subscriber-types.js";
|
|
9
10
|
/**
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
*
|
|
16
|
-
* @
|
|
11
|
+
* Create the auto-escalation event-bus subscriber.
|
|
12
|
+
*
|
|
13
|
+
* Watches for standalone (parentless, non-ROOT) tasks whose latest session
|
|
14
|
+
* goes IDLE and automatically creates an escalation.
|
|
15
|
+
*
|
|
16
|
+
* @param ctx - Plugin context providing event-bus access.
|
|
17
|
+
* @returns A Disposable that unsubscribes and clears dedup state.
|
|
17
18
|
*/
|
|
18
|
-
export declare function
|
|
19
|
+
export declare function createEscalationAutoSubscriber(ctx: PluginContext): Disposable;
|
|
19
20
|
//# sourceMappingURL=escalation-auto.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"escalation-auto.d.ts","sourceRoot":"","sources":["../../src/signals/escalation-auto.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;
|
|
1
|
+
{"version":3,"file":"escalation-auto.d.ts","sourceRoot":"","sources":["../../src/signals/escalation-auto.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AASH,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAKxE;;;;;;;;GAQG;AACH,wBAAgB,8BAA8B,CAAC,GAAG,EAAE,aAAa,GAAG,UAAU,CAgC7E"}
|
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
* to explicitly call `escalate_to_human`.
|
|
8
8
|
*/
|
|
9
9
|
import { SESSION_STATUS, ROOT_TASK_ID } from "@grackle-ai/common";
|
|
10
|
-
import { subscribe } from "../event-bus.js";
|
|
11
10
|
import { taskStore, sessionStore, escalationStore } from "@grackle-ai/database";
|
|
12
11
|
import { readLastTextEntry } from "../log-writer.js";
|
|
13
12
|
import { routeEscalation } from "../notification-router.js";
|
|
@@ -15,20 +14,19 @@ import { logger } from "../logger.js";
|
|
|
15
14
|
import { ulid } from "ulid";
|
|
16
15
|
/** How long (ms) to remember a delivered notification before allowing re-delivery. */
|
|
17
16
|
const DEDUP_TTL_MS = 3_600_000; // 1 hour
|
|
18
|
-
/** Track delivered notifications to prevent duplicates: key -> delivery timestamp. */
|
|
19
|
-
const delivered = new Map();
|
|
20
|
-
/** Whether the subscriber has been initialized. */
|
|
21
|
-
let initialized = false;
|
|
22
17
|
/**
|
|
23
|
-
*
|
|
24
|
-
*
|
|
18
|
+
* Create the auto-escalation event-bus subscriber.
|
|
19
|
+
*
|
|
20
|
+
* Watches for standalone (parentless, non-ROOT) tasks whose latest session
|
|
21
|
+
* goes IDLE and automatically creates an escalation.
|
|
22
|
+
*
|
|
23
|
+
* @param ctx - Plugin context providing event-bus access.
|
|
24
|
+
* @returns A Disposable that unsubscribes and clears dedup state.
|
|
25
25
|
*/
|
|
26
|
-
export function
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
initialized = true;
|
|
31
|
-
subscribe((event) => {
|
|
26
|
+
export function createEscalationAutoSubscriber(ctx) {
|
|
27
|
+
/** Track delivered notifications to prevent duplicates: key -> delivery timestamp. */
|
|
28
|
+
const delivered = new Map();
|
|
29
|
+
const unsubscribe = ctx.subscribe((event) => {
|
|
32
30
|
if (event.type !== "task.updated") {
|
|
33
31
|
return;
|
|
34
32
|
}
|
|
@@ -39,7 +37,7 @@ export function initEscalationAutoSubscriber() {
|
|
|
39
37
|
// Fire-and-forget async handler — errors are logged, never thrown
|
|
40
38
|
(async () => {
|
|
41
39
|
try {
|
|
42
|
-
await handleTaskUpdated(taskId);
|
|
40
|
+
await handleTaskUpdated(delivered, taskId);
|
|
43
41
|
}
|
|
44
42
|
catch (err) {
|
|
45
43
|
logger.error({ err, taskId }, "Escalation auto-detect handler error");
|
|
@@ -47,12 +45,18 @@ export function initEscalationAutoSubscriber() {
|
|
|
47
45
|
})().catch(() => { });
|
|
48
46
|
});
|
|
49
47
|
logger.info("Escalation auto-detect subscriber initialized");
|
|
48
|
+
return {
|
|
49
|
+
dispose() {
|
|
50
|
+
unsubscribe();
|
|
51
|
+
delivered.clear();
|
|
52
|
+
},
|
|
53
|
+
};
|
|
50
54
|
}
|
|
51
55
|
/**
|
|
52
56
|
* Handle a task.updated event: check if the task is a standalone root task
|
|
53
57
|
* whose latest session has gone IDLE, and if so, auto-escalate.
|
|
54
58
|
*/
|
|
55
|
-
async function handleTaskUpdated(taskId) {
|
|
59
|
+
async function handleTaskUpdated(delivered, taskId) {
|
|
56
60
|
const task = taskStore.getTask(taskId);
|
|
57
61
|
if (!task) {
|
|
58
62
|
return;
|
|
@@ -81,7 +85,7 @@ async function handleTaskUpdated(taskId) {
|
|
|
81
85
|
return;
|
|
82
86
|
}
|
|
83
87
|
// Prune expired entries to prevent unbounded growth
|
|
84
|
-
pruneDelivered(now);
|
|
88
|
+
pruneDelivered(delivered, now);
|
|
85
89
|
// Extract the last text message from the session log
|
|
86
90
|
const logPath = latestSession.logPath || undefined;
|
|
87
91
|
const lastEntry = logPath ? readLastTextEntry(logPath) : undefined;
|
|
@@ -112,19 +116,11 @@ async function handleTaskUpdated(taskId) {
|
|
|
112
116
|
logger.info({ taskId, escalationId, title: task.title }, "Auto-escalation created for idle standalone task");
|
|
113
117
|
}
|
|
114
118
|
/** Remove dedup entries older than DEDUP_TTL_MS. */
|
|
115
|
-
function pruneDelivered(now) {
|
|
119
|
+
function pruneDelivered(delivered, now) {
|
|
116
120
|
for (const [key, timestamp] of delivered) {
|
|
117
121
|
if (now - timestamp >= DEDUP_TTL_MS) {
|
|
118
122
|
delivered.delete(key);
|
|
119
123
|
}
|
|
120
124
|
}
|
|
121
125
|
}
|
|
122
|
-
/**
|
|
123
|
-
* Reset module state. For use in tests only.
|
|
124
|
-
* @internal
|
|
125
|
-
*/
|
|
126
|
-
export function _resetForTesting() {
|
|
127
|
-
delivered.clear();
|
|
128
|
-
initialized = false;
|
|
129
|
-
}
|
|
130
126
|
//# sourceMappingURL=escalation-auto.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"escalation-auto.js","sourceRoot":"","sources":["../../src/signals/escalation-auto.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"escalation-auto.js","sourceRoot":"","sources":["../../src/signals/escalation-auto.ts"],"names":[],"mappings":"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"}
|
|
@@ -6,11 +6,17 @@
|
|
|
6
6
|
* detects orphan conditions, and reparents children to the grandparent.
|
|
7
7
|
* The root task (PID 1) is the ultimate adopter.
|
|
8
8
|
*/
|
|
9
|
+
import type { Disposable, PluginContext } from "../subscriber-types.js";
|
|
9
10
|
/**
|
|
10
|
-
*
|
|
11
|
-
*
|
|
11
|
+
* Create the orphan reparenting event-bus subscriber.
|
|
12
|
+
*
|
|
13
|
+
* Watches for parent tasks reaching terminal state and reparents their
|
|
14
|
+
* non-terminal children to the grandparent (or root task as ultimate adopter).
|
|
15
|
+
*
|
|
16
|
+
* @param ctx - Plugin context providing event-bus access.
|
|
17
|
+
* @returns A Disposable that unsubscribes and clears dedup state.
|
|
12
18
|
*/
|
|
13
|
-
export declare function
|
|
19
|
+
export declare function createOrphanReparentSubscriber(ctx: PluginContext): Disposable;
|
|
14
20
|
/**
|
|
15
21
|
* Transfer ALL pipe subscriptions from a dead parent's sessions to the
|
|
16
22
|
* grandparent's active session. Called once per parent death (not per child).
|
|
@@ -22,9 +28,4 @@ export declare function initOrphanReparentSubscriber(): void;
|
|
|
22
28
|
* killSessionAndCleanup() before sessions are cleaned up.
|
|
23
29
|
*/
|
|
24
30
|
export declare function transferAllPipeSubscriptions(deadParentTaskId: string, grandparentTaskId: string): void;
|
|
25
|
-
/**
|
|
26
|
-
* Reset module state. For testing only.
|
|
27
|
-
* @internal
|
|
28
|
-
*/
|
|
29
|
-
export declare function _resetForTesting(): void;
|
|
30
31
|
//# sourceMappingURL=orphan-reparent.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"orphan-reparent.d.ts","sourceRoot":"","sources":["../../src/signals/orphan-reparent.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;
|
|
1
|
+
{"version":3,"file":"orphan-reparent.d.ts","sourceRoot":"","sources":["../../src/signals/orphan-reparent.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AASH,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAWxE;;;;;;;;GAQG;AACH,wBAAgB,8BAA8B,CAAC,GAAG,EAAE,aAAa,GAAG,UAAU,CAmC7E;AA+FD;;;;;;;;;GASG;AACH,wBAAgB,4BAA4B,CAC1C,gBAAgB,EAAE,MAAM,EACxB,iBAAiB,EAAE,MAAM,GACxB,IAAI,CAsDN"}
|
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
* The root task (PID 1) is the ultimate adopter.
|
|
8
8
|
*/
|
|
9
9
|
import { ROOT_TASK_ID, TASK_STATUS } from "@grackle-ai/common";
|
|
10
|
-
import { subscribe, emit } from "../event-bus.js";
|
|
11
10
|
import { taskStore, sessionStore } from "@grackle-ai/database";
|
|
12
11
|
import * as streamRegistry from "../stream-registry.js";
|
|
13
12
|
import { ensureAsyncDeliveryListener } from "../pipe-delivery.js";
|
|
@@ -20,20 +19,19 @@ const TERMINAL_TASK_STATUSES = new Set([
|
|
|
20
19
|
]);
|
|
21
20
|
/** How long (ms) to remember a processed parent before allowing re-processing. */
|
|
22
21
|
const DEDUP_TTL_MS = 3_600_000; // 1 hour
|
|
23
|
-
/** Track processed parents to prevent duplicate reparenting: parentTaskId → timestamp. */
|
|
24
|
-
const processed = new Map();
|
|
25
|
-
/** Whether the subscriber has been initialized. */
|
|
26
|
-
let initialized = false;
|
|
27
22
|
/**
|
|
28
|
-
*
|
|
29
|
-
*
|
|
23
|
+
* Create the orphan reparenting event-bus subscriber.
|
|
24
|
+
*
|
|
25
|
+
* Watches for parent tasks reaching terminal state and reparents their
|
|
26
|
+
* non-terminal children to the grandparent (or root task as ultimate adopter).
|
|
27
|
+
*
|
|
28
|
+
* @param ctx - Plugin context providing event-bus access.
|
|
29
|
+
* @returns A Disposable that unsubscribes and clears dedup state.
|
|
30
30
|
*/
|
|
31
|
-
export function
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
initialized = true;
|
|
36
|
-
subscribe((event) => {
|
|
31
|
+
export function createOrphanReparentSubscriber(ctx) {
|
|
32
|
+
/** Track processed parents to prevent duplicate reparenting: parentTaskId → timestamp. */
|
|
33
|
+
const processed = new Map();
|
|
34
|
+
const unsubscribe = ctx.subscribe((event) => {
|
|
37
35
|
if (event.type !== "task.completed" && event.type !== "task.updated") {
|
|
38
36
|
return;
|
|
39
37
|
}
|
|
@@ -48,18 +46,24 @@ export function initOrphanReparentSubscriber() {
|
|
|
48
46
|
// Fire-and-forget async handler — errors are logged, never thrown
|
|
49
47
|
(async () => {
|
|
50
48
|
try {
|
|
51
|
-
await handleParentTerminal(parentTaskId);
|
|
49
|
+
await handleParentTerminal(ctx, processed, parentTaskId);
|
|
52
50
|
}
|
|
53
51
|
catch (err) {
|
|
54
52
|
logger.error({ err, parentTaskId }, "Orphan reparenting failed for parent task");
|
|
55
53
|
}
|
|
56
54
|
})().catch(() => { });
|
|
57
55
|
});
|
|
56
|
+
return {
|
|
57
|
+
dispose() {
|
|
58
|
+
unsubscribe();
|
|
59
|
+
processed.clear();
|
|
60
|
+
},
|
|
61
|
+
};
|
|
58
62
|
}
|
|
59
63
|
/**
|
|
60
64
|
* Check if a parent task is terminal and reparent its non-terminal children.
|
|
61
65
|
*/
|
|
62
|
-
async function handleParentTerminal(parentTaskId) {
|
|
66
|
+
async function handleParentTerminal(ctx, processed, parentTaskId) {
|
|
63
67
|
const parentTask = taskStore.getTask(parentTaskId);
|
|
64
68
|
if (!parentTask) {
|
|
65
69
|
return;
|
|
@@ -98,13 +102,13 @@ async function handleParentTerminal(parentTaskId) {
|
|
|
98
102
|
for (const orphan of orphans) {
|
|
99
103
|
try {
|
|
100
104
|
taskStore.reparentTask(orphan.id, grandparentId);
|
|
101
|
-
emit("task.reparented", {
|
|
105
|
+
ctx.emit("task.reparented", {
|
|
102
106
|
taskId: orphan.id,
|
|
103
107
|
oldParentTaskId: parentTaskId,
|
|
104
108
|
newParentTaskId: grandparentId,
|
|
105
109
|
workspaceId: orphan.workspaceId || "",
|
|
106
110
|
});
|
|
107
|
-
emit("task.updated", {
|
|
111
|
+
ctx.emit("task.updated", {
|
|
108
112
|
taskId: orphan.id,
|
|
109
113
|
workspaceId: orphan.workspaceId || "",
|
|
110
114
|
});
|
|
@@ -168,12 +172,4 @@ export function transferAllPipeSubscriptions(deadParentTaskId, grandparentTaskId
|
|
|
168
172
|
logger.info({ deadParentTaskId, grandparentTaskId, grandparentSessionId, transferred }, "Transferred %d pipe fd(s) to grandparent session", transferred);
|
|
169
173
|
}
|
|
170
174
|
}
|
|
171
|
-
/**
|
|
172
|
-
* Reset module state. For testing only.
|
|
173
|
-
* @internal
|
|
174
|
-
*/
|
|
175
|
-
export function _resetForTesting() {
|
|
176
|
-
initialized = false;
|
|
177
|
-
processed.clear();
|
|
178
|
-
}
|
|
179
175
|
//# sourceMappingURL=orphan-reparent.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"orphan-reparent.js","sourceRoot":"","sources":["../../src/signals/orphan-reparent.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"orphan-reparent.js","sourceRoot":"","sources":["../../src/signals/orphan-reparent.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
import type { Disposable, PluginContext } from "../subscriber-types.js";
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
3
|
+
* Create the SIGCHLD event-bus subscriber.
|
|
4
|
+
*
|
|
5
|
+
* Watches for child tasks whose latest session reaches a SIGCHLD-triggering
|
|
6
|
+
* status (idle or stopped) and delivers a notification to the parent task.
|
|
7
|
+
*
|
|
8
|
+
* @param ctx - Plugin context providing event-bus access.
|
|
9
|
+
* @returns A Disposable that unsubscribes and clears dedup state.
|
|
5
10
|
*/
|
|
6
|
-
export declare function
|
|
11
|
+
export declare function createSigchldSubscriber(ctx: PluginContext): Disposable;
|
|
7
12
|
//# sourceMappingURL=sigchld.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sigchld.d.ts","sourceRoot":"","sources":["../../src/signals/sigchld.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"sigchld.d.ts","sourceRoot":"","sources":["../../src/signals/sigchld.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAgCxE;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,aAAa,GAAG,UAAU,CAgCtE"}
|