From 6c2654a4b5f2e378e71c268af3c77480375ab367 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B7=E7=94=B5=E8=8A=BD=E8=A1=A3?= Date: Mon, 20 Jul 2026 09:14:58 -0400 Subject: [PATCH] fix(go): mirror Claude async rewake metadata --- .../go/cmd/elydora/plugins/claudecode_contract.go | 10 +++++++++- .../elydora/plugins/claudecode_official_test.go | 15 ++++++++++++++- sdks/go/cmd/elydora/plugins/claudecode_test.go | 4 +++- 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/sdks/go/cmd/elydora/plugins/claudecode_contract.go b/sdks/go/cmd/elydora/plugins/claudecode_contract.go index bbdf2ce..4994cd3 100644 --- a/sdks/go/cmd/elydora/plugins/claudecode_contract.go +++ b/sdks/go/cmd/elydora/plugins/claudecode_contract.go @@ -34,7 +34,8 @@ var claudeHookEvents = stringSet( var claudeHandlerKeys = map[string]map[string]struct{}{ "command": stringSet( "if", "once", "statusMessage", "timeout", "args", "async", - "asyncRewake", "command", "shell", "type", + "asyncRewake", "command", "rewakeMessage", "rewakeSummary", + "shell", "type", ), "prompt": stringSet( "if", "once", "statusMessage", "timeout", "continueOnBlock", "model", @@ -252,6 +253,13 @@ func validateClaudeHandler( return nil, err } } + for _, field := range []string{"rewakeMessage", "rewakeSummary"} { + if _, exists := handler[field]; exists { + if _, err := requireClaudeString(handler, field, label); err != nil { + return nil, err + } + } + } if shell, exists := handler["shell"]; exists && shell != "bash" && shell != "powershell" { return nil, fmt.Errorf( diff --git a/sdks/go/cmd/elydora/plugins/claudecode_official_test.go b/sdks/go/cmd/elydora/plugins/claudecode_official_test.go index 451a3ec..d65ad1c 100644 --- a/sdks/go/cmd/elydora/plugins/claudecode_official_test.go +++ b/sdks/go/cmd/elydora/plugins/claudecode_official_test.go @@ -12,7 +12,20 @@ func TestClaudeOfficialBinaryAcceptsInstalledSettings(t *testing.T) { if binary == "" { t.Skip("ELYDORA_CLAUDE_BINARY is unset") } - fixture := prepareClaudeFixture(t, claudeFixtureOptions{}) + source := `{"hooks":{ + "Stop":[{"hooks":[{ + "type":"command", + "command":"node", + "args":["--version"], + "asyncRewake":true, + "rewakeMessage":"Background validation failed", + "rewakeSummary":"Validation feedback" + }]}] +}}` + fixture := prepareClaudeFixture( + t, + claudeFixtureOptions{existingRaw: &source}, + ) if err := fixture.plugin.Install(fixture.config); err != nil { t.Fatalf("install Claude hooks: %v", err) } diff --git a/sdks/go/cmd/elydora/plugins/claudecode_test.go b/sdks/go/cmd/elydora/plugins/claudecode_test.go index b5bbab3..8f8bcff 100644 --- a/sdks/go/cmd/elydora/plugins/claudecode_test.go +++ b/sdks/go/cmd/elydora/plugins/claudecode_test.go @@ -122,7 +122,7 @@ func TestClaudeMalformedSettingsFailBeforeRuntimeCreation(t *testing.T) { func TestClaudePreservesEveryOfficialHandlerType(t *testing.T) { source := `{"hooks":{ "SessionStart":[{"hooks":[{"type":"prompt","prompt":"Review context","model":"haiku","continueOnBlock":true,"if":"always","once":false,"statusMessage":"Reviewing","timeout":0.5}]}], - "PreToolUse":[{"matcher":"Bash","hooks":[{"type":"command","command":"user-command","args":["--safe"],"async":false,"asyncRewake":true,"shell":"powershell"}]}], + "PreToolUse":[{"matcher":"Bash","hooks":[{"type":"command","command":"user-command","args":["--safe"],"async":false,"asyncRewake":true,"rewakeMessage":"Background validation failed","rewakeSummary":"Validation feedback","shell":"powershell"}]}], "Stop":[{"hooks":[{"type":"agent","prompt":"Verify completion","model":"sonnet"}]}], "Notification":[{"hooks":[{"type":"http","url":"https://example.test/hook","headers":{"Authorization":"Bearer ${TOKEN}"},"allowedEnvVars":["TOKEN"]}]}], "PostToolUse":[{"hooks":[{"type":"mcp_tool","server":"audit","tool":"record","input":{"source":"claude"}}]}] @@ -163,6 +163,8 @@ func TestClaudeInvalidOfficialHookShapesFailBeforeWrites(t *testing.T) { {"command", `{"hooks":{"PreToolUse":[{"hooks":[{"type":"command","command":""}]}]}}`, "non-empty string"}, {"args", `{"hooks":{"PreToolUse":[{"hooks":[{"type":"command","command":"x","args":[1]}]}]}}`, "array of strings"}, {"timeout", `{"hooks":{"PreToolUse":[{"hooks":[{"type":"command","command":"x","timeout":0}]}]}}`, "positive finite"}, + {"rewake message", `{"hooks":{"Stop":[{"hooks":[{"type":"command","command":"x","rewakeMessage":""}]}]}}`, `field "rewakeMessage" must be a non-empty string`}, + {"rewake summary", `{"hooks":{"Stop":[{"hooks":[{"type":"command","command":"x","rewakeSummary":""}]}]}}`, `field "rewakeSummary" must be a non-empty string`}, {"headers", `{"hooks":{"PreToolUse":[{"hooks":[{"type":"http","url":"https://example.test","headers":{"A":1}}]}]}}`, "map names to strings"}, {"input", `{"hooks":{"PreToolUse":[{"hooks":[{"type":"mcp_tool","server":"s","tool":"t","input":[]}]}]}}`, "must be an object"}, }