Track moderation status through Bifrost transport
This commit is contained in:
parent
3da7440387
commit
6ffafc5cde
17 changed files with 940 additions and 32 deletions
|
|
@ -223,6 +223,65 @@ function Project-OpenCasesForRumination {
|
|||
)
|
||||
}
|
||||
|
||||
function Project-ModerationUserStatusesForRumination {
|
||||
param($Statuses, [DateTime] $Now)
|
||||
|
||||
return @(
|
||||
@(Convert-ToValueArray -Value $Statuses) |
|
||||
Where-Object {
|
||||
(Get-ObjectPropertyString -Value $_ -Name "status") -ne "retired" -or
|
||||
@(
|
||||
@(Convert-ToValueArray -Value (Get-ObjectPropertyValue -Value $_ -Name "pendingNotices")) |
|
||||
Where-Object { [string]::IsNullOrWhiteSpace((Get-ObjectPropertyString -Value $_ -Name "sentAt")) }
|
||||
).Count -gt 0
|
||||
} |
|
||||
ForEach-Object {
|
||||
$strikes = @(Convert-ToValueArray -Value (Get-ObjectPropertyValue -Value $_ -Name "strikes"))
|
||||
$activeStrikes = @(
|
||||
$strikes | Where-Object { [string]::IsNullOrWhiteSpace((Get-ObjectPropertyString -Value $_ -Name "expiredAt")) }
|
||||
)
|
||||
$pendingNotices = @(
|
||||
@(Convert-ToValueArray -Value (Get-ObjectPropertyValue -Value $_ -Name "pendingNotices")) |
|
||||
Where-Object { [string]::IsNullOrWhiteSpace((Get-ObjectPropertyString -Value $_ -Name "sentAt")) }
|
||||
)
|
||||
@{
|
||||
userId = Get-ObjectPropertyString -Value $_ -Name "userId"
|
||||
userName = Get-ObjectPropertyString -Value $_ -Name "userName"
|
||||
status = Get-ObjectPropertyString -Value $_ -Name "status"
|
||||
summary = Get-ObjectPropertyString -Value $_ -Name "summary"
|
||||
activeStrikeCount = [int]$activeStrikes.Count
|
||||
pendingNoticeCount = [int]$pendingNotices.Count
|
||||
activeStrikes = @(
|
||||
$activeStrikes | ForEach-Object {
|
||||
@{
|
||||
strikeId = Get-ObjectPropertyString -Value $_ -Name "strikeId"
|
||||
reason = Get-ObjectPropertyString -Value $_ -Name "reason"
|
||||
ruleRef = Get-ObjectPropertyString -Value $_ -Name "ruleRef"
|
||||
sourceMessageId = Get-ObjectPropertyString -Value $_ -Name "sourceMessageId"
|
||||
channelId = Get-ObjectPropertyString -Value $_ -Name "channelId"
|
||||
issued = Project-RelativeTimestamp -Value $_ -Name "issuedAt" -Now $Now
|
||||
expires = Project-RelativeTimestamp -Value $_ -Name "expiresAt" -Now $Now
|
||||
tags = @(Convert-ToValueArray -Value (Get-ObjectPropertyValue -Value $_ -Name "tags"))
|
||||
}
|
||||
}
|
||||
)
|
||||
pendingNotices = @(
|
||||
$pendingNotices | ForEach-Object {
|
||||
@{
|
||||
noticeId = Get-ObjectPropertyString -Value $_ -Name "noticeId"
|
||||
kind = Get-ObjectPropertyString -Value $_ -Name "kind"
|
||||
summary = Get-ObjectPropertyString -Value $_ -Name "summary"
|
||||
created = Project-RelativeTimestamp -Value $_ -Name "createdAt" -Now $Now
|
||||
tags = @(Convert-ToValueArray -Value (Get-ObjectPropertyValue -Value $_ -Name "tags"))
|
||||
}
|
||||
}
|
||||
)
|
||||
tags = @(Convert-ToValueArray -Value (Get-ObjectPropertyValue -Value $_ -Name "tags"))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
function Test-RuminationCaseTerminal {
|
||||
param([string] $Status)
|
||||
|
||||
|
|
|
|||
|
|
@ -48,6 +48,23 @@ $sendMessageScriptPath = if (-not [string]::IsNullOrWhiteSpace($env:VOID_SEND_DI
|
|||
} else {
|
||||
Join-Path $repoRoot "scripts\send-discord-message.mjs"
|
||||
}
|
||||
$bifrostRoot = if (-not [string]::IsNullOrWhiteSpace($env:BIFROST_ROOT)) {
|
||||
$ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($env:BIFROST_ROOT)
|
||||
} else {
|
||||
"E:\Projects\Bifrost"
|
||||
}
|
||||
$bifrostBridgeScriptPath = if (-not [string]::IsNullOrWhiteSpace($env:VOID_BIFROST_BRIDGE_SCRIPT)) {
|
||||
$ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($env:VOID_BIFROST_BRIDGE_SCRIPT)
|
||||
} else {
|
||||
Join-Path $bifrostRoot "tools\bifrost-bridge.mjs"
|
||||
}
|
||||
$discordTransportMode = if (-not [string]::IsNullOrWhiteSpace($env:VOID_DISCORD_TRANSPORT)) {
|
||||
$env:VOID_DISCORD_TRANSPORT.Trim().ToLowerInvariant()
|
||||
} elseif (Test-Path $bifrostBridgeScriptPath) {
|
||||
"bifrost"
|
||||
} else {
|
||||
"direct"
|
||||
}
|
||||
$startedAtUtc = [DateTime]::UtcNow
|
||||
|
||||
. $contextProjectionScriptPath
|
||||
|
|
@ -611,7 +628,8 @@ function Assert-AllowedRuminationOperation {
|
|||
"retire_status_read",
|
||||
"update_mood_dimensions",
|
||||
"queue_candidate_intervention",
|
||||
"retire_candidate_intervention"
|
||||
"retire_candidate_intervention",
|
||||
"upsert_moderation_user_status"
|
||||
)
|
||||
|
||||
if ($null -eq $operationName -or -not $allowed.Contains($operationName)) {
|
||||
|
|
@ -726,21 +744,37 @@ function Invoke-CandidateInterventionDeliveryFromIntervention {
|
|||
$utf8NoBom = [System.Text.UTF8Encoding]::new($false)
|
||||
[System.IO.File]::WriteAllText($contentPath, $draft, $utf8NoBom)
|
||||
|
||||
$arguments = @($sendMessageScriptPath, "--content-file", $contentPath)
|
||||
$mode = Get-ObjectPropertyString -Value $deliveryTarget -Name "mode"
|
||||
if ($mode -eq "owner_dm") {
|
||||
$arguments += "--owner-dm"
|
||||
} else {
|
||||
$arguments = @()
|
||||
if ($discordTransportMode -eq "bifrost") {
|
||||
if ($mode -eq "owner_dm") {
|
||||
throw "Candidate intervention '$interventionId' targets owner_dm. Bifrost is the default public transport; use VOID_DISCORD_TRANSPORT=direct only for standalone owner-private installs."
|
||||
}
|
||||
$channelId = Get-ObjectPropertyString -Value $deliveryTarget -Name "channelId"
|
||||
if ([string]::IsNullOrWhiteSpace($channelId)) {
|
||||
throw "Candidate intervention '$interventionId' has deliveryTarget without channelId."
|
||||
}
|
||||
$arguments += @("--channel-id", $channelId)
|
||||
$arguments = @($bifrostBridgeScriptPath, "discord-post", "--channel-id", $channelId, "--content-file", $contentPath)
|
||||
} else {
|
||||
$arguments = @($sendMessageScriptPath, "--content-file", $contentPath)
|
||||
if ($mode -eq "owner_dm") {
|
||||
$arguments += "--owner-dm"
|
||||
} else {
|
||||
$channelId = Get-ObjectPropertyString -Value $deliveryTarget -Name "channelId"
|
||||
if ([string]::IsNullOrWhiteSpace($channelId)) {
|
||||
throw "Candidate intervention '$interventionId' has deliveryTarget without channelId."
|
||||
}
|
||||
$arguments += @("--channel-id", $channelId)
|
||||
}
|
||||
}
|
||||
|
||||
$replyToMessageId = Get-ObjectPropertyString -Value $deliveryTarget -Name "replyToMessageId"
|
||||
if (-not [string]::IsNullOrWhiteSpace($replyToMessageId)) {
|
||||
$arguments += @("--reply-to", $replyToMessageId)
|
||||
if ($discordTransportMode -eq "bifrost") {
|
||||
$arguments += @("--reply-to-message-id", $replyToMessageId)
|
||||
} else {
|
||||
$arguments += @("--reply-to", $replyToMessageId)
|
||||
}
|
||||
}
|
||||
|
||||
$personaName = Get-ObjectPropertyString -Value $deliveryTarget -Name "personaName"
|
||||
|
|
@ -770,15 +804,155 @@ function Invoke-CandidateInterventionDeliveryFromIntervention {
|
|||
Remove-Item -LiteralPath $contentPath -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
if ($discordTransportMode -eq "bifrost") {
|
||||
$bridgeReceiptText = [string]($sendOutput -join [Environment]::NewLine)
|
||||
$speech = Convert-BifrostBridgeReceiptToSpeechStatus -Receipt ($bridgeReceiptText | ConvertFrom-Json) -Preview $draft
|
||||
return Convert-LastSpeechToSpokenCandidateOperation -InterventionId $interventionId -Speech $speech
|
||||
}
|
||||
|
||||
$lastSpeechPath = Join-Path $statusDir "void-last-speech.json"
|
||||
if (-not (Test-Path $lastSpeechPath)) {
|
||||
throw "Candidate intervention delivery did not write last speech status."
|
||||
}
|
||||
|
||||
$speech = Read-JsonFile -Path $lastSpeechPath
|
||||
return Convert-LastSpeechToSpokenCandidateOperation -InterventionId $interventionId -Speech $speech
|
||||
}
|
||||
|
||||
function Convert-BifrostBridgeReceiptToSpeechStatus {
|
||||
param(
|
||||
$Receipt,
|
||||
[string] $Preview
|
||||
)
|
||||
|
||||
if ($null -eq $Receipt) {
|
||||
return $null
|
||||
}
|
||||
|
||||
return [pscustomobject]@{
|
||||
sentAt = [DateTime]::UtcNow.ToString("o")
|
||||
mode = Get-ObjectPropertyString -Value $Receipt -Name "action"
|
||||
transport = Get-ObjectPropertyString -Value $Receipt -Name "transport"
|
||||
channelId = Get-ObjectPropertyString -Value $Receipt -Name "channelId"
|
||||
messageId = Get-ObjectPropertyString -Value $Receipt -Name "messageId"
|
||||
recipientId = Get-ObjectPropertyString -Value $Receipt -Name "recipientId"
|
||||
preview = if ([string]::IsNullOrWhiteSpace($Preview)) { $null } else { $Preview.Substring(0, [Math]::Min(280, $Preview.Length)) }
|
||||
}
|
||||
}
|
||||
|
||||
function Get-PendingModerationStatusNotices {
|
||||
param($TypedState)
|
||||
|
||||
$statuses = @(Convert-ToValueArray -Value $TypedState.moderationCursor.userStatuses)
|
||||
return @(
|
||||
$statuses | ForEach-Object {
|
||||
$status = $_
|
||||
$userId = Get-ObjectPropertyString -Value $status -Name "userId"
|
||||
if ([string]::IsNullOrWhiteSpace($userId)) {
|
||||
return
|
||||
}
|
||||
|
||||
@(Convert-ToValueArray -Value (Get-ObjectPropertyValue -Value $status -Name "pendingNotices")) |
|
||||
Where-Object { [string]::IsNullOrWhiteSpace((Get-ObjectPropertyString -Value $_ -Name "sentAt")) } |
|
||||
ForEach-Object {
|
||||
[pscustomobject]@{
|
||||
userId = $userId
|
||||
userName = Get-ObjectPropertyString -Value $status -Name "userName"
|
||||
notice = $_
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
function Convert-LastSpeechToModerationStatusNoticeSentOperation {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string] $UserId,
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string] $NoticeId,
|
||||
$Speech
|
||||
)
|
||||
|
||||
$sentAt = Get-ObjectPropertyString -Value $Speech -Name "sentAt"
|
||||
if ($null -eq $Speech -or [string]::IsNullOrWhiteSpace($sentAt)) {
|
||||
return $null
|
||||
}
|
||||
|
||||
$operation = @{
|
||||
operation = "mark_moderation_status_notice_sent"
|
||||
userId = $UserId
|
||||
noticeId = $NoticeId
|
||||
sentAt = $sentAt
|
||||
}
|
||||
|
||||
foreach ($key in @("channelId", "messageId")) {
|
||||
$propertyValue = Get-ObjectPropertyString -Value $Speech -Name $key
|
||||
if ($null -ne $propertyValue) {
|
||||
$operation[$key] = $propertyValue
|
||||
}
|
||||
}
|
||||
|
||||
return $operation
|
||||
}
|
||||
|
||||
function Invoke-ModerationStatusNoticeDelivery {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
$PendingNotice
|
||||
)
|
||||
|
||||
$userId = Get-ObjectPropertyString -Value $PendingNotice -Name "userId"
|
||||
$notice = Get-ObjectPropertyValue -Value $PendingNotice -Name "notice"
|
||||
$noticeId = Get-ObjectPropertyString -Value $notice -Name "noticeId"
|
||||
$body = Get-ObjectPropertyString -Value $notice -Name "body"
|
||||
if (
|
||||
[string]::IsNullOrWhiteSpace($userId) -or
|
||||
[string]::IsNullOrWhiteSpace($noticeId) -or
|
||||
[string]::IsNullOrWhiteSpace($body)
|
||||
) {
|
||||
return $null
|
||||
}
|
||||
|
||||
$contentPath = Join-Path $statusDir ("moderation-status-dm-{0}.txt" -f ([Guid]::NewGuid().ToString("n")))
|
||||
$utf8NoBom = [System.Text.UTF8Encoding]::new($false)
|
||||
[System.IO.File]::WriteAllText($contentPath, $body, $utf8NoBom)
|
||||
|
||||
$arguments = if ($discordTransportMode -eq "bifrost") {
|
||||
@($bifrostBridgeScriptPath, "discord-dm", "--recipient-id", $userId, "--content-file", $contentPath)
|
||||
} else {
|
||||
@($sendMessageScriptPath, "--content-file", $contentPath, "--user-dm", $userId)
|
||||
}
|
||||
$previousVoidStatusDir = $env:VOID_STATUS_DIR
|
||||
try {
|
||||
$env:VOID_STATUS_DIR = $statusDir
|
||||
$sendOutput = & node @arguments 2>&1
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
$sendDetail = [string]($sendOutput -join [Environment]::NewLine)
|
||||
if ([string]::IsNullOrWhiteSpace($sendDetail)) {
|
||||
throw "Moderation status DM '$noticeId' failed with exit code $LASTEXITCODE."
|
||||
}
|
||||
|
||||
throw "Moderation status DM '$noticeId' failed with exit code $LASTEXITCODE`: $($sendDetail.Trim())"
|
||||
}
|
||||
} finally {
|
||||
$env:VOID_STATUS_DIR = $previousVoidStatusDir
|
||||
Remove-Item -LiteralPath $contentPath -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
if ($discordTransportMode -eq "bifrost") {
|
||||
$bridgeReceiptText = [string]($sendOutput -join [Environment]::NewLine)
|
||||
$speech = Convert-BifrostBridgeReceiptToSpeechStatus -Receipt ($bridgeReceiptText | ConvertFrom-Json) -Preview $body
|
||||
return Convert-LastSpeechToModerationStatusNoticeSentOperation -UserId $userId -NoticeId $noticeId -Speech $speech
|
||||
}
|
||||
|
||||
$lastSpeechPath = Join-Path $statusDir "void-last-speech.json"
|
||||
if (-not (Test-Path $lastSpeechPath)) {
|
||||
throw "Moderation status DM '$noticeId' did not write last speech status."
|
||||
}
|
||||
$speech = Read-JsonFile -Path $lastSpeechPath
|
||||
return Convert-LastSpeechToModerationStatusNoticeSentOperation -UserId $userId -NoticeId $noticeId -Speech $speech
|
||||
}
|
||||
|
||||
function Test-LockProcessAlive {
|
||||
param($LockRecord)
|
||||
|
||||
|
|
@ -933,11 +1107,22 @@ if (Test-Path $operationOutputPath) {
|
|||
Remove-Item -LiteralPath $operationOutputPath -Force
|
||||
}
|
||||
|
||||
foreach ($requiredPath in @($contextProjectionScriptPath, $recentHistoryScriptPath, $repoActivityScriptPath, $selfStateScriptPath, $sendMessageScriptPath)) {
|
||||
foreach ($requiredPath in @($contextProjectionScriptPath, $recentHistoryScriptPath, $repoActivityScriptPath, $selfStateScriptPath)) {
|
||||
if (-not (Test-Path $requiredPath)) {
|
||||
throw "Missing required helper at $requiredPath"
|
||||
}
|
||||
}
|
||||
if ($discordTransportMode -eq "bifrost") {
|
||||
if (-not (Test-Path $bifrostBridgeScriptPath)) {
|
||||
throw "VOID_DISCORD_TRANSPORT=bifrost but Bifrost bridge helper is missing at $bifrostBridgeScriptPath"
|
||||
}
|
||||
} elseif ($discordTransportMode -eq "direct") {
|
||||
if (-not (Test-Path $sendMessageScriptPath)) {
|
||||
throw "VOID_DISCORD_TRANSPORT=direct but direct Discord helper is missing at $sendMessageScriptPath"
|
||||
}
|
||||
} else {
|
||||
throw "Unsupported VOID_DISCORD_TRANSPORT '$discordTransportMode'. Use bifrost or direct."
|
||||
}
|
||||
if (-not (Test-Path $promptTemplatePath)) {
|
||||
throw "Missing rumination prompt template at $promptTemplatePath"
|
||||
}
|
||||
|
|
@ -987,6 +1172,7 @@ $typedState = $typedContext.typedState
|
|||
$priorCursor = $typedState.moderationCursor
|
||||
$speechPressureObligations = @(Get-SpeechPressureObligations -TypedState $typedState)
|
||||
$deliverableCandidates = @(Get-DeliverableCandidateInterventions -TypedState $typedState)
|
||||
$pendingModerationStatusNotices = @(Get-PendingModerationStatusNotices -TypedState $typedState)
|
||||
|
||||
$historyArgs = @($recentHistoryScriptPath)
|
||||
$priorCursorTimestamp = Get-ObjectPropertyString -Value $priorCursor -Name "lastReviewedTimestamp"
|
||||
|
|
@ -1044,6 +1230,7 @@ Write-JsonFile -Path $contextPath -Data @{
|
|||
selfStateSummary = $typedContext.summary
|
||||
chronology = "Times in this prompt-facing context are relative phrases. Exact timestamps stay parent-owned for typed state and cursor bookkeeping."
|
||||
openCases = @(Project-OpenCasesForRumination -Cases $typedState.moderationCursor.openCases -Now $startedAtUtc)
|
||||
moderationUserStatuses = @(Project-ModerationUserStatusesForRumination -Statuses $typedState.moderationCursor.userStatuses -Now $startedAtUtc)
|
||||
speechReceipts = @(Project-SpeechReceiptsForRumination -Receipts $typedState.speechReceipts.recentReceipts -Now $startedAtUtc)
|
||||
memories = @(Project-MemoriesForRumination -Memories $typedState.thoughtMemory.memories -Now $startedAtUtc)
|
||||
shortTermMemories = @(Project-MemoriesForRumination -Memories $typedState.thoughtMemory.shortTerm -Now $startedAtUtc)
|
||||
|
|
@ -1067,7 +1254,7 @@ $openCaseCount = @(
|
|||
@(Convert-ToValueArray -Value $typedState.moderationCursor.openCases) |
|
||||
Where-Object { Test-OpenCaseRequiresRumination -Case $_ }
|
||||
).Count
|
||||
if ($isNapping -and $messageCount -eq 0 -and $openCaseCount -eq 0 -and $deliverableCandidates.Count -eq 0) {
|
||||
if ($isNapping -and $messageCount -eq 0 -and $openCaseCount -eq 0 -and $deliverableCandidates.Count -eq 0 -and $pendingModerationStatusNotices.Count -eq 0) {
|
||||
Append-RunLog "napping: no new room messages or open cases; skipping awake rumination."
|
||||
Write-JsonFile -Path $operationOutputPath -Data @()
|
||||
$finishedAtUtc = [DateTime]::UtcNow
|
||||
|
|
@ -1085,6 +1272,7 @@ if ($isNapping -and $messageCount -eq 0 -and $openCaseCount -eq 0 -and $delivera
|
|||
observedMessageCount = [int]$messageCount
|
||||
openCaseCount = [int]$openCaseCount
|
||||
deliverableCandidateCount = [int]$deliverableCandidates.Count
|
||||
pendingModerationStatusNoticeCount = [int]$pendingModerationStatusNotices.Count
|
||||
tracePath = [string]$tracePath
|
||||
lastMessagePath = [string]$lastMessagePath
|
||||
}
|
||||
|
|
@ -1201,6 +1389,7 @@ if (-not $NoPost -and -not $SkipModel -and -not $disableRepoCursorAdvance) {
|
|||
}
|
||||
|
||||
$deliveredCandidateCount = 0
|
||||
$deliveredModerationStatusNoticeCount = 0
|
||||
if (-not $NoPost) {
|
||||
$refreshedTypedContext = Get-TypedSelfState
|
||||
$alreadyAnsweredCandidateOperations = @(Get-AlreadyAnsweredCandidateRetireOperations -TypedState $refreshedTypedContext.typedState -RetiredAt ([DateTime]::UtcNow))
|
||||
|
|
@ -1224,6 +1413,19 @@ if (-not $NoPost) {
|
|||
$deliveredCandidateCount += 1
|
||||
}
|
||||
}
|
||||
|
||||
$refreshedTypedContext = Get-TypedSelfState
|
||||
$pendingModerationStatusNotices = @(Get-PendingModerationStatusNotices -TypedState $refreshedTypedContext.typedState)
|
||||
foreach ($pendingNotice in $pendingModerationStatusNotices) {
|
||||
if ($deliveredModerationStatusNoticeCount -ge 3) {
|
||||
break
|
||||
}
|
||||
$noticeOperation = Invoke-ModerationStatusNoticeDelivery -PendingNotice $pendingNotice
|
||||
if ($null -ne $noticeOperation) {
|
||||
$appliedOperations += Apply-TypedOperation -Operation $noticeOperation
|
||||
$deliveredModerationStatusNoticeCount += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Append-RunLog "writing final rumination status."
|
||||
|
|
@ -1235,6 +1437,8 @@ $lastMessageSummary = @(
|
|||
"proposed=" + [string]@($proposedOperations).Count
|
||||
"applied=" + [string]@($appliedOperations).Count
|
||||
"deliveredCandidates=" + [string]$deliveredCandidateCount
|
||||
"deliveredModerationStatusNotices=" + [string]$deliveredModerationStatusNoticeCount
|
||||
"discordTransport=" + [string]$discordTransportMode
|
||||
"finishedAt=" + $finishedAtUtc.ToString("o")
|
||||
) -join [Environment]::NewLine
|
||||
[System.IO.File]::WriteAllText($lastMessagePath, $lastMessageSummary + [Environment]::NewLine, [System.Text.UTF8Encoding]::new($false))
|
||||
|
|
@ -1260,6 +1464,8 @@ $finalStatus = [ordered]@{
|
|||
appliedOperationCount = [int]@($appliedOperations).Count
|
||||
repoCursorOperationCount = [int]@($repoCursorOperations).Count
|
||||
deliveredCandidateCount = [int]$deliveredCandidateCount
|
||||
deliveredModerationStatusNoticeCount = [int]$deliveredModerationStatusNoticeCount
|
||||
discordTransport = [string]$discordTransportMode
|
||||
stateUpdated = [bool](@($appliedOperations).Count -gt 0)
|
||||
tracePath = [string]$tracePath
|
||||
lastMessagePath = [string]$lastMessagePath
|
||||
|
|
|
|||
|
|
@ -27,30 +27,32 @@ async function main() {
|
|||
throw new Error("Message content is empty.");
|
||||
}
|
||||
|
||||
if (options.ownerDm) {
|
||||
const ownerId = env.DISCORD_OWNER_ID?.trim();
|
||||
if (options.ownerDm || options.userDm) {
|
||||
const recipientId = options.userDm ?? env.DISCORD_OWNER_ID?.trim();
|
||||
|
||||
if (!ownerId) {
|
||||
throw new Error("DISCORD_OWNER_ID is required for --owner-dm.");
|
||||
if (!recipientId) {
|
||||
throw new Error("DISCORD_OWNER_ID is required for --owner-dm, or provide --user-dm <recipientId>.");
|
||||
}
|
||||
|
||||
const channelId = await openOwnerDmChannel(botToken, ownerId);
|
||||
const mode = options.userDm ? "user_dm" : "owner_dm";
|
||||
const channelId = await openDiscordDmChannel(botToken, recipientId);
|
||||
await postChunkedDiscordMessage(botToken, channelId, content, undefined, undefined);
|
||||
writeLastSpeechStatus({
|
||||
sentAt: new Date().toISOString(),
|
||||
mode: "owner_dm",
|
||||
mode,
|
||||
transport: "bot",
|
||||
channelId,
|
||||
recipientId,
|
||||
contentLength: content.length,
|
||||
chunkCount: splitDiscordContent(content).length,
|
||||
preview: content.slice(0, 280),
|
||||
});
|
||||
process.stdout.write(`${JSON.stringify({ ok: true, mode: "owner_dm", channelId })}\n`);
|
||||
process.stdout.write(`${JSON.stringify({ ok: true, mode, channelId, recipientId })}\n`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!options.channelId) {
|
||||
throw new Error("Provide --owner-dm or --channel-id.");
|
||||
throw new Error("Provide --owner-dm, --user-dm, or --channel-id.");
|
||||
}
|
||||
|
||||
await postChunkedDiscordMessage(
|
||||
|
|
@ -89,6 +91,7 @@ async function main() {
|
|||
function parseArgs(args) {
|
||||
const options = {
|
||||
ownerDm: false,
|
||||
userDm: undefined,
|
||||
channelId: undefined,
|
||||
replyToMessageId: undefined,
|
||||
content: undefined,
|
||||
|
|
@ -104,6 +107,10 @@ function parseArgs(args) {
|
|||
case "--owner-dm":
|
||||
options.ownerDm = true;
|
||||
break;
|
||||
case "--user-dm":
|
||||
options.userDm = args[index + 1];
|
||||
index += 1;
|
||||
break;
|
||||
case "--channel-id":
|
||||
options.channelId = args[index + 1];
|
||||
index += 1;
|
||||
|
|
@ -133,8 +140,10 @@ function parseArgs(args) {
|
|||
}
|
||||
}
|
||||
|
||||
if (options.ownerDm && options.channelId) {
|
||||
throw new Error("Use either --owner-dm or --channel-id, not both.");
|
||||
const targetCount = [options.ownerDm, Boolean(options.userDm), Boolean(options.channelId)]
|
||||
.filter(Boolean).length;
|
||||
if (targetCount > 1) {
|
||||
throw new Error("Use only one of --owner-dm, --user-dm, or --channel-id.");
|
||||
}
|
||||
|
||||
return options;
|
||||
|
|
@ -209,7 +218,7 @@ async function readContent(options) {
|
|||
});
|
||||
}
|
||||
|
||||
async function openOwnerDmChannel(botToken, recipientId) {
|
||||
async function openDiscordDmChannel(botToken, recipientId) {
|
||||
const response = await fetch("https://discord.com/api/v10/users/@me/channels", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
|
|
|
|||
167
scripts/smoke-void-moderation-status-fixture.ps1
Normal file
167
scripts/smoke-void-moderation-status-fixture.ps1
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
Set-StrictMode -Version Latest
|
||||
$ErrorActionPreference = "Stop"
|
||||
$ProgressPreference = "SilentlyContinue"
|
||||
|
||||
$repoRoot = Split-Path -Parent $PSScriptRoot
|
||||
Set-Location -LiteralPath $repoRoot
|
||||
|
||||
$tempRoot = Join-Path $env:TEMP ("void-moderation-status-fixture-" + [guid]::NewGuid().ToString("n"))
|
||||
$stateFilePath = Join-Path $tempRoot "void-self-state.cc"
|
||||
$operationPath = Join-Path $tempRoot "operation.json"
|
||||
$fakeSendPath = Join-Path $tempRoot "fake-send-discord-message.mjs"
|
||||
$fixtureStatusDir = Join-Path $tempRoot "status"
|
||||
$fixtureLogDir = Join-Path $tempRoot "logs"
|
||||
$statusPath = Join-Path $fixtureStatusDir "moderation-rumination.json"
|
||||
$checkPath = Join-Path $tempRoot "check-moderation-status.mjs"
|
||||
|
||||
New-Item -ItemType Directory -Force -Path $tempRoot | Out-Null
|
||||
|
||||
try {
|
||||
$fakeSendSource = @'
|
||||
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
||||
import { dirname, resolve } from "node:path";
|
||||
|
||||
const statusDir = resolve(process.env.VOID_STATUS_DIR);
|
||||
const contentFileIndex = process.argv.indexOf("--content-file");
|
||||
const userDmIndex = process.argv.indexOf("--user-dm");
|
||||
|
||||
if (contentFileIndex < 0 || userDmIndex < 0) {
|
||||
console.error("fake sender expected --content-file and --user-dm");
|
||||
process.exit(2);
|
||||
}
|
||||
|
||||
const content = readFileSync(resolve(process.argv[contentFileIndex + 1]), "utf8");
|
||||
const recipientId = process.argv[userDmIndex + 1];
|
||||
const payload = {
|
||||
sentAt: "2026-06-04T12:00:00.000Z",
|
||||
mode: "user_dm",
|
||||
transport: "bot",
|
||||
channelId: "fixture-dm-channel",
|
||||
recipientId,
|
||||
contentLength: content.trim().length,
|
||||
chunkCount: 1,
|
||||
preview: content.trim().slice(0, 280),
|
||||
};
|
||||
|
||||
mkdirSync(statusDir, { recursive: true });
|
||||
writeFileSync(resolve(statusDir, "void-last-speech.json"), `${JSON.stringify(payload, null, 2)}\n`, "utf8");
|
||||
writeFileSync(resolve(statusDir, "void-speech-log.jsonl"), `${JSON.stringify(payload)}\n`, { encoding: "utf8", flag: "a" });
|
||||
process.stdout.write(JSON.stringify({ ok: true, mode: "user_dm", channelId: payload.channelId, recipientId }) + "\n");
|
||||
'@
|
||||
[System.IO.File]::WriteAllText($fakeSendPath, $fakeSendSource, [System.Text.UTF8Encoding]::new($false))
|
||||
|
||||
$expiredStrikeOperation = @{
|
||||
operation = "upsert_moderation_user_status"
|
||||
status = @{
|
||||
userId = "fixture-user-id"
|
||||
userName = "Fixture User"
|
||||
status = "active"
|
||||
summary = "Fixture user had one expired strike."
|
||||
strikes = @(
|
||||
@{
|
||||
strikeId = "fixture-expired-strike"
|
||||
reason = "posting a fixture rule violation"
|
||||
ruleRef = "fixture-rule"
|
||||
sourceMessageId = "fixture-message-id"
|
||||
channelId = "fixture-channel-id"
|
||||
issuedAt = "2026-05-20T00:00:00.000Z"
|
||||
expiresAt = "2099-01-01T00:00:00.000Z"
|
||||
issuedBy = "Void"
|
||||
tags = @("fixture")
|
||||
}
|
||||
)
|
||||
pendingNotices = @()
|
||||
updatedAt = "2026-05-20T00:00:00.000Z"
|
||||
tags = @("fixture")
|
||||
}
|
||||
}
|
||||
$expiredStrikeOperation | ConvertTo-Json -Depth 32 | Set-Content -LiteralPath $operationPath -Encoding UTF8
|
||||
node .\scripts\void-self-state.mjs apply-operation --canonical $stateFilePath --operation-file $operationPath | Out-Null
|
||||
|
||||
$cursorOperation = @{
|
||||
operation = "record_reviewed_messages"
|
||||
lastReviewedMessageId = "fixture-future-cursor"
|
||||
lastReviewedTimestamp = "2099-06-04T00:00:00.000Z"
|
||||
}
|
||||
$cursorOperation | ConvertTo-Json -Depth 32 | Set-Content -LiteralPath $operationPath -Encoding UTF8
|
||||
node .\scripts\void-self-state.mjs apply-operation --canonical $stateFilePath --operation-file $operationPath | Out-Null
|
||||
|
||||
$previousStatusDir = $env:VOID_RUMINATION_STATUS_DIR
|
||||
$previousLogDir = $env:VOID_RUMINATION_LOG_DIR
|
||||
$previousSendScript = $env:VOID_SEND_DISCORD_SCRIPT
|
||||
$previousDiscordTransport = $env:VOID_DISCORD_TRANSPORT
|
||||
$previousDisableRepoCursorAdvance = $env:VOID_RUMINATION_DISABLE_REPO_CURSOR_ADVANCE
|
||||
|
||||
try {
|
||||
$env:VOID_RUMINATION_STATUS_DIR = $fixtureStatusDir
|
||||
$env:VOID_RUMINATION_LOG_DIR = $fixtureLogDir
|
||||
$env:VOID_SEND_DISCORD_SCRIPT = $fakeSendPath
|
||||
$env:VOID_DISCORD_TRANSPORT = "direct"
|
||||
$env:VOID_RUMINATION_DISABLE_REPO_CURSOR_ADVANCE = "1"
|
||||
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File .\scripts\run-void-moderator-rumination.ps1 -StateFilePath $stateFilePath -SkipModel | Out-Null
|
||||
} finally {
|
||||
$env:VOID_RUMINATION_STATUS_DIR = $previousStatusDir
|
||||
$env:VOID_RUMINATION_LOG_DIR = $previousLogDir
|
||||
$env:VOID_SEND_DISCORD_SCRIPT = $previousSendScript
|
||||
$env:VOID_DISCORD_TRANSPORT = $previousDiscordTransport
|
||||
$env:VOID_RUMINATION_DISABLE_REPO_CURSOR_ADVANCE = $previousDisableRepoCursorAdvance
|
||||
}
|
||||
|
||||
if (-not (Test-Path $statusPath)) {
|
||||
throw "Rumination status was not written."
|
||||
}
|
||||
|
||||
$status = Get-Content -LiteralPath $statusPath -Raw | ConvertFrom-Json
|
||||
if ($status.status -ne "ok") {
|
||||
throw "Rumination did not finish ok: $($status.status)"
|
||||
}
|
||||
if ([int]$status.deliveredModerationStatusNoticeCount -ne 1) {
|
||||
throw "Expected one delivered moderation status notice, got $($status.deliveredModerationStatusNoticeCount)."
|
||||
}
|
||||
|
||||
$checkSource = @'
|
||||
import { existsSync } from "node:fs";
|
||||
import { createRequire } from "node:module";
|
||||
import { resolve } from "node:path";
|
||||
|
||||
const repoRoot = resolve(process.cwd());
|
||||
const require = createRequire(import.meta.url);
|
||||
const coreDistPath = resolve(repoRoot, "packages/core/dist/index.js");
|
||||
if (!existsSync(coreDistPath)) {
|
||||
throw new Error(`Missing built core package at ${coreDistPath}. Run npm run build first.`);
|
||||
}
|
||||
const { loadVoidSelfStateTypedDocuments } = require(coreDistPath);
|
||||
const state = await loadVoidSelfStateTypedDocuments({ canonicalPath: process.argv[2] });
|
||||
const userStatus = state.moderationCursor.userStatuses.find((entry) => entry.userId === "fixture-user-id");
|
||||
if (!userStatus) {
|
||||
throw new Error("Fixture moderation user status was not retained.");
|
||||
}
|
||||
const strike = userStatus.strikes.find((entry) => entry.strikeId === "fixture-expired-strike");
|
||||
if (!strike?.expiredAt) {
|
||||
throw new Error("Expired strike was not structurally expired.");
|
||||
}
|
||||
if (strike.expiresAt !== "2026-05-27T00:00:00.000Z") {
|
||||
throw new Error(`Expected canonical one-week expiry, got ${strike.expiresAt}.`);
|
||||
}
|
||||
const notice = userStatus.pendingNotices.find((entry) => entry.noticeId === "strike-expired-fixture-user-id-fixture-expired-strike");
|
||||
if (!notice?.sentAt) {
|
||||
throw new Error("Strike expiry notice was not marked sent.");
|
||||
}
|
||||
if (notice.channelId !== "fixture-dm-channel") {
|
||||
throw new Error(`Expected fixture DM channel, got ${notice.channelId}.`);
|
||||
}
|
||||
process.stdout.write(JSON.stringify({
|
||||
ok: true,
|
||||
userId: userStatus.userId,
|
||||
strikeExpiredAt: strike.expiredAt,
|
||||
noticeSentAt: notice.sentAt,
|
||||
}) + "\n");
|
||||
'@
|
||||
[System.IO.File]::WriteAllText($checkPath, $checkSource, [System.Text.UTF8Encoding]::new($false))
|
||||
node $checkPath $stateFilePath | Out-Null
|
||||
|
||||
Write-Host "Void moderation status fixture passed: expired strike generated and delivered a user DM status notice."
|
||||
} finally {
|
||||
Remove-Item -LiteralPath $tempRoot -Recurse -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
197
scripts/smoke-void-rumination-bifrost-transport-fixture.ps1
Normal file
197
scripts/smoke-void-rumination-bifrost-transport-fixture.ps1
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
Set-StrictMode -Version Latest
|
||||
$ErrorActionPreference = "Stop"
|
||||
$ProgressPreference = "SilentlyContinue"
|
||||
|
||||
$repoRoot = Split-Path -Parent $PSScriptRoot
|
||||
Set-Location -LiteralPath $repoRoot
|
||||
|
||||
$tempRoot = Join-Path $env:TEMP ("void-rumination-bifrost-transport-fixture-" + [guid]::NewGuid().ToString("n"))
|
||||
$stateFilePath = Join-Path $tempRoot "void-self-state.cc"
|
||||
$operationPath = Join-Path $tempRoot "operation.json"
|
||||
$fakeBridgePath = Join-Path $tempRoot "fake-bifrost-bridge.mjs"
|
||||
$fixtureStatusDir = Join-Path $tempRoot "status"
|
||||
$fixtureLogDir = Join-Path $tempRoot "logs"
|
||||
$statusPath = Join-Path $fixtureStatusDir "moderation-rumination.json"
|
||||
$checkPath = Join-Path $tempRoot "check-bifrost-transport.mjs"
|
||||
|
||||
New-Item -ItemType Directory -Force -Path $tempRoot | Out-Null
|
||||
|
||||
try {
|
||||
$fakeBridgeSource = @'
|
||||
import { readFileSync } from "node:fs";
|
||||
|
||||
const command = process.argv[2];
|
||||
const contentFileIndex = process.argv.indexOf("--content-file");
|
||||
const content = contentFileIndex >= 0 ? readFileSync(process.argv[contentFileIndex + 1], "utf8").trim() : "";
|
||||
|
||||
if (command === "discord-post") {
|
||||
const channelIndex = process.argv.indexOf("--channel-id");
|
||||
const replyIndex = process.argv.indexOf("--reply-to-message-id");
|
||||
process.stdout.write(JSON.stringify({
|
||||
action: "discord-post",
|
||||
ok: true,
|
||||
channelId: process.argv[channelIndex + 1],
|
||||
messageId: "fixture-bifrost-message",
|
||||
transport: "webhook",
|
||||
replyToMessageId: replyIndex >= 0 ? process.argv[replyIndex + 1] : undefined,
|
||||
preview: content.slice(0, 280),
|
||||
}) + "\n");
|
||||
} else if (command === "discord-dm") {
|
||||
const recipientIndex = process.argv.indexOf("--recipient-id");
|
||||
process.stdout.write(JSON.stringify({
|
||||
action: "discord-dm",
|
||||
ok: true,
|
||||
recipientId: process.argv[recipientIndex + 1],
|
||||
channelId: "fixture-bifrost-dm-channel",
|
||||
messageId: "fixture-bifrost-dm-message",
|
||||
transport: "bot",
|
||||
preview: content.slice(0, 280),
|
||||
}) + "\n");
|
||||
} else {
|
||||
console.error(`unexpected fake bridge command ${command}`);
|
||||
process.exit(2);
|
||||
}
|
||||
'@
|
||||
[System.IO.File]::WriteAllText($fakeBridgePath, $fakeBridgeSource, [System.Text.UTF8Encoding]::new($false))
|
||||
|
||||
$cursorOperation = @{
|
||||
operation = "record_reviewed_messages"
|
||||
lastReviewedMessageId = "fixture-future-cursor"
|
||||
lastReviewedTimestamp = "2099-06-04T00:00:00.000Z"
|
||||
}
|
||||
$cursorOperation | ConvertTo-Json -Depth 32 | Set-Content -LiteralPath $operationPath -Encoding UTF8
|
||||
node .\scripts\void-self-state.mjs apply-operation --canonical $stateFilePath --operation-file $operationPath | Out-Null
|
||||
|
||||
$candidateOperation = @{
|
||||
operation = "queue_candidate_intervention"
|
||||
intervention = @{
|
||||
interventionId = "fixture-bifrost-candidate"
|
||||
kind = "direct_reply"
|
||||
status = "queued"
|
||||
target = @{
|
||||
kind = "room"
|
||||
id = "fixture-channel"
|
||||
label = "Fixture channel"
|
||||
}
|
||||
summary = "Bifrost should own public Discord transport."
|
||||
draft = "Bifrost owns this public crossing; Void just brought the sentence."
|
||||
deliveryTarget = @{
|
||||
mode = "channel"
|
||||
channelId = "fixture-channel-id"
|
||||
replyToMessageId = "fixture-reply-id"
|
||||
personaName = "Void"
|
||||
}
|
||||
priority = 0.9
|
||||
mustEventuallyShare = $false
|
||||
createdAt = "2026-06-04T10:00:00.000Z"
|
||||
updatedAt = "2026-06-04T10:00:00.000Z"
|
||||
tags = @("fixture")
|
||||
}
|
||||
}
|
||||
$candidateOperation | ConvertTo-Json -Depth 32 | Set-Content -LiteralPath $operationPath -Encoding UTF8
|
||||
node .\scripts\void-self-state.mjs apply-operation --canonical $stateFilePath --operation-file $operationPath | Out-Null
|
||||
|
||||
$statusOperation = @{
|
||||
operation = "upsert_moderation_user_status"
|
||||
status = @{
|
||||
userId = "fixture-user-id"
|
||||
userName = "Fixture User"
|
||||
status = "active"
|
||||
summary = "Fixture user needs a status notice."
|
||||
strikes = @(
|
||||
@{
|
||||
strikeId = "fixture-active-strike"
|
||||
reason = "posting a fixture rule violation"
|
||||
ruleRef = "fixture-rule"
|
||||
issuedAt = "2026-06-04T10:00:00.000Z"
|
||||
expiresAt = "2026-06-11T10:00:00.000Z"
|
||||
tags = @("fixture")
|
||||
}
|
||||
)
|
||||
pendingNotices = @(
|
||||
@{
|
||||
noticeId = "fixture-status-notice"
|
||||
kind = "strike_added"
|
||||
summary = "Strike added"
|
||||
body = "Moderation status update: fixture strike added."
|
||||
createdAt = "2026-06-04T10:00:00.000Z"
|
||||
tags = @("fixture")
|
||||
}
|
||||
)
|
||||
updatedAt = "2026-06-04T10:00:00.000Z"
|
||||
tags = @("fixture")
|
||||
}
|
||||
}
|
||||
$statusOperation | ConvertTo-Json -Depth 32 | Set-Content -LiteralPath $operationPath -Encoding UTF8
|
||||
node .\scripts\void-self-state.mjs apply-operation --canonical $stateFilePath --operation-file $operationPath | Out-Null
|
||||
|
||||
$previousStatusDir = $env:VOID_RUMINATION_STATUS_DIR
|
||||
$previousLogDir = $env:VOID_RUMINATION_LOG_DIR
|
||||
$previousTransport = $env:VOID_DISCORD_TRANSPORT
|
||||
$previousBridgeScript = $env:VOID_BIFROST_BRIDGE_SCRIPT
|
||||
$previousDisableRepoCursorAdvance = $env:VOID_RUMINATION_DISABLE_REPO_CURSOR_ADVANCE
|
||||
|
||||
try {
|
||||
$env:VOID_RUMINATION_STATUS_DIR = $fixtureStatusDir
|
||||
$env:VOID_RUMINATION_LOG_DIR = $fixtureLogDir
|
||||
$env:VOID_DISCORD_TRANSPORT = "bifrost"
|
||||
$env:VOID_BIFROST_BRIDGE_SCRIPT = $fakeBridgePath
|
||||
$env:VOID_RUMINATION_DISABLE_REPO_CURSOR_ADVANCE = "1"
|
||||
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File .\scripts\run-void-moderator-rumination.ps1 -StateFilePath $stateFilePath -SkipModel | Out-Null
|
||||
} finally {
|
||||
$env:VOID_RUMINATION_STATUS_DIR = $previousStatusDir
|
||||
$env:VOID_RUMINATION_LOG_DIR = $previousLogDir
|
||||
$env:VOID_DISCORD_TRANSPORT = $previousTransport
|
||||
$env:VOID_BIFROST_BRIDGE_SCRIPT = $previousBridgeScript
|
||||
$env:VOID_RUMINATION_DISABLE_REPO_CURSOR_ADVANCE = $previousDisableRepoCursorAdvance
|
||||
}
|
||||
|
||||
$status = Get-Content -LiteralPath $statusPath -Raw | ConvertFrom-Json
|
||||
if ($status.status -ne "ok") {
|
||||
throw "Rumination did not finish ok: $($status.status)"
|
||||
}
|
||||
if ($status.discordTransport -ne "bifrost") {
|
||||
throw "Expected bifrost transport, got $($status.discordTransport)."
|
||||
}
|
||||
if ([int]$status.deliveredCandidateCount -ne 1) {
|
||||
throw "Expected one delivered candidate, got $($status.deliveredCandidateCount)."
|
||||
}
|
||||
if ([int]$status.deliveredModerationStatusNoticeCount -ne 1) {
|
||||
throw "Expected one delivered moderation status notice, got $($status.deliveredModerationStatusNoticeCount)."
|
||||
}
|
||||
|
||||
$checkSource = @'
|
||||
import { existsSync } from "node:fs";
|
||||
import { createRequire } from "node:module";
|
||||
import { resolve } from "node:path";
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
const coreDistPath = resolve(process.cwd(), "packages/core/dist/index.js");
|
||||
if (!existsSync(coreDistPath)) {
|
||||
throw new Error(`Missing built core package at ${coreDistPath}. Run npm run build first.`);
|
||||
}
|
||||
const { loadVoidSelfStateTypedDocuments } = require(coreDistPath);
|
||||
const state = await loadVoidSelfStateTypedDocuments({ canonicalPath: process.argv[2] });
|
||||
const candidate = state.candidateInterventions.interventions.find((entry) => entry.interventionId === "fixture-bifrost-candidate");
|
||||
if (candidate) {
|
||||
throw new Error("Bifrost-delivered candidate stayed live.");
|
||||
}
|
||||
const receipt = state.speechReceipts.recentReceipts.find((entry) => entry.candidateInterventionId === "fixture-bifrost-candidate");
|
||||
if (!receipt || receipt.transport !== "webhook" || receipt.channelId !== "fixture-channel-id") {
|
||||
throw new Error("Bifrost candidate receipt was not recorded correctly.");
|
||||
}
|
||||
const status = state.moderationCursor.userStatuses.find((entry) => entry.userId === "fixture-user-id");
|
||||
const notice = status?.pendingNotices.find((entry) => entry.noticeId === "fixture-status-notice");
|
||||
if (!notice?.sentAt || notice.channelId !== "fixture-bifrost-dm-channel" || notice.messageId !== "fixture-bifrost-dm-message") {
|
||||
throw new Error("Bifrost moderation notice receipt was not recorded correctly.");
|
||||
}
|
||||
process.stdout.write(JSON.stringify({ ok: true, receiptTransport: receipt.transport, noticeChannelId: notice.channelId }) + "\n");
|
||||
'@
|
||||
[System.IO.File]::WriteAllText($checkPath, $checkSource, [System.Text.UTF8Encoding]::new($false))
|
||||
node $checkPath $stateFilePath | Out-Null
|
||||
|
||||
Write-Host "Void Bifrost transport fixture passed: public speech and moderation DM receipts came from Bifrost."
|
||||
} finally {
|
||||
Remove-Item -LiteralPath $tempRoot -Recurse -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
|
@ -218,6 +218,7 @@ process.stdout.write(JSON.stringify({ ok: true, mode: "channel", channelId }) +
|
|||
$previousStatusDir = $env:VOID_RUMINATION_STATUS_DIR
|
||||
$previousLogDir = $env:VOID_RUMINATION_LOG_DIR
|
||||
$previousSendScript = $env:VOID_SEND_DISCORD_SCRIPT
|
||||
$previousDiscordTransport = $env:VOID_DISCORD_TRANSPORT
|
||||
$previousDisableRepoCursorAdvance = $env:VOID_RUMINATION_DISABLE_REPO_CURSOR_ADVANCE
|
||||
|
||||
try {
|
||||
|
|
@ -227,6 +228,7 @@ process.stdout.write(JSON.stringify({ ok: true, mode: "channel", channelId }) +
|
|||
$env:VOID_RUMINATION_STATUS_DIR = $fixtureStatusDir
|
||||
$env:VOID_RUMINATION_LOG_DIR = $fixtureLogDir
|
||||
$env:VOID_SEND_DISCORD_SCRIPT = $fakeSendPath
|
||||
$env:VOID_DISCORD_TRANSPORT = "direct"
|
||||
$env:VOID_RUMINATION_DISABLE_REPO_CURSOR_ADVANCE = "1"
|
||||
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File .\scripts\run-void-moderator-rumination.ps1 -StateFilePath $stateFilePath | Out-Null
|
||||
|
|
@ -237,6 +239,7 @@ process.stdout.write(JSON.stringify({ ok: true, mode: "channel", channelId }) +
|
|||
$env:VOID_RUMINATION_STATUS_DIR = $previousStatusDir
|
||||
$env:VOID_RUMINATION_LOG_DIR = $previousLogDir
|
||||
$env:VOID_SEND_DISCORD_SCRIPT = $previousSendScript
|
||||
$env:VOID_DISCORD_TRANSPORT = $previousDiscordTransport
|
||||
$env:VOID_RUMINATION_DISABLE_REPO_CURSOR_ADVANCE = $previousDisableRepoCursorAdvance
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue