ework-daemon 0.4.58 → 0.4.60
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/opencode.ts +44 -6
- package/src/trackers/gitea-tracker.ts +5 -0
- package/src/trackers/types.ts +2 -0
package/package.json
CHANGED
package/src/opencode.ts
CHANGED
|
@@ -309,6 +309,13 @@ function createDefaultBackend(cfg: Config): RuntimeBackend {
|
|
|
309
309
|
return new OpencodeBackend(cfg.opencode.binary, cfg.opencode.dbPath, cfg.childEnvDeny);
|
|
310
310
|
}
|
|
311
311
|
|
|
312
|
+
function createBackendFor(cfg: Config, runtime: string): RuntimeBackend {
|
|
313
|
+
if (runtime === "pi" && cfg.pi) {
|
|
314
|
+
return new PiBackend(cfg.pi.binary, cfg.pi.provider, cfg.pi.defaultModel, cfg.childEnvDeny);
|
|
315
|
+
}
|
|
316
|
+
return new OpencodeBackend(cfg.opencode.binary, cfg.opencode.dbPath, cfg.childEnvDeny);
|
|
317
|
+
}
|
|
318
|
+
|
|
312
319
|
export class Engine {
|
|
313
320
|
private cfg: Config;
|
|
314
321
|
private store: Store;
|
|
@@ -352,6 +359,11 @@ export class Engine {
|
|
|
352
359
|
|
|
353
360
|
private groupConfigs = new Map<string, GroupConfig>();
|
|
354
361
|
private cloneUrls = new Map<string, string>();
|
|
362
|
+
// Per-issue runtime override ("opencode"|"pi") from webhook payloads.
|
|
363
|
+
// Existing sessions stay pinned to their original backend via the
|
|
364
|
+
// opencodeSessionId prefix (ses_=opencode, bare uuid=pi) in backendFor().
|
|
365
|
+
private issueRuntimes = new Map<string, string>();
|
|
366
|
+
private altBackend?: RuntimeBackend;
|
|
355
367
|
private senders = new Map<string, string>();
|
|
356
368
|
private envInitialized = new Set<string>();
|
|
357
369
|
|
|
@@ -378,6 +390,28 @@ export class Engine {
|
|
|
378
390
|
void this.recover();
|
|
379
391
|
}
|
|
380
392
|
|
|
393
|
+
// An existing session must keep the backend that owns it: opencode session
|
|
394
|
+
// ids are "ses_..." while pi ids are bare uuids, so the prefix outvotes the
|
|
395
|
+
// per-issue override. New sessions follow the issue's runtime setting.
|
|
396
|
+
// k is the session key "tracker:scope#issue@sessionName"; the runtime map is
|
|
397
|
+
// keyed by the issue part, so strip the "@sessionName" suffix.
|
|
398
|
+
private backendFor(k: string, opencodeSessionId?: string): RuntimeBackend {
|
|
399
|
+
const issueKey = k.slice(0, k.lastIndexOf("@"));
|
|
400
|
+
if (opencodeSessionId) {
|
|
401
|
+
const wants = opencodeSessionId.startsWith("ses_") ? "opencode" : "pi";
|
|
402
|
+
if (wants !== this.cfg.runtime) return this.altBackendFor(wants);
|
|
403
|
+
return this.backend;
|
|
404
|
+
}
|
|
405
|
+
const runtime = this.issueRuntimes.get(issueKey);
|
|
406
|
+
if (!runtime || runtime === this.cfg.runtime) return this.backend;
|
|
407
|
+
return this.altBackendFor(runtime);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
private altBackendFor(runtime: string): RuntimeBackend {
|
|
411
|
+
if (!this.altBackend) this.altBackend = createBackendFor(this.cfg, runtime);
|
|
412
|
+
return this.altBackend;
|
|
413
|
+
}
|
|
414
|
+
|
|
381
415
|
private workdirLink(workdir: string): string {
|
|
382
416
|
const p = encodeURIComponent(workdir);
|
|
383
417
|
return `[${workdir}](/file?path=${p}&daemon_id=${this.daemonId})`;
|
|
@@ -743,6 +777,9 @@ export class Engine {
|
|
|
743
777
|
if (event.cloneUrl) {
|
|
744
778
|
this.cloneUrls.set(issueMapKey, event.cloneUrl);
|
|
745
779
|
}
|
|
780
|
+
if (event.runtime === "pi" || event.runtime === "opencode") {
|
|
781
|
+
this.issueRuntimes.set(issueMapKey, event.runtime);
|
|
782
|
+
}
|
|
746
783
|
if (event.sender) {
|
|
747
784
|
this.senders.set(issueMapKey, event.sender);
|
|
748
785
|
}
|
|
@@ -1203,13 +1240,14 @@ export class Engine {
|
|
|
1203
1240
|
const fromStrategy = await this.takeover.resumeOpenCodeSession(session);
|
|
1204
1241
|
if (fromStrategy) resumeSessionId = fromStrategy;
|
|
1205
1242
|
}
|
|
1206
|
-
if (resumeSessionId && !(await this.
|
|
1243
|
+
if (resumeSessionId && !(await this.backendFor(k, resumeSessionId).sessionExists(resumeSessionId))) {
|
|
1207
1244
|
log.warn(`stale session ${resumeSessionId} not found in db, starting fresh`);
|
|
1208
1245
|
resumeSessionId = undefined;
|
|
1209
1246
|
await this.store.updateSession(session.id, { opencodeSessionId: undefined });
|
|
1210
1247
|
}
|
|
1211
1248
|
|
|
1212
|
-
const
|
|
1249
|
+
const backend = this.backendFor(k, resumeSessionId);
|
|
1250
|
+
const model = msg.model || (backend instanceof PiBackend && this.cfg.pi ? this.cfg.pi.defaultModel : this.cfg.opencode.defaultModel);
|
|
1213
1251
|
this.currentModel.set(k, model);
|
|
1214
1252
|
|
|
1215
1253
|
if (msg.sourceCommentId) {
|
|
@@ -1221,7 +1259,7 @@ export class Engine {
|
|
|
1221
1259
|
let exitCode: number | null = null;
|
|
1222
1260
|
|
|
1223
1261
|
try {
|
|
1224
|
-
const handle = await
|
|
1262
|
+
const handle = await backend.spawn(
|
|
1225
1263
|
{
|
|
1226
1264
|
workdir,
|
|
1227
1265
|
prompt: msg.content,
|
|
@@ -1269,7 +1307,7 @@ export class Engine {
|
|
|
1269
1307
|
await this.store.updateSession(session.id, { opencodePid: handle.pid });
|
|
1270
1308
|
await this.persistRuntimeState(session.id);
|
|
1271
1309
|
|
|
1272
|
-
log.info(`engine: spawned pid=${handle.pid} for ${k} (backend=${
|
|
1310
|
+
log.info(`engine: spawned pid=${handle.pid} for ${k} (backend=${backend.name})`);
|
|
1273
1311
|
|
|
1274
1312
|
exitCode = await handle.exited;
|
|
1275
1313
|
const stderr = await handle.stderrText;
|
|
@@ -1382,7 +1420,7 @@ export class Engine {
|
|
|
1382
1420
|
});
|
|
1383
1421
|
log.info(`engine: [bot] reply found for ${k} after prompt (comment ${matched?.id ?? "?"} createdAt ${matched?.createdAt ?? "?"}), marking done`);
|
|
1384
1422
|
if (matched && !usedModel) {
|
|
1385
|
-
const fromSession = await this.
|
|
1423
|
+
const fromSession = await this.backendFor(k, session.opencodeSessionId).lastSessionModel(session.opencodeSessionId).catch(() => ({ model: "" }));
|
|
1386
1424
|
if (fromSession.model) {
|
|
1387
1425
|
void tracker.setCommentModel(ref, matched.id, fromSession.model).catch(() => { /* display-only */ });
|
|
1388
1426
|
}
|
|
@@ -1392,7 +1430,7 @@ export class Engine {
|
|
|
1392
1430
|
await this.persistRuntimeState(session.id);
|
|
1393
1431
|
void tracker.updateStatus(ref, "");
|
|
1394
1432
|
} else {
|
|
1395
|
-
const sessionOutput = await this.
|
|
1433
|
+
const sessionOutput = await this.backendFor(k, session.opencodeSessionId).getSessionOutputTokens(session.opencodeSessionId);
|
|
1396
1434
|
const emptyRound = this.emptyResponseRounds.get(k) ?? 0;
|
|
1397
1435
|
|
|
1398
1436
|
if (!sessionOutput.hasOutput && emptyRound < Engine.MAX_EMPTY_RESPONSE_ROUNDS) {
|
|
@@ -138,6 +138,8 @@ export class GiteaTracker implements IssueTracker {
|
|
|
138
138
|
// Empty/missing = no model override; engine omits --model.
|
|
139
139
|
const modelRaw = repository.ework_model;
|
|
140
140
|
const model = typeof modelRaw === "string" && modelRaw.trim() ? modelRaw.trim() : undefined;
|
|
141
|
+
const runtimeRaw = repository.ework_runtime;
|
|
142
|
+
const runtime = runtimeRaw === "pi" || runtimeRaw === "opencode" ? runtimeRaw : undefined;
|
|
141
143
|
|
|
142
144
|
const ref: TrackerRef = {
|
|
143
145
|
trackerType: "gitea",
|
|
@@ -162,6 +164,7 @@ export class GiteaTracker implements IssueTracker {
|
|
|
162
164
|
ai_status: aiStatus,
|
|
163
165
|
},
|
|
164
166
|
model,
|
|
167
|
+
runtime,
|
|
165
168
|
cloneUrl,
|
|
166
169
|
sender,
|
|
167
170
|
};
|
|
@@ -186,6 +189,7 @@ export class GiteaTracker implements IssueTracker {
|
|
|
186
189
|
authorKind: (comment as Record<string, unknown>).author_kind as string | undefined,
|
|
187
190
|
},
|
|
188
191
|
model,
|
|
192
|
+
runtime,
|
|
189
193
|
cloneUrl,
|
|
190
194
|
sender,
|
|
191
195
|
};
|
|
@@ -203,6 +207,7 @@ export class GiteaTracker implements IssueTracker {
|
|
|
203
207
|
ai_status: aiStatus,
|
|
204
208
|
},
|
|
205
209
|
model,
|
|
210
|
+
runtime,
|
|
206
211
|
cloneUrl,
|
|
207
212
|
sender,
|
|
208
213
|
};
|
package/src/trackers/types.ts
CHANGED
|
@@ -36,6 +36,8 @@ export interface TrackerEvent {
|
|
|
36
36
|
// global default). Empty/undefined = no override; engine omits --model
|
|
37
37
|
// and lets opencode pick per its own opencode.json + env.
|
|
38
38
|
model?: string;
|
|
39
|
+
// Runtime backend override for this issue from ework-web ("opencode"|"pi").
|
|
40
|
+
runtime?: string;
|
|
39
41
|
// Real clone URL from the upstream tracker (e.g. Gitea repository.clone_url).
|
|
40
42
|
// When present, RecloneStrategy uses this instead of the ework shim URL.
|
|
41
43
|
cloneUrl?: string;
|