feat(agentctl): add DM scheduled-message journey

Add `runDmScheduledMessageJourney` to validate queue creation, listing,
immediate send override, and cancellation in headless DM flows.

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
Raven Scott
2026-06-02 04:11:23 -04:00
co-authored by Cursor
parent 19e623ceb3
commit 192accb7cb
2 changed files with 41 additions and 0 deletions
+2
View File
@@ -4,6 +4,8 @@ Agent control plane for Pearcord — send the same IPC messages the UI sends, re
**Phase 659 (v0.8.638):** Added `runDmPollVoteJourney` to verify DM single-choice idempotent vote apply, multi-select, anonymous poll creation, pre-vote edit guard behavior, close-state vote blocking, DM poll deletion, thread-scoped poll voting in `dm_thread`, and no synthetic unread/mention bump from poll updates; smoke `test:agentctl-dm-poll-phase659`.
**Phase 659 (v0.8.638):** Added `runDmScheduledMessageJourney` to verify queue/create/list/send-now/cancel behavior for DM scheduled messages; smoke `test:agentctl-dm-scheduled-phase659`.
**Phase 658 (v0.8.638):** Added DM thread closure journeys: `runDmThreadArchiveJourney` (archive/unarchive state) and `runDmThreadP2PAuditJourney` (topic audit). Smokes: `test:agentctl-dm-thread-archive-phase658`, `test:agentctl-dm-thread-p2p-audit-phase658`.
**Phase 656 (v0.8.631):** `runDmVoiceLiveJourney`, `runGroupDmAddMemberJourney`; smokes `test:agentctl-dm-voice-live-phase656`, `test:agentctl-group-add-member-phase656`.
+39
View File
@@ -978,6 +978,45 @@ class HeadlessSession {
}
}
async runDmScheduledMessageJourney (peerUserId, peerDisplayName) {
if (!this.platform) throw new Error('headless not started')
await this.openDmWithPeer(peerUserId, peerDisplayName)
const queued = await this.platform.createDmScheduledMessage({
content: 'Scheduled hello from phase 659',
sendAt: Date.now() + 120000,
timezone: 'local',
recurrence: 'none'
})
if (!queued?.id) throw new Error('expected queued scheduled message id')
const list = await this.platform.listDmScheduledMessages({
channelId: this._lastView?.activeChannelId || null
})
if (!Array.isArray(list) || !list.some((r) => r.id === queued.id && r.status === 'queued')) {
throw new Error('expected queued scheduled message in list')
}
const sent = await this.platform.sendNowDmScheduledMessage(queued.id)
if (sent?.status !== 'sent' || !sent?.sentMessageId) {
throw new Error('expected scheduled message send-now to mark sent')
}
await this.refreshView()
if (!(this._lastView?.messages || []).some((m) => m.id === sent.sentMessageId)) {
throw new Error('expected sent scheduled message in dm timeline')
}
const queuedCancel = await this.platform.createDmScheduledMessage({
content: 'Cancel me scheduled message',
sendAt: Date.now() + 180000,
timezone: 'local',
recurrence: 'none'
})
const cancelled = await this.platform.cancelDmScheduledMessage(queuedCancel.id)
if (cancelled?.status !== 'cancelled') throw new Error('expected cancelled scheduled message state')
return {
queuedId: queued.id,
sentMessageId: sent.sentMessageId,
cancelledId: queuedCancel.id
}
}
async runDmVideoLiveJourney (peerUserId, peerDisplayName) {
if (!this.platform) throw new Error('headless not started')
await this.openDmWithPeer(peerUserId, peerDisplayName)