@markjaquith/agency 3.5.0 → 3.5.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.
- package/README.md +4 -2
- package/package.json +1 -1
- package/src/workbase/opencode-v2-tui-plugin-file.ts +108 -21
package/README.md
CHANGED
|
@@ -326,8 +326,10 @@ which open a fresh TUI session so the generated continuation prompt cannot be
|
|
|
326
326
|
routed to an unrelated prior session. By default Agency opens the agent without
|
|
327
327
|
a prompt. `--auto` uses its autonomous command and sends the generated task,
|
|
328
328
|
phase, or epic prompt. OpenCode V2 receives a launch-only environment marker;
|
|
329
|
-
Agency's managed TUI companion
|
|
330
|
-
|
|
329
|
+
Agency's managed TUI companion retries the native submit command until the exact
|
|
330
|
+
prompt appears as a persisted user message. It records whether OpenCode submitted
|
|
331
|
+
the prompt natively or submission followed a companion dispatch, and shows a
|
|
332
|
+
bounded error with a manual recovery instruction when delivery is not observed.
|
|
331
333
|
|
|
332
334
|
Custom agents are direct argv commands, never shell snippets:
|
|
333
335
|
|
package/package.json
CHANGED
|
@@ -35,16 +35,37 @@ export default plugin
|
|
|
35
35
|
`
|
|
36
36
|
|
|
37
37
|
const tuiBody = `const autosubmitTimeoutMs = 10_000
|
|
38
|
-
const autosubmitRetryMs =
|
|
38
|
+
const autosubmitRetryMs = 100
|
|
39
|
+
|
|
40
|
+
type AutosubmitObservation = {
|
|
41
|
+
event: "marker" | "dispatch" | "submitted" | "timeout" | "error"
|
|
42
|
+
detail?: string
|
|
43
|
+
dispatches: number
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
type AutosubmitMessage = {
|
|
47
|
+
type?: string
|
|
48
|
+
text?: string
|
|
49
|
+
info?: { role?: string }
|
|
50
|
+
role?: string
|
|
51
|
+
parts?: readonly { type?: string; text?: string }[]
|
|
52
|
+
}
|
|
39
53
|
|
|
40
54
|
type AutosubmitContext = {
|
|
41
55
|
keymap: {
|
|
42
56
|
commands(): readonly { id?: string }[]
|
|
43
57
|
dispatch(id: string): unknown
|
|
44
58
|
}
|
|
45
|
-
|
|
59
|
+
data?: {
|
|
60
|
+
session?: {
|
|
61
|
+
message?: {
|
|
62
|
+
sync?(sessionID: string): Promise<unknown>
|
|
63
|
+
list?(sessionID: string): readonly AutosubmitMessage[] | undefined
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
46
67
|
ui: {
|
|
47
|
-
router: { current(): { type: string } }
|
|
68
|
+
router: { current(): { type: string; sessionID?: string } }
|
|
48
69
|
toast: {
|
|
49
70
|
show(input: {
|
|
50
71
|
variant: "error"
|
|
@@ -57,7 +78,11 @@ type AutosubmitContext = {
|
|
|
57
78
|
}
|
|
58
79
|
|
|
59
80
|
export const createAgencyAutosubmit = (
|
|
60
|
-
options: {
|
|
81
|
+
options: {
|
|
82
|
+
timeoutMs?: number
|
|
83
|
+
retryMs?: number
|
|
84
|
+
observe?: (observation: AutosubmitObservation) => void
|
|
85
|
+
} = {},
|
|
61
86
|
) => {
|
|
62
87
|
let started = false
|
|
63
88
|
|
|
@@ -66,49 +91,111 @@ export const createAgencyAutosubmit = (
|
|
|
66
91
|
if (process.env.AGENCY_TUI_AUTOSUBMIT !== "1") return () => {}
|
|
67
92
|
if (!process.env.AGENCY_PROMPT) return () => {}
|
|
68
93
|
started = true
|
|
69
|
-
delete process.env.AGENCY_TUI_AUTOSUBMIT
|
|
70
94
|
|
|
71
95
|
const timeoutMs = options.timeoutMs ?? autosubmitTimeoutMs
|
|
72
96
|
const retryMs = options.retryMs ?? autosubmitRetryMs
|
|
73
97
|
const deadline = Date.now() + timeoutMs
|
|
98
|
+
const prompt = process.env.AGENCY_PROMPT
|
|
74
99
|
let timer: ReturnType<typeof setTimeout> | undefined
|
|
75
100
|
let stopped = false
|
|
101
|
+
let dispatches = 0
|
|
102
|
+
let lastRoute = "unknown"
|
|
103
|
+
|
|
104
|
+
const observe = (event: AutosubmitObservation["event"], detail?: string) => {
|
|
105
|
+
const observation = { event, detail, dispatches }
|
|
106
|
+
options.observe?.(observation)
|
|
107
|
+
if (
|
|
108
|
+
!options.observe &&
|
|
109
|
+
(event !== "dispatch" || dispatches === 1 || dispatches % 10 === 0)
|
|
110
|
+
) {
|
|
111
|
+
const suffix = detail ? ": " + detail : ""
|
|
112
|
+
console.info("[agency.tui] autosubmit " + event + suffix)
|
|
113
|
+
}
|
|
114
|
+
}
|
|
76
115
|
|
|
77
116
|
const stop = () => {
|
|
78
117
|
stopped = true
|
|
79
118
|
if (timer) clearTimeout(timer)
|
|
80
119
|
}
|
|
81
120
|
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
if (
|
|
85
|
-
|
|
86
|
-
|
|
121
|
+
const submitted = async (sessionID: string) => {
|
|
122
|
+
const messages = context.data?.session?.message
|
|
123
|
+
if (!messages?.list) return false
|
|
124
|
+
try {
|
|
125
|
+
await messages.sync?.(sessionID)
|
|
126
|
+
} catch (error) {
|
|
127
|
+
observe("error", "message sync failed: " + String(error))
|
|
128
|
+
return false
|
|
87
129
|
}
|
|
130
|
+
return (messages.list(sessionID) ?? []).some((message) => {
|
|
131
|
+
const role = message.type === "user" ? "user" : message.info?.role ?? message.role
|
|
132
|
+
const text =
|
|
133
|
+
message.text ??
|
|
134
|
+
(message.parts ?? [])
|
|
135
|
+
.filter((part) => part.type === "text")
|
|
136
|
+
.map((part) => part.text ?? "")
|
|
137
|
+
.join("")
|
|
138
|
+
return role === "user" && text === prompt
|
|
139
|
+
})
|
|
140
|
+
}
|
|
88
141
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
142
|
+
const finish = (event: "submitted" | "timeout", detail: string) => {
|
|
143
|
+
stop()
|
|
144
|
+
delete process.env.AGENCY_TUI_AUTOSUBMIT
|
|
145
|
+
observe(event, detail)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const attempt = async () => {
|
|
149
|
+
if (stopped) return
|
|
150
|
+
const route = context.ui.router.current()
|
|
151
|
+
lastRoute = route.type
|
|
152
|
+
if (route.type === "session" && route.sessionID) {
|
|
153
|
+
if (await submitted(route.sessionID)) {
|
|
154
|
+
finish(
|
|
155
|
+
"submitted",
|
|
156
|
+
dispatches === 0
|
|
157
|
+
? "native OpenCode submission observed"
|
|
158
|
+
: "submitted message observed after companion dispatch",
|
|
159
|
+
)
|
|
160
|
+
return
|
|
161
|
+
}
|
|
96
162
|
}
|
|
97
163
|
|
|
164
|
+
if (stopped) return
|
|
98
165
|
if (Date.now() >= deadline) {
|
|
99
|
-
|
|
166
|
+
finish(
|
|
167
|
+
"timeout",
|
|
168
|
+
"route=" + lastRoute + ", dispatches=" + String(dispatches),
|
|
169
|
+
)
|
|
100
170
|
context.ui.toast.show({
|
|
101
171
|
variant: "error",
|
|
102
172
|
title: "Agency launch",
|
|
103
|
-
message:
|
|
173
|
+
message:
|
|
174
|
+
"The task prompt was not observed as submitted after " +
|
|
175
|
+
String(dispatches) +
|
|
176
|
+
" automatic attempt(s). Press Enter to submit it manually.",
|
|
104
177
|
duration: 8_000,
|
|
105
178
|
})
|
|
106
179
|
return
|
|
107
180
|
}
|
|
108
|
-
|
|
181
|
+
|
|
182
|
+
const submitReady = context.keymap
|
|
183
|
+
.commands()
|
|
184
|
+
.some((command) => command.id === "prompt.submit")
|
|
185
|
+
if (route.type === "home" && submitReady) {
|
|
186
|
+
dispatches += 1
|
|
187
|
+
observe("dispatch", "prompt.submit")
|
|
188
|
+
try {
|
|
189
|
+
await context.keymap.dispatch("prompt.submit")
|
|
190
|
+
} catch (error) {
|
|
191
|
+
observe("error", "dispatch failed: " + String(error))
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
if (!stopped) timer = setTimeout(() => void attempt(), retryMs)
|
|
109
195
|
}
|
|
110
196
|
|
|
111
|
-
|
|
197
|
+
observe("marker", "autonomous prompt detected")
|
|
198
|
+
void attempt()
|
|
112
199
|
return stop
|
|
113
200
|
}
|
|
114
201
|
}
|