opencode-queue 0.10.0 → 0.11.0
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/README.md +1 -0
- package/index.ts +18 -15
- package/package.json +4 -1
package/README.md
CHANGED
|
@@ -79,6 +79,7 @@ When the session is busy:
|
|
|
79
79
|
|
|
80
80
|
- Queued entries are hidden from the transcript and from the running agent.
|
|
81
81
|
- The current agent run keeps using its original agent, model, and thinking variant.
|
|
82
|
+
- Each queued entry replays with the agent, model, and thinking variant selected when it was queued.
|
|
82
83
|
- Queued entries replay in order after the session completes normally and becomes idle.
|
|
83
84
|
- `/queue front ...` puts an entry before the existing queued entries.
|
|
84
85
|
- Only one queued entry is sent per idle transition, so queued work runs one item at a time.
|
package/index.ts
CHANGED
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
import type { Plugin } from "@opencode-ai/plugin"
|
|
2
2
|
import type { AgentPartInput, FilePart, FilePartInput, SubtaskPartInput, TextPart, TextPartInput } from "@opencode-ai/sdk"
|
|
3
|
+
import { HttpServerResponse } from "effect/unstable/http"
|
|
3
4
|
|
|
4
5
|
const QUEUE = /^\/queue(?:\s+([\s\S]*))?$/
|
|
5
6
|
const SUFFIX = /^([\s\S]*?)\s+\/queue(?:\s+(front))?\s*$/
|
|
6
7
|
const CMD = /^\/(\S+)(?:\s+([\s\S]*))?$/
|
|
7
8
|
const ITEM_NUMBER = /^[1-9]\d*$/
|
|
8
|
-
const HANDLED = "__QUEUE_HANDLED__"
|
|
9
9
|
const TUI_COMPACT = "session_compact"
|
|
10
10
|
|
|
11
11
|
type InputPart = TextPartInput | FilePartInput | AgentPartInput | SubtaskPartInput
|
|
12
12
|
type Model = { providerID: string; modelID: string }
|
|
13
|
-
type Meta = { variant?: string; controls?: string[]; fast?: boolean }
|
|
14
13
|
type Run = { agent: string; model?: Model }
|
|
15
|
-
type Info = { agent: string; model: Model
|
|
16
|
-
type Msg = { info: { role: string; agent?: string; mode?: string; model?: Model; providerID?: string; modelID?: string
|
|
14
|
+
type Info = { agent: string; model: Model; variant?: string }
|
|
15
|
+
type Msg = { info: { role: string; agent?: string; mode?: string; model?: Model; providerID?: string; modelID?: string; variant?: string } }
|
|
17
16
|
type Ask = { type: string; properties: { id: string; sessionID: string; questions: { question: string; header: string }[] } }
|
|
18
17
|
type Post = (input: { url: string; path?: Record<string, string>; body?: unknown; headers?: Record<string, string> }) => Promise<{ response?: Response; error?: unknown } | undefined>
|
|
19
18
|
type QueueInput = { body: string; front: boolean }
|
|
@@ -129,6 +128,11 @@ const control = (op: Op): op is ControlOp => {
|
|
|
129
128
|
const shouldQueue = (state?: State) => Boolean(state && (state.activity.kind !== "idle" || state.stopped))
|
|
130
129
|
const shouldDeclinePlan = (state?: State) => Boolean(state && (state.activity.kind === "sending" || (!state.stopped && state.items.length)))
|
|
131
130
|
const itemText = (item: Item) => (item.kind === "prompt" ? item.body.trim() || item.label : item.source)
|
|
131
|
+
// OpenCode's command hook has no cancel/noReply output. Throwing a raw Effect
|
|
132
|
+
// response is handled by OpenCode's HTTP layer as an empty successful command.
|
|
133
|
+
const handled = (): never => {
|
|
134
|
+
throw HttpServerResponse.empty({ status: 204 })
|
|
135
|
+
}
|
|
132
136
|
const plan = (event: unknown): event is Ask => {
|
|
133
137
|
if (typeof event !== "object" || !event || !("type" in event) || event.type !== "question.asked") return false
|
|
134
138
|
const question = (event as Ask).properties?.questions?.[0]
|
|
@@ -154,7 +158,7 @@ export const QueuePlugin: Plugin = async ({ client }) => {
|
|
|
154
158
|
|
|
155
159
|
const stop = async (message: string, variant: "info" | "error" = "info", duration = 5000): Promise<never> => {
|
|
156
160
|
await toast(message, variant, duration)
|
|
157
|
-
|
|
161
|
+
return handled()
|
|
158
162
|
}
|
|
159
163
|
|
|
160
164
|
const no = async (id: string) => {
|
|
@@ -202,9 +206,9 @@ export const QueuePlugin: Plugin = async ({ client }) => {
|
|
|
202
206
|
})
|
|
203
207
|
|
|
204
208
|
return ([...(Array.isArray(result) ? result : (result.data ?? []))].reverse() as Msg[]).flatMap((msg): Info[] => {
|
|
205
|
-
if (msg.info.role === "user" && msg.info.agent && msg.info.model) return [{ agent: msg.info.agent, model: msg.info.model, variant: msg.info.variant
|
|
209
|
+
if (msg.info.role === "user" && msg.info.agent && msg.info.model) return [{ agent: msg.info.agent, model: msg.info.model, variant: msg.info.variant }]
|
|
206
210
|
if (msg.info.role === "assistant" && (msg.info.agent || msg.info.mode) && msg.info.providerID && msg.info.modelID) {
|
|
207
|
-
return [{ agent: msg.info.agent ?? msg.info.mode!, model: { providerID: msg.info.providerID, modelID: msg.info.modelID }, variant: msg.info.variant
|
|
211
|
+
return [{ agent: msg.info.agent ?? msg.info.mode!, model: { providerID: msg.info.providerID, modelID: msg.info.modelID }, variant: msg.info.variant }]
|
|
208
212
|
}
|
|
209
213
|
return []
|
|
210
214
|
})[0]
|
|
@@ -217,7 +221,7 @@ export const QueuePlugin: Plugin = async ({ client }) => {
|
|
|
217
221
|
return { agent: "build" }
|
|
218
222
|
}
|
|
219
223
|
|
|
220
|
-
const opts = (info: Info) => ({ agent: info.agent, model: info.model, variant: info.variant
|
|
224
|
+
const opts = (info: Info) => ({ agent: info.agent, model: info.model, variant: info.variant })
|
|
221
225
|
|
|
222
226
|
const shell = (sid: string, command: string, info: Run) => client.session.shell({ path: { id: sid }, body: { agent: info.agent, model: info.model, command } })
|
|
223
227
|
// TUI command events target the focused session; queued replay must target the original session.
|
|
@@ -406,17 +410,17 @@ export const QueuePlugin: Plugin = async ({ client }) => {
|
|
|
406
410
|
if (!shouldQueue(sessions.get(sid))) {
|
|
407
411
|
if (op.kind === "shell") {
|
|
408
412
|
await shell(sid, op.shell, await run(sid))
|
|
409
|
-
|
|
413
|
+
return handled()
|
|
410
414
|
}
|
|
411
415
|
|
|
412
416
|
if (op.kind === "compact") {
|
|
413
417
|
await client.tui.executeCommand({ body: { command: TUI_COMPACT }, throwOnError: true })
|
|
414
|
-
|
|
418
|
+
return handled()
|
|
415
419
|
}
|
|
416
420
|
|
|
417
421
|
if (op.kind === "command") {
|
|
418
422
|
await client.session.command({ path: { id: sid }, body: { command: op.cmd, arguments: op.args, parts } as any })
|
|
419
|
-
|
|
423
|
+
return handled()
|
|
420
424
|
}
|
|
421
425
|
|
|
422
426
|
output.parts.splice(0, output.parts.length, { type: "text", text: op.body } as any, ...parts)
|
|
@@ -436,7 +440,7 @@ export const QueuePlugin: Plugin = async ({ client }) => {
|
|
|
436
440
|
const current = state(sid)
|
|
437
441
|
const parts = files(output.parts)
|
|
438
442
|
const op = parse(request, parts.length)
|
|
439
|
-
const
|
|
443
|
+
const info = { agent: input.agent ?? output.message.agent, model: input.model ?? output.message.model, variant: input.variant }
|
|
440
444
|
|
|
441
445
|
if (control(op)) {
|
|
442
446
|
hide(output.message.id, text)
|
|
@@ -454,19 +458,18 @@ export const QueuePlugin: Plugin = async ({ client }) => {
|
|
|
454
458
|
if (op.kind === "command") return
|
|
455
459
|
if (op.kind === "compact") {
|
|
456
460
|
hide(output.message.id, text)
|
|
457
|
-
await compact(sid,
|
|
461
|
+
await compact(sid, info)
|
|
458
462
|
return
|
|
459
463
|
}
|
|
460
464
|
if (op.kind === "shell") {
|
|
461
465
|
hide(output.message.id, text)
|
|
462
|
-
await shell(sid, op.shell,
|
|
466
|
+
await shell(sid, op.shell, info)
|
|
463
467
|
return
|
|
464
468
|
}
|
|
465
469
|
text.text = request.body
|
|
466
470
|
return
|
|
467
471
|
}
|
|
468
472
|
|
|
469
|
-
const info = { agent: output.message.agent, model: { ...output.message.model }, variant: meta.variant, controls: meta.controls, fast: meta.fast }
|
|
470
473
|
const prior = await latest(sid)
|
|
471
474
|
if (prior) Object.assign(output.message, opts(prior))
|
|
472
475
|
else console.warn("QueuePlugin could not neutralize queued placeholder metadata because the session has no previous message context")
|
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.
|
|
4
|
+
"version": "0.11.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Queue OpenCode prompts and slash commands until the agent is idle",
|
|
7
7
|
"main": "./index.ts",
|
|
@@ -37,6 +37,9 @@
|
|
|
37
37
|
"scripts": {
|
|
38
38
|
"typecheck": "tsc --noEmit"
|
|
39
39
|
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"effect": "4.0.0-beta.59"
|
|
42
|
+
},
|
|
40
43
|
"devDependencies": {
|
|
41
44
|
"@opencode-ai/plugin": "^1.14.37",
|
|
42
45
|
"@opencode-ai/sdk": "^1.14.37",
|