opencode-queue 0.9.0 → 0.9.1

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/index.ts +42 -21
  2. package/package.json +1 -1
package/index.ts CHANGED
@@ -27,7 +27,8 @@ type EntryOp =
27
27
  | { kind: "command"; source: string; cmd: string; args: string }
28
28
  | { kind: "shell"; source: string; shell: string }
29
29
 
30
- type State = { items: Item[]; busy: boolean; flushing: boolean; stopped: boolean; failed: boolean }
30
+ type Activity = { kind: "idle" } | { kind: "busy" } | { kind: "sending"; idle: boolean }
31
+ type State = { items: Item[]; activity: Activity; stopped: boolean; failed: boolean }
31
32
 
32
33
  type Op =
33
34
  | { kind: "list" }
@@ -127,7 +128,7 @@ export const QueuePlugin: Plugin = async ({ client }) => {
127
128
  const state = (sid: string) => {
128
129
  let current = sessions.get(sid)
129
130
  if (!current) {
130
- current = { items: [], busy: false, flushing: false, stopped: false, failed: false }
131
+ current = { items: [], activity: { kind: "idle" }, stopped: false, failed: false }
131
132
  sessions.set(sid, current)
132
133
  }
133
134
  return current
@@ -229,16 +230,42 @@ export const QueuePlugin: Plugin = async ({ client }) => {
229
230
  console.warn("QueuePlugin skipped queued item without replayable content")
230
231
  }
231
232
 
233
+ const advance = (sid: string) => {
234
+ const current = state(sid)
235
+ if (current.activity.kind !== "idle" || current.stopped || !current.items.length) return
236
+ void flush(sid, 1)
237
+ }
238
+
239
+ const settle = (sid: string, resume: boolean) => {
240
+ const current = state(sid)
241
+ current.activity = { kind: "idle" }
242
+ if (current.failed) {
243
+ current.failed = false
244
+ return
245
+ }
246
+
247
+ if (resume) advance(sid)
248
+ }
249
+
250
+ const idle = (sid: string) => {
251
+ const current = state(sid)
252
+ if (current.activity.kind === "sending") {
253
+ current.activity.idle = true
254
+ return
255
+ }
256
+ if (current.activity.kind === "busy") settle(sid, true)
257
+ }
258
+
232
259
  const flush = async (sid: string, count = Infinity) => {
233
260
  const current = state(sid)
234
261
  const items = current.items.splice(0, count)
235
262
  if (!items.length) return { sent: 0, failed: 0 }
236
263
 
237
- current.flushing = true
238
264
  let failed = 0
239
265
  const retry: Item[] = []
240
266
  try {
241
267
  for (const item of items) {
268
+ current.activity = { kind: "sending", idle: false }
242
269
  try {
243
270
  await replay(sid, item)
244
271
  } catch (error) {
@@ -250,17 +277,13 @@ export const QueuePlugin: Plugin = async ({ client }) => {
250
277
  }
251
278
  } finally {
252
279
  if (retry.length) current.items.unshift(...retry)
253
- current.flushing = false
280
+ const replayCompleted = current.activity.kind === "sending" && current.activity.idle
281
+ if (replayCompleted) settle(sid, count === 1 && failed === 0)
282
+ else current.activity = failed ? { kind: "idle" } : { kind: "busy" }
254
283
  }
255
284
  return { sent: items.length - failed, failed }
256
285
  }
257
286
 
258
- const advance = (sid: string) => {
259
- const current = state(sid)
260
- if (!current.items.length || current.busy || current.stopped || current.flushing) return
261
- void flush(sid, 1)
262
- }
263
-
264
287
  const manage = async (sid: string, op: ControlOp) => {
265
288
  const current = state(sid)
266
289
 
@@ -301,7 +324,7 @@ export const QueuePlugin: Plugin = async ({ client }) => {
301
324
  if (plan(event)) {
302
325
  const sid = event.properties.sessionID
303
326
  const current = sessions.get(sid)
304
- if (!current || (!current.flushing && (current.stopped || !current.items.length))) return
327
+ if (!current || (current.activity.kind !== "sending" && (current.stopped || !current.items.length))) return
305
328
  await no(event.properties.id)
306
329
  await toast("Declined plan approval to continue queued work", "info")
307
330
  return
@@ -318,23 +341,21 @@ export const QueuePlugin: Plugin = async ({ client }) => {
318
341
  }
319
342
 
320
343
  if (event.type === "session.idle") {
321
- const current = state(event.properties.sessionID)
322
- current.busy = false
323
- if (!current.failed) advance(event.properties.sessionID)
324
- current.failed = false
344
+ idle(event.properties.sessionID)
325
345
  return
326
346
  }
327
347
 
328
348
  if (event.type !== "session.status") return
329
349
 
330
- const current = state(event.properties.sessionID)
350
+ const sid = event.properties.sessionID
351
+ const current = state(sid)
331
352
  if (event.properties.status.type !== "idle") {
332
- current.busy = true
353
+ if (current.activity.kind !== "sending") current.activity = { kind: "busy" }
333
354
  current.failed = false
334
355
  return
335
356
  }
336
357
 
337
- current.busy = false
358
+ idle(sid)
338
359
  },
339
360
  "command.execute.before": async (input, output) => {
340
361
  const sid = input.sessionID
@@ -346,7 +367,7 @@ export const QueuePlugin: Plugin = async ({ client }) => {
346
367
  if (!queued) return
347
368
 
348
369
  const current = sessions.get(sid)
349
- const shouldQueue = Boolean(current?.busy || current?.stopped)
370
+ const shouldQueue = Boolean(current && (current.activity.kind !== "idle" || current.stopped))
350
371
  if (!shouldQueue) {
351
372
  for (const part of output.parts) if (part.type === "text") part.text = stripSuffix(part.text)
352
373
  return
@@ -357,7 +378,7 @@ export const QueuePlugin: Plugin = async ({ client }) => {
357
378
  }
358
379
 
359
380
  const current = sessions.get(sid)
360
- const shouldQueue = Boolean(current?.busy || current?.stopped)
381
+ const shouldQueue = Boolean(current && (current.activity.kind !== "idle" || current.stopped))
361
382
  const op = parse(parsePrefix(body), parts.length)
362
383
 
363
384
  if (control(op)) return stop(await manage(sid, op))
@@ -404,7 +425,7 @@ export const QueuePlugin: Plugin = async ({ client }) => {
404
425
  return
405
426
  }
406
427
 
407
- if (!current.busy && !current.stopped) {
428
+ if (current.activity.kind === "idle" && !current.stopped) {
408
429
  if (op.kind === "command") return
409
430
  if (op.kind === "shell") {
410
431
  hide(output.message.id, text)
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "opencode-queue",
4
- "version": "0.9.0",
4
+ "version": "0.9.1",
5
5
  "type": "module",
6
6
  "description": "Queue OpenCode prompts and slash commands until the agent is idle",
7
7
  "main": "./index.ts",