vault backup: 2026-04-17 12:50:20

This commit is contained in:
Vadym Samoilenko 2026-04-17 12:50:20 +01:00
parent 208de20525
commit cd9d8b33e1
6 changed files with 184 additions and 1 deletions

View file

@ -23,6 +23,10 @@ created: 2026-04-17
- **Local path:** `/Volumes/SSD/Projects/Oliver/Barclays-banner-builder`
## Sessions
### 2026-04-17 Create deployment script and app concept
**Asked:** Create deployment script and app concept for Docker-based Ubuntu server with Apache reverse proxy, avoiding WebSockets.
**Done:** Fixed Apache configuration routing issue by reordering Include directives so the app's config loads before hp-prod-tracker's catch-all rule.
### 2026-04-17 How to deploy the app on
**Asked:** How to deploy the app on Ubuntu server with Docker, idempotent deployment script, and fix Apache routing issues?
**Done:** Created idempotent deploy script with Docker build, database initialization, migrations, and fixed Apache VirtualHost configuration by reordering Include directives.
@ -127,6 +131,7 @@ created: 2026-04-17
## Change Log
| Date | Requested | Changed | Files |
|------|-----------|---------|-------|
| 2026-04-17 | Deployment setup | Apache conf Include order, deploy.sh idempotent script | deploy.sh, /etc/apache2/sites-available/optical-dev.oliver.solutions.conf |
| 2026-04-17 | Deployment setup | deploy.sh creation, Apache VirtualHost reordering, Docker build cache management | deploy.sh, /etc/apache2/sites-available/optical-dev.oliver.solutions.conf |
| 2026-04-17 | Deployment script & Apache config | Docker build caching, idempotent database init, Alembic migrations, Apache ProxyPass setup | deploy.sh, optical-dev.oliver.solutions.conf |
| 2026-04-17 | Deployment script & Apache config | Docker build caching, database migrations, Apache proxy setup | deploy.sh, optical-dev.oliver.solutions.conf |

View file

@ -158,3 +158,9 @@ tags: [daily]
- 12:49 (<1min) | `memory-compiler`
- **Asked:** Compile a new raw article into the wiki knowledge base with proper indexing.
- **Done:** Filed article as `wiki/agent-sdk/custom-tools.md` and updated both topic and master indices.
- 12:49 (<1min) | `Barclays-banner-builder`
- **Asked:** Create deployment script and app concept for Docker-based Ubuntu server with Apache reverse proxy, avoiding WebSockets.
- **Done:** Fixed Apache configuration routing issue by reordering Include directives so the app's config loads before hp-prod-tracker's catch-all rule.
- 12:50 | `memory-compiler`
- **Asked:** Compile a new article about user input approvals into the agent-sdk wiki section.
- **Done:** Created structured wiki article documenting canUseTool and AskUserQuestion with response strategies and workarounds.

View file

@ -30,7 +30,7 @@ This 3-hop pattern works for hundreds of articles without vector search.
| [[wiki/web-agency/_index\|web-agency/]] | AI-assisted website building & selling: Claude Code, Nanobanana 2, Kling, LaunchPath MCP | 1 |
| [[wiki/dotfiles/_index\|dotfiles/]] | Linux terminal ricing: Kitty, Fish, WezTerm CLI, modern Rust CLI tools, LazyVim, unified themes, Tabby | 9 |
| [[wiki/agent-sdk/_index\|agent-sdk/]] | Claude Agent SDK (formerly Claude Code SDK) — build autonomous AI agents in Python and TypeScript | 10 |
| [[wiki/agent-sdk/_index\|agent-sdk/]] | Claude Agent SDK (formerly Claude Code SDK) — build autonomous AI agents in Python and TypeScript | 11 |
| [[wiki/llm-models/_index\|llm-models/]] | OpenAI model catalog — GPT-5.x, o-series reasoning, audio/realtime, embeddings, moderation | 1 |
| [[wiki/claude-code/_index\|claude-code/]] | Claude Code product docs — install, capabilities, surfaces, MCP, hooks, scheduling, multi-agent, plugins, skills, error recovery | 7 |

View file

@ -24,3 +24,4 @@ Build production AI agents using the same tools, agent loop, and context managem
| [[wiki/agent-sdk/mcp-integration\|mcp-integration]] | MCP server setup: transport types (stdio/HTTP/SSE), allowedTools, auth, tool search, error handling | raw/Connect to external tools with MCP.md | 2026-04-17 |
| [[wiki/agent-sdk/structured-outputs\|structured-outputs]] | Return validated JSON from agent workflows using JSON Schema, Zod, or Pydantic; error subtypes, tips | raw/Get structured output from agents.md | 2026-04-17 |
| [[wiki/agent-sdk/custom-tools\|custom-tools]] | Define custom tools with @tool/@tool(), in-process MCP server, error handling, images, resources, annotations | raw/Give Claude custom tools.md | 2026-04-17 |
| [[wiki/agent-sdk/user-input-approvals\|user-input-approvals]] | canUseTool callback: tool approvals, AskUserQuestion clarifying questions, allow/deny/modify, Python streaming workaround | raw/Handle approvals and user input.md | 2026-04-17 |

View file

@ -0,0 +1,171 @@
---
title: "Handle Approvals and User Input"
aliases: [canUseTool, user-approvals, ask-user-question]
tags: [agent-sdk, permissions, user-input, python, typescript]
sources: [raw/Handle approvals and user input.md]
created: 2026-04-17
updated: 2026-04-17
---
# Handle Approvals and User Input
Surface Claude's approval requests and clarifying questions to users, then return their decisions back to the SDK via the `canUseTool` callback.
## When Claude Pauses for Input
Claude stops and fires `canUseTool` in two situations:
| Situation | `tool_name` value | Trigger |
|-----------|------------------|---------|
| Tool needs permission | `"Bash"`, `"Write"`, `"Edit"`, etc. | Tool not auto-approved by [[wiki/agent-sdk/configure-permissions\|permission rules]] |
| Clarifying question | `"AskUserQuestion"` | Claude needs direction before proceeding |
This is distinct from a normal conversation turn — execution is paused until your callback returns.
## canUseTool Callback Setup
```python
async def handle_tool_request(tool_name, input_data, context):
# prompt user, return allow or deny
...
options = ClaudeAgentOptions(can_use_tool=handle_tool_request)
```
**Python requirement:** streaming mode + a `PreToolUse` hook returning `{"continue_": True}` — without it, the stream closes before the callback fires.
```python
async def dummy_hook(input_data, tool_use_id, context):
return {"continue_": True}
options = ClaudeAgentOptions(
can_use_tool=can_use_tool,
hooks={"PreToolUse": [HookMatcher(matcher=None, hooks=[dummy_hook])]},
)
```
## Tool Approval Requests
### Callback arguments
| Argument | Description |
|----------|-------------|
| `tool_name` | Tool Claude wants to use (`"Bash"`, `"Write"`, `"Edit"`, `"Read"`) |
| `input_data` | Tool-specific parameters (e.g. `command`, `file_path`, `content`) |
| `context` | Suggestions (`PermissionUpdate` entries) + cancellation signal |
### Response types
| Response | Python | TypeScript |
|----------|--------|------------|
| Allow | `PermissionResultAllow(updated_input=input_data)` | `{ behavior: "allow", updatedInput }` |
| Deny | `PermissionResultDeny(message="reason")` | `{ behavior: "deny", message }` |
### Response strategies
- **Approve** — pass input unchanged
- **Approve with changes** — sanitize paths, add constraints before passing
- **Reject** — block the tool, Claude sees your message and may adjust
- **Suggest alternative** — block + guide Claude toward what user wants
- **Redirect** — use streaming input to send a new instruction entirely
## AskUserQuestion — Clarifying Questions
When Claude needs direction (common in [[wiki/agent-sdk/configure-permissions\|plan mode]]), it calls `AskUserQuestion`. Route on `tool_name == "AskUserQuestion"`.
### Input format Claude sends
```json
{
"questions": [
{
"question": "How should I format the output?",
"header": "Format",
"options": [
{ "label": "Summary", "description": "Brief overview of key points" },
{ "label": "Detailed", "description": "Full explanation with examples" }
],
"multiSelect": false
}
]
}
```
### Response format you return
```json
{
"questions": [ /* pass through original */ ],
"answers": {
"How should I format the output?": "Summary",
"Which sections should I include?": "Introduction, Conclusion"
}
}
```
- Single-select: one label
- Multi-select: labels joined with `", "`
- Free text: user's raw text (add an "Other" option in your UI)
### Option previews (TypeScript only)
Set `toolConfig.askUserQuestion.previewFormat` to `"markdown"` or `"html"` — Claude adds a `preview` field to options where a visual comparison helps. Check for `undefined` before rendering.
## Complete Python Example
```python
def parse_response(response: str, options: list) -> str:
try:
indices = [int(s.strip()) - 1 for s in response.split(",")]
labels = [options[i]["label"] for i in indices if 0 <= i < len(options)]
return ", ".join(labels) if labels else response
except ValueError:
return response
async def handle_ask_user_question(input_data: dict) -> PermissionResultAllow:
answers = {}
for q in input_data.get("questions", []):
print(f"\n{q['header']}: {q['question']}")
for i, opt in enumerate(q["options"]):
print(f" {i+1}. {opt['label']} - {opt['description']}")
response = input("Your choice: ").strip()
answers[q["question"]] = parse_response(response, q["options"])
return PermissionResultAllow(
updated_input={"questions": input_data.get("questions", []), "answers": answers}
)
async def can_use_tool(tool_name, input_data, context):
if tool_name == "AskUserQuestion":
return await handle_ask_user_question(input_data)
# display tool info, prompt y/n
response = input(f"Allow {tool_name}? (y/n): ")
if response.lower() == "y":
return PermissionResultAllow(updated_input=input_data)
return PermissionResultDeny(message="User denied this action")
```
## Limitations
- `AskUserQuestion` is **not available in subagents** spawned via the Agent tool
- Each call supports **14 questions**, each with **24 options**
## Alternatives to canUseTool
| Mechanism | Best for |
|-----------|----------|
| [[wiki/agent-sdk/hooks-guide\|Hooks]] | Auto-allow/deny without prompting; external notifications (Slack, email) via `PermissionRequest` hook |
| Streaming input | Interrupting mid-task, providing context, chat interfaces |
| [[wiki/agent-sdk/custom-tools\|Custom tools]] | Structured forms, multi-step workflows, external approval systems |
## Key Takeaways
- `canUseTool` is the single callback for both tool approvals and `AskUserQuestion` — branch on `tool_name`
- Python requires streaming mode + a `PreToolUse` dummy hook to keep the stream alive
- Deny responses include a message Claude reads — use it to redirect, not just block
- `AskUserQuestion` is especially common in plan mode; include it in your `tools` array if you define one
- You can modify tool input before allowing (sanitize, constrain) — not just pass-through
- For automation without user prompts, use [[wiki/agent-sdk/hooks-guide\|hooks]] instead of `canUseTool`
## Sources
- `raw/Handle approvals and user input.md` — official Agent SDK docs on user input handling