feat(sessions): deliver ask_user answers

Integrate the pending-ask store into the session service so an open ask is
visible, observable, and closable.

- `statusFromSession` projects `pendingAsk`, so a browser rehydrates an open
  ask from `GET /sessions/:sessionId/status` after reload or a web/API restart.
- `openAsk` publishes `ask.opened`, and publishes `ask.closed` first when the
  new ask supersedes an unanswered one.
- `submitAsk` / `cancelAsk` close the ask and hand the outcome to the model as
  a `pi-web.ask.answers` follow-up custom message (`triggerTurn`,
  `deliverAs: "followUp"`), the same delivery subsession notices use. A stale
  ask id is reported, not thrown: losing the race against a supersede or
  another browser is ordinary. Cancel still reports every question as
  unanswered so the model is not left waiting for a promised message.
- The open ask is forgotten when its runtime closes; nothing is left to
  receive the answers.
- `POST /sessions/:sessionId/ask/{submit,cancel}` behind the existing
  `/api/sessions/*` daemon proxy, allowlisted for machine federation.
This commit is contained in:
Federico Jaramillo Martinez
2026-07-26 22:38:15 +02:00
parent 07bdd7ad6b
commit 51ebfe4c00
7 changed files with 451 additions and 12 deletions
+4
View File
@@ -1,4 +1,6 @@
import type {
AskUserCloseResponse,
AskUserSubmission,
SavedPromptAttachment,
SessionBulkArchiveResponse,
SessionBulkDeleteArchivedResponse,
@@ -51,6 +53,8 @@ export interface SessionRouteService {
dismissNotification(ref: SessionRouteRef, request: Omit<SessionNotificationDismissRequest, "cwd">): SessionNotificationInboxSnapshot | Promise<SessionNotificationInboxSnapshot>;
dismissAllNotifications(ref: SessionRouteRef, request: Omit<SessionNotificationDismissAllRequest, "cwd">): SessionNotificationInboxSnapshot | Promise<SessionNotificationInboxSnapshot>;
clearQueue(ref: SessionRouteLookup): Promise<ClientSessionStatus>;
submitAsk(ref: SessionRouteLookup, askId: string, submission: AskUserSubmission): Promise<AskUserCloseResponse>;
cancelAsk(ref: SessionRouteLookup, askId: string): Promise<AskUserCloseResponse>;
dismissWarning(ref: SessionRouteLookup, dismissId: string): Promise<ClientSessionStatus>;
availableModels(ref: SessionRouteLookup): Promise<ClientSessionModel[]>;
setModel(ref: SessionRouteLookup, provider: string, modelId: string): Promise<ClientSessionStatus>;