ework-daemon 0.4.65 → 0.4.67

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/opencode.ts +24 -9
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ework-daemon",
3
- "version": "0.4.65",
3
+ "version": "0.4.67",
4
4
  "description": "Issue-driven AI development daemon. Spawns opencode subprocesses to resolve Gitea issues.",
5
5
  "module": "src/index.ts",
6
6
  "type": "module",
package/src/opencode.ts CHANGED
@@ -787,6 +787,21 @@ export class Engine {
787
787
  const tracker = this.getTracker(ref.trackerType);
788
788
  const scopeKey = tracker.formatScopeKey(ref.scope);
789
789
 
790
+ // State bookkeeping that must survive every wake gate: `issue_closed` is
791
+ // never gated, so if a later `reopened` (mapped to issue_opened) is
792
+ // dropped by paused/halted/dispatch_off/wake gates, the row stays
793
+ // "closed" forever and swallows ALL future comments ("is closed in DB").
794
+ // Heal the stale state unconditionally; dispatch remains gated below.
795
+ if (event.type === "issue_opened") {
796
+ const existing = await this.store.findIssue(ref.trackerType, scopeKey, ref.issueId);
797
+ if (existing?.state === "closed") {
798
+ await this.store.updateIssueState(existing.id, "active");
799
+ log.info(
800
+ `engine: reopened — cleared stale closed state for ${ref.trackerType}:${scopeKey}#${ref.issueId} (bookkeeping only; dispatch still gated)`,
801
+ );
802
+ }
803
+ }
804
+
790
805
  if (this.paused && (event.type === "issue_opened" || event.type === "comment_created")) {
791
806
  log.info(`engine: paused — skipping ${event.type} for ${ref.trackerType}:${scopeKey}#${ref.issueId}`);
792
807
  return;
@@ -923,7 +938,7 @@ export class Engine {
923
938
  // (or hit the 10-min cap), and the user must see pickup feedback immediately.
924
939
  // The session link is a placeholder until the backend reports its session id;
925
940
  // the onSessionId callback rewrites this comment with the real reference.
926
- await tracker.createComment(ref, `[system] 🔄 **${session.name}** picked up this issue — preparing workspace…`).then(
941
+ await tracker.createComment(ref, `[system] 🏷 ${this.sessionRef(session)} 🔄 **${session.name}** picked up this issue — preparing workspace…`).then(
927
942
  (c) => this.pickupCommentId.set(k, c.id),
928
943
  () => {},
929
944
  );
@@ -1014,7 +1029,7 @@ export class Engine {
1014
1029
 
1015
1030
  // Immediate ack
1016
1031
  {
1017
- const c = await tracker.createComment(ref, `[system] ✓ Message forwarded to **${session.name}**${this.running.has(this.sessionKey(session, issue)) ? " (running)" : ""}.\n> session: ${this.sessionRef(session)} | workdir: ${this.workdirLink(workdir)}`);
1032
+ const c = await tracker.createComment(ref, `[system] 🏷 ${this.sessionRef(session)} ✓ Message forwarded to **${session.name}**${this.running.has(this.sessionKey(session, issue)) ? " (running)" : ""}.\n> workdir: ${this.workdirLink(workdir)}`);
1018
1033
  if (!session.opencodeSessionId) this.forwardCommentId.set(this.sessionKey(session, issue), c.id);
1019
1034
  }
1020
1035
 
@@ -1028,7 +1043,7 @@ export class Engine {
1028
1043
  }
1029
1044
  const workdir = await this.resolveWorkdir(session, issue);
1030
1045
 
1031
- await tracker.createComment(ref, `[system] 🔄 **${session.name}** joined the conversation.`);
1046
+ await tracker.createComment(ref, `[system] 🏷 ${this.sessionRef(session)} 🔄 **${session.name}** joined the conversation.`);
1032
1047
 
1033
1048
  const instructions = tracker.getTrackerInstructions(ref);
1034
1049
  const payloadClone = this.cloneUrls.get(`${ref.trackerType}:${scopeKey}#${ref.issueId}`);
@@ -1065,7 +1080,7 @@ export class Engine {
1065
1080
  session.name, this.handleLargeContent(workdir, comment.body, `comment-${comment.id}.txt`),
1066
1081
  comment.author, comment.authorKind, issueData.title, workdir, instructions
1067
1082
  );
1068
- await tracker.createComment(ref, `[system] ✓ Message forwarded to **${session.name}**${this.running.has(this.sessionKey(session, issue)) ? " (running)" : ""}.\n> session: ${this.sessionRef(session)} | workdir: ${this.workdirLink(workdir)}`);
1083
+ await tracker.createComment(ref, `[system] 🏷 ${this.sessionRef(session)} ✓ Message forwarded to **${session.name}**${this.running.has(this.sessionKey(session, issue)) ? " (running)" : ""}.\n> workdir: ${this.workdirLink(workdir)}`);
1069
1084
  await this.enqueueOrRun(session, issue, prompt, comment.id, model);
1070
1085
  }
1071
1086
  } finally {
@@ -1377,13 +1392,13 @@ export class Engine {
1377
1392
  this.pickupCommentId.delete(k);
1378
1393
  this.forwardCommentId.delete(k);
1379
1394
  await tracker
1380
- .editComment(ref, pickupId, `[system] 🔄 **${session.name}** picked up this issue.\n> session: ${this.sessionRef(session)} | workdir: ${this.workdirLink(workdir)}`)
1395
+ .editComment(ref, pickupId, `[system] 🏷 ${this.sessionRef(session)} 🔄 **${session.name}** picked up this issue.\n> workdir: ${this.workdirLink(workdir)}`)
1381
1396
  .catch((err) => log.error(`engine: failed to rewrite pickup comment for ${k}:`, (err as Error).message));
1382
1397
  }
1383
1398
  const forwardId = this.forwardCommentId.get(k);
1384
1399
  if (forwardId) {
1385
1400
  this.forwardCommentId.delete(k);
1386
- const body = `[system] ✓ Message forwarded to **${session.name}**${this.running.has(k) ? " (running)" : ""}.\n> session: ${this.sessionRef(session)} | workdir: ${this.workdirLink(workdir)}`;
1401
+ const body = `[system] 🏷 ${this.sessionRef(session)} ✓ Message forwarded to **${session.name}**${this.running.has(k) ? " (running)" : ""}.\n> workdir: ${this.workdirLink(workdir)}`;
1387
1402
  await tracker.editComment(ref, forwardId, body).catch(() => { /* cosmetic rewrite */ });
1388
1403
  }
1389
1404
  }
@@ -1548,7 +1563,7 @@ export class Engine {
1548
1563
  log.error(`engine: empty model response for ${k} after ${emptyRound} retries, reporting error`);
1549
1564
  this.emptyResponseRounds.delete(k);
1550
1565
  this.nudgeRounds.delete(k);
1551
- await tracker.createComment(ref, `[system] ❌ **${session.name}** 模型返回空响应(0 token),已重试 ${emptyRound} 次。请检查模型配置或稍后重试。`).catch(() => {});
1566
+ await tracker.createComment(ref, `[system] 🏷 ${this.sessionRef(session)} ❌ **${session.name}** 模型返回空响应(0 token),已重试 ${emptyRound} 次。请检查模型配置或稍后重试。`).catch(() => {});
1552
1567
  void tracker.updateStatus(ref, "failed", "empty model response");
1553
1568
  } else {
1554
1569
  const nudgeRound = this.nudgeRounds.get(k) ?? 0;
@@ -1567,7 +1582,7 @@ export class Engine {
1567
1582
  log.info(`engine: no [bot] reply for ${k} (promptTime=${started ?? "unknown"}), marking done (nudge exhausted or process failed)`);
1568
1583
  this.nudgeRounds.delete(k);
1569
1584
  const detail = exitCode === 0 ? "ran but did not post a reply" : `crashed (exit ${exitCode})`;
1570
- await tracker.createComment(ref, `[system] ❌ **${session.name}** ${detail}. Try posting again or @${session.name} to retry.`).catch(() => {});
1585
+ await tracker.createComment(ref, `[system] 🏷 ${this.sessionRef(session)} ❌ **${session.name}** ${detail}. Try posting again or @${session.name} to retry.`).catch(() => {});
1571
1586
  void tracker.updateStatus(ref, "failed", detail);
1572
1587
  }
1573
1588
  }
@@ -1960,7 +1975,7 @@ export class Engine {
1960
1975
 
1961
1976
  const ref = this.sessionToRef(session, issue);
1962
1977
  const duration = this.formatDuration(now - started);
1963
- const body = `[system] ⏳ **${session.name}** processing, running for ${duration}...`;
1978
+ const body = `[system] 🏷 ${this.sessionRef(session)} ⏳ **${session.name}** processing, running for ${duration}...`;
1964
1979
 
1965
1980
  const existingId = this.progressCommentId.get(k);
1966
1981
  try {