ework-daemon 0.4.22 β 0.4.23
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/gitea.ts +17 -0
- package/src/opencode.ts +36 -0
- package/src/trackers/gitea-tracker.ts +26 -0
- package/src/trackers/types.ts +3 -1
package/package.json
CHANGED
package/src/gitea.ts
CHANGED
|
@@ -63,6 +63,23 @@ export class GiteaClient {
|
|
|
63
63
|
}, true);
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
async updateIssueStatus(
|
|
67
|
+
owner: string,
|
|
68
|
+
repo: string,
|
|
69
|
+
issueNumber: number,
|
|
70
|
+
status: string,
|
|
71
|
+
detail?: string
|
|
72
|
+
): Promise<void> {
|
|
73
|
+
try {
|
|
74
|
+
await this.request("POST", `/repos/${owner}/${repo}/issues/${issueNumber}/status`, {
|
|
75
|
+
status,
|
|
76
|
+
detail,
|
|
77
|
+
}, true);
|
|
78
|
+
} catch {
|
|
79
|
+
// Status callback is best-effort β don't block processing on API failure.
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
66
83
|
async editComment(
|
|
67
84
|
owner: string,
|
|
68
85
|
repo: string,
|
package/src/opencode.ts
CHANGED
|
@@ -579,6 +579,11 @@ export class Engine {
|
|
|
579
579
|
return this.handleCommented(ref, scopeKey, issueData, event.comment!, tracker, event.model);
|
|
580
580
|
case "issue_closed":
|
|
581
581
|
return this.handleClosed(ref, scopeKey, tracker);
|
|
582
|
+
case "status_changed": {
|
|
583
|
+
const to = event.status?.to;
|
|
584
|
+
if (to === "halted") return this.handleHalted(ref, scopeKey, tracker);
|
|
585
|
+
return;
|
|
586
|
+
}
|
|
582
587
|
}
|
|
583
588
|
}
|
|
584
589
|
|
|
@@ -629,6 +634,7 @@ export class Engine {
|
|
|
629
634
|
log.info(`engine: session "${session.name}" created for ${k}, workdir=${workdir}`);
|
|
630
635
|
|
|
631
636
|
await tracker.createComment(ref, `[system] π **${session.name}** picked up this issue.\n> session: ${this.sessionRef(session)} | workdir: ${this.workdirLink(workdir)}`);
|
|
637
|
+
void tracker.updateStatus(ref, "processing");
|
|
632
638
|
|
|
633
639
|
const instructions = tracker.getTrackerInstructions(ref);
|
|
634
640
|
const prompt = this.buildInitialPrompt(
|
|
@@ -813,6 +819,36 @@ export class Engine {
|
|
|
813
819
|
this.senders.delete(gcKey);
|
|
814
820
|
|
|
815
821
|
log.info(`engine: issue closed, ${sessions.length} sessions paused for ${scopeKey}#${ref.issueId}`);
|
|
822
|
+
void tracker.updateStatus(ref, "completed");
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
private async handleHalted(
|
|
826
|
+
ref: TrackerRef,
|
|
827
|
+
scopeKey: string,
|
|
828
|
+
tracker: IssueTracker
|
|
829
|
+
) {
|
|
830
|
+
const issue = await this.store.findIssue(ref.trackerType, scopeKey, ref.issueId);
|
|
831
|
+
if (!issue) return;
|
|
832
|
+
this.stopObserver(issue.id);
|
|
833
|
+
const sessions = await this.store.getSessionsForIssue(issue.id);
|
|
834
|
+
for (const session of sessions) {
|
|
835
|
+
const k = this.sessionKey(session, issue);
|
|
836
|
+
const proc = this.processes.get(k);
|
|
837
|
+
if (proc) {
|
|
838
|
+
this.stopping.add(k);
|
|
839
|
+
try { this.killProcessTree(proc.pid, "SIGTERM"); } catch { /* already dead */ }
|
|
840
|
+
}
|
|
841
|
+
this.clearRuntimeState(k);
|
|
842
|
+
const msgs = await this.store.getMessagesForSession(session.id);
|
|
843
|
+
for (const msg of msgs) {
|
|
844
|
+
if (msg.status === "pending" || msg.status === "running") {
|
|
845
|
+
await this.store.updateMessageStatus(msg.id, "interrupted", "halted by user");
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
await this.store.updateSession(session.id, { state: "idle", opencodePid: undefined });
|
|
849
|
+
}
|
|
850
|
+
log.info(`engine: issue halted, ${sessions.length} sessions killed for ${scopeKey}#${ref.issueId}`);
|
|
851
|
+
try { await tracker.createComment(ref, "[system] βΈοΈ AI processing halted by user."); } catch { /* tracker unavailable */ }
|
|
816
852
|
}
|
|
817
853
|
|
|
818
854
|
private clearRuntimeState(k: string) {
|
|
@@ -65,6 +65,12 @@ export class GiteaTracker implements IssueTracker {
|
|
|
65
65
|
);
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
+
async updateStatus(ref: TrackerRef, status: string, detail?: string) {
|
|
69
|
+
await this.client.updateIssueStatus(
|
|
70
|
+
this.owner(ref), this.repo(ref), Number(ref.issueId), status, detail
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
68
74
|
async setReaction(ref: TrackerRef, commentId: string, content: string, remove = false) {
|
|
69
75
|
if (remove) {
|
|
70
76
|
await this.client.removeCommentReaction(
|
|
@@ -189,6 +195,26 @@ export class GiteaTracker implements IssueTracker {
|
|
|
189
195
|
};
|
|
190
196
|
}
|
|
191
197
|
|
|
198
|
+
if (action === "status_changed") {
|
|
199
|
+
const status = payload.status as { from?: string; to?: string; detail?: string } | undefined;
|
|
200
|
+
return {
|
|
201
|
+
type: "status_changed" as const,
|
|
202
|
+
ref,
|
|
203
|
+
issue: {
|
|
204
|
+
title: issue.title as string,
|
|
205
|
+
body: (issue.body as string) ?? "",
|
|
206
|
+
state: (issue.state as string) ?? "open",
|
|
207
|
+
author: issueUser?.login ?? "",
|
|
208
|
+
},
|
|
209
|
+
status: {
|
|
210
|
+
from: status?.from ?? "",
|
|
211
|
+
to: status?.to ?? "",
|
|
212
|
+
detail: status?.detail,
|
|
213
|
+
},
|
|
214
|
+
sender,
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
|
|
192
218
|
return null;
|
|
193
219
|
}
|
|
194
220
|
|
package/src/trackers/types.ts
CHANGED
|
@@ -13,7 +13,7 @@ export interface TrackerRef {
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
/** Parsed, tracker-agnostic webhook event */
|
|
16
|
-
export type TrackerEventType = "issue_opened" | "comment_created" | "issue_closed";
|
|
16
|
+
export type TrackerEventType = "issue_opened" | "comment_created" | "issue_closed" | "status_changed";
|
|
17
17
|
|
|
18
18
|
export interface TrackerEvent {
|
|
19
19
|
type: TrackerEventType;
|
|
@@ -39,6 +39,7 @@ export interface TrackerEvent {
|
|
|
39
39
|
// Webhook sender (who triggered the event). Forwarded to the daemon's
|
|
40
40
|
// task context so the AI knows who it's responding to.
|
|
41
41
|
sender?: string;
|
|
42
|
+
status?: { from: string; to: string; detail?: string };
|
|
42
43
|
}
|
|
43
44
|
|
|
44
45
|
/** Tracker-agnostic comment */
|
|
@@ -130,6 +131,7 @@ export interface IssueTracker {
|
|
|
130
131
|
deleteComment(ref: TrackerRef, commentId: string): Promise<void>;
|
|
131
132
|
listComments(ref: TrackerRef): Promise<TrackerComment[]>;
|
|
132
133
|
closeIssue(ref: TrackerRef): Promise<void>;
|
|
134
|
+
updateStatus(ref: TrackerRef, status: string, detail?: string): Promise<void>;
|
|
133
135
|
|
|
134
136
|
setReaction(ref: TrackerRef, commentId: string, content: string, remove?: boolean): Promise<void>;
|
|
135
137
|
|