fix(claude): accept async rewake metadata
This commit is contained in:
1 parent
2dc633a506
commit
f619b04289
4 files changed
+27
-4
No files matched your search
@@ -27,7 +27,7 @@ This repository owns the public Go SDK, the `cmd/elydora` CLI, local signing beh
|
||||
- Select Kimi Code from an explicit `KIMI_CODE_HOME` or an existing `~/.kimi-code` home, and select legacy `kimi-cli` from an existing `~/.kimi` home. An empty `KIMI_CODE_HOME` uses `~/.kimi-code`; executable lookup is outside the activation contract, and equal stable and legacy config paths collapse to the stable contract.
|
||||
- Preserve Kimi TOML comments and unrelated formatting through range-based edits, then parse the complete rendered document before writing it. Own only exact three-field hooks for `PreToolUse`, `PostToolUse`, and `PostToolUseFailure`; require one complete triple per runtime identity, use encoded PowerShell commands on Windows, and retain the native snake_case payload.
|
||||
- Generate Kimi guard, config, private key, and audit runtimes together with every selected TOML document in one rollback-capable transaction. Preflight physical paths and existing runtime identity before creating state; status validates the complete runtime metadata, canonical private key, exact agent identity, and non-empty physical scripts.
|
||||
- Write Claude Code hooks only to `$CLAUDE_CONFIG_DIR/settings.json` with `~/.claude/settings.json` as the default; keep project, local, managed, plugin, skill, and agent sources read-only. Preserve the official exec-form command schema and native snake_case payload, own one exact matchless `PreToolUse`, `PostToolUse`, and `PostToolUseFailure` triple, propagate freeze and revocation through exit code `2`, and treat `disableAllHooks` as an explicit installation and health gate.
|
||||
- Write Claude Code hooks only to `$CLAUDE_CONFIG_DIR/settings.json` with `~/.claude/settings.json` as the default; keep project, local, managed, plugin, skill, and agent sources read-only. Validate the complete shipped handler schema, including command exec form, async rewake metadata, HTTP, MCP tool, prompt, and agent handlers. Preserve the native snake_case payload, own one exact matchless `PreToolUse`, `PostToolUse`, and `PostToolUseFailure` triple, propagate freeze and revocation through exit code `2`, and treat `disableAllHooks` as an explicit installation and health gate.
|
||||
- Generate Claude Code guard, config, private key, and audit runtimes with user settings in one rollback-capable transaction. Validate the full official event and handler schema before writes, migrate only the exact prior Go command contract, require strict runtime identity for health, and direct users to `/hooks` plus `claude doctor` for effective-source verification.
|
||||
- Write Gemini CLI hooks only to `$GEMINI_CLI_HOME/.gemini/settings.json` with `~/.gemini/settings.json` as the default. Keep workspace, system defaults, system override, and extension settings read-only. Preserve JSONC comments and line endings, reject trailing commas and duplicate keys, validate the official event and command-handler schema, and treat `hooksConfig.enabled` plus `hooksConfig.disabled` as installation and health gates.
|
||||
- Own one exact matchless `BeforeTool`/`AfterTool` pair named `elydora-guard` and `elydora-audit` with 10,000 millisecond timeouts. Generate guard, config, private key, and native-payload audit runtimes with user settings in one rollback-capable transaction. Use encoded PowerShell commands on Windows, emit `{}` for successful hook execution, propagate frozen and revoked agents through exit code `2`, require strict runtime identity for health, and direct users to `/hooks list` for effective-source verification.
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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"},
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user