ework-daemon 0.4.10 → 0.4.12

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ework-daemon",
3
- "version": "0.4.10",
3
+ "version": "0.4.12",
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/index.ts CHANGED
@@ -8,6 +8,8 @@ import { GiteaTracker } from "./trackers/gitea-tracker";
8
8
  import type { IssueTracker } from "./trackers/types";
9
9
  import { initDB } from "./db";
10
10
  import { hostname } from "os";
11
+ import * as fs from "node:fs";
12
+ import * as path from "node:path";
11
13
 
12
14
  const config = loadConfig();
13
15
  const isTest = config.env === "test";
@@ -20,6 +22,26 @@ log.info(` workdir: ${config.opencode.baseWorkdir}`);
20
22
  log.info(` db: ${config.db.driver === "mysql" ? `${config.db.user}@${config.db.host}:${config.db.port}/${config.db.name}` : config.db.path}${config.db.prefix ? ` (prefix=${config.db.prefix})` : ""}`);
21
23
  log.info(` work: capacity=${config.work.capacity} heartbeat=${config.work.heartbeatMs}ms leaseTtl=${config.work.leaseTtlMs}ms`);
22
24
 
25
+ function probePlugin() {
26
+ const configDir = process.env.XDG_CONFIG_HOME || path.join(process.env.HOME || "/tmp", ".config");
27
+ const cfgPath = path.join(configDir, "opencode", "opencode.json");
28
+ try {
29
+ const raw = fs.readFileSync(cfgPath, "utf8");
30
+ const parsed = JSON.parse(raw);
31
+ const plugin = parsed.plugin ?? parsed.plugins;
32
+ const plugins = Array.isArray(plugin) ? plugin : plugin ? [plugin] : [];
33
+ const hasEwork = plugins.some((p: unknown) =>
34
+ typeof p === "string" ? p.includes("opencode-ework") : false,
35
+ );
36
+ if (!hasEwork) {
37
+ log.warn(" ⚠️ opencode-ework plugin not found in opencode.json — the AI will lack the 'reply' tool and all nudges will fail. Install it: npm install -g opencode-ework");
38
+ }
39
+ } catch {
40
+ log.warn(` ⚠️ could not read opencode config at ${cfgPath} — cannot verify opencode-ework plugin is installed`);
41
+ }
42
+ }
43
+ probePlugin();
44
+
23
45
  const giteaClient = new GiteaClient(config.gitea, config.bot.token);
24
46
  const giteaTracker = new GiteaTracker(
25
47
  giteaClient,
package/src/opencode.ts CHANGED
@@ -221,6 +221,7 @@ export class Engine {
221
221
  private stopping = new Set<string>();
222
222
  private processingComments = new Set<string>();
223
223
  private currentMessage = new Map<string, string>();
224
+ private currentModel = new Map<string, string | undefined>();
224
225
  private lastOutputAt = new Map<string, number>();
225
226
  private startedAt = new Map<string, number>();
226
227
  private progressCommentId = new Map<string, string>();
@@ -687,6 +688,7 @@ export class Engine {
687
688
  this.running.delete(k);
688
689
  this.stopping.delete(k);
689
690
  this.currentMessage.delete(k);
691
+ this.currentModel.delete(k);
690
692
  this.lastOutputAt.delete(k);
691
693
  this.startedAt.delete(k);
692
694
  this.progressCommentId.delete(k);
@@ -738,6 +740,7 @@ export class Engine {
738
740
  // Don't clear stopping — let old execProcess detect preemption via process reference mismatch
739
741
  this.running.delete(k);
740
742
  this.currentMessage.delete(k);
743
+ this.currentModel.delete(k);
741
744
  this.startedAt.delete(k);
742
745
 
743
746
  // Execute new message
@@ -797,6 +800,7 @@ export class Engine {
797
800
  // Without this, opencode's own default (influenced by plugin agent
798
801
  // presets) can pick a non-existent model, causing ProviderModelNotFoundError.
799
802
  const model = msg.model || this.cfg.opencode.defaultModel;
803
+ this.currentModel.set(k, model);
800
804
  if (model) {
801
805
  args.push("--model", model);
802
806
  }
@@ -820,6 +824,12 @@ export class Engine {
820
824
  // them); only the explicit model-override var is neutralized.
821
825
  const childEnv = { ...process.env };
822
826
  if (!model) delete childEnv.OPENCODE_MODEL;
827
+ // Strip parent-session env vars so the child opencode reads its own
828
+ // opencode.json instead of inheriting a parent session's model/provider.
829
+ delete childEnv.OPENCODE;
830
+ delete childEnv.OPENCODE_PID;
831
+ delete childEnv.OPENCODE_RUN_ID;
832
+ delete childEnv.OPENCODE_PROCESS_ROLE;
823
833
 
824
834
  try {
825
835
  const proc = spawn({
@@ -929,6 +939,7 @@ export class Engine {
929
939
 
930
940
  // running.delete deferred to dequeuePending
931
941
  this.currentMessage.delete(k);
942
+ this.currentModel.delete(k);
932
943
 
933
944
  const started = this.startedAt.get(k);
934
945
  this.startedAt.delete(k);
@@ -1000,7 +1011,7 @@ export class Engine {
1000
1011
 
1001
1012
  const instructions = tracker.getTrackerInstructions(ref);
1002
1013
  const nudgePrompt = this.buildNudgePrompt(session, issue, instructions);
1003
- const nudgeMsg = await this.store.createMessage(session.id, nudgePrompt);
1014
+ const nudgeMsg = await this.store.createMessage(session.id, nudgePrompt, undefined, undefined, this.currentModel.get(k));
1004
1015
  await this.dequeueOrIdle(k, session, issue, nudgeMsg);
1005
1016
  return;
1006
1017
  }
@@ -1244,6 +1255,7 @@ export class Engine {
1244
1255
  if (msgId) {
1245
1256
  await this.store.updateMessageStatus(msgId, "failed", "process died unexpectedly");
1246
1257
  this.currentMessage.delete(k);
1258
+ this.currentModel.delete(k);
1247
1259
  }
1248
1260
 
1249
1261
  const ref = this.sessionToRef(session, issue);
@@ -1262,7 +1274,7 @@ export class Engine {
1262
1274
 
1263
1275
  const instructions = tracker.getTrackerInstructions(ref);
1264
1276
  const nudgePrompt = this.buildProcessExitNudgePrompt(session, issue, instructions);
1265
- const nudgeMsg = await this.store.createMessage(session.id, nudgePrompt);
1277
+ const nudgeMsg = await this.store.createMessage(session.id, nudgePrompt, undefined, undefined, this.currentModel.get(k));
1266
1278
  await this.dequeueOrIdle(k, session, issue, nudgeMsg);
1267
1279
  continue;
1268
1280
  } else {
@@ -1306,7 +1318,7 @@ export class Engine {
1306
1318
 
1307
1319
  const instructions = tracker.getTrackerInstructions(ref);
1308
1320
  const nudgePrompt = this.buildStuckNudgePrompt(session, issue, instructions, minutes);
1309
- const nudgeMsg = await this.store.createMessage(session.id, nudgePrompt);
1321
+ const nudgeMsg = await this.store.createMessage(session.id, nudgePrompt, undefined, undefined, this.currentModel.get(k));
1310
1322
  await this.dequeueOrIdle(k, session, issue, nudgeMsg);
1311
1323
  } else {
1312
1324
  log.warn(`engine: stuck for ${minutes}min on ${k}, stuck nudge exhausted (${stuckNudgeRound}/${this.maxStuckNudges}), giving up`);