Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions .github/workflows/integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@ name: Manual Integration

on:
workflow_dispatch:
inputs:
suite:
description: Integration suite to run
type: choice
default: all
options:
- all
- zcode-windows

permissions:
contents: read
Expand All @@ -12,6 +20,7 @@ concurrency:

jobs:
verify:
if: inputs.suite != 'zcode-windows'
runs-on: ubuntu-latest
timeout-minutes: 60

Expand All @@ -29,3 +38,28 @@ jobs:

- name: Run integration suite
run: make test-integration

zcode-windows:
if: inputs.suite == 'all' || inputs.suite == 'zcode-windows'
runs-on: windows-latest
timeout-minutes: 15

steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.sha }}

- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: go.mod
cache-dependency-path: go.sum

- name: Build Windows Memory product
run: go build -o mnemon.exe .

- name: Test installed ZCode hooks in Windows PowerShell 5.1
shell: powershell
run: |
$env:MNEMON_TEST_BIN = (Resolve-Path ./mnemon.exe).Path
go test ./test/memory/zcode -v -count=1
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,14 @@ sessions, add recall guidance before model calls, and prompt for durable-memory
writeback at stop. Without `--global`, setup installs only the project skill;
ZCode currently ignores project-level hook configuration.

The Stop hook honors `stop_hook_active` to avoid repeated reminders. Its
completion fallback recognizes `mnemon`, `durable memory`, `持久记忆`, and
`写入记忆`; the reminder asks for a `[mnemon]` marker so evaluations in any
language can finish without another reminder. Windows hooks decode stdin as
UTF-8, and the PowerShell script containing Chinese tokens includes a UTF-8 BOM
for Windows PowerShell 5.1. Re-run setup and start a new ZCode session to update
previously installed hooks.

### [MiniMax Code](https://github.com/MiniMax-AI/minimax-code)

```bash
Expand Down
6 changes: 6 additions & 0 deletions docs/zh/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,12 @@ ZCode 会将 Mnemon skill 安装到 `~/.zcode/skills/`,并在
模型调用前会收到 recall 指引,停止时会评估 durable-memory 回写。不加
`--global` 时只安装项目 skill,因为 ZCode 当前不会执行项目级 hooks 配置。

Stop hook 优先检查 `stop_hook_active`,避免重复提醒。完成标记兼容 `mnemon`、
`durable memory`、`持久记忆` 和 `写入记忆`;提醒会要求在评估结果中包含
`[mnemon]`,以支持任意语言。Windows hooks 显式以 UTF-8 解码 stdin,含中文
标记的 PowerShell 脚本带 UTF-8 BOM,以兼容 Windows PowerShell 5.1。
重新运行 setup 并开启新的 ZCode 会话,即可更新已安装的 hooks。

### [MiniMax Code](https://github.com/MiniMax-AI/minimax-code)

```bash
Expand Down
2 changes: 2 additions & 0 deletions internal/memory/setup/assets/zcode/prime.ps1
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# mnemon ZCode SessionStart hook for Windows PowerShell.

# ZCode sends UTF-8 JSON regardless of the Windows console code page.
[Console]::InputEncoding = [Text.Encoding]::UTF8
$null = [Console]::In.ReadToEnd()

function Resolve-Mnemon {
Expand Down
9 changes: 6 additions & 3 deletions internal/memory/setup/assets/zcode/stop.ps1
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# mnemon ZCode Stop hook for Windows PowerShell.
# mnemon ZCode Stop hook for Windows PowerShell.

# ZCode sends UTF-8 JSON regardless of the Windows console code page.
[Console]::InputEncoding = [Text.Encoding]::UTF8
$raw = [Console]::In.ReadToEnd()
try {
$payload = $raw | ConvertFrom-Json
Expand All @@ -15,11 +17,12 @@ $lastMessage = ""
if ($null -ne $payload -and $null -ne $payload.last_assistant_message) {
$lastMessage = ([string]$payload.last_assistant_message).ToLowerInvariant()
}
if ($lastMessage.Contains("mnemon") -or $lastMessage.Contains("durable memory")) {
if ($lastMessage.Contains("mnemon") -or $lastMessage.Contains("durable memory") -or
$lastMessage.Contains("持久记忆") -or $lastMessage.Contains("写入记忆")) {
exit 0
}

[ordered]@{
decision = "block"
reason = "[mnemon] Briefly evaluate whether this exchange warrants durable memory. If yes, use the mnemon skill/CLI to remember only durable, non-secret facts; otherwise say no durable memory is needed."
reason = "[mnemon] Briefly evaluate whether this exchange warrants durable memory. If yes, use the mnemon skill/CLI to remember only durable, non-secret facts; otherwise say no durable memory is needed. Include [mnemon] in your evaluation, in any language."
} | ConvertTo-Json -Compress
5 changes: 3 additions & 2 deletions internal/memory/setup/assets/zcode/stop.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,16 @@ if payload.get("stop_hook_active"):
sys.exit(0)

last_message = (payload.get("last_assistant_message") or "").lower()
if "mnemon" in last_message or "durable memory" in last_message:
if any(token in last_message for token in ("mnemon", "durable memory", "持久记忆", "写入记忆")):
sys.exit(0)

print(json.dumps({
"decision": "block",
"reason": (
"[mnemon] Briefly evaluate whether this exchange warrants durable memory. "
"If yes, use the mnemon skill/CLI to remember only durable, non-secret facts; "
"otherwise say no durable memory is needed."
"otherwise say no durable memory is needed. "
"Include [mnemon] in your evaluation, in any language."
),
}))
'
2 changes: 2 additions & 0 deletions internal/memory/setup/assets/zcode/user_prompt.ps1
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# mnemon ZCode UserPromptSubmit hook for Windows PowerShell.

# ZCode sends UTF-8 JSON regardless of the Windows console code page.
[Console]::InputEncoding = [Text.Encoding]::UTF8
$null = [Console]::In.ReadToEnd()

[ordered]@{
Expand Down
40 changes: 40 additions & 0 deletions internal/memory/setup/zcode_test.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,52 @@
package setup

import (
"bytes"
"os"
"path/filepath"
"strings"
"testing"
"unicode/utf8"

"github.com/mnemon-dev/mnemon/internal/memory/setup/assets"
)

func TestZCodePowerShellHookEncoding(t *testing.T) {
bom := []byte{0xef, 0xbb, 0xbf}
for _, hook := range []struct {
name string
body []byte
}{
{"prime.ps1", assets.ZCodePrimeHookPowerShell},
{"user_prompt.ps1", assets.ZCodeUserPromptHookPowerShell},
{"stop.ps1", assets.ZCodeStopHookPowerShell},
} {
t.Run(hook.name, func(t *testing.T) {
if !utf8.Valid(hook.body) {
t.Fatal("PowerShell asset is not valid UTF-8")
}
// Windows PowerShell 5.1 reads BOM-less script source as ANSI.
// Any asset with non-ASCII literals must carry its own UTF-8 BOM.
for _, b := range bytes.TrimPrefix(hook.body, bom) {
if b >= utf8.RuneSelf && !bytes.HasPrefix(hook.body, bom) {
t.Fatal("non-ASCII PowerShell source requires a UTF-8 BOM")
}
}
path, err := ZCodeWriteHook(t.TempDir(), hook.name, hook.body)
if err != nil {
t.Fatal(err)
}
installed, err := os.ReadFile(path)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(installed, hook.body) {
t.Fatal("setup changed the embedded PowerShell bytes")
}
})
}
}

func TestZCodeRegisterHooksPreservesUnrelatedConfig(t *testing.T) {
dir := t.TempDir()
configPath := filepath.Join(dir, "cli", "config.json")
Expand Down
32 changes: 32 additions & 0 deletions test/memory/zcode/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# ZCode hook integration tests

These opt-in process tests run with `go test ./test/memory/zcode -v -count=1`.
They are outside `make test`; `make test-integration` includes the package.
On Windows, `powershell.exe` 5.1 is required and the suite installs hooks through
the built product's `setup --target zcode --global --yes` command in a temporary
home containing spaces and Chinese characters. Set `MNEMON_TEST_BIN` to reuse
an existing product binary; otherwise the test builds one.

The **Manual Integration** workflow's `zcode-windows` suite runs only this
boundary on a Windows runner. The default `all` selection also runs the existing
Linux integration suite. The process writes UTF-8 JSON bytes to stdin while a
wrapper selects console code page 936 (GBK) or 65001 (UTF-8). The unchanged
`stop-before-142.ps1` fixture comes from commit
`4d312c4ebb42bf9f85442120fad4b9e85d4779e2`; its failures establish the regression
control in the same host. The fixed scripts must preserve installed bytes,
including the Stop script's UTF-8 BOM, honor the host's active guard, recognize
the documented completion tokens, and produce valid hook JSON.

Empty and malformed payloads retain the existing blocking reminder behavior.
These tests do not claim general natural-language evaluation detection.

On other platforms, the shell Stop cases run normally. Set
`MNEMON_TEST_POWERSHELL` to an explicit `pwsh` path to also probe the PowerShell
assets using the production hook writer. PowerShell 7 evidence does not replace
Windows PowerShell 5.1 validation of source encoding and native setup.

Encoding references:

- [Microsoft's PowerShell character encoding guide](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_character_encoding?view=powershell-5.1)
- [Console.InputEncoding](https://learn.microsoft.com/en-us/dotnet/api/system.console.inputencoding?view=netframework-4.8.1)
- [ZCode hook protocol](https://zcode.z.ai/en/docs/hooks)
Loading
Loading