fix: restore pending asks after resume
This commit is contained in:
@@ -18,6 +18,8 @@ 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, synthetic `ask_user` tool-call extraction, and tolerant text fallback
|
||||
- `src/ask-payload-store.ts` — branch-aware persisted ask payload lookup
|
||||
- `src/pending-ask.ts` — active-branch unresolved tool-call detection and recovery dismissal persistence
|
||||
- `src/resume-pending-ask.ts` — detached lifecycle wiring and recovered answer delivery
|
||||
- `src/notifications.ts` — best-effort ask notification payload rendering and channel execution
|
||||
- `src/remote-ask.ts` — package-prefixed local event contract, active-flow registry, and explicit remote submission validation
|
||||
- `src/schema.ts` — TypeBox schema
|
||||
@@ -78,6 +80,9 @@ The codebase is split so the implementation reads through file boundaries and na
|
||||
- persisted ask settings are migrated to the current schema version in memory, validated, and normalized before use without rewriting the config file on load
|
||||
- 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
|
||||
- interrupted-ask recovery selects only the newest unresolved `ask_user` call on the active branch, prefers its valid persisted payload, and validates original arguments as fallback
|
||||
- recovered forms open only for TUI `startup`, `resume`, and `fork` events; the flow stays detached from the lifecycle handler
|
||||
- recovered submit and cancel both persist dismissal before the same call can reopen; manual `/ask:replay` ignores that recovery marker
|
||||
- invalid persisted keymaps fall back to default keymaps for the current session without discarding valid behaviour, notification, or answer settings
|
||||
- invalid notification channels are skipped and fall back to the default bell channel if none are valid
|
||||
- ask settings behaviour and notification enabled changes attempt to persist immediately from the settings list; save failures revert the change and show an error; config reset is guarded by a short double-press confirmation
|
||||
|
||||
@@ -213,6 +213,7 @@ 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
|
||||
- automatic recovery of the newest unresolved `ask_user` form on startup, resume, or fork
|
||||
- ask settings list with binary behaviour/notification toggles and a guarded reset-to-defaults action
|
||||
- `?` in the ask flow and `/ask-settings` in pi open the same lightweight ask settings overlay
|
||||
- settings attempt to persist immediately when changed: `Auto-submit when answered without notes`, `Confirm dismiss when dirty`, `Double-press review shortcuts`, `Notifications`, and `Show footer hints`; `Present single-select as multi-select` persists immediately when saving succeeds but applies only to new/replayed ask flows; save failures revert the setting and show a manual-edit message; resetting config to defaults requires pressing the reset action twice within a short confirmation window
|
||||
@@ -295,6 +296,16 @@ See [`remote-events.md`](remote-events.md) for payload shapes, examples, and a l
|
||||
- submitted or elaborated command-flow results are sent back with user-message semantics
|
||||
- replay commands scan only `ctx.sessionManager.getBranch()`, ignore sibling/future branch payloads, and revalidate stored payloads before opening the UI
|
||||
|
||||
## Interrupted ask resume
|
||||
|
||||
- on `session_start` with reason `startup`, `resume`, or `fork`, pi-ask finds the newest `ask_user` tool call on the active branch that has neither a tool result nor an `ask:pending-dismissed` entry
|
||||
- recovery does not run for `new`, `reload`, or non-TUI sessions
|
||||
- the matching valid `ask:payload` supplies the form; if it is missing or invalid, pi-ask validates and uses the original tool call arguments instead
|
||||
- the recovery flow is detached from `session_start`, so an open form does not block other lifecycle handlers
|
||||
- because the interrupted `execute` promise no longer exists, submit sends the result with the same user-message semantics as replay commands
|
||||
- submit and cancel both append `ask:pending-dismissed`, which prevents another automatic reopen; `/ask:replay` still works
|
||||
- recovered flows emit remote lifecycle events with source `ask:resume`
|
||||
|
||||
The fallback message includes normalized pending questions and options so the caller can re-ask them manually. `details.questions` still contains normalized question metadata, while `details.answers` stays empty until a user responds.
|
||||
|
||||
## Skill alignment (advisory)
|
||||
|
||||
@@ -25,14 +25,14 @@ type PiAskStartedEvent = {
|
||||
version: 1;
|
||||
flowId: string;
|
||||
toolCallId?: string;
|
||||
source: "tool" | "answer" | "answer:again" | "ask:replay";
|
||||
source: "tool" | "answer" | "answer:again" | "ask:replay" | "ask:resume";
|
||||
title?: string;
|
||||
questions: AskQuestion[];
|
||||
createdAt: number;
|
||||
};
|
||||
```
|
||||
|
||||
Use `flowId` for submit/correlation. Use `questions[].id` and `questions[].options[].value` for answers.
|
||||
Use `flowId` for submit/correlation. Use `questions[].id` and `questions[].options[].value` for answers. `ask:resume` identifies a form recovered from an interrupted tool call.
|
||||
|
||||
## Submit an answer
|
||||
|
||||
@@ -111,7 +111,7 @@ type PiAskCompletedEvent = {
|
||||
version: 1;
|
||||
flowId: string;
|
||||
toolCallId?: string;
|
||||
source: "tool" | "answer" | "answer:again" | "ask:replay";
|
||||
source: "tool" | "answer" | "answer:again" | "ask:replay" | "ask:resume";
|
||||
result: AskResult;
|
||||
completedAt: number;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user