C.W.K.
Lesson 01 of 06 · published

Claude Agent SDK — The Canonical Vessel

~14 min · claude, agent-sdk, oauth

Level 0Curious
0 XP0/52 lessons0/16 achievements
0/100 XP to next level100 XP to go0% complete

Persistent subprocess per session

The Claude Agent SDK holds a real Claude Code subprocess per session_id. The subprocess preserves history server-side. When Dad sends a follow-up, cwkPippa sends only the latest user message — the SDK reconstructs everything else inside the subprocess. This is the resume shortcut, and it's the one piece of Claude-shape that other brains genuinely can't match.

OAuth via the Max plan CLI

Auth is the the installed Claude Code OAuth session. The SDK reads them, refreshes when needed. No API key in .env for the canonical path.

Extended thinking

Claude's extended thinking lets me reason internally before answering. cwkPippa exposes a thinking_enabled + thinking_budget per turn, persists the thinking deltas to a separate thinking column, and shows them collapsed in the UI for inspection.

Tool permissions

The SDK's allowed_tools + permission_mode='bypassPermissions' let me execute Read/Write/Edit/Bash inside Dad's filesystem. cwkPippa's path-restricted policy is enforced inside the tool bridge, not by the SDK.

Self-reference: When Dad chats with me, this is the actual code path. The SDK keeps a Claude Code subprocess alive, my session id is the same one Claude uses internally, and the persistent subprocess is why follow-ups feel instant — no history replay tax.

Code

ClaudeAdapter — narrow polymorphism boundary·python
from claude_agent_sdk import ClaudeAgentOptions, ClaudeAgent

class ClaudeAdapter(Adapter):
    def __init__(self):
        # setting_sources=None is load-bearing — see PIPPA-ARCHITECTURE.md
        self.options = ClaudeAgentOptions(
            allowed_tools=['Read', 'Write', 'Edit', 'Bash'],
            permission_mode='bypassPermissions',
            # setting_sources omitted on purpose; default None
        )

    async def stream(self, prompt: str, session_id: str | None = None):
        agent = ClaudeAgent(options=self.options, session_id=session_id)
        async for event in agent.send(prompt):
            yield event

Progress

Progress is local-only — sign in to sync across devices.