From c7dbd0e42ee00b1c664b1650233cfa006bd215e9 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Sun, 17 May 2026 20:17:28 +0100 Subject: [PATCH] Resolve live loop inspection complaints --- packages/core/src/void-self-state-domain.ts | 1 + scripts/check-voidbot-operations.ps1 | 38 +++++++++++++++---- .../void-rumination-context-projection.ps1 | 1 + scripts/run-void-moderator-rumination.ps1 | 24 ++++++++++-- .../smoke-void-rumination-speech-fixture.ps1 | 7 ++++ scripts/sync-voidbot-backup-offsite.ps1 | 11 +++++- state/evidence.jsonl | 1 + 7 files changed, 71 insertions(+), 12 deletions(-) diff --git a/packages/core/src/void-self-state-domain.ts b/packages/core/src/void-self-state-domain.ts index aa8565d..4b7f192 100644 --- a/packages/core/src/void-self-state-domain.ts +++ b/packages/core/src/void-self-state-domain.ts @@ -62,6 +62,7 @@ const moderationRepoCursorSchema = z.object({ const deliveryReceiptSchema = z.object({ receiptKey: nonEmptyStringSchema, + candidateInterventionId: nonEmptyStringSchema.optional(), sentAt: timestampSchema, mode: z.string().trim().min(1).optional(), transport: z.string().trim().min(1).optional(), diff --git a/scripts/check-voidbot-operations.ps1 b/scripts/check-voidbot-operations.ps1 index a4a7b20..0756e53 100644 --- a/scripts/check-voidbot-operations.ps1 +++ b/scripts/check-voidbot-operations.ps1 @@ -273,9 +273,19 @@ function Invoke-CommandWithTimeout { $stdoutPath = Join-Path ([System.IO.Path]::GetTempPath()) ([System.IO.Path]::GetRandomFileName()) $stderrPath = Join-Path ([System.IO.Path]::GetTempPath()) ([System.IO.Path]::GetRandomFileName()) + $quotedArguments = @( + $ArgumentList | ForEach-Object { + $argument = [string]$_ + if ($argument -notmatch '[\s"]') { + return $argument + } + + return '"' + ($argument -replace '(\\*)"', '$1$1\"' -replace '(\\+)$', '$1$1') + '"' + } + ) try { - $process = Start-Process -FilePath $FilePath -ArgumentList $ArgumentList -NoNewWindow -RedirectStandardOutput $stdoutPath -RedirectStandardError $stderrPath -PassThru + $process = Start-Process -FilePath $FilePath -ArgumentList ($quotedArguments -join " ") -NoNewWindow -RedirectStandardOutput $stdoutPath -RedirectStandardError $stderrPath -PassThru if (-not $process.WaitForExit($TimeoutSeconds * 1000)) { try { @@ -527,20 +537,34 @@ function Invoke-RemoteLinuxCommand { [AllowEmptyString()] [string] $IdentityFile = "", [Parameter(Mandatory = $true)] - [string] $Command + [string] $Command, + [Parameter(Mandatory = $false)] + [int] $TimeoutSeconds = 45 ) - $args = @("-o", "BatchMode=yes", "-o", "ConnectTimeout=10") + $args = @( + "-o", "BatchMode=yes", + "-o", "ConnectTimeout=10", + "-o", "ServerAliveInterval=5", + "-o", "ServerAliveCountMax=2" + ) if (-not [string]::IsNullOrWhiteSpace($IdentityFile)) { $args += @("-i", $IdentityFile) } - $args += @($Target, $Command) - $response = & ssh.exe @args + $remoteCommand = "timeout ${TimeoutSeconds}s $Command" + $response = & ssh.exe @($args + @($Target, $remoteCommand)) 2>&1 + $exitCode = $LASTEXITCODE - if ($LASTEXITCODE -ne 0) { - throw "SSH command failed for $Target." + if ($exitCode -eq 124) { + throw "SSH command timed out after $TimeoutSeconds second(s) for $Target." + } + + if ($exitCode -ne 0) { + $outputText = [string]($response -join "`n") + $output = if ([string]::IsNullOrWhiteSpace($outputText)) { "" } else { " $($outputText.Trim())" } + throw "SSH command failed for $Target.$output" } return [string]($response -join "`n") diff --git a/scripts/lib/void-rumination-context-projection.ps1 b/scripts/lib/void-rumination-context-projection.ps1 index 78fe617..8d6f080 100644 --- a/scripts/lib/void-rumination-context-projection.ps1 +++ b/scripts/lib/void-rumination-context-projection.ps1 @@ -176,6 +176,7 @@ function Project-SpeechReceiptsForRumination { @(Convert-ToValueArray -Value $Receipts) | ForEach-Object { @{ receiptKey = Get-ObjectPropertyString -Value $_ -Name "receiptKey" + candidateInterventionId = Get-ObjectPropertyString -Value $_ -Name "candidateInterventionId" sent = Project-RelativeTimestamp -Value $_ -Name "sentAt" -Now $Now mode = Get-ObjectPropertyString -Value $_ -Name "mode" transport = Get-ObjectPropertyString -Value $_ -Name "transport" diff --git a/scripts/run-void-moderator-rumination.ps1 b/scripts/run-void-moderator-rumination.ps1 index 3ba8bd9..c52ca3e 100644 --- a/scripts/run-void-moderator-rumination.ps1 +++ b/scripts/run-void-moderator-rumination.ps1 @@ -474,7 +474,10 @@ function Assert-AllowedRuminationOperation { } function Convert-LastSpeechToReceiptOperation { - param($Speech) + param( + $Speech, + [string] $CandidateInterventionId + ) $sentAt = Get-ObjectPropertyString -Value $Speech -Name "sentAt" if ($null -eq $Speech -or $null -eq $sentAt) { @@ -483,6 +486,7 @@ function Convert-LastSpeechToReceiptOperation { $receiptKeyParts = @( "speech", + $CandidateInterventionId, $sentAt, (Get-ObjectPropertyString -Value $Speech -Name "channelId"), (Get-ObjectPropertyString -Value $Speech -Name "replyToMessageId"), @@ -501,6 +505,10 @@ function Convert-LastSpeechToReceiptOperation { transport = if (Get-ObjectPropertyString -Value $Speech -Name "transport") { Get-ObjectPropertyString -Value $Speech -Name "transport" } else { "bot" } } + if (-not [string]::IsNullOrWhiteSpace($CandidateInterventionId)) { + $receipt.candidateInterventionId = $CandidateInterventionId + } + foreach ($key in @("channelId", "replyToMessageId", "personaName", "personaAvatarUrl", "preview", "previewHash")) { $propertyValue = Get-ObjectPropertyString -Value $Speech -Name $key if ($null -ne $propertyValue) { @@ -528,7 +536,7 @@ function Convert-LastSpeechToSpokenCandidateOperation { $Speech ) - $receiptOperation = Convert-LastSpeechToReceiptOperation -Speech $Speech + $receiptOperation = Convert-LastSpeechToReceiptOperation -Speech $Speech -CandidateInterventionId $InterventionId if ($null -eq $receiptOperation) { return $null } @@ -913,7 +921,16 @@ if (-not $NoPost) { Append-RunLog "writing final rumination status." $finishedAtUtc = [DateTime]::UtcNow Append-RunLog "final status timestamp captured." -$lastMessage = if (Test-Path $lastMessagePath) { Get-Content -Path $lastMessagePath -Raw -Encoding UTF8 } else { "" } +$lastMessageSummary = @( + "Void moderation rumination finished." + "mode=" + $(if ($SkipModel) { "typed_rumination_skip_model" } else { "typed_rumination" }) + "proposed=" + [string]@($proposedOperations).Count + "applied=" + [string]@($appliedOperations).Count + "deliveredCandidates=" + [string]$deliveredCandidateCount + "finishedAt=" + $finishedAtUtc.ToString("o") +) -join [Environment]::NewLine +[System.IO.File]::WriteAllText($lastMessagePath, $lastMessageSummary + [Environment]::NewLine, [System.Text.UTF8Encoding]::new($false)) +$lastMessage = Get-Content -Path $lastMessagePath -Raw -Encoding UTF8 Append-RunLog "final status last message loaded." $finalStatus = [ordered]@{ @@ -937,6 +954,7 @@ $finalStatus = [ordered]@{ stateUpdated = [bool](@($appliedOperations).Count -gt 0) tracePath = [string]$tracePath lastMessagePath = [string]$lastMessagePath + lastMessage = [string]$lastMessage } Write-JsonFile -Path $statusPath -Data $finalStatus diff --git a/scripts/smoke-void-rumination-speech-fixture.ps1 b/scripts/smoke-void-rumination-speech-fixture.ps1 index 9a26a3a..bc23cd9 100644 --- a/scripts/smoke-void-rumination-speech-fixture.ps1 +++ b/scripts/smoke-void-rumination-speech-fixture.ps1 @@ -169,6 +169,13 @@ process.stdout.write(JSON.stringify({ ok: true, mode: "channel", channelId }) + if ($receipt.channelId -ne "fixture-channel-id" -or $receipt.replyToMessageId -ne "fixture-message-id") { throw "Speech fixture did not preserve the delivery receipt target." } + if ($receipt.candidateInterventionId -ne "fixture-parent-owned-speech") { + throw "Speech fixture did not link the delivery receipt back to the spoken candidate." + } + $lastMessage = Get-Content -LiteralPath (Join-Path $fixtureStatusDir "moderation-rumination-last-message.txt") -Raw -Encoding UTF8 + if ($lastMessage -notmatch "deliveredCandidates=1") { + throw "Speech fixture did not refresh the final rumination message summary." + } @{ status = "ok" diff --git a/scripts/sync-voidbot-backup-offsite.ps1 b/scripts/sync-voidbot-backup-offsite.ps1 index 49cec64..0ddee0a 100644 --- a/scripts/sync-voidbot-backup-offsite.ps1 +++ b/scripts/sync-voidbot-backup-offsite.ps1 @@ -164,10 +164,17 @@ function Invoke-RemotePowerShell { $bytes = [System.Text.Encoding]::Unicode.GetBytes($Script) $encoded = [Convert]::ToBase64String($bytes) - $output = & ssh $Target powershell -NoProfile -NonInteractive -EncodedCommand $encoded + $output = & ssh.exe ` + -o BatchMode=yes ` + -o ConnectTimeout=10 ` + -o ServerAliveInterval=5 ` + -o ServerAliveCountMax=2 ` + $Target powershell -NoProfile -NonInteractive -EncodedCommand $encoded 2>&1 if ($LASTEXITCODE -ne 0) { - throw "Remote PowerShell command failed on $Target." + $outputText = [string]($output -join "`n") + $detail = if ([string]::IsNullOrWhiteSpace($outputText)) { "" } else { " $($outputText.Trim())" } + throw "Remote PowerShell command failed on $Target.$detail" } return ($output -join "`n").Trim() diff --git a/state/evidence.jsonl b/state/evidence.jsonl index fcf1cb0..2597ee4 100644 --- a/state/evidence.jsonl +++ b/state/evidence.jsonl @@ -124,3 +124,4 @@ {"ts":"2026-05-17T18:04:57Z","type":"implementation","status":"ok","note":"Added parent-side short-term rumination clustering: record_short_term_memory now merges repeated same-target/topic pressure into one provisional memory with merged anchors, plus a smoke fixture and prompt guidance. Consolidated the live AquaSynth/Aquarium duplicate cluster from five short-term records to one."} {"ts":"2026-05-17T18:21:36Z","type":"implementation","status":"ok","note":"Added agency-to-speech accountability: rumination now projects high-intensity active self/world advocacy pressure as speechPressureObligations and rejects silent [] unless the model queues/deferred a candidate or cools/retires the pressure. The fixture proves silent output fails and deferred candidate output passes."} {"ts":"2026-05-17T18:47:03Z","type":"implementation","status":"ok","note":"Closed the stranded-candidate speech gap. Metacrat's explicit channel permission was observed at 2026-05-17T18:15:24Z, but rumination queued the AquariumSynthHost draft as deferred with no delivery target, then nap skip left it unsent. The runner now treats deferred candidates as unresolved pressure, delivers existing queued candidates with delivery targets, and the stranded candidate was repaired to the permitted channel and posted with a typed receipt."} +{"ts":"2026-05-17T19:15:56Z","type":"implementation","status":"ok","note":"Live-loop inspection complaints resolved: candidate speech receipts now link back to the spoken intervention, rumination report text is refreshed by the runner, watchdog Repixelizer SSH checks no longer hang indefinitely, and offsite sync recovered with a fresh remote archive."}