Describe the bug
The ZCode Stop hook template (internal/memory/setup/assets/zcode/stop.ps1, also embedded in the 0.2.8 binaries) reads the hook payload with a bare [Console]::In.ReadToEnd(). On Windows, powershell.exe decodes piped stdin using the system ANSI/OEM code page — CP936 (GBK) on a zh-CN locale — while the client sends the payload as UTF-8. Any multi-byte content (e.g. Chinese text in last_assistant_message) arrives as mojibake and can make ConvertFrom-Json throw; the catch then sets $payload = $null, which silently skips the stop_hook_active early-exit. Net effect: the hook emits decision: block at the end of every turn, re-prompting the memory evaluation even when it was already done.
A second, encoding-independent problem hits non-English users in the same template: the exit guard only matches English keywords (mnemon, durable memory), so a reply that already performed the evaluation in another language (e.g. Chinese 「无需写入持久记忆」) never matches and the block loop still doesn't close.
To reproduce
- Windows with system locale zh-CN (OEM code page 936);
mnemon setup --target zcode (v0.2.8) to deploy the hooks.
- End a turn whose
last_assistant_message contains Chinese. The hook log shows ConvertFrom-Json failing (payload null → stop_hook_active check skipped) and the hook returning decision: block unconditionally.
- Alternatively, reply with a completed memory evaluation phrased in Chinese — the English-only guard misses it and the hook blocks again.
Expected behavior
The hook should decode the UTF-8 payload correctly regardless of the system code page, and close the loop when stop_hook_active is set or the evaluation was already done in a non-English reply.
Environment
- OS: Windows 11 x64 (build 26220), system locale zh-CN (OEM code page 936)
- mnemon version: 0.2.8 (
@mnemon-dev/mnemon via npm)
- LLM CLI: ZCode (hook scripts run under Windows PowerShell 5.1)
- Ollama: n/a
Workaround / suggested fix (verified locally)
At the top of stop.ps1, before reading stdin:
try { [Console]::InputEncoding = [Text.Encoding]::UTF8 } catch {}
Extend the guard list with non-English phrases:
$guards = @("mnemon", "durable memory", "持久记忆", "写入记忆")
foreach ($g in $guards) {
if ($lastMessage.Contains($g)) { exit 0 }
}
And ship the .ps1 asset as UTF-8 with BOM: Windows PowerShell 5.1 treats BOM-less files as ANSI, which corrupts the non-ASCII literals in the guard list (a generate-time concern for the embedded template).
With these three changes deployed locally, the hook parses the payload and exits correctly (verified via hook logs); without them, every turn end was blocked unconditionally. The same stdin-decoding consideration applies to the other ZCode .ps1 hooks if they ever parse non-ASCII payload fields.
Additional context
- Checked that the template is unchanged on
master (internal/memory/setup/assets/zcode/stop.ps1, as of 2026-09-15) and inside the 0.2.8 win32-x64 binary; no existing issue tracks this.
Describe the bug
The ZCode Stop hook template (
internal/memory/setup/assets/zcode/stop.ps1, also embedded in the 0.2.8 binaries) reads the hook payload with a bare[Console]::In.ReadToEnd(). On Windows,powershell.exedecodes piped stdin using the system ANSI/OEM code page — CP936 (GBK) on a zh-CN locale — while the client sends the payload as UTF-8. Any multi-byte content (e.g. Chinese text inlast_assistant_message) arrives as mojibake and can makeConvertFrom-Jsonthrow; thecatchthen sets$payload = $null, which silently skips thestop_hook_activeearly-exit. Net effect: the hook emitsdecision: blockat the end of every turn, re-prompting the memory evaluation even when it was already done.A second, encoding-independent problem hits non-English users in the same template: the exit guard only matches English keywords (
mnemon,durable memory), so a reply that already performed the evaluation in another language (e.g. Chinese 「无需写入持久记忆」) never matches and the block loop still doesn't close.To reproduce
mnemon setup --target zcode(v0.2.8) to deploy the hooks.last_assistant_messagecontains Chinese. The hook log showsConvertFrom-Jsonfailing (payload null →stop_hook_activecheck skipped) and the hook returningdecision: blockunconditionally.Expected behavior
The hook should decode the UTF-8 payload correctly regardless of the system code page, and close the loop when
stop_hook_activeis set or the evaluation was already done in a non-English reply.Environment
@mnemon-dev/mnemonvia npm)Workaround / suggested fix (verified locally)
At the top of
stop.ps1, before reading stdin:Extend the guard list with non-English phrases:
And ship the
.ps1asset as UTF-8 with BOM: Windows PowerShell 5.1 treats BOM-less files as ANSI, which corrupts the non-ASCII literals in the guard list (a generate-time concern for the embedded template).With these three changes deployed locally, the hook parses the payload and exits correctly (verified via hook logs); without them, every turn end was blocked unconditionally. The same stdin-decoding consideration applies to the other ZCode
.ps1hooks if they ever parse non-ASCII payload fields.Additional context
master(internal/memory/setup/assets/zcode/stop.ps1, as of 2026-09-15) and inside the 0.2.8 win32-x64 binary; no existing issue tracks this.