ework-daemon 0.4.39 → 0.4.40

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 +21 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ework-daemon",
3
- "version": "0.4.39",
3
+ "version": "0.4.40",
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
@@ -283,6 +283,7 @@ export interface EngineOptions {
283
283
  daemonId: number;
284
284
  takeover?: TakeoverStrategy;
285
285
  backend?: RuntimeBackend;
286
+ gateChecker?: (issue: Issue) => Promise<{ allowed: boolean; reason: string }>;
286
287
  }
287
288
 
288
289
  function createDefaultBackend(cfg: Config): RuntimeBackend {
@@ -299,6 +300,7 @@ export class Engine {
299
300
  private readonly daemonId: number;
300
301
  private readonly takeover: TakeoverStrategy;
301
302
  private readonly backend: RuntimeBackend;
303
+ private gateChecker: (issue: Issue) => Promise<{ allowed: boolean; reason: string }>;
302
304
  private heartbeatTimer?: ReturnType<typeof setInterval>;
303
305
  private maxConcurrent: number;
304
306
  private maxConcurrentExplicit: boolean;
@@ -350,6 +352,7 @@ export class Engine {
350
352
  this.daemonId = opts.daemonId;
351
353
  this.takeover = opts.takeover ?? new RecloneStrategy(cfg);
352
354
  this.backend = opts.backend ?? createDefaultBackend(cfg);
355
+ this.gateChecker = opts.gateChecker ?? ((issue: Issue) => this.webGateAllows(issue));
353
356
  this.maxConcurrent = cfg.work.maxConcurrent;
354
357
  this.maxConcurrentExplicit = cfg.work.maxConcurrentExplicit;
355
358
  this.startGlobalObserver();
@@ -1119,6 +1122,17 @@ export class Engine {
1119
1122
  // ─── Process Manager ───
1120
1123
 
1121
1124
  private async execProcess(k: string, session: OpSession, issue: Issue, msg: Message) {
1125
+ const gate = await this.gateChecker(issue);
1126
+ if (!gate.allowed) {
1127
+ log.info(`engine: execProcess blocked by web gate (${gate.reason}) for ${k}`);
1128
+ await this.store.updateMessageStatus(msg.id, "failed", `gate: ${gate.reason}`);
1129
+ this.running.delete(k);
1130
+ this.currentMessage.delete(k);
1131
+ this.currentModel.delete(k);
1132
+ void this.dequeueAfterGate(k, session, issue);
1133
+ return;
1134
+ }
1135
+
1122
1136
  const gen = (this.generation.get(k) ?? 0) + 1;
1123
1137
  this.generation.set(k, gen);
1124
1138
 
@@ -1392,6 +1406,12 @@ export class Engine {
1392
1406
  void this.drainGlobalPending();
1393
1407
  }
1394
1408
 
1409
+ private async dequeueAfterGate(k: string, session: OpSession, issue: Issue): Promise<void> {
1410
+ const next = await this.store.getNextPendingMessage(session.id);
1411
+ if (!next) return;
1412
+ await this.dequeueOrIdle(k, session, issue, next);
1413
+ }
1414
+
1395
1415
  private async drainGlobalPending(): Promise<void> {
1396
1416
  const slotsAvailable = this.maxConcurrent - this.running.size;
1397
1417
  if (slotsAvailable <= 0) return;
@@ -1574,7 +1594,7 @@ export class Engine {
1574
1594
 
1575
1595
  for (const issue of ownedIssues) {
1576
1596
  try {
1577
- const gate = await this.webGateAllows(issue);
1597
+ const gate = await this.gateChecker(issue);
1578
1598
  if (!gate.allowed) {
1579
1599
  log.info(`engine: observer — web gate blocked ${issue.trackerScopeKey}#${issue.trackerIssueId} (${gate.reason})`);
1580
1600
  continue;