ework-daemon 0.4.57 → 0.4.58
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/package.json +1 -1
- package/src/config.ts +5 -2
- package/src/opencode.ts +18 -0
package/package.json
CHANGED
package/src/config.ts
CHANGED
|
@@ -72,6 +72,7 @@ export const configSchema = z.object({
|
|
|
72
72
|
stuck: z.object({
|
|
73
73
|
thresholdMs: z.coerce.number().positive(),
|
|
74
74
|
maxNudges: z.coerce.number().int().nonnegative(),
|
|
75
|
+
maxRuntimeMs: z.coerce.number().positive(),
|
|
75
76
|
}).optional(),
|
|
76
77
|
file: z.object({
|
|
77
78
|
roots: z.array(z.string()).default([]),
|
|
@@ -170,9 +171,10 @@ export function loadConfig(): Config {
|
|
|
170
171
|
baseURL: process.env.COMPLETION_CHECK_BASE_URL ?? "",
|
|
171
172
|
model: process.env.COMPLETION_CHECK_MODEL ?? "",
|
|
172
173
|
} : undefined,
|
|
173
|
-
stuck: process.env.DAEMON_STUCK_THRESHOLD_MS || process.env.DAEMON_MAX_STUCK_NUDGES ? {
|
|
174
|
+
stuck: process.env.DAEMON_STUCK_THRESHOLD_MS || process.env.DAEMON_MAX_STUCK_NUDGES || process.env.DAEMON_STUCK_MAX_RUNTIME_MS ? {
|
|
174
175
|
thresholdMs: Number(process.env.DAEMON_STUCK_THRESHOLD_MS) || 30 * 60 * 1000,
|
|
175
176
|
maxNudges: Number(process.env.DAEMON_MAX_STUCK_NUDGES) || 1,
|
|
177
|
+
maxRuntimeMs: Number(process.env.DAEMON_STUCK_MAX_RUNTIME_MS) || 3 * 60 * 60 * 1000,
|
|
176
178
|
} : undefined,
|
|
177
179
|
childEnvDeny: (process.env.WORK_CHILD_ENV_DENY ?? "").split(",").map((s) => s.trim()).filter(Boolean),
|
|
178
180
|
});
|
|
@@ -217,9 +219,10 @@ export function loadConfig(): Config {
|
|
|
217
219
|
baseURL: process.env.COMPLETION_CHECK_BASE_URL ?? "",
|
|
218
220
|
model: process.env.COMPLETION_CHECK_MODEL ?? "",
|
|
219
221
|
} : undefined,
|
|
220
|
-
stuck: process.env.DAEMON_STUCK_THRESHOLD_MS || process.env.DAEMON_MAX_STUCK_NUDGES ? {
|
|
222
|
+
stuck: process.env.DAEMON_STUCK_THRESHOLD_MS || process.env.DAEMON_MAX_STUCK_NUDGES || process.env.DAEMON_STUCK_MAX_RUNTIME_MS ? {
|
|
221
223
|
thresholdMs: Number(process.env.DAEMON_STUCK_THRESHOLD_MS) || 30 * 60 * 1000,
|
|
222
224
|
maxNudges: Number(process.env.DAEMON_MAX_STUCK_NUDGES) || 1,
|
|
225
|
+
maxRuntimeMs: Number(process.env.DAEMON_STUCK_MAX_RUNTIME_MS) || 3 * 60 * 60 * 1000,
|
|
223
226
|
} : undefined,
|
|
224
227
|
file: {
|
|
225
228
|
roots: (process.env.WORK_FILE_ROOTS ?? "").split(":").filter(Boolean).length > 0
|
package/src/opencode.ts
CHANGED
|
@@ -359,6 +359,7 @@ export class Engine {
|
|
|
359
359
|
private static MAX_NUDGE_ROUNDS = 1;
|
|
360
360
|
private static MAX_EMPTY_RESPONSE_ROUNDS = 1;
|
|
361
361
|
private static MAX_STUCK_NUDGE_ROUNDS = 1;
|
|
362
|
+
private static MAX_RUNTIME_MS = 3 * 60 * 60 * 1000;
|
|
362
363
|
private static OBSERVER_INTERVAL_MS = 5 * 60 * 1000;
|
|
363
364
|
private static STUCK_THRESHOLD_MS = 30 * 60 * 1000;
|
|
364
365
|
private static MAX_PROCESS_EXIT_NUDGE_ROUNDS = 1;
|
|
@@ -557,6 +558,10 @@ export class Engine {
|
|
|
557
558
|
return this.cfg.stuck?.maxNudges ?? Engine.MAX_STUCK_NUDGE_ROUNDS;
|
|
558
559
|
}
|
|
559
560
|
|
|
561
|
+
private get maxRuntimeMs(): number {
|
|
562
|
+
return this.cfg.stuck?.maxRuntimeMs ?? Engine.MAX_RUNTIME_MS;
|
|
563
|
+
}
|
|
564
|
+
|
|
560
565
|
private getTracker(type: string): IssueTracker {
|
|
561
566
|
const tracker = this.trackers.get(type);
|
|
562
567
|
if (!tracker) throw new Error(`Unknown tracker type: ${type}`);
|
|
@@ -1749,6 +1754,19 @@ export class Engine {
|
|
|
1749
1754
|
continue;
|
|
1750
1755
|
}
|
|
1751
1756
|
|
|
1757
|
+
// Process alive — cap total run time. Output-silence detection cannot
|
|
1758
|
+
// catch loops that keep emitting (observed: 6h of failing compress calls
|
|
1759
|
+
// every ~5s), so any single run is hard-stopped after maxRuntimeMs.
|
|
1760
|
+
const started = this.startedAt.get(k);
|
|
1761
|
+
if (started && Date.now() - started >= this.maxRuntimeMs) {
|
|
1762
|
+
const hrs = (this.maxRuntimeMs / 3600000).toFixed(1);
|
|
1763
|
+
log.warn(`engine: run exceeded max runtime (${hrs}h) on ${k}, stopping`);
|
|
1764
|
+
await tracker.createComment(this.sessionToRef(session, issue), `[system] ⏹ **${session.name}** run exceeded ${hrs}h — stopped. Reply again on the issue to continue.`).catch(() => { /* best-effort */ });
|
|
1765
|
+
this.nudgeRounds.set(k, Engine.MAX_NUDGE_ROUNDS);
|
|
1766
|
+
this.forceStop(k);
|
|
1767
|
+
continue;
|
|
1768
|
+
}
|
|
1769
|
+
|
|
1752
1770
|
// Process alive — check stuck
|
|
1753
1771
|
if (lastTs && Date.now() - lastTs >= this.stuckThresholdMs) {
|
|
1754
1772
|
const minutes = Math.round((Date.now() - lastTs) / 60000);
|