feat: add ask notifications

This commit is contained in:
Erik Kotsiuba
2026-05-06 12:44:37 +02:00
parent 8c02565a5f
commit 43cee4c0f0
15 changed files with 480 additions and 79 deletions
+3 -1
View File
@@ -18,6 +18,7 @@ The codebase is split so the implementation reads through file boundaries and na
- `src/answer-commands.ts` — `/answer`, `/answer:again`, and `/ask:replay` command wiring
- `src/answer-extraction.ts` — configured extraction model selection and raw-JSON extraction retries
- `src/ask-payload-store.ts` — branch-aware persisted ask payload lookup
- `src/notifications.ts` — best-effort ask notification payload rendering and channel execution
- `src/schema.ts` — TypeBox schema
- `src/types.ts` — shared types
@@ -76,7 +77,8 @@ The codebase is split so the implementation reads through file boundaries and na
- config schema migrations preserve user-provided values and add new defaults only when fields are absent
- replay payload lookup scans only the current session branch and revalidates payloads before use
- invalid persisted keymaps fall back to default keymaps for the current session without discarding valid behaviour settings
- ask settings behaviour changes persist immediately from the settings list
- invalid notification channels are skipped and fall back to the default bell channel if none are valid
- ask settings behaviour and notification enabled changes persist immediately from the settings list
- when the ask config file is missing, the first ask use writes a default persisted config snapshot under `~/.pi/agent/extensions/`
- legacy root config files move into `~/.pi/agent/extensions/` only when the current config file is absent
- live config updates can affect an in-progress ask flow immediately
+65 -3
View File
@@ -28,7 +28,7 @@ Unsupported future versions or invalid files are backed up and defaults are load
```json
{
"schemaVersion": 2,
"schemaVersion": 3,
"answer": {
"extractionModels": [
{ "provider": "openai-codex", "id": "gpt-5.4-mini" },
@@ -51,6 +51,10 @@ Unsupported future versions or invalid files are backed up and defaults are load
"confirm": "enter",
"optionNote": "n",
"questionNote": "shift+n"
},
"notifications": {
"enabled": true,
"channels": ["bell"]
}
}
```
@@ -107,6 +111,52 @@ These settings affect only the `/answer` command. Normal `ask_user` tool calls d
- default: `true`
- effect: when disabled, the ask flow hides the footer keymap hints
## Notifications
Notifications are best-effort external alerts emitted once per ask session, when the ask UI opens and is waiting for input.
### `notifications.enabled`
- type: boolean
- default: `true`
- effect: enables or disables external ask notifications
- settings UI: this is the only notification field toggled by `/ask-settings` or `?` in the ask flow
### `notifications.channels`
- type: array
- default: `["bell"]`
- supported channels:
- `"bell"` writes BEL (`\u0007`)
- `"osc9"` writes an OSC 9 terminal notification
- `"osc777"` writes an OSC 777 title/body notification
- `{ "type": "command", "command": string }` runs a shell command
- effect: channels run in order; failures are ignored and never fail the ask flow
- invalid channels are skipped; if none are valid, pi-ask falls back to `["bell"]`
Notification text:
```txt
Question waiting: <label or prompt>
```
Command channels receive these environment variables:
```sh
ASK_NOTIFY_EVENT=question.waiting
ASK_NOTIFY_TITLE="pi ask"
ASK_NOTIFY_MESSAGE="Question waiting: <label or prompt>"
```
cmux example:
```json
{
"type": "command",
"command": "cmux notify --title \"$ASK_NOTIFY_TITLE\" --body \"$ASK_NOTIFY_MESSAGE\""
}
```
## Keymaps
`keymaps` must contain all 6 configurable actions if present.
@@ -216,7 +266,7 @@ Invalid keymaps include:
```json
{
"schemaVersion": 2,
"schemaVersion": 3,
"answer": {
"extractionRetries": 1,
"extractionTimeoutMs": 30000,
@@ -237,6 +287,15 @@ Invalid keymaps include:
"confirm": "ctrl+k",
"optionNote": "x",
"questionNote": "shift+x"
},
"notifications": {
"enabled": true,
"channels": [
{
"type": "command",
"command": "cmux notify --title \"$ASK_NOTIFY_TITLE\" --body \"$ASK_NOTIFY_MESSAGE\""
}
]
}
}
```
@@ -246,9 +305,12 @@ Invalid keymaps include:
When editing this config for a user:
- preserve unrelated fields
- keep `schemaVersion` at `2`
- keep `schemaVersion` at `3`
- preserve `answer.extractionModels` as explicit provider/id pairs
- keep `answer.extractionRetries` between `0` and `3`
- do not assign reserved bindings to configurable actions
- do not create duplicate configurable bindings
- preserve existing `notifications.channels` unless the user asks to change notification targets
- only toggle `notifications.enabled` unless the user asks to configure channels
- use a `cmux notify` command channel when the user asks for cmux notifications
- after changing the file, tell the user to run `/reload` or restart pi
+7 -3
View File
@@ -206,9 +206,9 @@ This document defines the stable external behavior. It does not explain internal
- `/answer` extraction may use an internal `freeform: true` option for open-ended questions with no explicit choices; these render as user-input-only questions with the label `Type your answer:`, no numbered option row, and no selection caret; this marker is not part of the public `ask_user` tool contract
- `/answer:again` command to replay the latest `/answer`-extracted form on the current branch
- `/ask:replay` command to replay the latest real `ask_user` form on the current branch
- ask settings list with binary behaviour toggles
- ask settings list with binary behaviour and notification toggles
- `?` in the ask flow and `/ask-settings` in pi open the same lightweight ask settings overlay
- behaviour settings persist immediately when changed: `Auto-submit when answered without notes`, `Confirm dismiss when dirty`, `Double-press review shortcuts`, and `Show footer hints`
- settings persist immediately when changed: `Auto-submit when answered without notes`, `Confirm dismiss when dirty`, `Double-press review shortcuts`, `Notifications`, and `Show footer hints`
- `Keymaps` is a persisted config section for `cancel`, `dismiss`, `toggle`, `confirm`, `optionNote`, and `questionNote`
- the settings list shows the absolute config file path for changing customizable ask keymaps
- if the flow is already on the review tab, all questions are answered, and no notes exist, enabling auto-submit can complete the current ask flow immediately
@@ -245,7 +245,11 @@ If `ctx.hasUI === false`, the tool returns a `Needs user input` message in `cont
Validation is handled inside the tool so malformed calls produce the same structured error shape as other invalid payloads instead of relying on pre-execution schema failures.
The ask flow subscribes to runtime settings updates while open. In practice, this means changing `Auto-submit when answered without notes`, `Confirm dismiss when dirty`, `Double-press review shortcuts`, `Show footer hints`, or reloading config-backed keymaps can affect the in-progress ask flow immediately instead of only future asks.
The ask flow subscribes to runtime settings updates while open. In practice, this means changing `Auto-submit when answered without notes`, `Confirm dismiss when dirty`, `Double-press review shortcuts`, `Notifications`, `Show footer hints`, or reloading config-backed keymaps can affect the in-progress ask flow immediately instead of only future asks.
## Notifications
When enabled, pi-ask emits one best-effort external notification per ask session after the ask UI opens and waits for input. The default title is `pi ask`; the message is `Question waiting: <label or prompt>`. Channels run in configured order and failures never fail or cancel the ask flow.
## Slash command replay/extraction