theslopmachine 1.0.24 → 1.0.25
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.
|
@@ -546,13 +546,13 @@ export async function waitForHookEvent(paths, startIndex, labels, timeoutMs, mat
|
|
|
546
546
|
return waitFor(async () => {
|
|
547
547
|
const events = await readJsonl(paths.hookEventsFile)
|
|
548
548
|
const pending = events.slice(startIndex)
|
|
549
|
-
const
|
|
550
|
-
if (
|
|
549
|
+
const matchIndex = pending.findIndex((event) => labels.has(event.label) && (!matcher || matcher(event)))
|
|
550
|
+
if (matchIndex === -1) {
|
|
551
551
|
return null
|
|
552
552
|
}
|
|
553
553
|
return {
|
|
554
|
-
event:
|
|
555
|
-
eventsLength:
|
|
554
|
+
event: pending[matchIndex],
|
|
555
|
+
eventsLength: startIndex + matchIndex + 1,
|
|
556
556
|
}
|
|
557
557
|
}, {
|
|
558
558
|
timeoutMs,
|
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
readState,
|
|
24
24
|
tmuxHasSession,
|
|
25
25
|
tmuxPasteFileAndEnter,
|
|
26
|
+
tmuxSendEnter,
|
|
26
27
|
waitForRateLimitReset,
|
|
27
28
|
waitForHookEvent,
|
|
28
29
|
writeState,
|
|
@@ -50,6 +51,8 @@ const runtimeDir = argv['runtime-dir']
|
|
|
50
51
|
const promptFile = typeof argv['prompt-file'] === 'string' ? argv['prompt-file'].trim() : ''
|
|
51
52
|
const turnTimeoutMs = Number.parseInt(argv['timeout-ms'] || String(DEFAULT_TURN_TIMEOUT_MS), 10)
|
|
52
53
|
const retryOnLimit = argv['retry-on-limit'] !== '0'
|
|
54
|
+
const SUBMIT_CONFIRM_TIMEOUT_MS = 5000
|
|
55
|
+
const SUBMIT_ENTER_RETRIES = 3
|
|
53
56
|
|
|
54
57
|
if (!runtimeDir || !promptFile) {
|
|
55
58
|
emitFailure('claude_live_turn_invalid_args', 'Missing required turn arguments', {
|
|
@@ -70,6 +73,38 @@ function emitTurnFailure(resultPayload) {
|
|
|
70
73
|
})
|
|
71
74
|
}
|
|
72
75
|
|
|
76
|
+
async function confirmPromptSubmitted({ paths, hookStartIndex, turnId, tmuxSession, runtimeDir, turnDir }) {
|
|
77
|
+
let currentHookStartIndex = hookStartIndex
|
|
78
|
+
for (let attempt = 0; attempt <= SUBMIT_ENTER_RETRIES; attempt += 1) {
|
|
79
|
+
try {
|
|
80
|
+
const { eventsLength } = await waitForHookEvent(
|
|
81
|
+
paths,
|
|
82
|
+
currentHookStartIndex,
|
|
83
|
+
new Set(['UserPromptSubmit']),
|
|
84
|
+
SUBMIT_CONFIRM_TIMEOUT_MS,
|
|
85
|
+
(hookEvent) => hookEvent?.turn_id === turnId,
|
|
86
|
+
)
|
|
87
|
+
await writeState(runtimeDir, {
|
|
88
|
+
current_turn_prompt_submitted_at: new Date().toISOString(),
|
|
89
|
+
current_turn_submit_enter_retries: attempt,
|
|
90
|
+
})
|
|
91
|
+
return { ok: true, eventsLength }
|
|
92
|
+
} catch {
|
|
93
|
+
if (attempt === SUBMIT_ENTER_RETRIES) {
|
|
94
|
+
break
|
|
95
|
+
}
|
|
96
|
+
await tmuxSendEnter(tmuxSession)
|
|
97
|
+
await writeState(runtimeDir, {
|
|
98
|
+
current_turn_submit_enter_retries: attempt + 1,
|
|
99
|
+
})
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const blockerFile = path.join(turnDir, `prompt-submit-unconfirmed-pane-${Date.now()}.txt`)
|
|
104
|
+
await captureTmuxPaneArtifact(tmuxSession, blockerFile)
|
|
105
|
+
return { ok: false, blockerFile }
|
|
106
|
+
}
|
|
107
|
+
|
|
73
108
|
try {
|
|
74
109
|
const promptSource = path.resolve(promptFile)
|
|
75
110
|
|
|
@@ -237,6 +272,34 @@ try {
|
|
|
237
272
|
current_turn_prompt_injected_at: new Date().toISOString(),
|
|
238
273
|
})
|
|
239
274
|
|
|
275
|
+
const submitConfirmation = await confirmPromptSubmitted({
|
|
276
|
+
paths,
|
|
277
|
+
hookStartIndex,
|
|
278
|
+
turnId,
|
|
279
|
+
tmuxSession: liveState.tmux_session || state.tmux_session,
|
|
280
|
+
runtimeDir,
|
|
281
|
+
turnDir,
|
|
282
|
+
})
|
|
283
|
+
if (!submitConfirmation.ok) {
|
|
284
|
+
const resultPayload = buildFailureResult('claude_prompt_submit_unconfirmed', 'Claude prompt was pasted, but UserPromptSubmit did not fire after repeated Enter sends', liveState.sid || state.sid)
|
|
285
|
+
await writeTurnArtifacts(paths, turnId, prompt, resultPayload, argv['result-file'] || null)
|
|
286
|
+
await writeState(runtimeDir, {
|
|
287
|
+
status: 'failed',
|
|
288
|
+
current_turn_id: null,
|
|
289
|
+
current_turn_prompt_file: null,
|
|
290
|
+
current_turn_prompt_source: null,
|
|
291
|
+
current_turn_started_at: null,
|
|
292
|
+
current_turn_transport: 'tmux-paste-buffer',
|
|
293
|
+
last_completed_turn_id: turnId,
|
|
294
|
+
last_turn_number: turnNumber + 1,
|
|
295
|
+
last_error: resultPayload.msg,
|
|
296
|
+
prompt_submit_unconfirmed_pane_file: submitConfirmation.blockerFile,
|
|
297
|
+
})
|
|
298
|
+
emitTurnFailure(resultPayload)
|
|
299
|
+
process.exit(1)
|
|
300
|
+
}
|
|
301
|
+
hookStartIndex = submitConfirmation.eventsLength
|
|
302
|
+
|
|
240
303
|
const { event, eventsLength } = await waitForHookEvent(
|
|
241
304
|
paths,
|
|
242
305
|
hookStartIndex,
|