Summary
Every tool call the Telegram bot (PULSE/modules/telegram.ts) makes fails with a ZodError from the Claude Agent SDK's permission-response validation. This affects both the allow and the deny path — Donna (the DA) can chat, but cannot Read/Write/Bash anything via Telegram.
Environment
- LifeOS version:
6.0.5
@anthropic-ai/claude-agent-sdk: 0.1.77 (installed; package.json pins ^0.1.0) — this is the package whose response-schema validation is throwing
- Claude Code CLI: was
2.0.77 at the time this was captured (per the SDK session transcript's version field); the CLI has since self-updated to 2.1.201, unrelated to this bug
- Linux, Pulse running as a
systemd --user service (headless)
Root cause
canUseTool in PULSE/modules/telegram.ts (around line 843) returns the wrong response shape:
canUseTool: (toolName: string, input: unknown) => {
if (toolName === "Bash") {
// ...
if (cmd.includes("31337") || cmd.includes("/notify")) {
return { result: "deny", reason: "..." } // <-- wrong keys
}
}
return { result: "allow" } // <-- wrong key, missing updatedInput
}
The installed SDK validates the callback's return value against a schema that expects:
{ behavior: "allow", updatedInput: {...} }
{ behavior: "deny", message: "..." }
result/reason matches neither variant of the union, so every tool-permission check throws, regardless of whether the intent was allow or deny. Captured directly from the SDK session transcript:
Tool permission request failed: ZodError: [
{
"code": "invalid_union",
"errors": [
[
{ "code": "invalid_value", "values": ["allow"], "path": ["behavior"], "message": "Invalid input: expected \"allow\"" },
{ "expected": "record", "code": "invalid_type", "path": ["updatedInput"], "message": "Invalid input: expected record, received undefined" }
],
[
{ "code": "invalid_value", "values": ["deny"], "path": ["behavior"], "message": "Invalid input: expected \"deny\"" },
{ "expected": "string", "code": "invalid_type", "path": ["message"], "message": "Invalid input: expected string, received undefined" }
]
],
"path": [],
"message": "Invalid input"
}
]
Reproduction
- Run Pulse with the Telegram module enabled.
- Message the bot with anything that requires a tool call, e.g. "read
~/.claude/LIFEOS/USER/TELOS/TELOS.md and tell me the first line."
- The tool call fails immediately with the ZodError above. Plain conversational replies (no tool use) still work, which makes this easy to miss until you ask for something that needs a file read.
Fix
- return { result: "deny", reason: "Telegram mode: /notify and port 31337 are blocked. Voice is delivered via Telegram sendVoice, not the desktop speaker." }
+ return { behavior: "deny", message: "Telegram mode: /notify and port 31337 are blocked. Voice is delivered via Telegram sendVoice, not the desktop speaker." }
}
}
- return { result: "allow" }
+ return { behavior: "allow", updatedInput: (input as Record<string, unknown>) ?? {} }
Applied and tested locally — confirmed via the raw SDK session transcript that both the allow path (a real Read of a TELOS file now returns actual file content) and the deny path (a blocked /notify health-check now returns a clean deny message instead of crashing) work correctly after this change.
Suggested fix
Same two-line change as above in PULSE/modules/telegram.ts. Worth grepping the rest of the codebase for other canUseTool/permission-callback implementations using the same old result/reason shape, in case this schema drifted in more than one place.
Summary
Every tool call the Telegram bot (
PULSE/modules/telegram.ts) makes fails with aZodErrorfrom the Claude Agent SDK's permission-response validation. This affects both the allow and the deny path — Donna (the DA) can chat, but cannot Read/Write/Bash anything via Telegram.Environment
6.0.5@anthropic-ai/claude-agent-sdk:0.1.77(installed;package.jsonpins^0.1.0) — this is the package whose response-schema validation is throwing2.0.77at the time this was captured (per the SDK session transcript'sversionfield); the CLI has since self-updated to2.1.201, unrelated to this bugsystemd --userservice (headless)Root cause
canUseToolinPULSE/modules/telegram.ts(around line 843) returns the wrong response shape:The installed SDK validates the callback's return value against a schema that expects:
result/reasonmatches neither variant of the union, so every tool-permission check throws, regardless of whether the intent was allow or deny. Captured directly from the SDK session transcript:Reproduction
~/.claude/LIFEOS/USER/TELOS/TELOS.mdand tell me the first line."Fix
Applied and tested locally — confirmed via the raw SDK session transcript that both the allow path (a real
Readof a TELOS file now returns actual file content) and the deny path (a blocked/notifyhealth-check now returns a clean deny message instead of crashing) work correctly after this change.Suggested fix
Same two-line change as above in
PULSE/modules/telegram.ts. Worth grepping the rest of the codebase for othercanUseTool/permission-callback implementations using the same oldresult/reasonshape, in case this schema drifted in more than one place.