ework-daemon 0.4.70 → 0.4.72
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/op.ts +16 -0
- package/src/opencode.ts +26 -2
package/package.json
CHANGED
package/src/op.ts
CHANGED
|
@@ -379,6 +379,22 @@ export class Store {
|
|
|
379
379
|
const db = getDB();
|
|
380
380
|
const cutoff = new Date(Date.now() - leaseTtlMs).toISOString();
|
|
381
381
|
|
|
382
|
+
// Identity reuse first: a fast restart (previous heartbeat still fresh)
|
|
383
|
+
// must keep the SAME row/id — daemon_id is embedded in web session links,
|
|
384
|
+
// so a fresh insert per boot orphans every historical link.
|
|
385
|
+
const mine = await db.get<{ id: number }>(
|
|
386
|
+
"SELECT id FROM {{daemons}} WHERE display_name = ? AND internal_endpoint = ? LIMIT 1",
|
|
387
|
+
[displayName, endpoint]
|
|
388
|
+
);
|
|
389
|
+
if (mine) {
|
|
390
|
+
const now = new Date().toISOString();
|
|
391
|
+
const res = await db.run(
|
|
392
|
+
"UPDATE {{daemons}} SET last_heartbeat = ?, status = 'active', capacity = ? WHERE id = ?",
|
|
393
|
+
[now, capacity, mine.id]
|
|
394
|
+
);
|
|
395
|
+
if (res.changes === 1) return mine.id;
|
|
396
|
+
}
|
|
397
|
+
|
|
382
398
|
const orphan = await db.get<{ id: number }>(
|
|
383
399
|
"SELECT id FROM {{daemons}} WHERE last_heartbeat < ? ORDER BY last_heartbeat LIMIT 1",
|
|
384
400
|
[cutoff]
|
package/src/opencode.ts
CHANGED
|
@@ -422,6 +422,13 @@ export class Engine {
|
|
|
422
422
|
private static MAX_INLINE_SIZE = 4000;
|
|
423
423
|
private static MAX_NUDGE_ROUNDS = 1;
|
|
424
424
|
private static MAX_EMPTY_RESPONSE_ROUNDS = 1;
|
|
425
|
+
// A queued (pending) message older than this is dead context — e.g. forwards
|
|
426
|
+
// left over from a webhook echo storm, or comments queued behind a session
|
|
427
|
+
// that ran for hours. Replaying them re-runs stale prompts and ping-pongs
|
|
428
|
+
// nudges ("reply → done → drain next stale → nudge → reply …"), observed on
|
|
429
|
+
// dog/tasks#3: 17:24 storm forwards replayed at 20:09–20:11. Expire instead
|
|
430
|
+
// of replay; explicit retryMessage bypasses this via { force: true }.
|
|
431
|
+
private static MAX_PENDING_AGE_MS = 30 * 60_000;
|
|
425
432
|
private static MAX_STUCK_NUDGE_ROUNDS = 1;
|
|
426
433
|
private static MAX_RUNTIME_MS = 3 * 60 * 60 * 1000;
|
|
427
434
|
private static OBSERVER_INTERVAL_MS = 5 * 60 * 1000;
|
|
@@ -1717,7 +1724,24 @@ export class Engine {
|
|
|
1717
1724
|
}
|
|
1718
1725
|
}
|
|
1719
1726
|
|
|
1720
|
-
private async dequeueOrIdle(k: string, session: OpSession, issue: Issue, msg: Message) {
|
|
1727
|
+
private async dequeueOrIdle(k: string, session: OpSession, issue: Issue, msg: Message, opts: { force?: boolean } = {}) {
|
|
1728
|
+
if (!opts.force) {
|
|
1729
|
+
let next: Message | undefined = msg;
|
|
1730
|
+
while (next) {
|
|
1731
|
+
const age = Date.now() - next.createdAt.getTime();
|
|
1732
|
+
if (age <= Engine.MAX_PENDING_AGE_MS) break;
|
|
1733
|
+
log.warn(`engine: expiring stale pending msg ${next.id.slice(0, 8)} for ${k} (age ${Math.round(age / 60_000)}min > ${Math.round(Engine.MAX_PENDING_AGE_MS / 60_000)}min) — skipping replay`);
|
|
1734
|
+
await this.store.updateMessageStatus(next.id, "failed", "expired: stale pending message not replayed");
|
|
1735
|
+
next = await this.store.getNextPendingMessage(session.id);
|
|
1736
|
+
}
|
|
1737
|
+
if (!next) {
|
|
1738
|
+
this.clearRuntimeState(k);
|
|
1739
|
+
await this.store.updateSession(session.id, { state: "idle" });
|
|
1740
|
+
void this.drainGlobalPending();
|
|
1741
|
+
return;
|
|
1742
|
+
}
|
|
1743
|
+
msg = next;
|
|
1744
|
+
}
|
|
1721
1745
|
log.info(`engine: running dequeued msg ${msg.id.slice(0, 8)} for ${k}`);
|
|
1722
1746
|
await this.store.updateMessageStatus(msg.id, "running");
|
|
1723
1747
|
this.running.add(k);
|
|
@@ -2238,7 +2262,7 @@ export class Engine {
|
|
|
2238
2262
|
await this.store.updateMessageStatus(messageId, "pending");
|
|
2239
2263
|
const k = this.sessionKey(session, issue);
|
|
2240
2264
|
if (!this.running.has(k)) {
|
|
2241
|
-
await this.dequeueOrIdle(k, session, issue, msg);
|
|
2265
|
+
await this.dequeueOrIdle(k, session, issue, msg, { force: true });
|
|
2242
2266
|
}
|
|
2243
2267
|
return true;
|
|
2244
2268
|
}
|