feat(agentctl): headless channel webhook create and execute

Add guildWebhooksMin assert, token capture from channel-webhook-created
sidecar, and create/execute headless steps for Phase 429 scenarios.

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
Raven Scott
2026-05-23 16:53:32 -04:00
co-authored by Cursor
parent 0a888ec634
commit 725d880ea1
2 changed files with 37 additions and 0 deletions
+4
View File
@@ -166,6 +166,10 @@ function matchView (view, spec) {
if (String(view?.auditLogExportFilter || '') !== String(expected)) return false if (String(view?.auditLogExportFilter || '') !== String(expected)) return false
continue continue
} }
if (key === 'guildWebhooksMin') {
if ((view?.guildWebhooks || []).length < Number(expected)) return false
continue
}
if (key === 'auditLogIncludesAction') { if (key === 'auditLogIncludesAction') {
const needle = String(expected) const needle = String(expected)
const hit = (view?.auditLog || []).some((e) => const hit = (view?.auditLog || []).some((e) =>
+33
View File
@@ -28,6 +28,7 @@ class HeadlessSession {
this._lastInboxNotificationId = null this._lastInboxNotificationId = null
this._lastStagedAttachmentId = null this._lastStagedAttachmentId = null
this._lastStagedStickerName = null this._lastStagedStickerName = null
this._lastWebhookToken = null
} }
get lastView () { get lastView () {
@@ -462,8 +463,40 @@ class HeadlessSession {
if (!matched) { if (!matched) {
throw new Error(`headless wait-event timeout: ${JSON.stringify(step.waitEvent)}`) throw new Error(`headless wait-event timeout: ${JSON.stringify(step.waitEvent)}`)
} }
if (matched.type === 'channel-webhook-created' && matched.token) {
this._lastWebhookToken = matched.token
}
results.push({ waitEvent: step.waitEvent, payload: matched }) results.push({ waitEvent: step.waitEvent, payload: matched })
} }
if (step.createWebhookActiveChannel) {
const spec =
step.createWebhookActiveChannel && typeof step.createWebhookActiveChannel === 'object'
? step.createWebhookActiveChannel
: {}
const chId = this._lastView?.activeChannelId
if (!chId) throw new Error('no active channel for webhook')
await this.ipc({
type: 'create-channel-webhook',
channelId: chId,
name: spec.name || 'Agentctl Webhook'
})
results.push({ createWebhookActiveChannel: true, channelId: chId })
}
if (step.executeLastChannelWebhook) {
const spec =
step.executeLastChannelWebhook && typeof step.executeLastChannelWebhook === 'object'
? step.executeLastChannelWebhook
: {}
const token = this._lastWebhookToken
if (!token) throw new Error('no webhook token from prior step')
const view = await this.ipc({
type: 'execute-channel-webhook',
token,
content: String(spec.content || 'webhook post from agentctl'),
username: spec.username || 'Agentctl Webhook'
})
results.push({ executeLastChannelWebhook: true, view })
}
} }
return results return results
} }