opencode-magi 0.0.0-dev-20260721055255 → 0.0.0-dev-20260722060754

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/dist/magi.js CHANGED
@@ -217,27 +217,69 @@ export class Magi {
217
217
  }
218
218
  }
219
219
  async promptSession(sessionID, text, signal) {
220
- const result = await this.input.client.session.prompt({
221
- parts: [{ text, type: "text" }],
222
- sessionID,
223
- }, { signal });
224
- if (result.error) {
225
- if (!isUndefined(result.response) && result.response.statusText)
226
- throw new Error(result.response.statusText);
227
- else if (result.error instanceof Error)
228
- throw new Error(result.error.message);
229
- else
230
- throw new Error(JSON.stringify(result.error));
220
+ const controller = new AbortController();
221
+ const events = await this.input.client.event.subscribe(undefined, {
222
+ signal: controller.signal,
223
+ });
224
+ try {
225
+ const result = await Promise.race([
226
+ this.input.client.session.prompt({
227
+ parts: [{ text, type: "text" }],
228
+ sessionID,
229
+ }, { signal }),
230
+ this.monitorSession(sessionID, events.stream),
231
+ ]);
232
+ if (result.error) {
233
+ if (!isUndefined(result.response) && result.response.statusText)
234
+ throw new Error(result.response.statusText);
235
+ else if (result.error instanceof Error)
236
+ throw new Error(result.error.message);
237
+ else
238
+ throw new Error(JSON.stringify(result.error));
239
+ }
240
+ else {
241
+ const output = result.data.parts
242
+ .filter((part) => part.type === "text")
243
+ .map((part) => part.text)
244
+ .join("\n");
245
+ if (!output)
246
+ throw new Error("OpenCode session.prompt did not return text output.");
247
+ return output;
248
+ }
231
249
  }
232
- else {
233
- const output = result.data.parts
234
- .filter((part) => part.type === "text")
235
- .map((part) => part.text)
236
- .join("\n");
237
- if (!output)
238
- throw new Error("OpenCode session.prompt did not return text output.");
239
- return output;
250
+ catch (e) {
251
+ if (e instanceof MagiError)
252
+ await this.input.client.session.abort({ sessionID });
253
+ throw e;
254
+ }
255
+ finally {
256
+ controller.abort();
257
+ }
258
+ }
259
+ async monitorSession(sessionID, events) {
260
+ for await (const ev of events) {
261
+ if (ev.type === "permission.asked" &&
262
+ ev.properties.sessionID === sessionID) {
263
+ const { id, patterns, permission } = ev.properties;
264
+ const result = await this.input.client.permission.reply({
265
+ reply: "reject",
266
+ requestID: id,
267
+ });
268
+ if (result.error)
269
+ throw new MagiError("blocked", "Could not reject permission request.");
270
+ throw new MagiError("blocked", `OpenCode session requested ${permission} permission for ${patterns.join(", ")}.`);
271
+ }
272
+ if (ev.type === "question.asked" &&
273
+ ev.properties.sessionID === sessionID) {
274
+ const result = await this.input.client.question.reject({
275
+ requestID: ev.properties.id,
276
+ });
277
+ if (result.error)
278
+ throw new MagiError("blocked", "Could not reject user question.");
279
+ throw new MagiError("blocked", "OpenCode session requested user input.");
280
+ }
240
281
  }
282
+ return new Promise(() => { });
241
283
  }
242
284
  async deleteSessions(state) {
243
285
  const sessionIds = this.getSessionIds(state);
@@ -43,7 +43,11 @@ export async function edit() {
43
43
  }
44
44
  return output;
45
45
  }, {
46
- error: (_, count) => this.updateEvent(`Attempt ${count} failed to edit. Retrying...`),
46
+ error: async (e, count) => {
47
+ if (e instanceof MagiError)
48
+ throw e;
49
+ await this.updateEvent(`Attempt ${count} failed to edit. Retrying...`);
50
+ },
47
51
  retries: this.config.output.repairAttempts,
48
52
  signal: this.context.abort,
49
53
  });
@@ -134,7 +138,11 @@ export async function resolveConflict() {
134
138
  responses: [],
135
139
  };
136
140
  }, {
137
- error: (_, count) => this.updateEvent(`Attempt ${count} failed to resolve conflicts. Retrying...`),
141
+ error: async (e, count) => {
142
+ if (e instanceof MagiError)
143
+ throw e;
144
+ await this.updateEvent(`Attempt ${count} failed to resolve conflicts. Retrying...`);
145
+ },
138
146
  retries: this.config.output.repairAttempts,
139
147
  signal: this.context.abort,
140
148
  });
@@ -86,7 +86,11 @@ export async function postReviews() {
86
86
  throw new Error("Invalid output for operator.");
87
87
  return parsed.comment;
88
88
  }, {
89
- error: (_, count) => this.updateEvent(`Attempt ${count} failed to post comment by operator. Retrying...`),
89
+ error: async (e, count) => {
90
+ if (e instanceof MagiError)
91
+ throw e;
92
+ await this.updateEvent(`Attempt ${count} failed to post comment by operator. Retrying...`);
93
+ },
90
94
  retries: this.config.output.repairAttempts,
91
95
  signal: this.context.abort,
92
96
  });
@@ -113,7 +113,11 @@ export async function classifyChecks() {
113
113
  throw new Error(`Invalid output for reviewer ${id}.`);
114
114
  return parsed;
115
115
  }, {
116
- error: (_, count) => this.updateEvent(`Attempt ${count} failed to classify CI checks with reviewer ${id}. Retrying...`),
116
+ error: async (e, count) => {
117
+ if (e instanceof MagiError)
118
+ throw e;
119
+ await this.updateEvent(`Attempt ${count} failed to classify CI checks with reviewer ${id}. Retrying...`);
120
+ },
117
121
  retries: this.config.output.repairAttempts,
118
122
  signal: this.context.abort,
119
123
  });
@@ -125,7 +125,11 @@ export async function review() {
125
125
  ]).join("\n\n"));
126
126
  return parsed;
127
127
  }, {
128
- error: (_, count) => this.updateEvent(`Attempt ${count} failed to ${label} with reviewer ${id}. Retrying...`),
128
+ error: async (e, count) => {
129
+ if (e instanceof MagiError)
130
+ throw e;
131
+ await this.updateEvent(`Attempt ${count} failed to ${label} with reviewer ${id}. Retrying...`);
132
+ },
129
133
  retries: this.config.output.repairAttempts,
130
134
  signal: this.context.abort,
131
135
  });
@@ -208,7 +212,11 @@ export async function reconsiderClose() {
208
212
  validateInlineCommentTargets("initial", parsed, inlineCommentTargets);
209
213
  return parsed;
210
214
  }, {
211
- error: (_, count) => this.updateEvent(`Attempt ${count} failed to reconsider close verdict with reviewer ${id}. Retrying...`),
215
+ error: async (e, count) => {
216
+ if (e instanceof MagiError)
217
+ throw e;
218
+ await this.updateEvent(`Attempt ${count} failed to reconsider close verdict with reviewer ${id}. Retrying...`);
219
+ },
212
220
  retries: this.config.output.repairAttempts,
213
221
  signal: this.context.abort,
214
222
  });
@@ -287,7 +295,11 @@ async function collectAcceptedFindings(findings) {
287
295
  throw new Error(`Missing finding vote: ${key}.`);
288
296
  return parsed;
289
297
  }, {
290
- error: (_, count) => this.updateEvent(`Attempt ${count} failed to validate review findings with reviewer ${id}. Retrying...`),
298
+ error: async (e, count) => {
299
+ if (e instanceof MagiError)
300
+ throw e;
301
+ await this.updateEvent(`Attempt ${count} failed to validate review findings with reviewer ${id}. Retrying...`);
302
+ },
291
303
  retries: this.config.output.repairAttempts,
292
304
  signal: this.context.abort,
293
305
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-magi",
3
- "version": "0.0.0-dev-20260721055255",
3
+ "version": "0.0.0-dev-20260722060754",
4
4
  "description": "Multi-agent PR review and merge orchestration plugin for OpenCode.",
5
5
  "license": "MIT",
6
6
  "author": "Hirotomo Yamada <hirotomo.yamada@avap.co.jp>",